diff options
author | Mark Brown <broonie@kernel.org> | 2019-02-26 16:22:47 +0000 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2019-02-26 16:22:47 +0000 |
commit | 3146089d235b304e665dc551472ae9cb9ab58572 (patch) | |
tree | d1a9141c302f269cb8468f0d4de78124ea5e389a | |
parent | b5e806ae4ad81de66c334d3d333fa42f960fbbd1 (diff) | |
parent | 7b6b0049e2b70d103adf1b7d0320802f70ddceca (diff) |
Merge branch 'asoc-5.1' into asoc-next
216 files changed, 31718 insertions, 3664 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,adau1977.txt b/Documentation/devicetree/bindings/sound/adi,adau1977.txt index 11acd94b1665..9225472c80b4 100644 --- a/Documentation/devicetree/bindings/sound/adi,adau1977.txt +++ b/Documentation/devicetree/bindings/sound/adi,adau1977.txt @@ -23,6 +23,12 @@ Optional properties: - DVDD-supply: supply voltage for the digital core, please consult Documentation/devicetree/bindings/regulator/regulator.txt +- adi,micbias: configures the voltage setting for the MICBIAS pin. + Select 0/1/2/3/4/5/6/7/8 to specify MICBIAS voltage + 5V/5.5V/6V/6.5V/7V/7.5V/8V/8.5V/9V + If not specified the default value will be "7" meaning 8.5 Volts. + This property is only valid for the ADAU1977 + For required properties on SPI, please consult Documentation/devicetree/bindings/spi/spi-bus.txt @@ -40,6 +46,7 @@ Examples: AVDD-supply = <®ulator>; DVDD-supply = <®ulator_digital>; + adi,micbias = <3>; reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>; }; diff --git a/Documentation/devicetree/bindings/sound/ak4458.txt b/Documentation/devicetree/bindings/sound/ak4458.txt index 7839be78448d..e5820235e0d5 100644 --- a/Documentation/devicetree/bindings/sound/ak4458.txt +++ b/Documentation/devicetree/bindings/sound/ak4458.txt @@ -4,7 +4,7 @@ This device supports I2C mode. Required properties: -- compatible : "asahi-kasei,ak4458" +- compatible : "asahi-kasei,ak4458" or "asahi-kasei,ak4497" - reg : The I2C address of the device for I2C Optional properties: diff --git a/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt deleted file mode 100644 index 62d42768a00b..000000000000 --- a/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt +++ /dev/null @@ -1,123 +0,0 @@ -Audio-Graph-SCU-Card: - -Audio-Graph-SCU-Card is "Audio-Graph-Card" + "ALSA DPCM". - -It is based on common bindings for device graphs. -see ${LINUX}/Documentation/devicetree/bindings/graph.txt - -Basically, Audio-Graph-SCU-Card property is same as -Simple-Card / Simple-SCU-Card / Audio-Graph-Card. -see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt - ${LINUX}/Documentation/devicetree/bindings/sound/simple-scu-card.txt - ${LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt - -Below are same as Simple-Card / Audio-Graph-Card. - -- label -- dai-format -- frame-master -- bitclock-master -- bitclock-inversion -- frame-inversion -- dai-tdm-slot-num -- dai-tdm-slot-width -- clocks / system-clock-frequency - -Below are same as Simple-SCU-Card. - -- convert-rate -- convert-channels -- prefix -- routing - -Required properties: - -- compatible : "audio-graph-scu-card"; -- dais : list of CPU DAI port{s} - -Example 1. Sampling Rate Conversion - - sound_card { - compatible = "audio-graph-scu-card"; - - label = "sound-card"; - prefix = "codec"; - routing = "codec Playback", "DAI0 Playback", - "DAI0 Capture", "codec Capture"; - convert-rate = <48000>; - - dais = <&cpu_port>; - }; - - audio-codec { - ... - - port { - codec_endpoint: endpoint { - remote-endpoint = <&cpu_endpoint>; - }; - }; - }; - - dai-controller { - ... - cpu_port: port { - cpu_endpoint: endpoint { - remote-endpoint = <&codec_endpoint>; - - dai-format = "left_j"; - ... - }; - }; - }; - -Example 2. 2 CPU 1 Codec (Mixing) - - sound_card { - compatible = "audio-graph-scu-card"; - - label = "sound-card"; - routing = "codec Playback", "DAI0 Playback", - "codec Playback", "DAI1 Playback", - "DAI0 Capture", "codec Capture"; - - dais = <&cpu_port0 - &cpu_port1>; - }; - - audio-codec { - ... - - audio-graph-card,prefix = "codec"; - audio-graph-card,convert-rate = <48000>; - port { - codec_endpoint0: endpoint { - remote-endpoint = <&cpu_endpoint0>; - }; - codec_endpoint1: endpoint { - remote-endpoint = <&cpu_endpoint1>; - }; - }; - }; - - dai-controller { - ... - ports { - cpu_port0: port { - cpu_endpoint0: endpoint { - remote-endpoint = <&codec_endpoint0>; - - dai-format = "left_j"; - ... - }; - }; - cpu_port1: port { - cpu_endpoint1: endpoint { - remote-endpoint = <&codec_endpoint1>; - - dai-format = "left_j"; - ... - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/cs35l36.txt b/Documentation/devicetree/bindings/sound/cs35l36.txt new file mode 100644 index 000000000000..912bd162b477 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs35l36.txt @@ -0,0 +1,168 @@ +CS35L36 Speaker Amplifier + +Required properties: + + - compatible : "cirrus,cs35l36" + + - reg : the I2C address of the device for I2C + + - VA-supply, VP-supply : power supplies for the device, + as covered in + Documentation/devicetree/bindings/regulator/regulator.txt. + + - cirrus,boost-ctl-millivolt : Boost Voltage Value. Configures the boost + converter's output voltage in mV. The range is from 2550mV to 12000mV with + increments of 50mV. + (Default) VP + + - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA. + Configures the peak current by monitoring the current through the boost FET. + Range starts at 1600mA and goes to a maximum of 4500mA with increments of + 50mA. + (Default) 4.50 Amps + + - cirrus,boost-ind-nanohenry : Inductor estimation LBST reference value. + Seeds the digital boost converter's inductor estimation block with the initial + inductance value to reference. + + 1000 = 1uH (Default) + 1200 = 1.2uH + +Optional properties: + - cirrus,multi-amp-mode : Boolean to determine if there are more than + one amplifier in the system. If more than one it is best to Hi-Z the ASP + port to prevent bus contention on the output signal + + - cirrus,boost-ctl-select : Boost conerter control source selection. + Selects the source of the BST_CTL target VBST voltage for the boost + converter to generate. + 0x00 - Control Port Value + 0x01 - Class H Tracking (Default) + 0x10 - MultiDevice Sync Value + + - cirrus,amp-pcm-inv : Boolean to determine Amplifier will invert incoming + PCM data + + - cirrus,imon-pol-inv : Boolean to determine Amplifier will invert the + polarity of outbound IMON feedback data + + - cirrus,vmon-pol-inv : Boolean to determine Amplifier will invert the + polarity of outbound VMON feedback data + + - cirrus,dcm-mode-enable : Boost converter automatic DCM Mode enable. + This enables the digital boost converter to operate in a low power + (Discontinuous Conduction) mode during low loading conditions. + + - cirrus,weak-fet-disable : Boolean : The strength of the output drivers is + reduced when operating in a Weak-FET Drive Mode and must not be used to drive + a large load. + + - cirrus,classh-wk-fet-delay : Weak-FET entry delay. Controls the delay + (in ms) before the Class H algorithm switches to the weak-FET voltage + (after the audio falls and remains below the value specified in WKFET_AMP_THLD). + + 0 = 0ms + 1 = 5ms + 2 = 10ms + 3 = 50ms + 4 = 100ms (Default) + 5 = 200ms + 6 = 500ms + 7 = 1000ms + + - cirrus,classh-weak-fet-thld-millivolt : Weak-FET amplifier drive threshold. + Configures the signal threshold at which the PWM output stage enters + weak-FET operation. The range is 50mV to 700mV in 50mV increments. + + - cirrus,temp-warn-threshold : Amplifier overtemperature warning threshold. + Configures the threshold at which the overtemperature warning condition occurs. + When the threshold is met, the overtemperature warning attenuation is applied + and the TEMP_WARN_EINT interrupt status bit is set. + If TEMP_WARN_MASK = 0, INTb is asserted. + + 0 = 105C + 1 = 115C + 2 = 125C (Default) + 3 = 135C + + - cirrus,irq-drive-select : Selects the driver type of the selected interrupt + output. + + 0 = Open-drain + 1 = Push-pull (Default) + + - cirrus,irq-gpio-select : Selects the pin to serve as the programmable + interrupt output. + + 0 = PDM_DATA / SWIRE_SD / INT (Default) + 1 = GPIO + +Optional properties for the "cirrus,vpbr-config" Sub-node + + - cirrus,vpbr-en : VBST brownout prevention enable. Configures whether the + VBST brownout prevention algorithm is enabled or disabled. + + 0 = VBST brownout prevention disabled (default) + 1 = VBST brownout prevention enabled + + See Section 7.31.1 VPBR Config for configuration options & further details + + - cirrus,vpbr-thld : Initial VPBR threshold. Configures the VP brownout + threshold voltage + + - cirrus,cirrus,vpbr-atk-rate : Attenuation attack step rate. Configures the + amount delay between consecutive volume attenuation steps when a brownout + condition is present and the VP brownout condition is in an attacking state. + + - cirrus,vpbr-atk-vol : VP brownout prevention step size. Configures the VP + brownout prevention attacking attenuation step size when operating in either + digital volume or analog gain modes. + + - cirrus,vpbr-max-attn : Maximum attenuation that the VP brownout prevention + can apply to the audio signal. + + - cirrus,vpbr-wait : Configures the delay time between a brownout condition + no longer being present and the VP brownout prevention entering an attenuation + release state. + + - cirrus,vpbr-rel-rate : Attenuation release step rate. Configures the delay + between consecutive volume attenuation release steps when a brownout condition + is not longer present and the VP brownout is in an attenuation release state. + + - cirrus,vpbr-mute-en : During the attack state, if the vpbr-max-attn value + is reached, the error condition still remains, and this bit is set, the audio + is muted. + +Example: + +cs35l36: cs35l36@40 { + compatible = "cirrus,cs35l36"; + reg = <0x40>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + reset-gpios = <&gpio0 54 0>; + interrupt-parent = <&gpio8>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + + cirrus,boost-ind-nanohenry = <1000>; + cirrus,boost-ctl-millivolt = <10000>; + cirrus,boost-peak-milliamp = <4500>; + cirrus,boost-ctl-select = <0x00>; + cirrus,weak-fet-delay = <0x04>; + cirrus,weak-fet-thld = <0x01>; + cirrus,temp-warn-threshold = <0x01>; + cirrus,multi-amp-mode; + cirrus,irq-drive-select = <0x01>; + cirrus,irq-gpio-select = <0x01>; + + cirrus,vpbr-config { + cirrus,vpbr-en = <0x00>; + cirrus,vpbr-thld = <0x05>; + cirrus,vpbr-atk-rate = <0x02>; + cirrus,vpbr-atk-vol = <0x01>; + cirrus,vpbr-max-attn = <0x09>; + cirrus,vpbr-wait = <0x01>; + cirrus,vpbr-rel-rate = <0x05>; + cirrus,vpbr-mute-en = <0x00>; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/cs4341.txt b/Documentation/devicetree/bindings/sound/cs4341.txt new file mode 100644 index 000000000000..12b4aa8ef0db --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs4341.txt @@ -0,0 +1,22 @@ +Cirrus Logic CS4341 audio DAC + +This device supports both I2C and SPI (configured with pin strapping +on the board). + +Required properties: + - compatible: "cirrus,cs4341a" + - reg : the I2C address of the device for I2C, the chip select + number for SPI. + +For required properties on I2C-bus, please consult +Documentation/devicetree/bindings/i2c/i2c.txt +For required properties on SPI-bus, please consult +Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: + codec: cs4341@0 { + #sound-dai-cells = <0>; + compatible = "cirrus,cs4341a"; + reg = <0>; + spi-max-frequency = <6000000>; + }; diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index b279b6072bd5..a58f79f5345c 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -45,6 +45,23 @@ Optional properties: - fck_parent : Should contain a valid clock name which will be used as parent for the McASP fck +Optional GPIO support: +If any McASP pin need to be used as GPIO then the McASP node must have: +... + gpio-controller + #gpio-cells = <2>; +... + +When requesting a GPIO, the first parameter is the PIN index in McASP_P* +registers. +For example to request the AXR2 pin of mcasp8: +function-gpios = <&mcasp8 2 0>; + +Or to request the ACLKR pin of mcasp8: +function-gpios = <&mcasp8 29 0>; + +For generic gpio information, please refer to bindings/gpio/gpio.txt + Example: mcasp0: mcasp0@1d00000 { diff --git a/Documentation/devicetree/bindings/sound/fsl,micfil.txt b/Documentation/devicetree/bindings/sound/fsl,micfil.txt new file mode 100644 index 000000000000..53e227b15277 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,micfil.txt @@ -0,0 +1,32 @@ +NXP MICFIL Digital Audio Interface (MICFIL). + +The MICFIL digital interface provides a 16-bit audio signal from a PDM +microphone bitstream in a configurable output sampling rate. + +Required properties: + + - compatible : Compatible list, contains "fsl,imx8mm-micfil" + + - reg : Offset and length of the register set for the device. + + - interrupts : Contains the micfil interrupts. + + - clocks : Must contain an entry for each entry in clock-names. + + - clock-names : Must include the "ipg_clk" for register access and + "ipg_clk_app" for internal micfil clock. + + - dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. + +Example: +micfil: micfil@30080000 { + compatible = "fsl,imx8mm-micfil"; + reg = <0x0 0x30080000 0x0 0x10000>; + interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk IMX8MM_CLK_PDM_IPG>, + <&clk IMX8MM_CLK_PDM_ROOT>; + clock-names = "ipg_clk", "ipg_clk_app"; + dmas = <&sdma2 24 26 0x80000000>; +}; diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt new file mode 100644 index 000000000000..1084f7f22eea --- /dev/null +++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt @@ -0,0 +1,26 @@ +* Audio codec controlled by ChromeOS EC + +Google's ChromeOS EC codec is a digital mic codec provided by the +Embedded Controller (EC) and is controlled via a host-command interface. + +An EC codec node should only be found as a sub-node of the EC node (see +Documentation/devicetree/bindings/mfd/cros-ec.txt). + +Required properties: +- compatible: Must contain "google,cros-ec-codec" +- #sound-dai-cells: Should be 1. The cell specifies number of DAIs. +- max-dmic-gain: A number for maximum gain in dB on digital microphone. + +Example: + +cros-ec@0 { + compatible = "google,cros-ec-spi"; + + ... + + cros_ec_codec: ec-codec { + compatible = "google,cros-ec-codec"; + #sound-dai-cells = <1>; + max-dmic-gain = <43>; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/ingenic,jz4725b-codec.txt b/Documentation/devicetree/bindings/sound/ingenic,jz4725b-codec.txt new file mode 100644 index 000000000000..05adc0d47b13 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ingenic,jz4725b-codec.txt @@ -0,0 +1,20 @@ +Ingenic JZ4725B codec controller + +Required properties: +- compatible : "ingenic,jz4725b-codec" +- reg : codec registers location and length +- clocks : phandle to the AIC clock. +- clock-names: must be set to "aic". +- #sound-dai-cells: Must be set to 0. + +Example: + +codec: audio-codec@100200a4 { + compatible = "ingenic,jz4725b-codec"; + reg = <0x100200a4 0x8>; + + #sound-dai-cells = <0>; + + clocks = <&cgu JZ4725B_CLK_AIC>; + clock-names = "aic"; +}; diff --git a/Documentation/devicetree/bindings/sound/ingenic,jz4740-codec.txt b/Documentation/devicetree/bindings/sound/ingenic,jz4740-codec.txt new file mode 100644 index 000000000000..1ffcade87e7b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ingenic,jz4740-codec.txt @@ -0,0 +1,20 @@ +Ingenic JZ4740 codec controller + +Required properties: +- compatible : "ingenic,jz4740-codec" +- reg : codec registers location and length +- clocks : phandle to the AIC clock. +- clock-names: must be set to "aic". +- #sound-dai-cells: Must be set to 0. + +Example: + +codec: audio-codec@10020080 { + compatible = "ingenic,jz4740-codec"; + reg = <0x10020080 0x8>; + + #sound-dai-cells = <0>; + + clocks = <&cgu JZ4740_CLK_AIC>; + clock-names = "aic"; +}; diff --git a/Documentation/devicetree/bindings/sound/mt6358.txt b/Documentation/devicetree/bindings/sound/mt6358.txt new file mode 100644 index 000000000000..5465730013a1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt6358.txt @@ -0,0 +1,18 @@ +Mediatek MT6358 Audio Codec + +The communication between MT6358 and SoC is through Mediatek PMIC wrapper. +For more detail, please visit Mediatek PMIC wrapper documentation. + +Must be a child node of PMIC wrapper. + +Required properties: + +- compatible : "mediatek,mt6358-sound". +- Avdd-supply : power source of AVDD + +Example: + +mt6358_snd { + compatible = "mediatek,mt6358-sound"; + Avdd-supply = <&mt6358_vaud28_reg>; +}; diff --git a/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt new file mode 100644 index 000000000000..396ba38619f6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt @@ -0,0 +1,36 @@ +Mediatek AFE PCM controller for mt8183 + +Required properties: +- compatible = "mediatek,mt68183-audio"; +- reg: register location and size +- interrupts: should contain AFE interrupt +- power-domains: should define the power domain +- clocks: Must contain an entry for each entry in clock-names +- clock-names: should have these clock names: + "infra_sys_audio_clk", + "mtkaif_26m_clk", + "top_mux_audio", + "top_mux_aud_intbus", + "top_sys_pll3_d4", + "top_clk26m_clk"; + +Example: + + afe: mt8183-afe-pcm@11220000 { + compatible = "mediatek,mt8183-audio"; + reg = <0 0x11220000 0 0x1000>; + interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_LOW>; + power-domains = <&scpsys MT8183_POWER_DOMAIN_AUDIO>; + clocks = <&infrasys CLK_INFRA_AUDIO>, + <&infrasys CLK_INFRA_AUDIO_26M_BCLK>, + <&topckgen CLK_TOP_MUX_AUDIO>, + <&topckgen CLK_TOP_MUX_AUD_INTBUS>, + <&topckgen CLK_TOP_SYSPLL_D2_D4>, + <&clk26m>; + clock-names = "infra_sys_audio_clk", + "mtkaif_26m_clk", + "top_mux_audio", + "top_mux_aud_intbus", + "top_sys_pll_d2_d4", + "top_clk26m_clk"; + }; diff --git a/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt b/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt new file mode 100644 index 000000000000..679e44839b48 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt @@ -0,0 +1,24 @@ +Mediatek ALSA BT SCO CVSD/MSBC Driver + +Required properties: +- compatible = "mediatek,mtk-btcvsd-snd"; +- reg: register location and size of PKV and SRAM_BANK2 +- interrupts: should contain BTSCO interrupt +- mediatek,infracfg: the phandles of INFRASYS +- mediatek,offset: Array contains of register offset and mask + infra_misc_offset, + infra_conn_bt_cvsd_mask, + cvsd_mcu_read_offset, + cvsd_mcu_write_offset, + cvsd_packet_indicator_offset + +Example: + + mtk-btcvsd-snd@18000000 { + compatible = "mediatek,mtk-btcvsd-snd"; + reg=<0 0x18000000 0 0x1000>, + <0 0x18080000 0 0x8000>; + interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_LOW>; + mediatek,infracfg = <&infrasys>; + mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt index fdcea3d12ee5..e7d17dda55db 100644 --- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt @@ -30,6 +30,7 @@ Required properties - vdd-cdc-io-supply: phandle to VDD_CDC_IO regulator DT node. - vdd-cdc-tx-rx-cx-supply: phandle to VDD_CDC_TX/RX/CX regulator DT node. - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node. + Optional Properties: - qcom,mbhc-vthreshold-low: Array of 5 threshold voltages in mV for 5 buttons detection on headset when the mbhc is powered up @@ -92,9 +93,9 @@ spmi_bus { "cdc_ear_cnp_int", "cdc_hphr_cnp_int", "cdc_hphl_cnp_int"; - VDD-CDC-IO-supply = <&pm8916_l5>; - VDD-CDC-TX-RX-CX-supply = <&pm8916_l5>; - VDD-MICBIAS-supply = <&pm8916_l13>; + vdd-cdc-io-supply = <&pm8916_l5>; + vdd-cdc-tx-rx-cx-supply = <&pm8916_l5>; + vdd-micbias-supply = <&pm8916_l13>; #sound-dai-cells = <1>; }; }; diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd9335.txt b/Documentation/devicetree/bindings/sound/qcom,wcd9335.txt index 1d8d49e30af7..5d6ea66a863f 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wcd9335.txt +++ b/Documentation/devicetree/bindings/sound/qcom,wcd9335.txt @@ -34,12 +34,12 @@ Required properties with SLIMbus Interface: Definition: Interrupt names of WCD INTR1 and INTR2 Should be: "intr1", "intr2" -- reset-gpio: +- reset-gpios: Usage: required Value type: <String Array> Definition: Reset gpio line -- qcom,ifd: +- slim-ifc-dev: Usage: required Value type: <phandle> Definition: SLIM interface device @@ -104,13 +104,13 @@ Required properties with SLIMbus Interface: Value type: <u32> Definition: Must be 1 -codec@1{ +audio-codec@1{ compatible = "slim217,1a0"; reg = <1 0>; interrupts = <&msmgpio 54 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "intr2" - reset-gpio = <&msmgpio 64 0>; - qcom,ifd = <&wc9335_ifd>; + reset-gpios = <&msmgpio 64 0>; + slim-ifc-dev = <&wc9335_ifd>; clock-names = "mclk", "native"; clocks = <&rpmcc RPM_SMD_DIV_CLK1>, <&rpmcc RPM_SMD_BB_CLK1>; diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt new file mode 100644 index 000000000000..2469588c7ccb --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt @@ -0,0 +1,23 @@ +* Rockchip Rk3328 internal codec + +Required properties: + +- compatible: "rockchip,rk3328-codec" +- reg: physical base address of the controller and length of memory mapped + region. +- rockchip,grf: the phandle of the syscon node for GRF register. +- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names. +- clock-names: should be "pclk". +- spk-depop-time-ms: speak depop time msec. + +Example for rk3328 internal codec: + +codec: codec@ff410000 { + compatible = "rockchip,rk3328-codec"; + reg = <0x0 0xff410000 0x0 0x1000>; + rockchip,grf = <&grf>; + clocks = <&cru PCLK_ACODEC>; + clock-names = "pclk"; + spk-depop-time-ms = 100; + status = "disabled"; +}; diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt index 9c58f724396a..9d9ff5184939 100644 --- a/Documentation/devicetree/bindings/sound/sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/sgtl5000.txt @@ -37,6 +37,15 @@ VDDIO 1.8V 2.5V 3.3V 2 = 3.33 mA 5.74 mA 8.03 mA 3 = 4.99 mA 8.61 mA 12.05 mA +- sclk-strength: the SCLK pad strength. Possible values are: +0, 1, 2 and 3 as per the table below: + +VDDIO 1.8V 2.5V 3.3V +0 = Disable +1 = 1.66 mA 2.87 mA 4.02 mA +2 = 3.33 mA 5.74 mA 8.03 mA +3 = 4.99 mA 8.61 mA 12.05 mA + Example: sgtl5000: codec@a { diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt deleted file mode 100644 index 3a2f71616cda..000000000000 --- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt +++ /dev/null @@ -1,94 +0,0 @@ -ASoC Simple SCU Sound Card - -Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM". -For example, you can use this driver if you want to exchange sampling rate convert, -Mixing, etc... - -Required properties: - -- compatible : "simple-scu-audio-card" - "renesas,rsrc-card" -Optional properties: - -- simple-audio-card,name : see simple-audio-card.txt -- simple-audio-card,cpu : see simple-audio-card.txt -- simple-audio-card,codec : see simple-audio-card.txt - -Optional subnode properties: - -- simple-audio-card,format : see simple-audio-card.txt -- simple-audio-card,frame-master : see simple-audio-card.txt -- simple-audio-card,bitclock-master : see simple-audio-card.txt -- simple-audio-card,bitclock-inversion : see simple-audio-card.txt -- simple-audio-card,frame-inversion : see simple-audio-card.txt -- simple-audio-card,convert-rate : platform specified sampling rate convert -- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch) -- simple-audio-card,prefix : see routing -- simple-audio-card,widgets : Please refer to widgets.txt. -- simple-audio-card,routing : A list of the connections between audio components. - Each entry is a pair of strings, the first being the connection's sink, - the second being the connection's source. Valid names for sources. - use audio-prefix if some components is using same sink/sources naming. - it can be used if compatible was "renesas,rsrc-card"; - -Required CPU/CODEC subnodes properties: - -- sound-dai : see simple-audio-card.txt - -Optional CPU/CODEC subnodes properties: - -- clocks / system-clock-frequency : see simple-audio-card.txt - -Example 1. Sampling Rate Conversion - -sound { - compatible = "simple-scu-audio-card"; - - simple-audio-card,name = "rsnd-ak4643"; - simple-audio-card,format = "left_j"; - simple-audio-card,bitclock-master = <&sndcodec>; - simple-audio-card,frame-master = <&sndcodec>; - - simple-audio-card,convert-rate = <48000>; - - simple-audio-card,prefix = "ak4642"; - simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", - "DAI0 Capture", "ak4642 Capture"; - - sndcpu: simple-audio-card,cpu { - sound-dai = <&rcar_sound>; - }; - - sndcodec: simple-audio-card,codec { - sound-dai = <&ak4643>; - system-clock-frequency = <11289600>; - }; -}; - -Example 2. 2 CPU 1 Codec (Mixing) - -sound { - compatible = "simple-scu-audio-card"; - - simple-audio-card,name = "rsnd-ak4643"; - simple-audio-card,format = "left_j"; - simple-audio-card,bitclock-master = <&dpcmcpu>; - simple-audio-card,frame-master = <&dpcmcpu>; - - simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", - "ak4642 Playback", "DAI1 Playback"; - - dpcmcpu: cpu@0 { - sound-dai = <&rcar_sound 0>; - }; - - cpu@1 { - sound-dai = <&rcar_sound 1>; - }; - - codec { - prefix = "ak4642"; - sound-dai = <&ak4643>; - clocks = <&audio_clock>; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/sprd-pcm.txt b/Documentation/devicetree/bindings/sound/sprd-pcm.txt new file mode 100644 index 000000000000..4b23e84b2e57 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/sprd-pcm.txt @@ -0,0 +1,23 @@ +* Spreadtrum DMA platfrom bindings + +Required properties: +- compatible: Should be "sprd,pcm-platform". +- dmas: Specify the list of DMA controller phandle and DMA request line ordered pairs. +- dma-names: Identifier string for each DMA request line in the dmas property. + These strings correspond 1:1 with the ordered pairs in dmas. + +Example: + + audio_platform:platform@0 { + compatible = "sprd,pcm-platform"; + dmas = <&agcp_dma 1 1>, <&agcp_dma 2 2>, + <&agcp_dma 3 3>, <&agcp_dma 4 4>, + <&agcp_dma 5 5>, <&agcp_dma 6 6>, + <&agcp_dma 7 7>, <&agcp_dma 8 8>, + <&agcp_dma 9 9>, <&agcp_dma 10 10>; + dma-names = "normal_p_l", "normal_p_r", + "normal_c_l", "normal_c_r", + "voice_c", "fast_p", + "loop_c", "loop_p", + "voip_c", "voip_p"; + }; diff --git a/Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt b/Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt new file mode 100644 index 000000000000..cbc93c8f4963 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt @@ -0,0 +1,29 @@ +Device-Tree bindings for Xilinx PL audio formatter + +The IP core supports DMA, data formatting(AES<->PCM conversion) +of audio samples. + +Required properties: + - compatible: "xlnx,audio-formatter-1.0" + - interrupt-names: Names specified to list of interrupts in same + order mentioned under "interrupts". + List of supported interrupt names are: + "irq_mm2s" : interrupt from MM2S block + "irq_s2mm" : interrupt from S2MM block + - interrupts-parent: Phandle for interrupt controller. + - interrupts: List of Interrupt numbers. + - reg: Base address and size of the IP core instance. + - clock-names: List of input clocks. + Required elements: "s_axi_lite_aclk", "aud_mclk" + - clocks: Input clock specifier. Refer to common clock bindings. + +Example: + audio_ss_0_audio_formatter_0: audio_formatter@80010000 { + compatible = "xlnx,audio-formatter-1.0"; + interrupt-names = "irq_mm2s", "irq_s2mm"; + interrupt-parent = <&gic>; + interrupts = <0 104 4>, <0 105 4>; + reg = <0x0 0x80010000 0x0 0x1000>; + clock-names = "s_axi_lite_aclk", "aud_mclk"; + clocks = <&clk 71>, <&clk_wiz_1 0>; + }; diff --git a/Documentation/devicetree/bindings/sound/xlnx,spdif.txt b/Documentation/devicetree/bindings/sound/xlnx,spdif.txt new file mode 100644 index 000000000000..15c2d64d247c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/xlnx,spdif.txt @@ -0,0 +1,28 @@ +Device-Tree bindings for Xilinx SPDIF IP + +The IP supports playback and capture of SPDIF audio + +Required properties: + - compatible: "xlnx,spdif-2.0" + - clock-names: List of input clocks. + Required elements: "s_axi_aclk", "aud_clk_i" + - clocks: Input clock specifier. Refer to common clock bindings. + - reg: Base address and address length of the IP core instance. + - interrupts-parent: Phandle for interrupt controller. + - interrupts: List of Interrupt numbers. + - xlnx,spdif-mode: 0 :- receiver mode + 1 :- transmitter mode + - xlnx,aud_clk_i: input audio clock value. + +Example: + spdif_0: spdif@80010000 { + clock-names = "aud_clk_i", "s_axi_aclk"; + clocks = <&misc_clk_0>, <&clk 71>; + compatible = "xlnx,spdif-2.0"; + interrupt-names = "spdif_interrupt"; + interrupt-parent = <&gic>; + interrupts = <0 91 4>; + reg = <0x0 0x80010000 0x0 0x10000>; + xlnx,spdif-mode = <1>; + xlnx,aud_clk_i = <49152913>; + }; diff --git a/Documentation/sound/soc/dpcm.rst b/Documentation/sound/soc/dpcm.rst index f6845b2278ea..77f67ded53de 100644 --- a/Documentation/sound/soc/dpcm.rst +++ b/Documentation/sound/soc/dpcm.rst @@ -13,7 +13,7 @@ drivers that expose several ALSA PCMs and can route to multiple DAIs. The DPCM runtime routing is determined by the ALSA mixer settings in the same way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM graph representing the DSP internal audio paths and uses the mixer settings to -determine the patch used by each ALSA PCM. +determine the path used by each ALSA PCM. DPCM re-uses all the existing component codec, platform and DAI drivers without any modifications. @@ -101,7 +101,7 @@ The audio driver processes this as follows :- 4. Machine driver or audio HAL enables the speaker path. -5. DPCM runs the PCM ops for startup(), hw_params(), prepapre() and +5. DPCM runs the PCM ops for startup(), hw_params(), prepare() and trigger(start) for DAI1 Speakers since the path is enabled. In this example, the machine driver or userspace audio HAL can alter the routing @@ -221,7 +221,7 @@ like a BT phone call :- This allows the host CPU to sleep while the DSP, MODEM DAI and the BT DAI are still in operation. -A BE DAI link can also set the codec to a dummy device if the code is a device +A BE DAI link can also set the codec to a dummy device if the codec is a device that is managed externally. Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the @@ -249,7 +249,7 @@ configuration. struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - /* The DSP will covert the FE rate to 48k, stereo */ + /* The DSP will convert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; channels->min = channels->max = 2; @@ -386,5 +386,3 @@ This means creating a new FE that is connected with a virtual path to both DAI links. The DAI links will be started when the FE PCM is started and stopped when the FE PCM is stopped. Note that the FE PCM cannot read or write data in this configuration. - - diff --git a/MAINTAINERS b/MAINTAINERS index dce5c099f43c..24b9e3578d3a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3699,6 +3699,14 @@ N: cros_ec N: cros-ec F: drivers/power/supply/cros_usbpd-charger.c +CHROMEOS EC CODEC DRIVER +M: Cheng-Yi Chiang <cychiang@chromium.org> +S: Maintained +R: Enric Balletbo i Serra <enric.balletbo@collabora.com> +R: Guenter Roeck <groeck@chromium.org> +F: Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt +F: sound/soc/codecs/cros_ec_codec.* + CIRRUS LOGIC AUDIO CODEC DRIVERS M: Brian Austin <brian.austin@cirrus.com> M: Paul Handrigan <Paul.Handrigan@cirrus.com> diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 259cf6ab969b..4d291b75cb9f 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -530,17 +530,24 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) if (ares->type != ACPI_RESOURCE_TYPE_GPIO) return 1; - if (lookup->n++ == lookup->index && !lookup->desc) { + if (!lookup->desc) { const struct acpi_resource_gpio *agpio = &ares->data.gpio; - int pin_index = lookup->pin_index; + bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; + int pin_index; + if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) + lookup->index++; + + if (lookup->n++ != lookup->index) + return 1; + + pin_index = lookup->pin_index; if (pin_index >= agpio->pin_table_length) return 1; lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr, agpio->pin_table[pin_index]); - lookup->info.gpioint = - agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; + lookup->info.gpioint = gpioint; /* * Polarity and triggering are only specified for GpioInt diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 87715f20b69a..03b4c4f225d0 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1014,6 +1014,13 @@ struct acpi_gpio_mapping { /* Ignore IoRestriction field */ #define ACPI_GPIO_QUIRK_NO_IO_RESTRICTION BIT(0) +/* + * When ACPI GPIO mapping table is in use the index parameter inside it + * refers to the GPIO resource in _CRS method. That index has no + * distinction of actual type of the resource. When consumer wants to + * get GpioIo type explicitly, this quirk may be used. + */ +#define ACPI_GPIO_QUIRK_ONLY_GPIOIO BIT(1) unsigned int quirks; }; diff --git a/include/sound/cs35l36.h b/include/sound/cs35l36.h new file mode 100644 index 000000000000..8f8049d390f0 --- /dev/null +++ b/include/sound/cs35l36.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/sound/cs35l36.h -- Platform data for CS35L36 + * + * Copyright 2018 Cirrus Logic, Inc. + * + * Author: James Schulman <james.schulman@cirrus.com> + * + */ + +#ifndef __CS35L36_H +#define __CS35L36_H + +struct cs35l36_vpbr_cfg { + bool is_present; + bool vpbr_en; + int vpbr_thld; + int vpbr_atk_rate; + int vpbr_atk_vol; + int vpbr_max_attn; + int vpbr_wait; + int vpbr_rel_rate; + int vpbr_mute_en; +}; + +struct cs35l36_platform_data { + bool multi_amp_mode; + bool dcm_mode; + bool amp_pcm_inv; + bool imon_pol_inv; + bool vmon_pol_inv; + int boost_ind; + int bst_vctl; + int bst_vctl_sel; + int bst_ipk; + bool extern_boost; + int temp_warn_thld; + int irq_drv_sel; + int irq_gpio_sel; + struct cs35l36_vpbr_cfg vpbr_config; +}; + +#endif /* __CS35L36_H */ diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 2c4cfaa135a6..c679f6116580 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -99,10 +99,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data( * playback. */ #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3) -/* - * The PCM streams have custom channel names specified. - */ -#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4) /** * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 6d69ed2bd7b1..7afe45389972 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -75,7 +75,7 @@ void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai); &dai_link->codec_dai_name, \ list_name, cells_name, NULL) #define asoc_simple_card_parse_platform(node, dai_link, list_name, cells_name) \ - asoc_simple_card_parse_dai(node, dai_link->platform, \ + asoc_simple_card_parse_dai(node, dai_link->platforms, \ &dai_link->platform_of_node, \ NULL, list_name, cells_name, NULL) int asoc_simple_card_parse_dai(struct device_node *node, @@ -108,7 +108,7 @@ int asoc_simple_card_parse_graph_dai(struct device_node *ep, int asoc_simple_card_init_dai(struct snd_soc_dai *dai, struct asoc_simple_dai *simple_dai); -int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link); +void asoc_simple_card_canonicalize_platform(struct snd_soc_dai_link *dai_link); void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, int is_single_links); diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 266e64e3c24c..35b38e41e5b2 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -22,20 +22,37 @@ struct snd_soc_acpi_package_context { #define SND_ACPI_I2C_ID_LEN (4 + ACPI_ID_LEN + 3 + 1) #if IS_ENABLED(CONFIG_ACPI) +/* acpi match */ +struct snd_soc_acpi_mach * +snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines); + bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], struct snd_soc_acpi_package_context *ctx); + +/* check all codecs */ +struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg); + #else +/* acpi match */ +static inline struct snd_soc_acpi_mach * +snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines) +{ + return NULL; +} + static inline bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], struct snd_soc_acpi_package_context *ctx) { return false; } -#endif -/* acpi match */ -struct snd_soc_acpi_mach * -snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines); +/* check all codecs */ +static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) +{ + return NULL; +} +#endif /** * snd_soc_acpi_mach_params: interface for machine driver configuration @@ -69,9 +86,6 @@ struct snd_soc_acpi_mach_params { * is not constant since this field may be updated at run-time * @sof_fw_filename: Sound Open Firmware file name, if enabled * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled - * @asoc_plat_name: ASoC platform name, used for binding machine drivers - * if non NULL - * @new_mach_data: machine driver private data fixup */ /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { @@ -85,8 +99,6 @@ struct snd_soc_acpi_mach { struct snd_soc_acpi_mach_params mach_params; const char *sof_fw_filename; const char *sof_tplg_filename; - const char *asoc_plat_name; - struct platform_device * (*new_mach_data)(void *pdata); }; #define SND_SOC_ACPI_MAX_CODECS 3 @@ -105,7 +117,4 @@ struct snd_soc_acpi_codecs { u8 codecs[SND_SOC_ACPI_MAX_CODECS][ACPI_ID_LEN]; }; -/* check all codecs */ -struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg); - #endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index bd8163f151cb..c00a0b8ade08 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -214,21 +214,21 @@ struct device; .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} /* stream domain */ -#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \ +#define SND_SOC_DAPM_AIF_IN(wname, stname, wchan, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } -#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \ + .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } +#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wchan, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } -#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \ +#define SND_SOC_DAPM_AIF_OUT(wname, stname, wchan, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } -#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \ + .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } +#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wchan, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \ { .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ @@ -407,6 +407,10 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); +int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + /* dapm path setup */ int snd_soc_dapm_new_widgets(struct snd_soc_card *card); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); @@ -519,6 +523,9 @@ enum snd_soc_dapm_type { snd_soc_dapm_asrc, /* DSP/CODEC ASRC component */ snd_soc_dapm_encoder, /* FW/SW audio encoder component */ snd_soc_dapm_decoder, /* FW/SW audio decoder component */ + + /* Don't edit below this line */ + SND_SOC_DAPM_TYPE_COUNT }; enum snd_soc_dapm_subclass { @@ -540,6 +547,8 @@ struct snd_soc_dapm_route { /* Note: currently only supported for links where source is a supply */ int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink); + + struct snd_soc_dobj dobj; }; /* dapm audio path between two widgets */ @@ -625,6 +634,8 @@ struct snd_soc_dapm_widget { int endpoints[2]; struct clk *clk; + + int channel; }; struct snd_soc_dapm_update { diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index fa4b8413d2e2..5223896de26f 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -38,12 +38,14 @@ struct snd_soc_dapm_route; enum snd_soc_dobj_type { SND_SOC_DOBJ_NONE = 0, /* object is not dynamic */ SND_SOC_DOBJ_MIXER, - SND_SOC_DOBJ_ENUM, SND_SOC_DOBJ_BYTES, - SND_SOC_DOBJ_PCM, + SND_SOC_DOBJ_ENUM, + SND_SOC_DOBJ_GRAPH, + SND_SOC_DOBJ_WIDGET, SND_SOC_DOBJ_DAI_LINK, + SND_SOC_DOBJ_PCM, SND_SOC_DOBJ_CODEC_LINK, - SND_SOC_DOBJ_WIDGET, + SND_SOC_DOBJ_BACKEND_LINK, }; /* dynamic control object */ diff --git a/include/sound/soc.h b/include/sound/soc.h index e665f111b0d2..eb7db605955b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -802,6 +802,9 @@ struct snd_soc_component_driver { int probe_order; int remove_order; + /* signal if the module handling the component cannot be removed */ + unsigned int ignore_module_refcount:1; + /* bits */ unsigned int idle_bias_on:1; unsigned int suspend_bias_off:1; @@ -891,6 +894,18 @@ struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ const char *stream_name; /* Stream name */ + + /* + * cpu_name + * cpu_of_node + * cpu_dai_name + * + * These are legacy style, and will be replaced to + * modern style (= snd_soc_dai_link_component) in the future, + * but, not yet supported so far. + * If modern style was supported for CPU, all driver will switch + * to use it, and, legacy style code will be removed from ALSA SoC. + */ /* * You MAY specify the link's CPU-side device, either by device name, * or by DT/OF node, but not both. If this information is omitted, @@ -906,6 +921,19 @@ struct snd_soc_dai_link { * only, which only works well when that device exposes a single DAI. */ const char *cpu_dai_name; + + /* + * codec_name + * codec_of_node + * codec_dai_name + * + * These are legacy style, it will be converted to modern style + * (= snd_soc_dai_link_component) automatically in soc-core + * if driver is using legacy style. + * Driver shouldn't use both legacy and modern style in the same time. + * If modern style was supported for CPU, all driver will switch + * to use it, and, legacy style code will be removed from ALSA SoC. + */ /* * You MUST specify the link's codec, either by device name, or by * DT/OF node, but not both. @@ -919,13 +947,25 @@ struct snd_soc_dai_link { unsigned int num_codecs; /* + * platform_name + * platform_of_node + * + * These are legacy style, it will be converted to modern style + * (= snd_soc_dai_link_component) automatically in soc-core + * if driver is using legacy style. + * Driver shouldn't use both legacy and modern style in the same time. + * If modern style was supported for CPU, all driver will switch + * to use it, and, legacy style code will be removed from ALSA SoC. + */ + /* * You MAY specify the link's platform/PCM/DMA driver, either by * device name, or by DT/OF node, but not both. Some forms of link * do not need a platform. */ const char *platform_name; struct device_node *platform_of_node; - struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link_component *platforms; + unsigned int num_platforms; int id; /* optional ID for machine driver link identification */ @@ -1543,6 +1583,37 @@ struct snd_soc_dai *snd_soc_card_get_codec_dai(struct snd_soc_card *card, return NULL; } +static inline +int snd_soc_fixup_dai_links_platform_name(struct snd_soc_card *card, + const char *platform_name) +{ + struct snd_soc_dai_link *dai_link; + const char *name; + int i; + + if (!platform_name) /* nothing to do */ + return 0; + + /* set platform name for each dailink */ + for_each_card_prelinks(card, i, dai_link) { + name = devm_kstrdup(card->dev, platform_name, GFP_KERNEL); + if (!name) + return -ENOMEM; + + if (dai_link->platforms) + /* only single platform is supported for now */ + dai_link->platforms->name = name; + else + /* + * legacy mode, this case will be removed when all + * derivers are switched to modern style dai_link. + */ + dai_link->platform_name = name; + } + + return 0; +} + #ifdef CONFIG_DEBUG_FS extern struct dentry *snd_soc_debugfs_root; #endif diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 6592a422a047..aa35940f5c50 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -64,6 +64,7 @@ source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" source "sound/soc/spear/Kconfig" +source "sound/soc/sprd/Kconfig" source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 48c48c1c893c..974fb9821e17 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ obj-$(CONFIG_SND_SOC) += spear/ +obj-$(CONFIG_SND_SOC) += sprd/ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 62bdb7e333b8..419114edfd57 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -50,10 +50,12 @@ config SND_SOC_ALL_CODECS select SND_SOC_BT_SCO select SND_SOC_BD28623 select SND_SOC_CQ0093VC + select SND_SOC_CROS_EC_CODEC if MFD_CROS_EC select SND_SOC_CS35L32 if I2C select SND_SOC_CS35L33 if I2C select SND_SOC_CS35L34 if I2C select SND_SOC_CS35L35 if I2C + select SND_SOC_CS35L36 if I2C select SND_SOC_CS42L42 if I2C select SND_SOC_CS42L51_I2C if I2C select SND_SOC_CS42L52 if I2C && INPUT @@ -65,6 +67,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271_SPI if SPI_MASTER select SND_SOC_CS42XX8_I2C if I2C select SND_SOC_CS43130 if I2C + select SND_SOC_CS4341 if SND_SOC_I2C_AND_SPI select SND_SOC_CS4349 if I2C select SND_SOC_CS47L24 if MFD_CS47L24 select SND_SOC_CS53L30 if I2C @@ -88,6 +91,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_INNO_RK3036 select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC + select SND_SOC_JZ4725B_CODEC select SND_SOC_LM4857 if I2C select SND_SOC_LM49453 if I2C select SND_SOC_MAX98088 if I2C @@ -109,6 +113,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C select SND_SOC_MT6351 if MTK_PMIC_WRAP + select SND_SOC_MT6358 if MTK_PMIC_WRAP select SND_SOC_NAU8540 if I2C select SND_SOC_NAU8810 if I2C select SND_SOC_NAU8822 if I2C @@ -129,6 +134,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM5102A select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER + select SND_SOC_RK3328 select SND_SOC_RT274 if I2C select SND_SOC_RT286 if I2C select SND_SOC_RT298 if I2C @@ -185,6 +191,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TWL6040 if TWL6040_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C + select SND_SOC_WCD9335 if SLIMBUS select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WM0010 if SPI_MASTER select SND_SOC_WM1250_EV1 if I2C @@ -455,6 +462,13 @@ config SND_SOC_CPCAP config SND_SOC_CQ0093VC tristate +config SND_SOC_CROS_EC_CODEC + tristate "codec driver for ChromeOS EC" + depends on MFD_CROS_EC + help + If you say yes here you will get support for the + ChromeOS Embedded Controller's Audio Codec. + config SND_SOC_CS35L32 tristate "Cirrus Logic CS35L32 CODEC" depends on I2C @@ -471,6 +485,10 @@ config SND_SOC_CS35L35 tristate "Cirrus Logic CS35L35 CODEC" depends on I2C +config SND_SOC_CS35L36 + tristate "Cirrus Logic CS35L36 CODEC" + depends on I2C + config SND_SOC_CS42L42 tristate "Cirrus Logic CS42L42 CODEC" depends on I2C @@ -542,6 +560,12 @@ config SND_SOC_CS43130 tristate "Cirrus Logic CS43130 CODEC" depends on I2C +config SND_SOC_CS4341 + tristate "Cirrus Logic CS4341 CODEC" + depends on SND_SOC_I2C_AND_SPI + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + # Cirrus Logic CS4349 HiFi DAC config SND_SOC_CS4349 tristate "Cirrus Logic CS4349 CODEC" @@ -560,8 +584,26 @@ config SND_SOC_CX20442 depends on TTY config SND_SOC_JZ4740_CODEC + depends on MIPS || COMPILE_TEST select REGMAP_MMIO - tristate + tristate "Ingenic JZ4740 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4740 SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4740-codec. + +config SND_SOC_JZ4725B_CODEC + depends on MIPS || COMPILE_TEST + select REGMAP + tristate "Ingenic JZ4725B internal CODEC" + help + Enable support for the internal CODEC found in the JZ4725B SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4725b-codec. config SND_SOC_L3 tristate @@ -698,6 +740,7 @@ config SND_SOC_MSM8916_WCD_ANALOG config SND_SOC_MSM8916_WCD_DIGITAL tristate "Qualcomm MSM8916 WCD DIGITAL Codec" + select REGMAP_MMIO config SND_SOC_PCM1681 tristate "Texas Instruments PCM1681 CODEC" @@ -799,6 +842,10 @@ config SND_SOC_PCM512x_SPI select SND_SOC_PCM512x select REGMAP_SPI +config SND_SOC_RK3328 + tristate "Rockchip RK3328 audio CODEC" + select REGMAP_MMIO + config SND_SOC_RL6231 tristate default y if SND_SOC_RT5514=y @@ -1100,6 +1147,15 @@ config SND_SOC_UDA1380 tristate depends on I2C +config SND_SOC_WCD9335 + tristate "WCD9335 Codec" + depends on SLIMBUS + select REGMAP_SLIMBUS + help + The WCD9335 is a standalone Hi-Fi audio CODEC IC, supports + Qualcomm Technologies, Inc. (QTI) multimedia solutions, + including the MSM8996, MSM8976, and MSM8956 chipsets. + config SND_SOC_WL1273 tristate @@ -1211,7 +1267,8 @@ config SND_SOC_WM8903 depends on I2C config SND_SOC_WM8904 - tristate + tristate "Wolfson Microelectronics WM8904 CODEC" + depends on I2C config SND_SOC_WM8940 tristate @@ -1325,6 +1382,12 @@ config SND_SOC_ML26124 config SND_SOC_MT6351 tristate "MediaTek MT6351 Codec" +config SND_SOC_MT6358 + tristate "MediaTek MT6358 Codec" + help + Enable support for the platform which uses MT6358 as + external codec device. + config SND_SOC_NAU8540 tristate "Nuvoton Technology Corporation NAU85L40 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 66f55d185620..aab2ad95a137 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -42,10 +42,12 @@ snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o +snd-soc-cros-ec-codec-objs := cros_ec_codec.o snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o snd-soc-cs35l35-objs := cs35l35.o +snd-soc-cs35l36-objs := cs35l36.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o @@ -60,6 +62,7 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o snd-soc-cs42xx8-objs := cs42xx8.o snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o snd-soc-cs43130-objs := cs43130.o +snd-soc-cs4341-objs := cs4341.o snd-soc-cs4349-objs := cs4349.o snd-soc-cs47l24-objs := cs47l24.o snd-soc-cs53l30-objs := cs53l30.o @@ -84,6 +87,7 @@ snd-soc-ics43432-objs := ics43432.o snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o +snd-soc-jz4725b-codec-objs := jz4725b.o snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o @@ -106,6 +110,7 @@ snd-soc-ml26124-objs := ml26124.o snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o snd-soc-mt6351-objs := mt6351.o +snd-soc-mt6358-objs := mt6358.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o snd-soc-nau8822-objs := nau8822.o @@ -132,6 +137,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o +snd-soc-rk3328-objs := rk3328_codec.o snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt1305-objs := rt1305.o @@ -198,6 +204,7 @@ 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-wcd9335-objs := wcd-clsh-v2.o wcd9335.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -308,10 +315,12 @@ obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o +obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o +obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o @@ -326,6 +335,7 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o +obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o @@ -350,6 +360,7 @@ obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o +obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o @@ -372,6 +383,7 @@ obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o +obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o @@ -398,6 +410,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o +obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o @@ -463,6 +476,7 @@ 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_WCD9335) += snd-soc-wcd9335.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 4b60ebee491d..96d7cb2e4a56 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -37,6 +37,13 @@ static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1, static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0); +static const unsigned int ad193x_sb[] = {32}; + +static struct snd_pcm_hw_constraint_list constr = { + .list = ad193x_sb, + .count = ARRAY_SIZE(ad193x_sb), +}; + static const struct snd_kcontrol_new ad193x_snd_controls[] = { /* DAC volume control */ SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL, @@ -93,6 +100,15 @@ static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = { SND_SOC_DAPM_INPUT("ADC2IN"), }; +static int ad193x_check_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + + return !!ad193x->sysclk; +} + static const struct snd_soc_dapm_route audio_paths[] = { { "DAC", NULL, "SYSCLK" }, { "DAC Output", NULL, "DAC" }, @@ -101,7 +117,7 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "DAC2OUT", NULL, "DAC Output" }, { "DAC3OUT", NULL, "DAC Output" }, { "DAC4OUT", NULL, "DAC Output" }, - { "SYSCLK", NULL, "PLL_PWR" }, + { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll }, }; static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { @@ -181,23 +197,26 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, { struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(codec_dai->component); unsigned int adc_serfmt = 0; + unsigned int dac_serfmt = 0; unsigned int adc_fmt = 0; unsigned int dac_fmt = 0; /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S - * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) + * with TDM), ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) and DAC I2S mode + * (SND_SOC_DAIFMT_I2S) */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: adc_serfmt |= AD193X_ADC_SERFMT_TDM; + dac_serfmt |= AD193X_DAC_SERFMT_STEREO; break; case SND_SOC_DAIFMT_DSP_A: adc_serfmt |= AD193X_ADC_SERFMT_AUX; + dac_serfmt |= AD193X_DAC_SERFMT_TDM; break; default: if (ad193x_has_adc(ad193x)) return -EINVAL; - break; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -221,6 +240,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } + /* For DSP_*, LRCLK's polarity must be inverted */ + if (fmt & SND_SOC_DAIFMT_DSP_A) { + change_bit(ffs(AD193X_DAC_LEFT_HIGH) - 1, + (unsigned long *)&dac_fmt); + } + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ adc_fmt |= AD193X_ADC_LCR_MASTER; @@ -248,6 +273,8 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, AD193X_ADC_FMT_MASK, adc_fmt); } + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, + AD193X_DAC_SERFMT_MASK, dac_serfmt); regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_FMT_MASK, dac_fmt); @@ -258,7 +285,22 @@ static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + + if (clk_id == AD193X_SYSCLK_MCLK) { + /* MCLK must be 512 x fs */ + if (dir == SND_SOC_CLOCK_OUT || freq != 24576000) + return -EINVAL; + + regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL1, + AD193X_PLL_SRC_MASK, + AD193X_PLL_DAC_SRC_MCLK | + AD193X_PLL_CLK_SRC_MCLK); + + snd_soc_dapm_sync(dapm); + return 0; + } switch (freq) { case 12288000: case 18432000: @@ -321,7 +363,16 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, return 0; } +static int ad193x_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &constr); +} + static const struct snd_soc_dai_ops ad193x_dai_ops = { + .startup = ad193x_startup, .hw_params = ad193x_hw_params, .digital_mute = ad193x_mute, .set_tdm_slot = ad193x_set_tdm_slot, @@ -351,6 +402,20 @@ static struct snd_soc_dai_driver ad193x_dai = { .ops = &ad193x_dai_ops, }; +/* codec DAI instance for DAC only */ +static struct snd_soc_dai_driver ad193x_no_adc_dai = { + .name = "ad193x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ad193x_dai_ops, +}; + static int ad193x_component_probe(struct snd_soc_component *component) { struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); @@ -444,8 +509,11 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, ad193x); + if (ad193x_has_adc(ad193x)) + return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, + &ad193x_dai, 1); return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, - &ad193x_dai, 1); + &ad193x_no_adc_dai, 1); } EXPORT_SYMBOL_GPL(ad193x_probe); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h index 8b1e65f928d2..27d6afbd7dfb 100644 --- a/sound/soc/codecs/ad193x.h +++ b/sound/soc/codecs/ad193x.h @@ -31,6 +31,11 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, #define AD193X_PLL_INPUT_512 (2 << 1) #define AD193X_PLL_INPUT_768 (3 << 1) #define AD193X_PLL_CLK_CTRL1 0x01 +#define AD193X_PLL_SRC_MASK 0x03 +#define AD193X_PLL_DAC_SRC_PLL 0 +#define AD193X_PLL_DAC_SRC_MCLK 1 +#define AD193X_PLL_CLK_SRC_PLL (0 << 1) +#define AD193X_PLL_CLK_SRC_MCLK (1 << 1) #define AD193X_DAC_CTRL0 0x02 #define AD193X_DAC_POWERDOWN 0x01 #define AD193X_DAC_SERFMT_MASK 0xC0 @@ -96,4 +101,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, #define AD193X_NUM_REGS 17 +#define AD193X_SYSCLK_PLL 0 +#define AD193X_SYSCLK_MCLK 1 + #endif diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 116af6a9ce3b..11c53bcb71dd 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -885,13 +885,15 @@ static int adau1977_setup_micbias(struct adau1977 *adau1977) struct adau1977_platform_data *pdata = adau1977->dev->platform_data; unsigned int micbias; - if (pdata) { + if (pdata) micbias = pdata->micbias; - if (micbias > ADAU1977_MICBIAS_9V0) - return -EINVAL; - - } else { + else if (device_property_read_u32(adau1977->dev, "adi,micbias", + &micbias)) micbias = ADAU1977_MICBIAS_8V5; + + if (micbias > ADAU1977_MICBIAS_9V0) { + dev_err(adau1977->dev, "Invalid value for 'adi,micbias'\n"); + return -EINVAL; } return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS, diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c index fdff86878287..a8deb37fc78a 100644 --- a/sound/soc/codecs/adau7002.c +++ b/sound/soc/codecs/adau7002.c @@ -8,6 +8,7 @@ */ #include <linux/acpi.h> +#include <linux/delay.h> #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> @@ -15,12 +16,55 @@ #include <sound/soc.h> +struct adau7002_priv { + int wakeup_delay; +}; + +static int adau7002_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct adau7002_priv *adau7002 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (adau7002->wakeup_delay) + msleep(adau7002->wakeup_delay); + break; + } + + return 0; +} + +static int adau7002_component_probe(struct snd_soc_component *component) +{ + struct adau7002_priv *adau7002; + + adau7002 = devm_kzalloc(component->dev, sizeof(*adau7002), + GFP_KERNEL); + if (!adau7002) + return -ENOMEM; + + device_property_read_u32(component->dev, "wakeup-delay-ms", + &adau7002->wakeup_delay); + + snd_soc_component_set_drvdata(component, adau7002); + + return 0; +} + static const struct snd_soc_dapm_widget adau7002_widgets[] = { + SND_SOC_DAPM_AIF_OUT_E("ADAU AIF", "Capture", 0, + SND_SOC_NOPM, 0, 0, adau7002_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_INPUT("PDM_DAT"), SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0), }; static const struct snd_soc_dapm_route adau7002_routes[] = { + { "ADAU AIF", NULL, "PDM_DAT"}, { "Capture", NULL, "PDM_DAT" }, { "Capture", NULL, "IOVDD" }, }; @@ -40,6 +84,7 @@ static struct snd_soc_dai_driver adau7002_dai = { }; static const struct snd_soc_component_driver adau7002_component_driver = { + .probe = adau7002_component_probe, .dapm_widgets = adau7002_widgets, .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets), .dapm_routes = adau7002_routes, diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index 238ab29f2bf4..ce419e8cf890 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -6,6 +6,7 @@ */ #include <linux/i2c.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/of_gpio.h> diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 70d4c89bd6fc..eab7c76cfcd9 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -21,6 +21,11 @@ #include "ak4458.h" +struct ak4458_drvdata { + struct snd_soc_dai_driver *dai_drv; + const struct snd_soc_component_driver *comp_drv; +}; + /* AK4458 Codec Private Data */ struct ak4458_priv { struct device *dev; @@ -258,6 +263,33 @@ static const struct snd_soc_dapm_route ak4458_intercon[] = { {"AK4458 AOUTD", NULL, "AK4458 DAC4"}, }; +/* ak4497 controls */ +static const struct snd_kcontrol_new ak4497_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC Playback Volume", AK4458_03_LCHATT, + AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv), + SOC_ENUM("AK4497 De-emphasis Response DAC", ak4458_dac1_dem_enum), + SOC_ENUM_EXT("AK4497 Digital Filter Setting", ak4458_digfil_enum, + get_digfil, set_digfil), + SOC_ENUM("AK4497 Inverting Enable of DZFB", ak4458_dzfb_enum), + SOC_ENUM("AK4497 Sound Mode", ak4458_sm_enum), + SOC_ENUM("AK4497 Attenuation transition Time Setting", + ak4458_ats_enum), +}; + +/* ak4497 dapm widgets */ +static const struct snd_soc_dapm_widget ak4497_dapm_widgets[] = { + SND_SOC_DAPM_DAC("AK4497 DAC", NULL, AK4458_0A_CONTROL6, 2, 0), + SND_SOC_DAPM_AIF_IN("AK4497 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("AK4497 AOUT"), +}; + +/* ak4497 dapm routes */ +static const struct snd_soc_dapm_route ak4497_intercon[] = { + {"AK4497 DAC", NULL, "AK4497 SDTI"}, + {"AK4497 AOUT", NULL, "AK4497 DAC"}, + +}; + static int ak4458_rstn_control(struct snd_soc_component *component, int bit) { int ret; @@ -476,6 +508,18 @@ static struct snd_soc_dai_driver ak4458_dai = { .ops = &ak4458_dai_ops, }; +static struct snd_soc_dai_driver ak4497_dai = { + .name = "ak4497-aif", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = AK4458_FORMATS, + }, + .ops = &ak4458_dai_ops, +}; + static void ak4458_power_off(struct ak4458_priv *ak4458) { if (ak4458->reset_gpiod) { @@ -573,6 +617,21 @@ static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { .non_legacy_dai_naming = 1, }; +static const struct snd_soc_component_driver soc_codec_dev_ak4497 = { + .probe = ak4458_probe, + .remove = ak4458_remove, + .controls = ak4497_snd_controls, + .num_controls = ARRAY_SIZE(ak4497_snd_controls), + .dapm_widgets = ak4497_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets), + .dapm_routes = ak4497_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4497_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + static const struct regmap_config ak4458_regmap = { .reg_bits = 8, .val_bits = 8, @@ -583,6 +642,16 @@ static const struct regmap_config ak4458_regmap = { .cache_type = REGCACHE_RBTREE, }; +static const struct ak4458_drvdata ak4458_drvdata = { + .dai_drv = &ak4458_dai, + .comp_drv = &soc_codec_dev_ak4458, +}; + +static const struct ak4458_drvdata ak4497_drvdata = { + .dai_drv = &ak4497_dai, + .comp_drv = &soc_codec_dev_ak4497, +}; + static const struct dev_pm_ops ak4458_pm = { SET_RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, @@ -592,6 +661,7 @@ static const struct dev_pm_ops ak4458_pm = { static int ak4458_i2c_probe(struct i2c_client *i2c) { struct ak4458_priv *ak4458; + const struct ak4458_drvdata *drvdata; int ret; ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL); @@ -605,6 +675,8 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, ak4458); ak4458->dev = &i2c->dev; + drvdata = of_device_get_match_data(&i2c->dev); + ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ak4458->reset_gpiod)) @@ -615,8 +687,8 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) if (IS_ERR(ak4458->mute_gpiod)) return PTR_ERR(ak4458->mute_gpiod); - ret = devm_snd_soc_register_component(ak4458->dev, &soc_codec_dev_ak4458, - &ak4458_dai, 1); + ret = devm_snd_soc_register_component(ak4458->dev, drvdata->comp_drv, + drvdata->dai_drv, 1); if (ret < 0) { dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret); return ret; @@ -635,7 +707,8 @@ static int ak4458_i2c_remove(struct i2c_client *i2c) } static const struct of_device_id ak4458_of_match[] = { - { .compatible = "asahi-kasei,ak4458", }, + { .compatible = "asahi-kasei,ak4458", .data = &ak4458_drvdata}, + { .compatible = "asahi-kasei,ak4497", .data = &ak4497_drvdata}, { }, }; diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c new file mode 100644 index 000000000000..99a3af8a15ff --- /dev/null +++ b/sound/soc/codecs/cros_ec_codec.c @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for ChromeOS Embedded Controller codec. + * + * This driver uses the cros-ec interface to communicate with the ChromeOS + * EC for audio function. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define DRV_NAME "cros-ec-codec" + +/** + * struct cros_ec_codec_data - ChromeOS EC codec driver data. + * @dev: Device structure used in sysfs. + * @ec_device: cros_ec_device structure to talk to the physical device. + * @component: Pointer to the component. + * @max_dmic_gain: Maximum gain in dB supported by EC codec. + */ +struct cros_ec_codec_data { + struct device *dev; + struct cros_ec_device *ec_device; + struct snd_soc_component *component; + unsigned int max_dmic_gain; +}; + +static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0); + +static int ec_command_get_gain(struct snd_soc_component *component, + struct ec_param_codec_i2s *param, + struct ec_response_codec_gain *resp) +{ + struct cros_ec_codec_data *codec_data = + snd_soc_component_get_drvdata(component); + struct cros_ec_device *ec_device = codec_data->ec_device; + u8 buffer[sizeof(struct cros_ec_command) + + max(sizeof(struct ec_param_codec_i2s), + sizeof(struct ec_response_codec_gain))]; + struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; + int ret; + + msg->version = 0; + msg->command = EC_CMD_CODEC_I2S; + msg->outsize = sizeof(struct ec_param_codec_i2s); + msg->insize = sizeof(struct ec_response_codec_gain); + + memcpy(msg->data, param, msg->outsize); + + ret = cros_ec_cmd_xfer_status(ec_device, msg); + if (ret > 0) + memcpy(resp, msg->data, msg->insize); + + return ret; +} + +/* + * Wrapper for EC command without response. + */ +static int ec_command_no_resp(struct snd_soc_component *component, + struct ec_param_codec_i2s *param) +{ + struct cros_ec_codec_data *codec_data = + snd_soc_component_get_drvdata(component); + struct cros_ec_device *ec_device = codec_data->ec_device; + u8 buffer[sizeof(struct cros_ec_command) + + sizeof(struct ec_param_codec_i2s)]; + struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; + + msg->version = 0; + msg->command = EC_CMD_CODEC_I2S; + msg->outsize = sizeof(struct ec_param_codec_i2s); + msg->insize = 0; + + memcpy(msg->data, param, msg->outsize); + + return cros_ec_cmd_xfer_status(ec_device, msg); +} + +static int set_i2s_config(struct snd_soc_component *component, + enum ec_i2s_config i2s_config) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set I2S format to %u\n", __func__, + i2s_config); + + param.cmd = EC_CODEC_I2S_SET_CONFIG; + param.i2s_config = i2s_config; + + return ec_command_no_resp(component, ¶m); +} + +static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + enum ec_i2s_config i2s_config; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + i2s_config = EC_DAI_FMT_I2S; + break; + + case SND_SOC_DAIFMT_RIGHT_J: + i2s_config = EC_DAI_FMT_RIGHT_J; + break; + + case SND_SOC_DAIFMT_LEFT_J: + i2s_config = EC_DAI_FMT_LEFT_J; + break; + + case SND_SOC_DAIFMT_DSP_A: + i2s_config = EC_DAI_FMT_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + i2s_config = EC_DAI_FMT_PCM_B; + break; + + default: + return -EINVAL; + } + + return set_i2s_config(component, i2s_config); +} + +static int set_i2s_sample_depth(struct snd_soc_component *component, + enum ec_sample_depth_value depth) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth); + + param.cmd = EC_CODEC_SET_SAMPLE_DEPTH; + param.depth = depth; + + return ec_command_no_resp(component, ¶m); +} + +static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk); + + param.cmd = EC_CODEC_I2S_SET_BCLK; + param.bclk = bclk; + + return ec_command_no_resp(component, ¶m); +} + +static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int rate, bclk; + int ret; + + rate = params_rate(params); + if (rate != 48000) + return -EINVAL; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16); + break; + case SNDRV_PCM_FORMAT_S24_LE: + ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24); + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + + bclk = snd_soc_params_to_bclk(params); + return set_i2s_bclk(component, bclk); +} + +static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = { + .hw_params = cros_ec_i2s_hw_params, + .set_fmt = cros_ec_i2s_set_dai_fmt, +}; + +static struct snd_soc_dai_driver cros_ec_dai[] = { + { + .name = "cros_ec_codec I2S", + .id = 0, + .capture = { + .stream_name = "I2S Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &cros_ec_i2s_dai_ops, + } +}; + +static int get_ec_mic_gain(struct snd_soc_component *component, + u8 *left, u8 *right) +{ + struct ec_param_codec_i2s param; + struct ec_response_codec_gain resp; + int ret; + + param.cmd = EC_CODEC_GET_GAIN; + + ret = ec_command_get_gain(component, ¶m, &resp); + if (ret < 0) + return ret; + + *left = resp.left; + *right = resp.right; + + return 0; +} + +static int mic_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + u8 left, right; + int ret; + + ret = get_ec_mic_gain(component, &left, &right); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = left; + ucontrol->value.integer.value[1] = right; + + return 0; +} + +static int set_ec_mic_gain(struct snd_soc_component *component, + u8 left, u8 right) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set mic gain to %u, %u\n", + __func__, left, right); + + param.cmd = EC_CODEC_SET_GAIN; + param.gain.left = left; + param.gain.right = right; + + return ec_command_no_resp(component, ¶m); +} + +static int mic_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_data *codec_data = + snd_soc_component_get_drvdata(component); + int left = ucontrol->value.integer.value[0]; + int right = ucontrol->value.integer.value[1]; + unsigned int max_dmic_gain = codec_data->max_dmic_gain; + + if (left > max_dmic_gain || right > max_dmic_gain) + return -EINVAL; + + return set_ec_mic_gain(component, (u8)left, (u8)right); +} + +static struct snd_kcontrol_new mic_gain_control = + SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0, + mic_gain_get, mic_gain_put, ec_mic_gain_tlv); + +static int enable_i2s(struct snd_soc_component *component, int enable) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable); + + param.cmd = EC_CODEC_I2S_ENABLE; + param.i2s_enable = enable; + + return ec_command_no_resp(component, ¶m); +} + +static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(component->dev, + "%s got SND_SOC_DAPM_PRE_PMU event\n", __func__); + return enable_i2s(component, 1); + + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(component->dev, + "%s got SND_SOC_DAPM_PRE_PMD event\n", __func__); + return enable_i2s(component, 0); + } + + return 0; +} + +/* + * The goal of this DAPM route is to turn on/off I2S using EC + * host command when capture stream is started/stopped. + */ +static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC"), + + /* + * Control EC to enable/disable I2S. + */ + SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM, + 0, 0, cros_ec_i2s_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = { + { "I2STX", NULL, "DMIC" }, + { "I2STX", NULL, "I2S Enable" }, +}; + +/* + * Read maximum gain from device property and set it to mixer control. + */ +static int cros_ec_set_gain_range(struct device *dev) +{ + struct soc_mixer_control *control; + struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev); + int rc; + + rc = device_property_read_u32(dev, "max-dmic-gain", + &codec_data->max_dmic_gain); + if (rc) + return rc; + + control = (struct soc_mixer_control *) + mic_gain_control.private_value; + control->max = codec_data->max_dmic_gain; + control->platform_max = codec_data->max_dmic_gain; + + return 0; +} + +static int cros_ec_codec_probe(struct snd_soc_component *component) +{ + int rc; + + struct cros_ec_codec_data *codec_data = + snd_soc_component_get_drvdata(component); + + rc = cros_ec_set_gain_range(codec_data->dev); + if (rc) + return rc; + + return snd_soc_add_component_controls(component, &mic_gain_control, 1); +} + +static const struct snd_soc_component_driver cros_ec_component_driver = { + .probe = cros_ec_codec_probe, + .dapm_widgets = cros_ec_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cros_ec_codec_dapm_widgets), + .dapm_routes = cros_ec_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cros_ec_codec_dapm_routes), +}; + +/* + * Platform device and platform driver fro cros-ec-codec. + */ +static int cros_ec_codec_platform_probe(struct platform_device *pd) +{ + struct device *dev = &pd->dev; + struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent); + struct cros_ec_codec_data *codec_data; + + codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data), + GFP_KERNEL); + if (!codec_data) + return -ENOMEM; + + codec_data->dev = dev; + codec_data->ec_device = ec_device; + + platform_set_drvdata(pd, codec_data); + + return snd_soc_register_component(dev, &cros_ec_component_driver, + cros_ec_dai, ARRAY_SIZE(cros_ec_dai)); +} + +#ifdef CONFIG_OF +static const struct of_device_id cros_ec_codec_of_match[] = { + { .compatible = "google,cros-ec-codec" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match); +#endif + +static struct platform_driver cros_ec_codec_platform_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(cros_ec_codec_of_match), + }, + .probe = cros_ec_codec_platform_probe, +}; + +module_platform_driver(cros_ec_codec_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS EC codec driver"); +MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c new file mode 100644 index 000000000000..e9b5f76f27a8 --- /dev/null +++ b/sound/soc/codecs/cs35l36.c @@ -0,0 +1,1957 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l36.c -- CS35L36 ALSA SoC audio driver +// +// Copyright 2018 Cirrus Logic, Inc. +// +// Author: James Schulman <james.schulman@cirrus.com> + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <linux/gpio.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/cs35l36.h> +#include <linux/of_irq.h> +#include <linux/completion.h> + +#include "cs35l36.h" + +/* + * Some fields take zero as a valid value so use a high bit flag that won't + * get written to the device to mark those. + */ +#define CS35L36_VALID_PDATA 0x80000000 + +static const char * const cs35l36_supplies[] = { + "VA", + "VP", +}; + +struct cs35l36_private { + struct device *dev; + struct cs35l36_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data supplies[2]; + int num_supplies; + int clksrc; + int chip_version; + int rev_id; + int ldm_mode_sel; + struct gpio_desc *reset_gpio; +}; + +struct cs35l36_pll_config { + int freq; + int clk_cfg; + int fll_igain; +}; + +static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = { + {32768, 0x00, 0x05}, + {8000, 0x01, 0x03}, + {11025, 0x02, 0x03}, + {12000, 0x03, 0x03}, + {16000, 0x04, 0x04}, + {22050, 0x05, 0x04}, + {24000, 0x06, 0x04}, + {32000, 0x07, 0x05}, + {44100, 0x08, 0x05}, + {48000, 0x09, 0x05}, + {88200, 0x0A, 0x06}, + {96000, 0x0B, 0x06}, + {128000, 0x0C, 0x07}, + {176400, 0x0D, 0x07}, + {192000, 0x0E, 0x07}, + {256000, 0x0F, 0x08}, + {352800, 0x10, 0x08}, + {384000, 0x11, 0x08}, + {512000, 0x12, 0x09}, + {705600, 0x13, 0x09}, + {750000, 0x14, 0x09}, + {768000, 0x15, 0x09}, + {1000000, 0x16, 0x0A}, + {1024000, 0x17, 0x0A}, + {1200000, 0x18, 0x0A}, + {1411200, 0x19, 0x0A}, + {1500000, 0x1A, 0x0A}, + {1536000, 0x1B, 0x0A}, + {2000000, 0x1C, 0x0A}, + {2048000, 0x1D, 0x0A}, + {2400000, 0x1E, 0x0A}, + {2822400, 0x1F, 0x0A}, + {3000000, 0x20, 0x0A}, + {3072000, 0x21, 0x0A}, + {3200000, 0x22, 0x0A}, + {4000000, 0x23, 0x0A}, + {4096000, 0x24, 0x0A}, + {4800000, 0x25, 0x0A}, + {5644800, 0x26, 0x0A}, + {6000000, 0x27, 0x0A}, + {6144000, 0x28, 0x0A}, + {6250000, 0x29, 0x08}, + {6400000, 0x2A, 0x0A}, + {6500000, 0x2B, 0x08}, + {6750000, 0x2C, 0x09}, + {7526400, 0x2D, 0x0A}, + {8000000, 0x2E, 0x0A}, + {8192000, 0x2F, 0x0A}, + {9600000, 0x30, 0x0A}, + {11289600, 0x31, 0x0A}, + {12000000, 0x32, 0x0A}, + {12288000, 0x33, 0x0A}, + {12500000, 0x34, 0x08}, + {12800000, 0x35, 0x0A}, + {13000000, 0x36, 0x0A}, + {13500000, 0x37, 0x0A}, + {19200000, 0x38, 0x0A}, + {22579200, 0x39, 0x0A}, + {24000000, 0x3A, 0x0A}, + {24576000, 0x3B, 0x0A}, + {25000000, 0x3C, 0x0A}, + {25600000, 0x3D, 0x0A}, + {26000000, 0x3E, 0x0A}, + {27000000, 0x3F, 0x0A}, +}; + +static struct reg_default cs35l36_reg[] = { + {CS35L36_TESTKEY_CTRL, 0x00000000}, + {CS35L36_USERKEY_CTL, 0x00000000}, + {CS35L36_OTP_CTRL1, 0x00002460}, + {CS35L36_OTP_CTRL2, 0x00000000}, + {CS35L36_OTP_CTRL3, 0x00000000}, + {CS35L36_OTP_CTRL4, 0x00000000}, + {CS35L36_OTP_CTRL5, 0x00000000}, + {CS35L36_PAC_CTL1, 0x00000004}, + {CS35L36_PAC_CTL2, 0x00000000}, + {CS35L36_PAC_CTL3, 0x00000000}, + {CS35L36_PWR_CTRL1, 0x00000000}, + {CS35L36_PWR_CTRL2, 0x00003321}, + {CS35L36_PWR_CTRL3, 0x01000010}, + {CS35L36_CTRL_OVRRIDE, 0x00000002}, + {CS35L36_AMP_OUT_MUTE, 0x00000000}, + {CS35L36_OTP_TRIM_STATUS, 0x00000000}, + {CS35L36_DISCH_FILT, 0x00000000}, + {CS35L36_PROTECT_REL_ERR, 0x00000000}, + {CS35L36_PAD_INTERFACE, 0x00000038}, + {CS35L36_PLL_CLK_CTRL, 0x00000010}, + {CS35L36_GLOBAL_CLK_CTRL, 0x00000003}, + {CS35L36_ADC_CLK_CTRL, 0x00000000}, + {CS35L36_SWIRE_CLK_CTRL, 0x00000000}, + {CS35L36_SP_SCLK_CLK_CTRL, 0x00000000}, + {CS35L36_MDSYNC_EN, 0x00000000}, + {CS35L36_MDSYNC_TX_ID, 0x00000000}, + {CS35L36_MDSYNC_PWR_CTRL, 0x00000000}, + {CS35L36_MDSYNC_DATA_TX, 0x00000000}, + {CS35L36_MDSYNC_TX_STATUS, 0x00000002}, + {CS35L36_MDSYNC_RX_STATUS, 0x00000000}, + {CS35L36_MDSYNC_ERR_STATUS, 0x00000000}, + {CS35L36_BSTCVRT_VCTRL1, 0x00000000}, + {CS35L36_BSTCVRT_VCTRL2, 0x00000001}, + {CS35L36_BSTCVRT_PEAK_CUR, 0x0000004A}, + {CS35L36_BSTCVRT_SFT_RAMP, 0x00000003}, + {CS35L36_BSTCVRT_COEFF, 0x00002424}, + {CS35L36_BSTCVRT_SLOPE_LBST, 0x00005800}, + {CS35L36_BSTCVRT_SW_FREQ, 0x00010000}, + {CS35L36_BSTCVRT_DCM_CTRL, 0x00002001}, + {CS35L36_BSTCVRT_DCM_MODE_FORCE, 0x00000000}, + {CS35L36_BSTCVRT_OVERVOLT_CTRL, 0x00000130}, + {CS35L36_VPI_LIMIT_MODE, 0x00000000}, + {CS35L36_VPI_LIMIT_MINMAX, 0x00003000}, + {CS35L36_VPI_VP_THLD, 0x00101010}, + {CS35L36_VPI_TRACK_CTRL, 0x00000000}, + {CS35L36_VPI_TRIG_MODE_CTRL, 0x00000000}, + {CS35L36_VPI_TRIG_STEPS, 0x00000000}, + {CS35L36_VI_SPKMON_FILT, 0x00000003}, + {CS35L36_VI_SPKMON_GAIN, 0x00000909}, + {CS35L36_VI_SPKMON_IP_SEL, 0x00000000}, + {CS35L36_DTEMP_WARN_THLD, 0x00000002}, + {CS35L36_DTEMP_STATUS, 0x00000000}, + {CS35L36_VPVBST_FS_SEL, 0x00000001}, + {CS35L36_VPVBST_VP_CTRL, 0x000001C0}, + {CS35L36_VPVBST_VBST_CTRL, 0x000001C0}, + {CS35L36_ASP_TX_PIN_CTRL, 0x00000028}, + {CS35L36_ASP_RATE_CTRL, 0x00090000}, + {CS35L36_ASP_FORMAT, 0x00000002}, + {CS35L36_ASP_FRAME_CTRL, 0x00180018}, + {CS35L36_ASP_TX1_TX2_SLOT, 0x00010000}, + {CS35L36_ASP_TX3_TX4_SLOT, 0x00030002}, + {CS35L36_ASP_TX5_TX6_SLOT, 0x00050004}, + {CS35L36_ASP_TX7_TX8_SLOT, 0x00070006}, + {CS35L36_ASP_RX1_SLOT, 0x00000000}, + {CS35L36_ASP_RX_TX_EN, 0x00000000}, + {CS35L36_ASP_RX1_SEL, 0x00000008}, + {CS35L36_ASP_TX1_SEL, 0x00000018}, + {CS35L36_ASP_TX2_SEL, 0x00000019}, + {CS35L36_ASP_TX3_SEL, 0x00000028}, + {CS35L36_ASP_TX4_SEL, 0x00000029}, + {CS35L36_ASP_TX5_SEL, 0x00000020}, + {CS35L36_ASP_TX6_SEL, 0x00000000}, + {CS35L36_SWIRE_P1_TX1_SEL, 0x00000018}, + {CS35L36_SWIRE_P1_TX2_SEL, 0x00000019}, + {CS35L36_SWIRE_P2_TX1_SEL, 0x00000028}, + {CS35L36_SWIRE_P2_TX2_SEL, 0x00000029}, + {CS35L36_SWIRE_P2_TX3_SEL, 0x00000020}, + {CS35L36_SWIRE_DP1_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_DP2_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_DP3_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_PCM_RX_DATA, 0x00000000}, + {CS35L36_SWIRE_FS_SEL, 0x00000001}, + {CS35L36_AMP_DIG_VOL_CTRL, 0x00008000}, + {CS35L36_VPBR_CFG, 0x02AA1905}, + {CS35L36_VBBR_CFG, 0x02AA1905}, + {CS35L36_VPBR_STATUS, 0x00000000}, + {CS35L36_VBBR_STATUS, 0x00000000}, + {CS35L36_OVERTEMP_CFG, 0x00000001}, + {CS35L36_AMP_ERR_VOL, 0x00000000}, + {CS35L36_CLASSH_CFG, 0x000B0405}, + {CS35L36_CLASSH_FET_DRV_CFG, 0x00000111}, + {CS35L36_NG_CFG, 0x00000033}, + {CS35L36_AMP_GAIN_CTRL, 0x00000273}, + {CS35L36_PWM_MOD_IO_CTRL, 0x00000000}, + {CS35L36_PWM_MOD_STATUS, 0x00000000}, + {CS35L36_DAC_MSM_CFG, 0x00000000}, + {CS35L36_AMP_SLOPE_CTRL, 0x00000B00}, + {CS35L36_AMP_PDM_VOLUME, 0x00000000}, + {CS35L36_AMP_PDM_RATE_CTRL, 0x00000000}, + {CS35L36_PDM_CH_SEL, 0x00000000}, + {CS35L36_AMP_NG_CTRL, 0x0000212F}, + {CS35L36_PDM_HIGHFILT_CTRL, 0x00000000}, + {CS35L36_PAC_INT0_CTRL, 0x00000001}, + {CS35L36_PAC_INT1_CTRL, 0x00000001}, + {CS35L36_PAC_INT2_CTRL, 0x00000001}, + {CS35L36_PAC_INT3_CTRL, 0x00000001}, + {CS35L36_PAC_INT4_CTRL, 0x00000001}, + {CS35L36_PAC_INT5_CTRL, 0x00000001}, + {CS35L36_PAC_INT6_CTRL, 0x00000001}, + {CS35L36_PAC_INT7_CTRL, 0x00000001}, +}; + +static bool cs35l36_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_SW_RESET: + case CS35L36_SW_REV: + case CS35L36_HW_REV: + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_OTP_MEM30: + case CS35L36_OTP_CTRL1: + case CS35L36_OTP_CTRL2: + case CS35L36_OTP_CTRL3: + case CS35L36_OTP_CTRL4: + case CS35L36_OTP_CTRL5: + case CS35L36_PAC_CTL1: + case CS35L36_PAC_CTL2: + case CS35L36_PAC_CTL3: + case CS35L36_DEVICE_ID: + case CS35L36_FAB_ID: + case CS35L36_REV_ID: + case CS35L36_PWR_CTRL1: + case CS35L36_PWR_CTRL2: + case CS35L36_PWR_CTRL3: + case CS35L36_CTRL_OVRRIDE: + case CS35L36_AMP_OUT_MUTE: + case CS35L36_OTP_TRIM_STATUS: + case CS35L36_DISCH_FILT: + case CS35L36_PROTECT_REL_ERR: + case CS35L36_PAD_INTERFACE: + case CS35L36_PLL_CLK_CTRL: + case CS35L36_GLOBAL_CLK_CTRL: + case CS35L36_ADC_CLK_CTRL: + case CS35L36_SWIRE_CLK_CTRL: + case CS35L36_SP_SCLK_CLK_CTRL: + case CS35L36_TST_FS_MON0: + case CS35L36_MDSYNC_EN: + case CS35L36_MDSYNC_TX_ID: + case CS35L36_MDSYNC_PWR_CTRL: + case CS35L36_MDSYNC_DATA_TX: + case CS35L36_MDSYNC_TX_STATUS: + case CS35L36_MDSYNC_RX_STATUS: + case CS35L36_MDSYNC_ERR_STATUS: + case CS35L36_BSTCVRT_VCTRL1: + case CS35L36_BSTCVRT_VCTRL2: + case CS35L36_BSTCVRT_PEAK_CUR: + case CS35L36_BSTCVRT_SFT_RAMP: + case CS35L36_BSTCVRT_COEFF: + case CS35L36_BSTCVRT_SLOPE_LBST: + case CS35L36_BSTCVRT_SW_FREQ: + case CS35L36_BSTCVRT_DCM_CTRL: + case CS35L36_BSTCVRT_DCM_MODE_FORCE: + case CS35L36_BSTCVRT_OVERVOLT_CTRL: + case CS35L36_BST_TST_MANUAL: + case CS35L36_BST_ANA2_TEST: + case CS35L36_VPI_LIMIT_MODE: + case CS35L36_VPI_LIMIT_MINMAX: + case CS35L36_VPI_VP_THLD: + case CS35L36_VPI_TRACK_CTRL: + case CS35L36_VPI_TRIG_MODE_CTRL: + case CS35L36_VPI_TRIG_STEPS: + case CS35L36_VI_SPKMON_FILT: + case CS35L36_VI_SPKMON_GAIN: + case CS35L36_VI_SPKMON_IP_SEL: + case CS35L36_DTEMP_WARN_THLD: + case CS35L36_DTEMP_STATUS: + case CS35L36_VPVBST_FS_SEL: + case CS35L36_VPVBST_VP_CTRL: + case CS35L36_VPVBST_VBST_CTRL: + case CS35L36_ASP_TX_PIN_CTRL: + case CS35L36_ASP_RATE_CTRL: + case CS35L36_ASP_FORMAT: + case CS35L36_ASP_FRAME_CTRL: + case CS35L36_ASP_TX1_TX2_SLOT: + case CS35L36_ASP_TX3_TX4_SLOT: + case CS35L36_ASP_TX5_TX6_SLOT: + case CS35L36_ASP_TX7_TX8_SLOT: + case CS35L36_ASP_RX1_SLOT: + case CS35L36_ASP_RX_TX_EN: + case CS35L36_ASP_RX1_SEL: + case CS35L36_ASP_TX1_SEL: + case CS35L36_ASP_TX2_SEL: + case CS35L36_ASP_TX3_SEL: + case CS35L36_ASP_TX4_SEL: + case CS35L36_ASP_TX5_SEL: + case CS35L36_ASP_TX6_SEL: + case CS35L36_SWIRE_P1_TX1_SEL: + case CS35L36_SWIRE_P1_TX2_SEL: + case CS35L36_SWIRE_P2_TX1_SEL: + case CS35L36_SWIRE_P2_TX2_SEL: + case CS35L36_SWIRE_P2_TX3_SEL: + case CS35L36_SWIRE_DP1_FIFO_CFG: + case CS35L36_SWIRE_DP2_FIFO_CFG: + case CS35L36_SWIRE_DP3_FIFO_CFG: + case CS35L36_SWIRE_PCM_RX_DATA: + case CS35L36_SWIRE_FS_SEL: + case CS35L36_AMP_DIG_VOL_CTRL: + case CS35L36_VPBR_CFG: + case CS35L36_VBBR_CFG: + case CS35L36_VPBR_STATUS: + case CS35L36_VBBR_STATUS: + case CS35L36_OVERTEMP_CFG: + case CS35L36_AMP_ERR_VOL: + case CS35L36_CLASSH_CFG: + case CS35L36_CLASSH_FET_DRV_CFG: + case CS35L36_NG_CFG: + case CS35L36_AMP_GAIN_CTRL: + case CS35L36_PWM_MOD_IO_CTRL: + case CS35L36_PWM_MOD_STATUS: + case CS35L36_DAC_MSM_CFG: + case CS35L36_AMP_SLOPE_CTRL: + case CS35L36_AMP_PDM_VOLUME: + case CS35L36_AMP_PDM_RATE_CTRL: + case CS35L36_PDM_CH_SEL: + case CS35L36_AMP_NG_CTRL: + case CS35L36_PDM_HIGHFILT_CTRL: + case CS35L36_INT1_STATUS: + case CS35L36_INT2_STATUS: + case CS35L36_INT3_STATUS: + case CS35L36_INT4_STATUS: + case CS35L36_INT1_RAW_STATUS: + case CS35L36_INT2_RAW_STATUS: + case CS35L36_INT3_RAW_STATUS: + case CS35L36_INT4_RAW_STATUS: + case CS35L36_INT1_MASK: + case CS35L36_INT2_MASK: + case CS35L36_INT3_MASK: + case CS35L36_INT4_MASK: + case CS35L36_INT1_EDGE_LVL_CTRL: + case CS35L36_INT3_EDGE_LVL_CTRL: + case CS35L36_PAC_INT_STATUS: + case CS35L36_PAC_INT_RAW_STATUS: + case CS35L36_PAC_INT_FLUSH_CTRL: + case CS35L36_PAC_INT0_CTRL: + case CS35L36_PAC_INT1_CTRL: + case CS35L36_PAC_INT2_CTRL: + case CS35L36_PAC_INT3_CTRL: + case CS35L36_PAC_INT4_CTRL: + case CS35L36_PAC_INT5_CTRL: + case CS35L36_PAC_INT6_CTRL: + case CS35L36_PAC_INT7_CTRL: + return true; + default: + if (reg >= CS35L36_PAC_PMEM_WORD0 && + reg <= CS35L36_PAC_PMEM_WORD1023) + return true; + else + return false; + } +} + +static bool cs35l36_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_TST_FS_MON0: + return true; + default: + return false; + } +} + +static bool cs35l36_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_SW_RESET: + case CS35L36_SW_REV: + case CS35L36_HW_REV: + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_DEVICE_ID: + case CS35L36_FAB_ID: + case CS35L36_REV_ID: + case CS35L36_INT1_STATUS: + case CS35L36_INT2_STATUS: + case CS35L36_INT3_STATUS: + case CS35L36_INT4_STATUS: + case CS35L36_INT1_RAW_STATUS: + case CS35L36_INT2_RAW_STATUS: + case CS35L36_INT3_RAW_STATUS: + case CS35L36_INT4_RAW_STATUS: + case CS35L36_INT1_MASK: + case CS35L36_INT2_MASK: + case CS35L36_INT3_MASK: + case CS35L36_INT4_MASK: + case CS35L36_INT1_EDGE_LVL_CTRL: + case CS35L36_INT3_EDGE_LVL_CTRL: + case CS35L36_PAC_INT_STATUS: + case CS35L36_PAC_INT_RAW_STATUS: + case CS35L36_PAC_INT_FLUSH_CTRL: + return true; + default: + if (reg >= CS35L36_PAC_PMEM_WORD0 && + reg <= CS35L36_PAC_PMEM_WORD1023) + return true; + else + return false; + } +} + +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 25, 0); +static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1); + +static const char * const cs35l36_pcm_sftramp_text[] = { + "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"}; + +static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L36_AMP_DIG_VOL_CTRL, 0, + cs35l36_pcm_sftramp_text); + +static int cs35l36_ldm_sel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = cs35l36->ldm_mode_sel; + + return 0; +} + +static int cs35l36_ldm_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int val = (ucontrol->value.integer.value[0]) ? CS35L36_NG_AMP_EN_MASK : + 0; + + cs35l36->ldm_mode_sel = val; + + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_AMP_EN_MASK, val); + + return 0; +} + +static const struct snd_kcontrol_new cs35l36_aud_controls[] = { + SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L36_AMP_DIG_VOL_CTRL, + 3, 0x4D0, 0x390, dig_vol_tlv), + SOC_SINGLE_TLV("Analog PCM Volume", CS35L36_AMP_GAIN_CTRL, 5, 0x13, 0, + amp_gain_tlv), + SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp), + SOC_SINGLE("Amp Gain Zero-Cross Switch", CS35L36_AMP_GAIN_CTRL, + CS35L36_AMP_ZC_SHIFT, 1, 0), + SOC_SINGLE("PDM LDM Enter Ramp Switch", CS35L36_DAC_MSM_CFG, + CS35L36_PDM_LDM_ENTER_SHIFT, 1, 0), + SOC_SINGLE("PDM LDM Exit Ramp Switch", CS35L36_DAC_MSM_CFG, + CS35L36_PDM_LDM_EXIT_SHIFT, 1, 0), + SOC_SINGLE_BOOL_EXT("LDM Select Switch", 0, cs35l36_ldm_sel_get, + cs35l36_ldm_sel_put), +}; + +static int cs35l36_main_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + u32 reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1, + CS35L36_GLOBAL_EN_MASK, + 1 << CS35L36_GLOBAL_EN_SHIFT); + + usleep_range(2000, 2100); + + regmap_read(cs35l36->regmap, CS35L36_INT4_RAW_STATUS, ®); + + if (WARN_ON_ONCE(reg & CS35L36_PLL_UNLOCK_MASK)) + dev_crit(cs35l36->dev, "PLL Unlocked\n"); + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL, + CS35L36_PCM_RX_SEL_MASK, + CS35L36_PCM_RX_SEL_PCM); + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_MASK, + 0 << CS35L36_AMP_MUTE_SHIFT); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL, + CS35L36_PCM_RX_SEL_MASK, + CS35L36_PCM_RX_SEL_ZERO); + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_MASK, + 1 << CS35L36_AMP_MUTE_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1, + CS35L36_GLOBAL_EN_MASK, + 0 << CS35L36_GLOBAL_EN_SHIFT); + + usleep_range(2000, 2100); + break; + default: + dev_dbg(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + + return 0; +} + +static int cs35l36_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!cs35l36->pdata.extern_boost) + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2, + CS35L36_BST_EN_MASK, + CS35L36_BST_EN << + CS35L36_BST_EN_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + if (!cs35l36->pdata.extern_boost) + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2, + CS35L36_BST_EN_MASK, + CS35L36_BST_DIS_VP << + CS35L36_BST_EN_SHIFT); + break; + default: + dev_dbg(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + + return 0; +} + +static const char * const cs35l36_chan_text[] = { + "RX1", + "RX2", +}; + +static SOC_ENUM_SINGLE_DECL(chansel_enum, CS35L36_ASP_RX1_SLOT, 0, + cs35l36_chan_text); + +static const struct snd_kcontrol_new cs35l36_chan_mux = + SOC_DAPM_ENUM("Input Mux", chansel_enum); + +static const struct snd_kcontrol_new amp_enable_ctrl = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_SHIFT, 1, 1); + +static const struct snd_kcontrol_new boost_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const char * const asp_tx_src_text[] = { + "Zero Fill", "ASPRX1", "VMON", "IMON", "ERRVOL", "VPMON", "VBSTMON" +}; + +static const unsigned int asp_tx_src_values[] = { + 0x00, 0x08, 0x18, 0x19, 0x20, 0x28, 0x29 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx1_src_enum, CS35L36_ASP_TX1_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx1_src = + SOC_DAPM_ENUM("ASPTX1SRC", asp_tx1_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx2_src_enum, CS35L36_ASP_TX2_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx2_src = + SOC_DAPM_ENUM("ASPTX2SRC", asp_tx2_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx3_src_enum, CS35L36_ASP_TX3_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx3_src = + SOC_DAPM_ENUM("ASPTX3SRC", asp_tx3_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx4_src_enum, CS35L36_ASP_TX4_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx4_src = + SOC_DAPM_ENUM("ASPTX4SRC", asp_tx4_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx5_src_enum, CS35L36_ASP_TX5_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx5_src = + SOC_DAPM_ENUM("ASPTX5SRC", asp_tx5_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx6_src_enum, CS35L36_ASP_TX6_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx6_src = + SOC_DAPM_ENUM("ASPTX6SRC", asp_tx6_src_enum); + +static const struct snd_soc_dapm_widget cs35l36_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Channel Mux", SND_SOC_NOPM, 0, 0, &cs35l36_chan_mux), + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS35L36_ASP_RX_TX_EN, 16, 0), + + SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L36_PWR_CTRL2, 0, 0, NULL, 0, + cs35l36_main_amp_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, &_enable_ctrl), + SND_SOC_DAPM_MIXER("CLASS H", CS35L36_PWR_CTRL3, 4, 0, NULL, 0), + SND_SOC_DAPM_SWITCH_E("BOOST Enable", SND_SOC_NOPM, 0, 0, &boost_ctrl, + cs35l36_boost_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L36_ASP_RX_TX_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, CS35L36_ASP_RX_TX_EN, 1, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, CS35L36_ASP_RX_TX_EN, 2, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, CS35L36_ASP_RX_TX_EN, 3, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, CS35L36_ASP_RX_TX_EN, 4, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, CS35L36_ASP_RX_TX_EN, 5, 0), + + SND_SOC_DAPM_MUX("ASPTX1SRC", SND_SOC_NOPM, 0, 0, &asp_tx1_src), + SND_SOC_DAPM_MUX("ASPTX2SRC", SND_SOC_NOPM, 0, 0, &asp_tx2_src), + SND_SOC_DAPM_MUX("ASPTX3SRC", SND_SOC_NOPM, 0, 0, &asp_tx3_src), + SND_SOC_DAPM_MUX("ASPTX4SRC", SND_SOC_NOPM, 0, 0, &asp_tx4_src), + SND_SOC_DAPM_MUX("ASPTX5SRC", SND_SOC_NOPM, 0, 0, &asp_tx5_src), + SND_SOC_DAPM_MUX("ASPTX6SRC", SND_SOC_NOPM, 0, 0, &asp_tx6_src), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L36_PWR_CTRL2, 12, 0), + SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L36_PWR_CTRL2, 13, 0), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L36_PWR_CTRL2, 8, 0), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L36_PWR_CTRL2, 9, 0), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("VBST"), + SND_SOC_DAPM_INPUT("VSENSE"), +}; + +static const struct snd_soc_dapm_route cs35l36_audio_map[] = { + {"VPMON ADC", NULL, "VP"}, + {"VBSTMON ADC", NULL, "VBST"}, + {"IMON ADC", NULL, "VSENSE"}, + {"VMON ADC", NULL, "VSENSE"}, + + {"ASPTX1SRC", "IMON", "IMON ADC"}, + {"ASPTX1SRC", "VMON", "VMON ADC"}, + {"ASPTX1SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX1SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX2SRC", "IMON", "IMON ADC"}, + {"ASPTX2SRC", "VMON", "VMON ADC"}, + {"ASPTX2SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX2SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX3SRC", "IMON", "IMON ADC"}, + {"ASPTX3SRC", "VMON", "VMON ADC"}, + {"ASPTX3SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX3SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX4SRC", "IMON", "IMON ADC"}, + {"ASPTX4SRC", "VMON", "VMON ADC"}, + {"ASPTX4SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX4SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX5SRC", "IMON", "IMON ADC"}, + {"ASPTX5SRC", "VMON", "VMON ADC"}, + {"ASPTX5SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX5SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX6SRC", "IMON", "IMON ADC"}, + {"ASPTX6SRC", "VMON", "VMON ADC"}, + {"ASPTX6SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX6SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX1", NULL, "ASPTX1SRC"}, + {"ASPTX2", NULL, "ASPTX2SRC"}, + {"ASPTX3", NULL, "ASPTX3SRC"}, + {"ASPTX4", NULL, "ASPTX4SRC"}, + {"ASPTX5", NULL, "ASPTX5SRC"}, + {"ASPTX6", NULL, "ASPTX6SRC"}, + + {"AMP Capture", NULL, "ASPTX1"}, + {"AMP Capture", NULL, "ASPTX2"}, + {"AMP Capture", NULL, "ASPTX3"}, + {"AMP Capture", NULL, "ASPTX4"}, + {"AMP Capture", NULL, "ASPTX5"}, + {"AMP Capture", NULL, "ASPTX6"}, + + {"AMP Enable", "Switch", "AMP Playback"}, + {"SDIN", NULL, "AMP Enable"}, + {"Channel Mux", "RX1", "SDIN"}, + {"Channel Mux", "RX2", "SDIN"}, + {"BOOST Enable", "Switch", "Channel Mux"}, + {"CLASS H", NULL, "BOOST Enable"}, + {"Main AMP", NULL, "Channel Mux"}, + {"Main AMP", NULL, "CLASS H"}, + {"SPK", NULL, "Main AMP"}, +}; + +static int cs35l36_set_dai_fmt(struct snd_soc_dai *component_dai, + unsigned int fmt) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component_dai->component); + unsigned int asp_fmt, lrclk_fmt, sclk_fmt, slave_mode, clk_frc; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + slave_mode = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + slave_mode = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_MSTR_MASK, + slave_mode << CS35L36_SCLK_MSTR_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_MSTR_MASK, + slave_mode << CS35L36_LRCLK_MSTR_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + clk_frc = 1; + break; + case SND_SOC_DAIFMT_GATED: + clk_frc = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_FRC_MASK, clk_frc << + CS35L36_SCLK_FRC_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_FRC_MASK, clk_frc << + CS35L36_LRCLK_FRC_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = 0; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = 2; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + lrclk_fmt = 1; + sclk_fmt = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + lrclk_fmt = 0; + sclk_fmt = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + lrclk_fmt = 1; + sclk_fmt = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + lrclk_fmt = 0; + sclk_fmt = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_INV_MASK, + lrclk_fmt << CS35L36_LRCLK_INV_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_INV_MASK, + sclk_fmt << CS35L36_SCLK_INV_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FORMAT, + CS35L36_ASP_FMT_MASK, asp_fmt); + + return 0; +} + +struct cs35l36_global_fs_config { + int rate; + int fs_cfg; +}; + +static const struct cs35l36_global_fs_config cs35l36_fs_rates[] = { + {12000, 0x01}, + {24000, 0x02}, + {48000, 0x03}, + {96000, 0x04}, + {192000, 0x05}, + {384000, 0x06}, + {11025, 0x09}, + {22050, 0x0A}, + {44100, 0x0B}, + {88200, 0x0C}, + {176400, 0x0D}, + {8000, 0x11}, + {16000, 0x12}, + {32000, 0x13}, +}; + +static int cs35l36_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(dai->component); + unsigned int asp_width, global_fs = params_rate(params); + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l36_fs_rates); i++) { + if (global_fs == cs35l36_fs_rates[i].rate) + regmap_update_bits(cs35l36->regmap, + CS35L36_GLOBAL_CLK_CTRL, + CS35L36_GLOBAL_FS_MASK, + cs35l36_fs_rates[i].fs_cfg << + CS35L36_GLOBAL_FS_SHIFT); + } + + switch (params_width(params)) { + case 16: + asp_width = CS35L36_ASP_WIDTH_16; + break; + case 24: + asp_width = CS35L36_ASP_WIDTH_24; + break; + case 32: + asp_width = CS35L36_ASP_WIDTH_32; + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL, + CS35L36_ASP_RX_WIDTH_MASK, + asp_width << CS35L36_ASP_RX_WIDTH_SHIFT); + } else { + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL, + CS35L36_ASP_TX_WIDTH_MASK, + asp_width << CS35L36_ASP_TX_WIDTH_SHIFT); + } + + return 0; +} + +static int cs35l36_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int fs1, fs2; + + if (freq > CS35L36_FS_NOM_6MHZ) { + fs1 = CS35L36_FS1_DEFAULT_VAL; + fs2 = CS35L36_FS2_DEFAULT_VAL; + } else { + fs1 = 3 * ((CS35L36_FS_NOM_6MHZ * 4 + freq - 1) / freq) + 4; + fs2 = 5 * ((CS35L36_FS_NOM_6MHZ * 4 + freq - 1) / freq) + 4; + } + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_TST_FS_MON0, + CS35L36_FS1_WINDOW_MASK | CS35L36_FS2_WINDOW_MASK, + fs1 | (fs2 << CS35L36_FS2_WINDOW_SHIFT)); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + return 0; +} + +static const struct cs35l36_pll_config *cs35l36_get_clk_config( + struct cs35l36_private *cs35l36, int freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l36_pll_sysclk); i++) { + if (cs35l36_pll_sysclk[i].freq == freq) + return &cs35l36_pll_sysclk[i]; + } + + return NULL; +} + +static const unsigned int cs35l36_src_rates[] = { + 8000, 12000, 11025, 16000, 22050, 24000, 32000, + 44100, 48000, 88200, 96000, 176400, 192000, 384000 +}; + +static const struct snd_pcm_hw_constraint_list cs35l36_constraints = { + .count = ARRAY_SIZE(cs35l36_src_rates), + .list = cs35l36_src_rates, +}; + +static int cs35l36_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &cs35l36_constraints); + + return 0; +} + +static const struct snd_soc_dai_ops cs35l36_ops = { + .startup = cs35l36_pcm_startup, + .set_fmt = cs35l36_set_dai_fmt, + .hw_params = cs35l36_pcm_hw_params, + .set_sysclk = cs35l36_dai_set_sysclk, +}; + +static struct snd_soc_dai_driver cs35l36_dai[] = { + { + .name = "cs35l36-pcm", + .id = 0, + .playback = { + .stream_name = "AMP Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L36_RX_FORMATS, + }, + .capture = { + .stream_name = "AMP Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L36_TX_FORMATS, + }, + .ops = &cs35l36_ops, + .symmetric_rates = 1, + }, +}; + +static int cs35l36_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, + int dir) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + const struct cs35l36_pll_config *clk_cfg; + int prev_clksrc; + bool pdm_switch; + + prev_clksrc = cs35l36->clksrc; + + switch (clk_id) { + case 0: + cs35l36->clksrc = CS35L36_PLLSRC_SCLK; + break; + case 1: + cs35l36->clksrc = CS35L36_PLLSRC_LRCLK; + break; + case 2: + cs35l36->clksrc = CS35L36_PLLSRC_PDMCLK; + break; + case 3: + cs35l36->clksrc = CS35L36_PLLSRC_SELF; + break; + case 4: + cs35l36->clksrc = CS35L36_PLLSRC_MCLK; + break; + default: + return -EINVAL; + } + + clk_cfg = cs35l36_get_clk_config(cs35l36, freq); + if (clk_cfg == NULL) { + dev_err(component->dev, "Invalid CLK Config Freq: %d\n", freq); + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_OPENLOOP_MASK, + 1 << CS35L36_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_REFCLK_FREQ_MASK, + clk_cfg->clk_cfg << CS35L36_REFCLK_FREQ_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_REFCLK_EN_MASK, + 0 << CS35L36_PLL_REFCLK_EN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_CLK_SEL_MASK, + cs35l36->clksrc); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_OPENLOOP_MASK, + 0 << CS35L36_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_REFCLK_EN_MASK, + 1 << CS35L36_PLL_REFCLK_EN_SHIFT); + + if (cs35l36->rev_id == CS35L36_REV_A0) { + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_write(cs35l36->regmap, CS35L36_DCO_CTRL, 0x00036DA8); + regmap_write(cs35l36->regmap, CS35L36_MISC_CTRL, 0x0100EE0E); + + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS, + CS35L36_PLL_IGAIN_MASK, + CS35L36_PLL_IGAIN << + CS35L36_PLL_IGAIN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS, + CS35L36_PLL_FFL_IGAIN_MASK, + clk_cfg->fll_igain); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + if (cs35l36->clksrc == CS35L36_PLLSRC_PDMCLK) { + pdm_switch = cs35l36->ldm_mode_sel && + (prev_clksrc != CS35L36_PLLSRC_PDMCLK); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 0 << CS35L36_NG_DELAY_SHIFT); + + regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG, + CS35L36_PDM_MODE_MASK, + 1 << CS35L36_PDM_MODE_SHIFT); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 3 << CS35L36_NG_DELAY_SHIFT); + } else { + pdm_switch = cs35l36->ldm_mode_sel && + (prev_clksrc == CS35L36_PLLSRC_PDMCLK); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 0 << CS35L36_NG_DELAY_SHIFT); + + regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG, + CS35L36_PDM_MODE_MASK, + 0 << CS35L36_PDM_MODE_SHIFT); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 3 << CS35L36_NG_DELAY_SHIFT); + } + + return 0; +} + +static int cs35l36_boost_inductor(struct cs35l36_private *cs35l36, int inductor) +{ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF, + CS35L36_BSTCVRT_K1_MASK, 0x3C); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF, + CS35L36_BSTCVRT_K2_MASK, + 0x3C << CS35L36_BSTCVRT_K2_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SW_FREQ, + CS35L36_BSTCVRT_CCMFREQ_MASK, 0x00); + + switch (inductor) { + case 1000: /* 1 uH */ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_SLOPE_MASK, + 0x75 << CS35L36_BSTCVRT_SLOPE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_LBSTVAL_MASK, 0x00); + break; + case 1200: /* 1.2 uH */ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_SLOPE_MASK, + 0x6B << CS35L36_BSTCVRT_SLOPE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_LBSTVAL_MASK, 0x01); + break; + default: + dev_err(cs35l36->dev, "%s Invalid Inductor Value %d uH\n", + __func__, inductor); + return -EINVAL; + } + + return 0; +} + +static int cs35l36_component_probe(struct snd_soc_component *component) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int ret = 0; + + if ((cs35l36->rev_id == CS35L36_REV_A0) && cs35l36->pdata.dcm_mode) { + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_DCM_CTRL, + CS35L36_DCM_AUTO_MASK, + CS35L36_DCM_AUTO_MASK); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL, + CS35L36_BST_MAN_IPKCOMP_MASK, + 0 << CS35L36_BST_MAN_IPKCOMP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL, + CS35L36_BST_MAN_IPKCOMP_EN_MASK, + CS35L36_BST_MAN_IPKCOMP_EN_MASK); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + if (cs35l36->pdata.amp_pcm_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_DIG_VOL_CTRL, + CS35L36_AMP_PCM_INV_MASK, + CS35L36_AMP_PCM_INV_MASK); + + if (cs35l36->pdata.multi_amp_mode) + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_ASP_TX_HIZ_MASK, + CS35L36_ASP_TX_HIZ_MASK); + + if (cs35l36->pdata.imon_pol_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT, + CS35L36_IMON_POL_MASK, 0); + + if (cs35l36->pdata.vmon_pol_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT, + CS35L36_VMON_POL_MASK, 0); + + if (cs35l36->pdata.bst_vctl) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1, + CS35L35_BSTCVRT_CTL_MASK, + cs35l36->pdata.bst_vctl); + + if (cs35l36->pdata.bst_vctl_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2, + CS35L35_BSTCVRT_CTL_SEL_MASK, + cs35l36->pdata.bst_vctl_sel); + + if (cs35l36->pdata.bst_ipk) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_PEAK_CUR, + CS35L36_BST_IPK_MASK, + cs35l36->pdata.bst_ipk); + + if (cs35l36->pdata.boost_ind) { + ret = cs35l36_boost_inductor(cs35l36, cs35l36->pdata.boost_ind); + if (ret < 0) { + dev_err(cs35l36->dev, + "Boost inductor config failed(%d)\n", ret); + return ret; + } + } + + if (cs35l36->pdata.temp_warn_thld) + regmap_update_bits(cs35l36->regmap, CS35L36_DTEMP_WARN_THLD, + CS35L36_TEMP_THLD_MASK, + cs35l36->pdata.temp_warn_thld); + + if (cs35l36->pdata.irq_drv_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_DRV_SEL_MASK, + cs35l36->pdata.irq_drv_sel << + CS35L36_INT_DRV_SEL_SHIFT); + + if (cs35l36->pdata.irq_gpio_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_GPIO_SEL_MASK, + cs35l36->pdata.irq_gpio_sel << + CS35L36_INT_GPIO_SEL_SHIFT); + + /* + * Rev B0 has 2 versions + * L36 is 10V + * L37 is 12V + * If L36 we need to clamp some values for safety + * after probe has setup dt values. We want to make + * sure we dont miss any values set in probe + */ + if (cs35l36->chip_version == CS35L36_10V_L36) { + regmap_update_bits(cs35l36->regmap, + CS35L36_BSTCVRT_OVERVOLT_CTRL, + CS35L36_BST_OVP_THLD_MASK, + CS35L36_BST_OVP_THLD_11V); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_BST_ANA2_TEST, + CS35L36_BST_OVP_TRIM_MASK, + CS35L36_BST_OVP_TRIM_11V << + CS35L36_BST_OVP_TRIM_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2, + CS35L36_BST_CTRL_LIM_MASK, + 1 << CS35L36_BST_CTRL_LIM_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1, + CS35L35_BSTCVRT_CTL_MASK, + CS35L36_BST_CTRL_10V_CLAMP); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + /* + * RevA and B require the disabling of + * SYNC_GLOBAL_OVR when GLOBAL_EN = 0. + * Just turn it off from default + */ + regmap_update_bits(cs35l36->regmap, CS35L36_CTRL_OVRRIDE, + CS35L36_SYNC_GLOBAL_OVR_MASK, + 0 << CS35L36_SYNC_GLOBAL_OVR_SHIFT); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l36 = { + .probe = &cs35l36_component_probe, + .set_sysclk = cs35l36_component_set_sysclk, + .dapm_widgets = cs35l36_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l36_dapm_widgets), + .dapm_routes = cs35l36_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l36_audio_map), + .controls = cs35l36_aud_controls, + .num_controls = ARRAY_SIZE(cs35l36_aud_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static struct regmap_config cs35l36_regmap = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = CS35L36_PAC_PMEM_WORD1023, + .reg_defaults = cs35l36_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l36_reg), + .precious_reg = cs35l36_precious_reg, + .volatile_reg = cs35l36_volatile_reg, + .readable_reg = cs35l36_readable_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static irqreturn_t cs35l36_irq(int irq, void *data) +{ + struct cs35l36_private *cs35l36 = data; + unsigned int status[4]; + unsigned int masks[4]; + int ret = IRQ_NONE; + + /* ack the irq by reading all status registers */ + regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_STATUS, status, + ARRAY_SIZE(status)); + + regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_MASK, masks, + ARRAY_SIZE(masks)); + + /* Check to see if unmasked bits are active */ + if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && + !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) { + return IRQ_NONE; + } + + /* + * The following interrupts require a + * protection release cycle to get the + * speaker out of Safe-Mode. + */ + if (status[2] & CS35L36_AMP_SHORT_ERR) { + dev_crit(cs35l36->dev, "Amp short error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, + CS35L36_AMP_SHORT_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT3_STATUS, + CS35L36_AMP_SHORT_ERR, + CS35L36_AMP_SHORT_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_TEMP_WARN) { + dev_crit(cs35l36->dev, "Over temperature warning\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, + CS35L36_TEMP_WARN_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_TEMP_WARN, CS35L36_TEMP_WARN); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_TEMP_ERR) { + dev_crit(cs35l36->dev, "Over temperature error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_TEMP_ERR, CS35L36_TEMP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_OVP_ERR) { + dev_crit(cs35l36->dev, "VBST Over Voltage error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_OVP_ERR, CS35L36_BST_OVP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_DCM_UVP_ERR) { + dev_crit(cs35l36->dev, "DCM VBST Under Voltage Error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, + CS35L36_BST_UVP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_DCM_UVP_ERR, + CS35L36_BST_DCM_UVP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_SHORT_ERR) { + dev_crit(cs35l36->dev, "LBST SHORT error!\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, + CS35L36_BST_SHORT_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_SHORT_ERR, + CS35L36_BST_SHORT_ERR); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int cs35l36_handle_of_data(struct i2c_client *i2c_client, + struct cs35l36_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config; + struct device_node *vpbr_node; + unsigned int val; + int ret; + + if (!np) + return 0; + + ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val); + if (!ret) { + if (val < 2550 || val > 12000) { + dev_err(&i2c_client->dev, + "Invalid Boost Voltage %d mV\n", val); + return -EINVAL; + } + pdata->bst_vctl = (((val - 2550) / 100) + 1) << 1; + } else { + dev_err(&i2c_client->dev, + "Unable to find required parameter 'cirrus,boost-ctl-millivolt'"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "cirrus,boost-ctl-select", &val); + if (!ret) + pdata->bst_vctl_sel = val | CS35L36_VALID_PDATA; + + ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val); + if (!ret) { + if (val < 1600 || val > 4500) { + dev_err(&i2c_client->dev, + "Invalid Boost Peak Current %u mA\n", val); + return -EINVAL; + } + + pdata->bst_ipk = (val - 1600) / 50; + } else { + dev_err(&i2c_client->dev, + "Unable to find required parameter 'cirrus,boost-peak-milliamp'"); + return -EINVAL; + } + + pdata->multi_amp_mode = of_property_read_bool(np, + "cirrus,multi-amp-mode"); + + pdata->dcm_mode = of_property_read_bool(np, + "cirrus,dcm-mode-enable"); + + pdata->amp_pcm_inv = of_property_read_bool(np, + "cirrus,amp-pcm-inv"); + + pdata->imon_pol_inv = of_property_read_bool(np, + "cirrus,imon-pol-inv"); + + pdata->vmon_pol_inv = of_property_read_bool(np, + "cirrus,vmon-pol-inv"); + + if (of_property_read_u32(np, "cirrus,temp-warn-threshold", &val) >= 0) + pdata->temp_warn_thld = val | CS35L36_VALID_PDATA; + + if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) { + pdata->boost_ind = val; + } else { + dev_err(&i2c_client->dev, "Inductor not specified.\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "cirrus,irq-drive-select", &val) >= 0) + pdata->irq_drv_sel = val | CS35L36_VALID_PDATA; + + if (of_property_read_u32(np, "cirrus,irq-gpio-select", &val) >= 0) + pdata->irq_gpio_sel = val | CS35L36_VALID_PDATA; + + /* VPBR Config */ + vpbr_node = of_get_child_by_name(np, "cirrus,vpbr-config"); + vpbr_config->is_present = vpbr_node ? true : false; + if (vpbr_config->is_present) { + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-en", + &val) >= 0) + vpbr_config->vpbr_en = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-thld", + &val) >= 0) + vpbr_config->vpbr_thld = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-rate", + &val) >= 0) + vpbr_config->vpbr_atk_rate = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-vol", + &val) >= 0) + vpbr_config->vpbr_atk_vol = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-max-attn", + &val) >= 0) + vpbr_config->vpbr_max_attn = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-wait", + &val) >= 0) + vpbr_config->vpbr_wait = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-rel-rate", + &val) >= 0) + vpbr_config->vpbr_rel_rate = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-mute-en", + &val) >= 0) + vpbr_config->vpbr_mute_en = val; + } + of_node_put(vpbr_node); + + return 0; +} + +static int cs35l36_pac(struct cs35l36_private *cs35l36) +{ + int ret, count; + unsigned int val; + + if (cs35l36->rev_id != CS35L36_REV_B0) + return 0; + + /* + * Magic code for internal PAC + */ + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + usleep_range(9500, 10500); + + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_RESET); + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3, + CS35L36_PAC_MEM_ACCESS); + regmap_write(cs35l36->regmap, CS35L36_PAC_PMEM_WORD0, + CS35L36_B0_PAC_PATCH); + + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3, + CS35L36_PAC_MEM_ACCESS_CLR); + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_ENABLE_MASK); + + usleep_range(9500, 10500); + + ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, &val); + if (ret < 0) { + dev_err(cs35l36->dev, "Failed to read int4_status %d\n", ret); + return ret; + } + + count = 0; + while (!(val & CS35L36_MCU_CONFIG_CLR)) { + usleep_range(100, 200); + count++; + + ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, + &val); + if (ret < 0) { + dev_err(cs35l36->dev, "Failed to read int4_status %d\n", + ret); + return ret; + } + + if (count >= 100) + return -EINVAL; + } + + regmap_write(cs35l36->regmap, CS35L36_INT4_STATUS, + CS35L36_MCU_CONFIG_CLR); + regmap_update_bits(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_ENABLE_MASK, 0); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + + return 0; +} + +static void cs35l36_apply_vpbr_config(struct cs35l36_private *cs35l36) +{ + struct cs35l36_platform_data *pdata = &cs35l36->pdata; + struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config; + + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL3, + CS35L36_VPBR_EN_MASK, + vpbr_config->vpbr_en << + CS35L36_VPBR_EN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_THLD_MASK, + vpbr_config->vpbr_thld << + CS35L36_VPBR_THLD_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_MAX_ATTN_MASK, + vpbr_config->vpbr_max_attn << + CS35L36_VPBR_MAX_ATTN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_ATK_VOL_MASK, + vpbr_config->vpbr_atk_vol << + CS35L36_VPBR_ATK_VOL_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_ATK_RATE_MASK, + vpbr_config->vpbr_atk_rate << + CS35L36_VPBR_ATK_RATE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_WAIT_MASK, + vpbr_config->vpbr_wait << + CS35L36_VPBR_WAIT_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_REL_RATE_MASK, + vpbr_config->vpbr_rel_rate << + CS35L36_VPBR_REL_RATE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_MUTE_EN_MASK, + vpbr_config->vpbr_mute_en << + CS35L36_VPBR_MUTE_EN_SHIFT); +} + +static const struct reg_sequence cs35l36_reva0_errata_patch[] = { + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 }, + /* Errata Writes */ + { CS35L36_OTP_CTRL1, 0x00002060 }, + { CS35L36_OTP_CTRL2, 0x00000001 }, + { CS35L36_OTP_CTRL1, 0x00002460 }, + { CS35L36_OTP_CTRL2, 0x00000001 }, + { 0x00002088, 0x012A1838 }, + { 0x00003014, 0x0100EE0E }, + { 0x00003008, 0x0008184A }, + { 0x00007418, 0x509001C8 }, + { 0x00007064, 0x0929A800 }, + { 0x00002D10, 0x0002C01C }, + { 0x0000410C, 0x00000A11 }, + { 0x00006E08, 0x8B19140C }, + { 0x00006454, 0x0300000A }, + { CS35L36_AMP_NG_CTRL, 0x000020EF }, + { 0x00007E34, 0x0000000E }, + { 0x0000410C, 0x00000A11 }, + { 0x00007410, 0x20514B00 }, + /* PAC Config */ + { CS35L36_CTRL_OVRRIDE, 0x00000000 }, + { CS35L36_PAC_INT0_CTRL, 0x00860001 }, + { CS35L36_PAC_INT1_CTRL, 0x00860001 }, + { CS35L36_PAC_INT2_CTRL, 0x00860001 }, + { CS35L36_PAC_INT3_CTRL, 0x00860001 }, + { CS35L36_PAC_INT4_CTRL, 0x00860001 }, + { CS35L36_PAC_INT5_CTRL, 0x00860001 }, + { CS35L36_PAC_INT6_CTRL, 0x00860001 }, + { CS35L36_PAC_INT7_CTRL, 0x00860001 }, + { CS35L36_PAC_INT_FLUSH_CTRL, 0x000000FF }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, +}; + +static const struct reg_sequence cs35l36_revb0_errata_patch[] = { + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 }, + { 0x00007064, 0x0929A800 }, + { 0x00007850, 0x00002FA9 }, + { 0x00007854, 0x0003F1D5 }, + { 0x00007858, 0x0003F5E3 }, + { 0x0000785C, 0x00001137 }, + { 0x00007860, 0x0001A7A5 }, + { 0x00007864, 0x0002F16A }, + { 0x00007868, 0x00003E21 }, + { 0x00007848, 0x00000001 }, + { 0x00003854, 0x05180240 }, + { 0x00007418, 0x509001C8 }, + { 0x0000394C, 0x028764BD }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, +}; + +static int cs35l36_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs35l36_private *cs35l36; + struct device *dev = &i2c_client->dev; + struct cs35l36_platform_data *pdata = dev_get_platdata(dev); + struct irq_data *irq_d; + int ret, irq_pol, chip_irq_pol, i; + u32 reg_id, reg_revid, l37_id_reg; + + cs35l36 = devm_kzalloc(dev, sizeof(struct cs35l36_private), GFP_KERNEL); + if (!cs35l36) + return -ENOMEM; + + cs35l36->dev = dev; + + i2c_set_clientdata(i2c_client, cs35l36); + cs35l36->regmap = devm_regmap_init_i2c(i2c_client, &cs35l36_regmap); + if (IS_ERR(cs35l36->regmap)) { + ret = PTR_ERR(cs35l36->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + goto err; + } + + cs35l36->num_supplies = ARRAY_SIZE(cs35l36_supplies); + for (i = 0; i < ARRAY_SIZE(cs35l36_supplies); i++) + cs35l36->supplies[i].supply = cs35l36_supplies[i]; + + ret = devm_regulator_bulk_get(dev, cs35l36->num_supplies, + cs35l36->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l36->pdata = *pdata; + } else { + pdata = devm_kzalloc(dev, sizeof(struct cs35l36_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + ret = cs35l36_handle_of_data(i2c_client, pdata); + if (ret != 0) + return ret; + + } + + cs35l36->pdata = *pdata; + } + + ret = regulator_bulk_enable(cs35l36->num_supplies, cs35l36->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", ret); + return ret; + } + + /* returning NULL can be an option if in stereo mode */ + cs35l36->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l36->reset_gpio)) { + ret = PTR_ERR(cs35l36->reset_gpio); + cs35l36->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_info(dev, "Reset line busy, assuming shared reset\n"); + } else { + dev_err(dev, "Failed to get reset GPIO: %d\n", ret); + goto err_disable_regs; + } + } + + if (cs35l36->reset_gpio) + gpiod_set_value_cansleep(cs35l36->reset_gpio, 1); + + usleep_range(2000, 2100); + + /* initialize amplifier */ + ret = regmap_read(cs35l36->regmap, CS35L36_SW_RESET, ®_id); + if (ret < 0) { + dev_err(dev, "Get Device ID failed %d\n", ret); + goto err; + } + + if (reg_id != CS35L36_CHIP_ID) { + dev_err(dev, "Device ID (%X). Expected ID %X\n", reg_id, + CS35L36_CHIP_ID); + ret = -ENODEV; + goto err; + } + + ret = regmap_read(cs35l36->regmap, CS35L36_REV_ID, ®_revid); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed %d\n", ret); + goto err; + } + + cs35l36->rev_id = reg_revid >> 8; + + ret = regmap_read(cs35l36->regmap, CS35L36_OTP_MEM30, &l37_id_reg); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n", + ret); + return ret; + } + + if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37) + cs35l36->chip_version = CS35L36_12V_L37; + else + cs35l36->chip_version = CS35L36_10V_L36; + + switch (cs35l36->rev_id) { + case CS35L36_REV_A0: + ret = regmap_register_patch(cs35l36->regmap, + cs35l36_reva0_errata_patch, + ARRAY_SIZE(cs35l36_reva0_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply A0 errata patch %d\n", + ret); + goto err; + } + break; + case CS35L36_REV_B0: + ret = cs35l36_pac(cs35l36); + if (ret < 0) { + dev_err(dev, "Failed to Trim OTP %d\n", ret); + goto err; + } + + ret = regmap_register_patch(cs35l36->regmap, + cs35l36_revb0_errata_patch, + ARRAY_SIZE(cs35l36_revb0_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply B0 errata patch %d\n", + ret); + goto err; + } + break; + } + + if (pdata->vpbr_config.is_present) + cs35l36_apply_vpbr_config(cs35l36); + + irq_d = irq_get_irq_data(i2c_client->irq); + if (!irq_d) { + dev_err(&i2c_client->dev, "Invalid IRQ: %d\n", i2c_client->irq); + ret = -ENODEV; + goto err; + } + + irq_pol = irqd_get_trigger_type(irq_d); + + switch (irq_pol) { + case IRQF_TRIGGER_FALLING: + case IRQF_TRIGGER_LOW: + chip_irq_pol = 0; + break; + case IRQF_TRIGGER_RISING: + case IRQF_TRIGGER_HIGH: + chip_irq_pol = 1; + break; + default: + dev_err(cs35l36->dev, "Invalid IRQ polarity: %d\n", irq_pol); + ret = -EINVAL; + goto err; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_POL_SEL_MASK, + chip_irq_pol << CS35L36_INT_POL_SEL_SHIFT); + + ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l36_irq, + IRQF_ONESHOT | irq_pol, "cs35l36", + cs35l36); + if (ret != 0) { + dev_err(dev, "Failed to request IRQ: %d\n", ret); + goto err; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_OUTPUT_EN_MASK, 1); + + /* Set interrupt masks for critical errors */ + regmap_write(cs35l36->regmap, CS35L36_INT1_MASK, + CS35L36_INT1_MASK_DEFAULT); + regmap_write(cs35l36->regmap, CS35L36_INT3_MASK, + CS35L36_INT3_MASK_DEFAULT); + + dev_info(&i2c_client->dev, "Cirrus Logic CS35L%d, Revision: %02X\n", + cs35l36->chip_version, reg_revid >> 8); + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l36, + cs35l36_dai, + ARRAY_SIZE(cs35l36_dai)); + if (ret < 0) { + dev_err(dev, "%s: Register component failed %d\n", __func__, + ret); + goto err; + } + + return 0; + +err: + gpiod_set_value_cansleep(cs35l36->reset_gpio, 0); + +err_disable_regs: + regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies); + return ret; +} + +static int cs35l36_i2c_remove(struct i2c_client *client) +{ + struct cs35l36_private *cs35l36 = i2c_get_clientdata(client); + + /* Reset interrupt masks for device removal */ + regmap_write(cs35l36->regmap, CS35L36_INT1_MASK, + CS35L36_INT1_MASK_RESET); + regmap_write(cs35l36->regmap, CS35L36_INT3_MASK, + CS35L36_INT3_MASK_RESET); + + if (cs35l36->reset_gpio) + gpiod_set_value_cansleep(cs35l36->reset_gpio, 0); + + regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies); + + return 0; +} +static const struct of_device_id cs35l36_of_match[] = { + {.compatible = "cirrus,cs35l36"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l36_of_match); + +static const struct i2c_device_id cs35l36_id[] = { + {"cs35l36", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l36_id); + +static struct i2c_driver cs35l36_i2c_driver = { + .driver = { + .name = "cs35l36", + .of_match_table = cs35l36_of_match, + }, + .id_table = cs35l36_id, + .probe = cs35l36_i2c_probe, + .remove = cs35l36_i2c_remove, +}; +module_i2c_driver(cs35l36_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L36 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l36.h b/sound/soc/codecs/cs35l36.h new file mode 100644 index 000000000000..f6e38c633b93 --- /dev/null +++ b/sound/soc/codecs/cs35l36.h @@ -0,0 +1,446 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cs35l36.h -- CS35L36 ALSA SoC audio driver + * + * Copyright 2018 Cirrus Logic, Inc. + * + * Author: James Schulman <james.schulman@cirrus.com> + * + */ + +#ifndef __CS35L36_H__ +#define __CS35L36_H__ + +#include <linux/regmap.h> + +#define CS35L36_FIRSTREG 0x00000000 +#define CS35L36_LASTREG 0x00E037FC +#define CS35L36_SW_RESET 0x00000000 +#define CS35L36_SW_REV 0x00000004 +#define CS35L36_HW_REV 0x00000008 +#define CS35L36_TESTKEY_CTRL 0x00000020 +#define CS35L36_USERKEY_CTL 0x00000024 +#define CS35L36_OTP_MEM30 0x00000478 +#define CS35L36_OTP_CTRL1 0x00000500 +#define CS35L36_OTP_CTRL2 0x00000504 +#define CS35L36_OTP_CTRL3 0x00000508 +#define CS35L36_OTP_CTRL4 0x0000050C +#define CS35L36_OTP_CTRL5 0x00000510 +#define CS35L36_PAC_CTL1 0x00000C00 +#define CS35L36_PAC_CTL2 0x00000C04 +#define CS35L36_PAC_CTL3 0x00000C08 +#define CS35L36_DEVICE_ID 0x00002004 +#define CS35L36_FAB_ID 0x00002008 +#define CS35L36_REV_ID 0x0000200C +#define CS35L36_PWR_CTRL1 0x00002014 +#define CS35L36_PWR_CTRL2 0x00002018 +#define CS35L36_PWR_CTRL3 0x0000201C +#define CS35L36_CTRL_OVRRIDE 0x00002020 +#define CS35L36_AMP_OUT_MUTE 0x00002024 +#define CS35L36_OTP_TRIM_STATUS 0x00002028 +#define CS35L36_DISCH_FILT 0x0000202C +#define CS35L36_OSC_TRIM 0x00002030 +#define CS35L36_PROTECT_REL_ERR 0x00002034 +#define CS35L36_PAD_INTERFACE 0x00002400 +#define CS35L36_PLL_CLK_CTRL 0x00002C04 +#define CS35L36_GLOBAL_CLK_CTRL 0x00002C0C +#define CS35L36_ADC_CLK_CTRL 0x00002C10 +#define CS35L36_SWIRE_CLK_CTRL 0x00002C14 +#define CS35L36_SP_SCLK_CLK_CTRL 0x00002D00 +#define CS35L36_TST_FS_MON0 0x00002D10 +#define CS35L36_PLL_LOOP_PARAMS 0x00003008 +#define CS35L36_DCO_CTRL 0x00003010 +#define CS35L36_MISC_CTRL 0x00003014 +#define CS35L36_MDSYNC_EN 0x00003404 +#define CS35L36_MDSYNC_TX_ID 0x00003408 +#define CS35L36_MDSYNC_PWR_CTRL 0x0000340C +#define CS35L36_MDSYNC_DATA_TX 0x00003410 +#define CS35L36_MDSYNC_TX_STATUS 0x0000341C +#define CS35L36_MDSYNC_RX_STATUS 0x00003420 +#define CS35L36_MDSYNC_ERR_STATUS 0x00003424 +#define CS35L36_BSTCVRT_VCTRL1 0x00003800 +#define CS35L36_BSTCVRT_VCTRL2 0x00003804 +#define CS35L36_BSTCVRT_PEAK_CUR 0x00003808 +#define CS35L36_BSTCVRT_SFT_RAMP 0x0000380C +#define CS35L36_BSTCVRT_COEFF 0x00003810 +#define CS35L36_BSTCVRT_SLOPE_LBST 0x00003814 +#define CS35L36_BSTCVRT_SW_FREQ 0x00003818 +#define CS35L36_BSTCVRT_DCM_CTRL 0x0000381C +#define CS35L36_BSTCVRT_DCM_MODE_FORCE 0x00003820 +#define CS35L36_BSTCVRT_OVERVOLT_CTRL 0x00003830 +#define CS35L36_BST_TST_MANUAL 0x0000393C +#define CS35L36_BST_ANA2_TEST 0x0000394C +#define CS35L36_VPI_LIMIT_MODE 0x00003C04 +#define CS35L36_VPI_LIMIT_MINMAX 0x00003C08 +#define CS35L36_VPI_VP_THLD 0x00003C0C +#define CS35L36_VPI_TRACK_CTRL 0x00003C10 +#define CS35L36_VPI_TRIG_MODE_CTRL 0x00003C14 +#define CS35L36_VPI_TRIG_STEPS 0x00003C18 +#define CS35L36_VI_SPKMON_FILT 0x00004004 +#define CS35L36_VI_SPKMON_GAIN 0x00004008 +#define CS35L36_VI_SPKMON_IP_SEL 0x00004100 +#define CS35L36_DTEMP_WARN_THLD 0x00004220 +#define CS35L36_DTEMP_STATUS 0x00004300 +#define CS35L36_VPVBST_FS_SEL 0x00004400 +#define CS35L36_VPVBST_VP_CTRL 0x00004440 +#define CS35L36_VPVBST_VBST_CTRL 0x00004444 +#define CS35L36_ASP_TX_PIN_CTRL 0x00004800 +#define CS35L36_ASP_RATE_CTRL 0x00004804 +#define CS35L36_ASP_FORMAT 0x00004808 +#define CS35L36_ASP_FRAME_CTRL 0x00004818 +#define CS35L36_ASP_TX1_TX2_SLOT 0x0000481C +#define CS35L36_ASP_TX3_TX4_SLOT 0x00004820 +#define CS35L36_ASP_TX5_TX6_SLOT 0x00004824 +#define CS35L36_ASP_TX7_TX8_SLOT 0x00004828 +#define CS35L36_ASP_RX1_SLOT 0x0000482C +#define CS35L36_ASP_RX_TX_EN 0x0000483C +#define CS35L36_ASP_RX1_SEL 0x00004C00 +#define CS35L36_ASP_TX1_SEL 0x00004C20 +#define CS35L36_ASP_TX2_SEL 0x00004C24 +#define CS35L36_ASP_TX3_SEL 0x00004C28 +#define CS35L36_ASP_TX4_SEL 0x00004C2C +#define CS35L36_ASP_TX5_SEL 0x00004C30 +#define CS35L36_ASP_TX6_SEL 0x00004C34 +#define CS35L36_SWIRE_P1_TX1_SEL 0x00004C40 +#define CS35L36_SWIRE_P1_TX2_SEL 0x00004C44 +#define CS35L36_SWIRE_P2_TX1_SEL 0x00004C60 +#define CS35L36_SWIRE_P2_TX2_SEL 0x00004C64 +#define CS35L36_SWIRE_P2_TX3_SEL 0x00004C68 +#define CS35L36_SWIRE_DP1_FIFO_CFG 0x00005000 +#define CS35L36_SWIRE_DP2_FIFO_CFG 0x00005004 +#define CS35L36_SWIRE_DP3_FIFO_CFG 0x00005008 +#define CS35L36_SWIRE_PCM_RX_DATA 0x0000500C +#define CS35L36_SWIRE_FS_SEL 0x00005010 +#define CS35L36_SPARE_CP_BITS 0x00005C00 +#define CS35L36_AMP_DIG_VOL_CTRL 0x00006000 +#define CS35L36_VPBR_CFG 0x00006404 +#define CS35L36_VBBR_CFG 0x00006408 +#define CS35L36_VPBR_STATUS 0x0000640C +#define CS35L36_VBBR_STATUS 0x00006410 +#define CS35L36_OVERTEMP_CFG 0x00006414 +#define CS35L36_AMP_ERR_VOL 0x00006418 +#define CS35L36_CLASSH_CFG 0x00006800 +#define CS35L36_CLASSH_FET_DRV_CFG 0x00006804 +#define CS35L36_NG_CFG 0x00006808 +#define CS35L36_AMP_GAIN_CTRL 0x00006C04 +#define CS35L36_PWM_MOD_IO_CTRL 0x0000706C +#define CS35L36_PWM_MOD_STATUS 0x00007070 +#define CS35L36_DAC_MSM_CFG 0x00007400 +#define CS35L36_AMP_SLOPE_CTRL 0x00007410 +#define CS35L36_AMP_PDM_VOLUME 0x00007E04 +#define CS35L36_AMP_PDM_RATE_CTRL 0x00007E08 +#define CS35L36_PDM_CH_SEL 0x00007E10 +#define CS35L36_AMP_NG_CTRL 0x00007E14 +#define CS35L36_PDM_HIGHFILT_CTRL 0x00007E3C +#define CS35L36_INT1_STATUS 0x00D00000 +#define CS35L36_INT2_STATUS 0x00D00004 +#define CS35L36_INT3_STATUS 0x00D00008 +#define CS35L36_INT4_STATUS 0x00D0000C +#define CS35L36_INT1_RAW_STATUS 0x00D00020 +#define CS35L36_INT2_RAW_STATUS 0x00D00024 +#define CS35L36_INT3_RAW_STATUS 0x00D00028 +#define CS35L36_INT4_RAW_STATUS 0x00D0002C +#define CS35L36_INT1_MASK 0x00D00040 +#define CS35L36_INT2_MASK 0x00D00044 +#define CS35L36_INT3_MASK 0x00D00048 +#define CS35L36_INT4_MASK 0x00D0004C +#define CS35L36_INT1_EDGE_LVL_CTRL 0x00D00060 +#define CS35L36_INT3_EDGE_LVL_CTRL 0x00D00068 +#define CS35L36_PAC_INT_STATUS 0x00D00200 +#define CS35L36_PAC_INT_RAW_STATUS 0x00D00210 +#define CS35L36_PAC_INT_FLUSH_CTRL 0x00D00218 +#define CS35L36_PAC_INT0_CTRL 0x00D00220 +#define CS35L36_PAC_INT1_CTRL 0x00D00224 +#define CS35L36_PAC_INT2_CTRL 0x00D00228 +#define CS35L36_PAC_INT3_CTRL 0x00D0022C +#define CS35L36_PAC_INT4_CTRL 0x00D00230 +#define CS35L36_PAC_INT5_CTRL 0x00D00234 +#define CS35L36_PAC_INT6_CTRL 0x00D00238 +#define CS35L36_PAC_INT7_CTRL 0x00D0023C +#define CS35L36_PAC_PMEM_WORD0 0x00E02800 +#define CS35L36_PAC_PMEM_WORD1 0x00E02804 +#define CS35L36_PAC_PMEM_WORD1023 0x00E037FC + +#define CS35L36_INTPAC_REG_COUNT 25 +#define CS35L36_CHIP_ID 0x00035A36 + +#define CS35L36_INT_OUTPUT_EN_MASK 0x01 +#define CS35L36_INT_GPIO_SEL_MASK 0x02 +#define CS35L36_INT_GPIO_SEL_SHIFT 1 +#define CS35L36_INT_POL_SEL_MASK 0x04 +#define CS35L36_INT_POL_SEL_SHIFT 2 +#define CS35L36_INT_DRV_SEL_MASK 0x20 +#define CS35L36_INT_DRV_SEL_SHIFT 5 +#define CS35L36_IRQ_SRC_MASK 0x08 +#define CS35L36_IRQ_SRC_SHIFT 3 + +#define CS35L36_SCLK_MSTR_MASK 0x40 +#define CS35L36_SCLK_MSTR_SHIFT 6 +#define CS35L36_LRCLK_MSTR_MASK 0x01 +#define CS35L36_LRCLK_MSTR_SHIFT 0 +#define CS35L36_SCLK_INV_MASK 0x100 +#define CS35L36_SCLK_INV_SHIFT 8 +#define CS35L36_LRCLK_INV_MASK 0x04 +#define CS35L36_LRCLK_INV_SHIFT 2 +#define CS35L36_SCLK_FRC_MASK 0x80 +#define CS35L36_SCLK_FRC_SHIFT 7 +#define CS35L36_LRCLK_FRC_MASK 0x02 +#define CS35L36_LRCLK_FRC_SHIFT 1 + +#define CS35L36_PDM_MODE_MASK 0x01 +#define CS35L36_PDM_MODE_SHIFT 0 + +#define CS35L36_ASP_FMT_MASK 0x07 +#define CS35L36_ASP_FMT_SHIFT 0 + +#define CS35L36_ASP_RX_WIDTH_MASK 0xFF0000 +#define CS35L36_ASP_RX_WIDTH_SHIFT 16 +#define CS35L36_ASP_TX_WIDTH_MASK 0xFF +#define CS35L36_ASP_TX_WIDTH_SHIFT 0 +#define CS35L36_ASP_WIDTH_16 0x10 +#define CS35L36_ASP_WIDTH_24 0x18 +#define CS35L36_ASP_WIDTH_32 0x20 + +#define CS35L36_ASP_RX1_SLOT_MASK 0x3F +#define CS35L36_ASP_RX1_EN_MASK 0x00010000 +#define CS35L36_ASP_RX1_EN_SHIFT 16 + +#define CS35L36_ASP_TX1_SLOT_MASK 0x3F +#define CS35L36_ASP_TX2_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX2_SLOT_SHIFT 16 +#define CS35L36_ASP_TX3_SLOT_MASK 0x3F +#define CS35L36_ASP_TX4_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX4_SLOT_SHIFT 16 +#define CS35L36_ASP_TX5_SLOT_MASK 0x3F +#define CS35L36_ASP_TX6_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX6_SLOT_SHIFT 16 +#define CS35L36_ASP_TX7_SLOT_MASK 0x3F +#define CS35L36_ASP_TX8_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX8_SLOT_SHIFT 16 +#define CS35L36_ASP_TX_HIZ_MASK 0x200000 + +#define CS35L36_APS_TX_SEL_MASK 0x7F + +#define CS35L36_ASP_TX1_EN_MASK 0x01 +#define CS35L36_ASP_TX2_EN_MASK 0x02 +#define CS35L36_ASP_TX2_EN_SHIFT 1 +#define CS35L36_ASP_TX3_EN_MASK 0x04 +#define CS35L36_ASP_TX3_EN_SHIFT 2 +#define CS35L36_ASP_TX4_EN_MASK 0x08 +#define CS35L36_ASP_TX4_EN_SHIFT 3 +#define CS35L36_ASP_TX5_EN_MASK 0x10 +#define CS35L36_ASP_TX5_EN_SHIFT 4 +#define CS35L36_ASP_TX6_EN_MASK 0x20 +#define CS35L36_ASP_TX6_EN_SHIFT 5 +#define CS35L36_ASP_TX7_EN_MASK 0x40 +#define CS35L36_ASP_TX7_EN_SHIFT 6 +#define CS35L36_ASP_TX8_EN_MASK 0x80 +#define CS35L36_ASP_TX8_EN_SHIFT 7 + + +#define CS35L36_PLL_CLK_SEL_MASK 0x07 +#define CS35L36_PLL_CLK_SEL_SHIFT 0 +#define CS35L36_PLLSRC_SCLK 0 +#define CS35L36_PLLSRC_LRCLK 1 +#define CS35L36_PLLSRC_SELF 3 +#define CS35L36_PLLSRC_PDMCLK 4 +#define CS35L36_PLLSRC_MCLK 5 +#define CS35L36_PLLSRC_SWIRE 7 +#define CS35L36_REFCLK_FREQ_MASK 0x7E0 +#define CS35L36_REFCLK_FREQ_SHIFT 5 +#define CS35L36_PLL_OPENLOOP_MASK 0x800 +#define CS35L36_PLL_OPENLOOP_SHIFT 11 +#define CS35L36_PLL_REFCLK_EN_MASK 0x10 +#define CS35L36_PLL_REFCLK_EN_SHIFT 4 + + +#define CS35L36_GLOBAL_FS_MASK 0x1F +#define CS35L36_GLOBAL_FS_SHIFT 0 + +#define CS35L36_HPF_PCM_EN_MASK 0x800 +#define CS35L36_HPF_PCM_EN_SHIFT 15 +#define CS35L36_PCM_RX_SEL_MASK 0x7F +#define CS35L36_PCM_RX_SEL_SHIFT 0 + +#define CS35L36_PCM_RX_SEL_ZERO 0x00 +#define CS35L36_PCM_RX_SEL_PCM 0x08 +#define CS35L36_PCM_RX_SEL_SWIRE 0x10 +#define CS35L36_PCM_RX_SEL_DIAG 0x04 + +#define CS35L36_GLOBAL_EN_MASK 0x01 +#define CS35L36_GLOBAL_EN_SHIFT 0x00 + +#define CS35L36_AMP_PCM_INV_MASK 0x4000 +#define CS35L36_AMP_PCM_INV_SHIFT 14 + +#define CS35L36_AMP_VOL_PCM_MASK 0x3FF8 +#define CS35L36_AMP_VOL_PCM_SHIFT 3 +#define CS35L36_DIGITAL_MUTE 0x04CF + +#define CS35L36_AMP_RAMP_MASK 0x0007 +#define CS35L36_AMP_RAMP_SHIFT 0 + +#define CS35L36_AMP_MUTE_MASK 0x0010 +#define CS35L36_AMP_MUTE_SHIFT 4 + +#define CS35L36_GLOBAL_RESYNC_FS1_MASK 0x00000200 +#define CS35L36_GLOBAL_RESYNC_FS2_MASK 0x00000400 +#define CS35L36_SYNC_GLOBAL_OVR_MASK 0x00000002 +#define CS35L36_SYNC_GLOBAL_OVR_SHIFT 1 + +#define CS35L36_REFCLK_IN_MASK 0x00100000 +#define CS35L36_PLL_UNLOCK_MASK 0x00002000 + +#define CS35L36_ASP_RX_UDF_MASK 0x00000040 +#define CS35L36_ASP_RX_OVF_MASK 0x00000080 + +#define CS35L36_IMON_POL_MASK 0x02 +#define CS35L36_IMON_POL_SHIFT 1 + +#define CS35L36_VMON_POL_MASK 0x01 +#define CS35L36_VMON_POL_SHIFT 0 + +#define CS35L36_PDN_DONE 0x40 +#define CS35L36_PDN_DONE_SHIFT 6 +#define CS35L36_PUP_DONE 0x80 +#define CS35L36_PUP_DONE_SHIFT 7 +#define CS35L36_GLOBAL_EN_ASSRT 0x20 +#define CS35L36_PUP_DONE_IRQ_UNMASK 0x7F +#define CS35L36_PUP_DONE_IRQ_MASK 0xBF + +#define CS35L36_FS1_WINDOW_MASK 0x000007FF +#define CS35L36_FS2_WINDOW_MASK 0x00FFF800 +#define CS35L36_FS2_WINDOW_SHIFT 12 + +#define CS35L36_PLL_FFL_IGAIN_MASK 0x0F +#define CS35L36_PLL_IGAIN_MASK 0x3F0 +#define CS35L36_PLL_IGAIN_SHIFT 4 +#define CS35L36_PLL_IGAIN 0x04 + +#define CS35L36_BST_EN_MASK 0x30 +#define CS35L36_BST_EN 0x02 +#define CS35L36_BST_DIS_VP 0x01 +#define CS35L36_BST_DIS_EXTN 0x00 +#define CS35L36_BST_EN_SHIFT 4 +#define CS35L36_BST_MAN_IPKCOMP_MASK 0x200 +#define CS35L36_BST_MAN_IPKCOMP_SHIFT 9 + +#define CS35L36_BST_MAN_IPKCOMP_EN_MASK 0x100 +#define CS35L36_BST_MAN_IPKCOMP_EN_SHIFT 8 + +#define CS35L36_BST_IPK_MASK 0x7F +#define CS35L36_BST_OVP_THLD_MASK 0x3F +#define CS35L36_BST_OVP_THLD_11V 0x10 +#define CS35L36_BST_OVP_TRIM_MASK 0x00078000 +#define CS35L36_BST_OVP_TRIM_SHIFT 15 +#define CS35L36_BST_OVP_TRIM_11V 0x0C +#define CS35L36_BST_CTRL_LIM_MASK 0x04 +#define CS35L36_BST_CTRL_LIM_SHIFT 2 +#define CS35L36_BST_CTRL_10V_CLAMP 0x96 + +#define CS35L36_NG_AMP_EN_MASK 0x3F00 +#define CS35L36_NG_DELAY_MASK 0x70 +#define CS35L36_NG_DELAY_SHIFT 4 +#define CS35L36_AMP_ZC_SHIFT 10 +#define CS35L36_PDM_LDM_ENTER_SHIFT 3 +#define CS35L36_PDM_LDM_EXIT_SHIFT 4 + +#define CS35L36_BSTCVRT_K1_MASK 0xFF +#define CS35L36_BSTCVRT_K2_MASK 0xFF00 +#define CS35L36_BSTCVRT_K2_SHIFT 8 +#define CS35L36_BSTCVRT_SLOPE_MASK 0xFF00 +#define CS35L36_BSTCVRT_SLOPE_SHIFT 8 +#define CS35L36_BSTCVRT_CCMFREQ_MASK 0x0F +#define CS35L36_BSTCVRT_LBSTVAL_MASK 0x03 +#define CS35L35_BSTCVRT_CTL_MASK 0xFF +#define CS35L35_BSTCVRT_CTL_SEL_MASK 0x03 +#define CS35L36_DCM_AUTO_MASK 0x01 + +#define CS35L36_INT1_MASK_DEFAULT 0xF9BA7FFF +#define CS35L36_INT1_MASK_RESET 0xFFFFFFFF +#define CS35L36_INT3_MASK_DEFAULT 0xFFFFEFFF +#define CS35L36_INT3_MASK_RESET 0xFFFFFFFF + + +#define CS35L36_AMP_SHORT_ERR 0x1000 +#define CS35L36_BST_SHORT_ERR 0x40000 +#define CS35L36_TEMP_WARN 0x2000000 +#define CS35L36_TEMP_ERR 0x4000000 +#define CS35L36_BST_OVP_ERR 0x10000 +#define CS35L36_BST_DCM_UVP_ERR 0x20000 + +#define CS35L36_AMP_SHORT_ERR_RLS 0x02 +#define CS35L36_BST_SHORT_ERR_RLS 0x04 +#define CS35L36_BST_OVP_ERR_RLS 0x08 +#define CS35L36_BST_UVP_ERR_RLS 0x10 +#define CS35L36_TEMP_WARN_ERR_RLS 0x20 +#define CS35L36_TEMP_ERR_RLS 0x40 +#define CS35L36_TEMP_THLD_MASK 0x03 + +#define CS35L36_REV_B0 0xb0 +#define CS35L36_REV_A0 0xa0 +#define CS35L36_B0_PAC_PATCH 0x00DD0102 + +#define CS35L36_OTP_ECC_EN_MASK 0x400 +#define CS35L36_OTP_ECC_EN_SHIFT 10 +#define CS35L36_OTP_RUN_BOOT_MASK 0x01 +#define CS35L36_OTP_BOOT_DONE 0x2000000 +#define CS35L36_PAC_RESET_MASK 0x04 +#define CS35L36_PAC_RESET_SHIFT 2 +#define CS35L36_PAC_STALL_MASK 0x02 +#define CS35L36_PAC_STALL_SHIFT 1 +#define CS35L36_PAC_ENABLE_MASK 0x00000001 +#define CS35L36_PAC_MEM_ACCESS 0x01 +#define CS35L36_PAC_MEM_ACCESS_CLR 0 +#define CS35L36_SOFT_RESET 0x5AAA +#define CS35L36_MCU_BOOT_COMPLETE 0x02 +#define CS35L36_MCU_CONFIG_UNMASK 0x00FEFFFF +#define CS35L36_MCU_CONFIG_CLR 0x00010000 +#define CS35L36_MCU_CONFIG_MASK 0x00FFFFFF +#define CS35L36_GPIO_INT_SEL_MASK 0x0000003B +#define CS35L36_GPIO_INT_SEL_UNMASK 0x0000003A +#define CS35L36_PAC_RESET 0x00000000 +#define CS35L36_OTP_REV_MASK 0x00FF0000 +#define CS35L36_OTP_REV_L37 0x00CC0000 +#define CS35L36_12V_L37 37 +#define CS35L36_10V_L36 36 + +#define CS35L36_VPBR_EN_MASK 0x00001000 +#define CS35L36_VPBR_EN_SHIFT 12 + +#define CS35L36_VPBR_THLD_MASK 0x0000001F +#define CS35L36_VPBR_THLD_SHIFT 0 +#define CS35L36_VPBR_MAX_ATTN_MASK 0x00000F00 +#define CS35L36_VPBR_MAX_ATTN_SHIFT 8 +#define CS35L36_VPBR_ATK_VOL_MASK 0x0000F000 +#define CS35L36_VPBR_ATK_VOL_SHIFT 12 +#define CS35L36_VPBR_ATK_RATE_MASK 0x00070000 +#define CS35L36_VPBR_ATK_RATE_SHIFT 16 +#define CS35L36_VPBR_WAIT_MASK 0x00180000 +#define CS35L36_VPBR_WAIT_SHIFT 19 +#define CS35L36_VPBR_REL_RATE_MASK 0x00E00000 +#define CS35L36_VPBR_REL_RATE_SHIFT 21 +#define CS35L36_VPBR_MUTE_EN_MASK 0x01000000 +#define CS35L36_VPBR_MUTE_EN_SHIFT 24 + +#define CS35L36_OSC_FREQ_TRIM_MASK 0x070 +#define CS35L36_OSC_TRIM_DONE 0x08 + +#define CS35L36_FS1_DEFAULT_VAL 16 +#define CS35L36_FS2_DEFAULT_VAL 36 +#define CS35L36_FS_NOM_6MHZ 6000000 + +#define CS35L36_TEST_UNLOCK1 0x00005555 +#define CS35L36_TEST_UNLOCK2 0x0000AAAA +#define CS35L36_TEST_LOCK1 0x0000CCCC +#define CS35L36_TEST_LOCK2 0x00003333 + +#define CS35L36_PAC_PROG_MEM 512 + +#define CS35L36_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define CS35L36_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +extern const int cs35l36_a0_pac_patch[CS35L36_PAC_PROG_MEM]; + +#endif diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 849fdb2cb260..1104830edaf8 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -223,10 +223,10 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, switch (format & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: - cs4271->master = 0; + cs4271->master = false; break; case SND_SOC_DAIFMT_CBM_CFM: - cs4271->master = 1; + cs4271->master = true; val |= CS4271_MODE1_MASTER; break; default: diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c new file mode 100644 index 000000000000..ade7477d04f1 --- /dev/null +++ b/sound/soc/codecs/cs4341.c @@ -0,0 +1,346 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Cirrus Logic CS4341A ALSA SoC Codec Driver + * Author: Alexander Shiyan <shc_work@mail.ru> + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define CS4341_REG_MODE1 0x00 +#define CS4341_REG_MODE2 0x01 +#define CS4341_REG_MIX 0x02 +#define CS4341_REG_VOLA 0x03 +#define CS4341_REG_VOLB 0x04 + +#define CS4341_MODE2_DIF (7 << 4) +#define CS4341_MODE2_DIF_I2S_24 (0 << 4) +#define CS4341_MODE2_DIF_I2S_16 (1 << 4) +#define CS4341_MODE2_DIF_LJ_24 (2 << 4) +#define CS4341_MODE2_DIF_RJ_24 (3 << 4) +#define CS4341_MODE2_DIF_RJ_16 (5 << 4) +#define CS4341_VOLX_MUTE (1 << 7) + +struct cs4341_priv { + unsigned int fmt; + struct regmap *regmap; + struct regmap_config regcfg; +}; + +static const struct reg_default cs4341_reg_defaults[] = { + { CS4341_REG_MODE1, 0x00 }, + { CS4341_REG_MODE2, 0x82 }, + { CS4341_REG_MIX, 0x49 }, + { CS4341_REG_VOLA, 0x80 }, + { CS4341_REG_VOLB, 0x80 }, +}; + +static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component); + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs4341_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component); + unsigned int mode = 0; + int b24 = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S24_LE: + b24 = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + break; + default: + dev_err(component->dev, "Unsupported PCM format 0x%08x.\n", + params_format(params)); + return -EINVAL; + } + + switch (cs4341->fmt) { + case SND_SOC_DAIFMT_I2S: + mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16; + break; + case SND_SOC_DAIFMT_LEFT_J: + mode = CS4341_MODE2_DIF_LJ_24; + break; + case SND_SOC_DAIFMT_RIGHT_J: + mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16; + break; + default: + dev_err(component->dev, "Unsupported DAI format 0x%08x.\n", + cs4341->fmt); + return -EINVAL; + } + + return snd_soc_component_update_bits(component, CS4341_REG_MODE2, + CS4341_MODE2_DIF, mode); +} + +static int cs4341_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + int ret; + + ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA, + CS4341_VOLX_MUTE, + mute ? CS4341_VOLX_MUTE : 0); + if (ret < 0) + return ret; + + return snd_soc_component_update_bits(component, CS4341_REG_VOLB, + CS4341_VOLX_MUTE, + mute ? CS4341_VOLX_MUTE : 0); +} + +static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0); + +static const char * const deemph[] = { + "None", "44.1k", "48k", "32k", +}; + +static const struct soc_enum deemph_enum = + SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph); + +static const char * const srzc[] = { + "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC", +}; + +static const struct soc_enum srzc_enum = + SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc); + + +static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = { + SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OutA"), + SND_SOC_DAPM_OUTPUT("OutB"), +}; + +static const struct snd_soc_dapm_route cs4341_routes[] = { + { "OutA", NULL, "HiFi DAC" }, + { "OutB", NULL, "HiFi DAC" }, + { "DAC Playback", NULL, "OutA" }, + { "DAC Playback", NULL, "OutB" }, +}; + +static const struct snd_kcontrol_new cs4341_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", + CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv), + SOC_ENUM("De-Emphasis Control", deemph_enum), + SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum), + SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0), + SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0), +}; + +static const struct snd_soc_dai_ops cs4341_dai_ops = { + .set_fmt = cs4341_set_fmt, + .hw_params = cs4341_hw_params, + .digital_mute = cs4341_digital_mute, +}; + +static struct snd_soc_dai_driver cs4341_dai = { + .name = "cs4341a-hifi", + .playback = { + .stream_name = "DAC Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &cs4341_dai_ops, + .symmetric_rates = 1, +}; + +static const struct snd_soc_component_driver soc_component_cs4341 = { + .controls = cs4341_controls, + .num_controls = ARRAY_SIZE(cs4341_controls), + .dapm_widgets = cs4341_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4341_dapm_widgets), + .dapm_routes = cs4341_routes, + .num_dapm_routes = ARRAY_SIZE(cs4341_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct of_device_id __maybe_unused cs4341_dt_ids[] = { + { .compatible = "cirrus,cs4341a", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4341_dt_ids); + +static int cs4341_probe(struct device *dev) +{ + struct cs4341_priv *cs4341 = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++) + regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg, + cs4341_reg_defaults[i].def); + + return devm_snd_soc_register_component(dev, &soc_component_cs4341, + &cs4341_dai, 1); +} + +#if IS_ENABLED(CONFIG_I2C) +static int cs4341_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct cs4341_priv *cs4341; + + cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL); + if (!cs4341) + return -ENOMEM; + + i2c_set_clientdata(i2c, cs4341); + + cs4341->regcfg.reg_bits = 8; + cs4341->regcfg.val_bits = 8; + cs4341->regcfg.max_register = CS4341_REG_VOLB; + cs4341->regcfg.cache_type = REGCACHE_FLAT; + cs4341->regcfg.reg_defaults = cs4341_reg_defaults; + cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults); + cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg); + if (IS_ERR(cs4341->regmap)) + return PTR_ERR(cs4341->regmap); + + return cs4341_probe(&i2c->dev); +} + +static const struct i2c_device_id cs4341_i2c_id[] = { + { "cs4341", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id); + +static struct i2c_driver cs4341_i2c_driver = { + .driver = { + .name = "cs4341-i2c", + .of_match_table = of_match_ptr(cs4341_dt_ids), + }, + .probe = cs4341_i2c_probe, + .id_table = cs4341_i2c_id, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) +static bool cs4341_reg_readable(struct device *dev, unsigned int reg) +{ + return false; +} + +static int cs4341_spi_probe(struct spi_device *spi) +{ + struct cs4341_priv *cs4341; + int ret; + + cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL); + if (!cs4341) + return -ENOMEM; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + if (!spi->max_speed_hz) + spi->max_speed_hz = 6000000; + ret = spi_setup(spi); + if (ret) + return ret; + + spi_set_drvdata(spi, cs4341); + + cs4341->regcfg.reg_bits = 16; + cs4341->regcfg.val_bits = 8; + cs4341->regcfg.write_flag_mask = 0x20; + cs4341->regcfg.max_register = CS4341_REG_VOLB; + cs4341->regcfg.cache_type = REGCACHE_FLAT; + cs4341->regcfg.readable_reg = cs4341_reg_readable; + cs4341->regcfg.reg_defaults = cs4341_reg_defaults; + cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults); + cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg); + if (IS_ERR(cs4341->regmap)) + return PTR_ERR(cs4341->regmap); + + return cs4341_probe(&spi->dev); +} + +static struct spi_driver cs4341_spi_driver = { + .driver = { + .name = "cs4341-spi", + .of_match_table = of_match_ptr(cs4341_dt_ids), + }, + .probe = cs4341_spi_probe, +}; +#endif + +static int __init cs4341_init(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&cs4341_i2c_driver); + if (ret) + return ret; +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&cs4341_spi_driver); +#endif + + return ret; +} +module_init(cs4341_init); + +static void __exit cs4341_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&cs4341_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&cs4341_spi_driver); +#endif +} +module_exit(cs4341_exit); + +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 45e50fe3bf25..b16832a6a9af 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -500,72 +500,72 @@ SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index 2c7d5088e6f2..e0964b20a389 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -117,7 +117,7 @@ static void da7219_aad_hptest_work(struct work_struct *work) struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - u16 tonegen_freq_hptest; + __le16 tonegen_freq_hptest; u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8; int report = 0, ret = 0; diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index e46e9f4bc994..121a8190f93e 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -423,7 +423,7 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *mixer_ctrl = (struct soc_mixer_control *) kcontrol->private_value; unsigned int reg = mixer_ctrl->reg; - u16 val; + __le16 val; int ret; mutex_lock(&da7219->ctrl_lock); @@ -450,7 +450,7 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *mixer_ctrl = (struct soc_mixer_control *) kcontrol->private_value; unsigned int reg = mixer_ctrl->reg; - u16 val; + __le16 val; int ret; /* @@ -838,7 +838,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w, ++i; msleep(50); } - } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock)); + } while ((i < DA7219_SRM_CHECK_RETRIES) && (!srm_lock)); if (!srm_lock) dev_warn(component->dev, "SRM failed to lock\n"); @@ -1376,11 +1376,7 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - /* By default 64 BCLKs per WCLK is supported */ - dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64; - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK | DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK, dai_clk_mode); snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK, @@ -1395,69 +1391,83 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai, { struct snd_soc_component *component = dai->component; struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - u8 dai_bclks_per_wclk; + unsigned int ch_mask; + u8 dai_bclks_per_wclk, slot_offset; u16 offset; + __le16 dai_offset; u32 frame_size; - /* No channels enabled so disable TDM, revert to 64-bit frames */ + /* No channels enabled so disable TDM */ if (!tx_mask) { snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL, DA7219_DAI_TDM_CH_EN_MASK | DA7219_DAI_TDM_MODE_EN_MASK, 0); - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK, - DA7219_DAI_BCLKS_PER_WCLK_64); + da7219->tdm_en = false; return 0; } /* Check we have valid slots */ - if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) { - dev_err(component->dev, "Invalid number of slots, max = %d\n", + slot_offset = ffs(tx_mask) - 1; + ch_mask = (tx_mask >> slot_offset); + if (fls(ch_mask) > DA7219_DAI_TDM_MAX_SLOTS) { + dev_err(component->dev, + "Invalid number of slots, max = %d\n", DA7219_DAI_TDM_MAX_SLOTS); return -EINVAL; } - /* Check we have a valid offset given */ - if (rx_mask > DA7219_DAI_OFFSET_MAX) { - dev_err(component->dev, "Invalid slot offset, max = %d\n", - DA7219_DAI_OFFSET_MAX); + /* + * Ensure we have a valid offset into the frame, based on slot width + * and slot offset of first slot we're interested in. + */ + offset = slot_offset * slot_width; + if (offset > DA7219_DAI_OFFSET_MAX) { + dev_err(component->dev, "Invalid frame offset %d\n", offset); return -EINVAL; } - /* Calculate & validate frame size based on slot info provided. */ - frame_size = slots * slot_width; - switch (frame_size) { - case 32: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; - break; - case 64: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; - break; - case 128: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; - break; - case 256: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; - break; - default: - dev_err(component->dev, "Invalid frame size %d\n", frame_size); - return -EINVAL; - } + /* + * If we're master, calculate & validate frame size based on slot info + * provided as we have a limited set of rates available. + */ + if (da7219->master) { + frame_size = slots * slot_width; + switch (frame_size) { + case 32: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; + break; + default: + dev_err(component->dev, "Invalid frame size %d\n", + frame_size); + return -EINVAL; + } - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK, - dai_bclks_per_wclk); + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + } - offset = cpu_to_le16(rx_mask); + dai_offset = cpu_to_le16(offset); regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER, - &offset, sizeof(offset)); + &dai_offset, sizeof(dai_offset)); snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL, DA7219_DAI_TDM_CH_EN_MASK | DA7219_DAI_TDM_MODE_EN_MASK, - (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | + (ch_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | DA7219_DAI_TDM_MODE_EN_MASK); + da7219->tdm_en = true; + return 0; } @@ -1466,10 +1476,13 @@ static int da7219_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - u8 dai_ctrl = 0, fs; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs; unsigned int channels; + int word_len = params_width(params); + int frame_size; - switch (params_width(params)) { + switch (word_len) { case 16: dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE; break; @@ -1533,6 +1546,23 @@ static int da7219_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* + * If we're master, then we have a limited set of BCLK rates we + * support. For slave mode this isn't the case and the codec can detect + * the BCLK rate automatically. + */ + if (da7219->master && !da7219->tdm_en) { + frame_size = word_len * 2; + if (frame_size <= 32) + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + else + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + } + snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_WORD_LENGTH_MASK | DA7219_DAI_CH_NUM_MASK, @@ -1767,7 +1797,7 @@ static int da7219_dai_clks_prepare(struct clk_hw *hw) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, dai_clks_hw); - struct snd_soc_component *component = da7219->aad->component; + struct snd_soc_component *component = da7219->component; snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, DA7219_DAI_CLK_EN_MASK, @@ -1780,7 +1810,7 @@ static void da7219_dai_clks_unprepare(struct clk_hw *hw) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, dai_clks_hw); - struct snd_soc_component *component = da7219->aad->component; + struct snd_soc_component *component = da7219->component; snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, DA7219_DAI_CLK_EN_MASK, 0); @@ -1790,7 +1820,7 @@ static int da7219_dai_clks_is_prepared(struct clk_hw *hw) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, dai_clks_hw); - struct snd_soc_component *component = da7219->aad->component; + struct snd_soc_component *component = da7219->component; u8 clk_reg; clk_reg = snd_soc_component_read32(component, DA7219_DAI_CLK_MODE); @@ -1798,13 +1828,50 @@ static int da7219_dai_clks_is_prepared(struct clk_hw *hw) return !!(clk_reg & DA7219_DAI_CLK_EN_MASK); } +static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, dai_clks_hw); + struct snd_soc_component *component = da7219->component; + u8 fs = snd_soc_component_read32(component, DA7219_SR); + + switch (fs & DA7219_SR_MASK) { + case DA7219_SR_8000: + return 8000; + case DA7219_SR_11025: + return 11025; + case DA7219_SR_12000: + return 12000; + case DA7219_SR_16000: + return 16000; + case DA7219_SR_22050: + return 22050; + case DA7219_SR_24000: + return 24000; + case DA7219_SR_32000: + return 32000; + case DA7219_SR_44100: + return 44100; + case DA7219_SR_48000: + return 48000; + case DA7219_SR_88200: + return 88200; + case DA7219_SR_96000: + return 96000; + default: + return 0; + } +} + static const struct clk_ops da7219_dai_clks_ops = { .prepare = da7219_dai_clks_prepare, .unprepare = da7219_dai_clks_unprepare, .is_prepared = da7219_dai_clks_is_prepared, + .recalc_rate = da7219_dai_clks_recalc_rate, }; -static void da7219_register_dai_clks(struct snd_soc_component *component) +static int da7219_register_dai_clks(struct snd_soc_component *component) { struct device *dev = component->dev; struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); @@ -1812,18 +1879,27 @@ static void da7219_register_dai_clks(struct snd_soc_component *component) struct clk_init_data init = {}; struct clk *dai_clks; struct clk_lookup *dai_clks_lookup; + const char *parent_name; + + if (da7219->mclk) { + parent_name = __clk_get_name(da7219->mclk); + init.parent_names = &parent_name; + init.num_parents = 1; + } else { + init.parent_names = NULL; + init.num_parents = 0; + } - init.parent_names = NULL; - init.num_parents = 0; init.name = pdata->dai_clks_name; init.ops = &da7219_dai_clks_ops; + init.flags = CLK_GET_RATE_NOCACHE; da7219->dai_clks_hw.init = &init; dai_clks = devm_clk_register(dev, &da7219->dai_clks_hw); if (IS_ERR(dai_clks)) { dev_warn(dev, "Failed to register DAI clocks: %ld\n", PTR_ERR(dai_clks)); - return; + return PTR_ERR(dai_clks); } da7219->dai_clks = dai_clks; @@ -1835,13 +1911,18 @@ static void da7219_register_dai_clks(struct snd_soc_component *component) dai_clks_lookup = clkdev_create(dai_clks, pdata->dai_clks_name, "%s", dev_name(dev)); if (!dai_clks_lookup) - dev_warn(dev, "Failed to create DAI clkdev"); + return -ENOMEM; else da7219->dai_clks_lookup = dai_clks_lookup; } + + return 0; } #else -static inline void da7219_register_dai_clks(struct snd_soc_component *component) {} +static inline int da7219_register_dai_clks(struct snd_soc_component *component) +{ + return 0; +} #endif /* CONFIG_COMMON_CLK */ static void da7219_handle_pdata(struct snd_soc_component *component) @@ -1854,8 +1935,6 @@ static void da7219_handle_pdata(struct snd_soc_component *component) da7219->wakeup_source = pdata->wakeup_source; - da7219_register_dai_clks(component); - /* Mic Bias voltages */ switch (pdata->micbias_lvl) { case DA7219_MICBIAS_1_6V: @@ -1901,6 +1980,7 @@ static int da7219_probe(struct snd_soc_component *component) unsigned int rev; int ret; + da7219->component = component; mutex_init(&da7219->ctrl_lock); mutex_init(&da7219->pll_lock); @@ -1947,6 +2027,11 @@ static int da7219_probe(struct snd_soc_component *component) } } + /* Register CCF DAI clock control */ + ret = da7219_register_dai_clks(component); + if (ret) + return ret; + /* Default PC counter to free-running */ snd_soc_component_update_bits(component, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK, DA7219_PC_FREERUN_MASK); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index 3a006862f0e7..018819c631fb 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -809,6 +809,7 @@ struct da7219_aad_priv; /* Private data */ struct da7219_priv { + struct snd_soc_component *component; struct da7219_aad_priv *aad; struct da7219_pdata *pdata; @@ -829,6 +830,7 @@ struct da7219_priv { int clk_src; bool master; + bool tdm_en; bool alc_en; bool micbias_on_event; unsigned int mic_pga_delay; diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index da921da50ef0..de041369e5a7 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -44,8 +44,8 @@ struct dmic { int modeswitch_delay; }; -int dmic_daiops_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) +static int dmic_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct dmic *dmic = snd_soc_component_get_drvdata(component); diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index e97d12d578b0..6d4a323f786b 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -15,12 +15,14 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/mod_devicetable.h> +#include <linux/mutex.h> #include <linux/regmap.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/tlv.h> +#include <sound/jack.h> #include "es8316.h" /* In slave mode at single speed, the codec is documented as accepting 5 @@ -33,6 +35,11 @@ static const unsigned int supported_mclk_lrck_ratios[] = { }; struct es8316_priv { + struct mutex lock; + struct regmap *regmap; + struct snd_soc_component *component; + struct snd_soc_jack *jack; + int irq; unsigned int sysclk; unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS]; struct snd_pcm_hw_constraint_list sysclk_constraints; @@ -94,6 +101,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = { SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0), SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0), SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0), + SOC_SINGLE("DAC Mono Mix Switch", ES8316_DAC_SET3, 3, 1, 0), SOC_ENUM("Capture Polarity", adcpol), SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0), @@ -159,8 +167,6 @@ static const char * const es8316_hpmux_texts[] = { "lin-rin with Boost and PGA" }; -static const unsigned int es8316_hpmux_values[] = { 0, 1, 2, 3 }; - static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL, 4, es8316_hpmux_texts); @@ -191,8 +197,6 @@ static const char * const es8316_dacsrc_texts[] = { "RDATA TO LDAC, LDATA TO RDAC", }; -static const unsigned int es8316_dacsrc_values[] = { 0, 1, 2, 3 }; - static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1, 6, es8316_dacsrc_texts); @@ -529,8 +533,162 @@ static struct snd_soc_dai_driver es8316_dai = { .symmetric_rates = 1, }; +static void es8316_enable_micbias_for_mic_gnd_short_detect( + struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Bias"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Analog power"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Mic Bias"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); + + msleep(20); +} + +static void es8316_disable_micbias_for_mic_gnd_short_detect( + struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Bias"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Analog power"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Bias"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static irqreturn_t es8316_irq(int irq, void *data) +{ + struct es8316_priv *es8316 = data; + struct snd_soc_component *comp = es8316->component; + unsigned int flags; + + mutex_lock(&es8316->lock); + + regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); + if (flags == 0x00) + goto out; /* Powered-down / reset */ + + /* Catch spurious IRQ before set_jack is called */ + if (!es8316->jack) + goto out; + + dev_dbg(comp->dev, "gpio flags %#04x\n", flags); + if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) { + /* Jack removed, or spurious IRQ? */ + if (es8316->jack->status & SND_JACK_MICROPHONE) + es8316_disable_micbias_for_mic_gnd_short_detect(comp); + + if (es8316->jack->status & SND_JACK_HEADPHONE) { + snd_soc_jack_report(es8316->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + dev_dbg(comp->dev, "jack unplugged\n"); + } + } else if (!(es8316->jack->status & SND_JACK_HEADPHONE)) { + /* Jack inserted, determine type */ + es8316_enable_micbias_for_mic_gnd_short_detect(comp); + regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); + dev_dbg(comp->dev, "gpio flags %#04x\n", flags); + if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) { + /* Jack unplugged underneath us */ + es8316_disable_micbias_for_mic_gnd_short_detect(comp); + } else if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) { + /* Open, headset */ + snd_soc_jack_report(es8316->jack, + SND_JACK_HEADSET, + SND_JACK_HEADSET); + /* Keep mic-gnd-short detection on for button press */ + } else { + /* Shorted, headphones */ + snd_soc_jack_report(es8316->jack, + SND_JACK_HEADPHONE, + SND_JACK_HEADSET); + /* No longer need mic-gnd-short detection */ + es8316_disable_micbias_for_mic_gnd_short_detect(comp); + } + } else if (es8316->jack->status & SND_JACK_MICROPHONE) { + /* Interrupt while jack inserted, report button state */ + if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) { + /* Open, button release */ + snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0); + } else { + /* Short, button press */ + snd_soc_jack_report(es8316->jack, + SND_JACK_BTN_0, + SND_JACK_BTN_0); + } + } + +out: + mutex_unlock(&es8316->lock); + return IRQ_HANDLED; +} + +static void es8316_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + mutex_lock(&es8316->lock); + + es8316->jack = jack; + + if (es8316->jack->status & SND_JACK_MICROPHONE) + es8316_enable_micbias_for_mic_gnd_short_detect(component); + + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, + ES8316_GPIO_ENABLE_INTERRUPT); + + mutex_unlock(&es8316->lock); + + /* Enable irq and sync initial jack state */ + enable_irq(es8316->irq); + es8316_irq(es8316->irq, es8316); +} + +static void es8316_disable_jack_detect(struct snd_soc_component *component) +{ + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + disable_irq(es8316->irq); + + mutex_lock(&es8316->lock); + + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, 0); + + if (es8316->jack->status & SND_JACK_MICROPHONE) { + es8316_disable_micbias_for_mic_gnd_short_detect(component); + snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0); + } + + es8316->jack = NULL; + + mutex_unlock(&es8316->lock); +} + +static int es8316_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack) + es8316_enable_jack_detect(component, jack); + else + es8316_disable_jack_detect(component); + + return 0; +} + static int es8316_probe(struct snd_soc_component *component) { + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + es8316->component = component; + /* Reset codec and enable current state machine */ snd_soc_component_write(component, ES8316_RESET, 0x3f); usleep_range(5000, 5500); @@ -555,6 +713,7 @@ static int es8316_probe(struct snd_soc_component *component) static const struct snd_soc_component_driver soc_component_dev_es8316 = { .probe = es8316_probe, + .set_jack = es8316_set_jack, .controls = es8316_snd_controls, .num_controls = ARRAY_SIZE(es8316_snd_controls), .dapm_widgets = es8316_dapm_widgets, @@ -566,18 +725,29 @@ static const struct snd_soc_component_driver soc_component_dev_es8316 = { .non_legacy_dai_naming = 1, }; +static const struct regmap_range es8316_volatile_ranges[] = { + regmap_reg_range(ES8316_GPIO_FLAG, ES8316_GPIO_FLAG), +}; + +static const struct regmap_access_table es8316_volatile_table = { + .yes_ranges = es8316_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(es8316_volatile_ranges), +}; + static const struct regmap_config es8316_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = 0x53, + .volatile_table = &es8316_volatile_table, .cache_type = REGCACHE_RBTREE, }; static int es8316_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { + struct device *dev = &i2c_client->dev; struct es8316_priv *es8316; - struct regmap *regmap; + int ret; es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv), GFP_KERNEL); @@ -586,9 +756,23 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client, i2c_set_clientdata(i2c_client, es8316); - regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); + if (IS_ERR(es8316->regmap)) + return PTR_ERR(es8316->regmap); + + es8316->irq = i2c_client->irq; + mutex_init(&es8316->lock); + + ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "es8316", es8316); + if (ret == 0) { + /* Gets re-enabled by es8316_set_jack() */ + disable_irq(es8316->irq); + } else { + dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret); + es8316->irq = -ENXIO; + } return devm_snd_soc_register_component(&i2c_client->dev, &soc_component_dev_es8316, diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h index 6bcdd63ea459..439a0130cbb7 100644 --- a/sound/soc/codecs/es8316.h +++ b/sound/soc/codecs/es8316.h @@ -126,4 +126,11 @@ #define ES8316_SERDATA2_LEN_16 0x0c #define ES8316_SERDATA2_LEN_32 0x10 +/* ES8316_GPIO_DEBOUNCE */ +#define ES8316_GPIO_ENABLE_INTERRUPT 0x02 + +/* ES8316_GPIO_FLAG */ +#define ES8316_GPIO_FLAG_GM_NOT_SHORTED 0x02 +#define ES8316_GPIO_FLAG_HP_NOT_INSERTED 0x04 + #endif diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index b19d7a3e7a2c..5eeb0fe836a9 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1176,13 +1176,15 @@ static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid) struct hdac_hdmi_cvt *cvt; char name[NAME_SIZE]; - cvt = kzalloc(sizeof(*cvt), GFP_KERNEL); + cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL); if (!cvt) return -ENOMEM; cvt->nid = nid; sprintf(name, "cvt %d", cvt->nid); - cvt->name = kstrdup(name, GFP_KERNEL); + cvt->name = devm_kstrdup(&hdev->dev, name, GFP_KERNEL); + if (!cvt->name) + return -ENOMEM; list_add_tail(&cvt->head, &hdmi->cvt_list); hdmi->num_cvt++; @@ -1287,8 +1289,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, mutex_unlock(&hdmi->pin_mutex); } -static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, - struct hdac_hdmi_pin *pin) +static int hdac_hdmi_add_ports(struct hdac_device *hdev, + struct hdac_hdmi_pin *pin) { struct hdac_hdmi_port *ports; int max_ports = HDA_MAX_PORTS; @@ -1300,7 +1302,7 @@ static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, * implemented. */ - ports = kcalloc(max_ports, sizeof(*ports), GFP_KERNEL); + ports = devm_kcalloc(&hdev->dev, max_ports, sizeof(*ports), GFP_KERNEL); if (!ports) return -ENOMEM; @@ -1319,14 +1321,14 @@ static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) struct hdac_hdmi_pin *pin; int ret; - pin = kzalloc(sizeof(*pin), GFP_KERNEL); + pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL); if (!pin) return -ENOMEM; pin->nid = nid; pin->mst_capable = false; pin->hdev = hdev; - ret = hdac_hdmi_add_ports(hdmi, pin); + ret = hdac_hdmi_add_ports(hdev, pin); if (ret < 0) return ret; @@ -1468,8 +1470,6 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, { hda_nid_t nid; int i, num_nodes; - struct hdac_hdmi_cvt *temp_cvt, *cvt_next; - struct hdac_hdmi_pin *temp_pin, *pin_next; struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int ret; @@ -1497,51 +1497,35 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, case AC_WID_AUD_OUT: ret = hdac_hdmi_add_cvt(hdev, nid); if (ret < 0) - goto free_widgets; + return ret; break; case AC_WID_PIN: ret = hdac_hdmi_add_pin(hdev, nid); if (ret < 0) - goto free_widgets; + return ret; break; } } if (!hdmi->num_pin || !hdmi->num_cvt) { ret = -EIO; - goto free_widgets; + dev_err(&hdev->dev, "Bad pin/cvt setup in %s\n", __func__); + return ret; } ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt); if (ret) { dev_err(&hdev->dev, "Failed to create dais with err: %d\n", - ret); - goto free_widgets; + ret); + return ret; } *num_dais = hdmi->num_cvt; ret = hdac_hdmi_init_dai_map(hdev); if (ret < 0) - goto free_widgets; - - return ret; - -free_widgets: - list_for_each_entry_safe(temp_cvt, cvt_next, &hdmi->cvt_list, head) { - list_del(&temp_cvt->head); - kfree(temp_cvt->name); - kfree(temp_cvt); - } - - list_for_each_entry_safe(temp_pin, pin_next, &hdmi->pin_list, head) { - for (i = 0; i < temp_pin->num_ports; i++) - temp_pin->ports[i].pin = NULL; - kfree(temp_pin->ports); - list_del(&temp_pin->head); - kfree(temp_pin); - } - + dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n", + ret); return ret; } @@ -1782,7 +1766,7 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, * this is a new PCM device, create new pcm and * add to the pcm list */ - pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL); if (!pcm) return -ENOMEM; pcm->pcm_id = device; @@ -1798,7 +1782,6 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, dev_err(&hdev->dev, "chmap control add failed with err: %d for pcm: %d\n", err, device); - kfree(pcm); return err; } } @@ -2075,42 +2058,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) static int hdac_hdmi_dev_remove(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); - struct hdac_hdmi_pin *pin, *pin_next; - struct hdac_hdmi_cvt *cvt, *cvt_next; - struct hdac_hdmi_pcm *pcm, *pcm_next; - struct hdac_hdmi_port *port, *port_next; - int i; - snd_hdac_display_power(hdev->bus, hdev->addr, false); - list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) { - pcm->cvt = NULL; - if (list_empty(&pcm->port_list)) - continue; - - list_for_each_entry_safe(port, port_next, - &pcm->port_list, head) - list_del(&port->head); - - list_del(&pcm->head); - kfree(pcm); - } - - list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) { - list_del(&cvt->head); - kfree(cvt->name); - kfree(cvt); - } - - list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) { - for (i = 0; i < pin->num_ports; i++) - pin->ports[i].pin = NULL; - kfree(pin->ports); - list_del(&pin->head); - kfree(pin); - } - return 0; } diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c new file mode 100644 index 000000000000..766354c73076 --- /dev/null +++ b/sound/soc/codecs/jz4725b.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// JZ4725B CODEC driver +// +// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/regmap.h> +#include <linux/clk.h> + +#include <linux/delay.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define ICDC_RGADW_OFFSET 0x00 +#define ICDC_RGDATA_OFFSET 0x04 + +/* ICDC internal register access control register(RGADW) */ +#define ICDC_RGADW_RGWR BIT(16) + +#define ICDC_RGADW_RGADDR_OFFSET 8 +#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET) + +#define ICDC_RGADW_RGDIN_OFFSET 0 +#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET) + +/* ICDC internal register data output register (RGDATA)*/ +#define ICDC_RGDATA_IRQ BIT(8) + +#define ICDC_RGDATA_RGDOUT_OFFSET 0 +#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET) + +/* JZ internal register space */ +enum { + JZ4725B_CODEC_REG_AICR, + JZ4725B_CODEC_REG_CR1, + JZ4725B_CODEC_REG_CR2, + JZ4725B_CODEC_REG_CCR1, + JZ4725B_CODEC_REG_CCR2, + JZ4725B_CODEC_REG_PMR1, + JZ4725B_CODEC_REG_PMR2, + JZ4725B_CODEC_REG_CRR, + JZ4725B_CODEC_REG_ICR, + JZ4725B_CODEC_REG_IFR, + JZ4725B_CODEC_REG_CGR1, + JZ4725B_CODEC_REG_CGR2, + JZ4725B_CODEC_REG_CGR3, + JZ4725B_CODEC_REG_CGR4, + JZ4725B_CODEC_REG_CGR5, + JZ4725B_CODEC_REG_CGR6, + JZ4725B_CODEC_REG_CGR7, + JZ4725B_CODEC_REG_CGR8, + JZ4725B_CODEC_REG_CGR9, + JZ4725B_CODEC_REG_CGR10, + JZ4725B_CODEC_REG_TR1, + JZ4725B_CODEC_REG_TR2, + JZ4725B_CODEC_REG_CR3, + JZ4725B_CODEC_REG_AGC1, + JZ4725B_CODEC_REG_AGC2, + JZ4725B_CODEC_REG_AGC3, + JZ4725B_CODEC_REG_AGC4, + JZ4725B_CODEC_REG_AGC5, +}; + +#define REG_AICR_CONFIG1_OFFSET 0 +#define REG_AICR_CONFIG1_MASK (0xf << REG_AICR_CONFIG1_OFFSET) + +#define REG_CR1_SB_MICBIAS_OFFSET 7 +#define REG_CR1_MONO_OFFSET 6 +#define REG_CR1_DAC_MUTE_OFFSET 5 +#define REG_CR1_HP_DIS_OFFSET 4 +#define REG_CR1_DACSEL_OFFSET 3 +#define REG_CR1_BYPASS_OFFSET 2 + +#define REG_CR2_DAC_DEEMP_OFFSET 7 +#define REG_CR2_DAC_ADWL_OFFSET 5 +#define REG_CR2_DAC_ADWL_MASK (0x3 << REG_CR2_DAC_ADWL_OFFSET) +#define REG_CR2_ADC_ADWL_OFFSET 3 +#define REG_CR2_ADC_ADWL_MASK (0x3 << REG_CR2_ADC_ADWL_OFFSET) +#define REG_CR2_ADC_HPF_OFFSET 2 + +#define REG_CR3_SB_MIC1_OFFSET 7 +#define REG_CR3_SB_MIC2_OFFSET 6 +#define REG_CR3_SIDETONE1_OFFSET 5 +#define REG_CR3_SIDETONE2_OFFSET 4 +#define REG_CR3_MICDIFF_OFFSET 3 +#define REG_CR3_MICSTEREO_OFFSET 2 +#define REG_CR3_INSEL_OFFSET 0 +#define REG_CR3_INSEL_MASK (0x3 << REG_CR3_INSEL_OFFSET) + +#define REG_CCR1_CONFIG4_OFFSET 0 +#define REG_CCR1_CONFIG4_MASK (0xf << REG_CCR1_CONFIG4_OFFSET) + +#define REG_CCR2_DFREQ_OFFSET 4 +#define REG_CCR2_DFREQ_MASK (0xf << REG_CCR2_DFREQ_OFFSET) +#define REG_CCR2_AFREQ_OFFSET 0 +#define REG_CCR2_AFREQ_MASK (0xf << REG_CCR2_AFREQ_OFFSET) + +#define REG_PMR1_SB_DAC_OFFSET 7 +#define REG_PMR1_SB_OUT_OFFSET 6 +#define REG_PMR1_SB_MIX_OFFSET 5 +#define REG_PMR1_SB_ADC_OFFSET 4 +#define REG_PMR1_SB_LIN_OFFSET 3 +#define REG_PMR1_SB_IND_OFFSET 0 + +#define REG_PMR2_LRGI_OFFSET 7 +#define REG_PMR2_RLGI_OFFSET 6 +#define REG_PMR2_LRGOD_OFFSET 5 +#define REG_PMR2_RLGOD_OFFSET 4 +#define REG_PMR2_GIM_OFFSET 3 +#define REG_PMR2_SB_MC_OFFSET 2 +#define REG_PMR2_SB_OFFSET 1 +#define REG_PMR2_SB_SLEEP_OFFSET 0 + +#define REG_IFR_RAMP_UP_DONE_OFFSET 3 +#define REG_IFR_RAMP_DOWN_DONE_OFFSET 2 + +#define REG_CGR1_GODL_OFFSET 4 +#define REG_CGR1_GODL_MASK (0xf << REG_CGR1_GODL_OFFSET) +#define REG_CGR1_GODR_OFFSET 0 +#define REG_CGR1_GODR_MASK (0xf << REG_CGR1_GODR_OFFSET) + +#define REG_CGR2_GO1R_OFFSET 0 +#define REG_CGR2_GO1R_MASK (0x1f << REG_CGR2_GO1R_OFFSET) + +#define REG_CGR3_GO1L_OFFSET 0 +#define REG_CGR3_GO1L_MASK (0x1f << REG_CGR3_GO1L_OFFSET) + +struct jz_icdc { + struct regmap *regmap; + void __iomem *base; + struct clk *clk; +}; + +static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(jz4725b_dac_tlv, -2250, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(jz4725b_line_tlv, -1500, 600); + +static const struct snd_kcontrol_new jz4725b_codec_controls[] = { + SOC_DOUBLE_TLV("Master Playback Volume", + JZ4725B_CODEC_REG_CGR1, + REG_CGR1_GODL_OFFSET, + REG_CGR1_GODR_OFFSET, + 0xf, 1, jz4725b_dac_tlv), + SOC_DOUBLE_R_TLV("Master Capture Volume", + JZ4725B_CODEC_REG_CGR3, + JZ4725B_CODEC_REG_CGR2, + REG_CGR2_GO1R_OFFSET, + 0x1f, 1, jz4725b_line_tlv), + + SOC_SINGLE("Master Playback Switch", JZ4725B_CODEC_REG_CR1, + REG_CR1_DAC_MUTE_OFFSET, 1, 1), + + SOC_SINGLE("Deemphasize Filter Playback Switch", + JZ4725B_CODEC_REG_CR2, + REG_CR2_DAC_DEEMP_OFFSET, 1, 0), + + SOC_SINGLE("High-Pass Filter Capture Switch", + JZ4725B_CODEC_REG_CR2, + REG_CR2_ADC_HPF_OFFSET, 1, 0), +}; + +static const char * const jz4725b_codec_adc_src_texts[] = { + "Mic 1", "Mic 2", "Line In", "Mixer", +}; +static const unsigned int jz4725b_codec_adc_src_values[] = { 0, 1, 2, 3, }; +static SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum, + JZ4725B_CODEC_REG_CR3, + REG_CR3_INSEL_OFFSET, + REG_CR3_INSEL_MASK, + jz4725b_codec_adc_src_texts, + jz4725b_codec_adc_src_values); +static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl = + SOC_DAPM_ENUM("Route", jz4725b_codec_adc_src_enum); + +static const struct snd_kcontrol_new jz4725b_codec_mixer_controls[] = { + SOC_DAPM_SINGLE("Line In Bypass", JZ4725B_CODEC_REG_CR1, + REG_CR1_BYPASS_OFFSET, 1, 0), +}; + +static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct jz_icdc *icdc = snd_soc_component_get_drvdata(codec); + struct regmap *map = icdc->regmap; + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return regmap_update_bits(map, JZ4725B_CODEC_REG_IFR, + BIT(REG_IFR_RAMP_UP_DONE_OFFSET), 0); + case SND_SOC_DAPM_POST_PMU: + return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, + val, val & BIT(REG_IFR_RAMP_UP_DONE_OFFSET), + 100000, 500000); + case SND_SOC_DAPM_PRE_PMD: + return regmap_update_bits(map, JZ4725B_CODEC_REG_IFR, + BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET), 0); + case SND_SOC_DAPM_POST_PMD: + return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, + val, val & BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET), + 100000, 500000); + default: + return -EINVAL; + } +} + +static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = { + /* DAC */ + SND_SOC_DAPM_DAC("DAC", "Playback", + JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_DAC_OFFSET, 1), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC", "Capture", + JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_ADC_OFFSET, 1), + + SND_SOC_DAPM_MUX("ADC Source", SND_SOC_NOPM, 0, 0, + &jz4725b_codec_adc_src_ctrl), + + /* Mixer */ + SND_SOC_DAPM_MIXER("Mixer", JZ4725B_CODEC_REG_PMR1, + REG_PMR1_SB_MIX_OFFSET, 1, + jz4725b_codec_mixer_controls, + ARRAY_SIZE(jz4725b_codec_mixer_controls)), + SND_SOC_DAPM_MIXER("DAC to Mixer", JZ4725B_CODEC_REG_CR1, + REG_CR1_DACSEL_OFFSET, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Line In", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("HP Out", JZ4725B_CODEC_REG_CR1, + REG_CR1_HP_DIS_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_MIXER("Mic 1", JZ4725B_CODEC_REG_CR3, + REG_CR3_SB_MIC1_OFFSET, 1, NULL, 0), + SND_SOC_DAPM_MIXER("Mic 2", JZ4725B_CODEC_REG_CR3, + REG_CR3_SB_MIC2_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_MIXER_E("Out Stage", JZ4725B_CODEC_REG_PMR1, + REG_PMR1_SB_OUT_OFFSET, 1, NULL, 0, + jz4725b_out_stage_enable, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("Mixer to ADC", JZ4725B_CODEC_REG_PMR1, + REG_PMR1_SB_IND_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", JZ4725B_CODEC_REG_CR1, + REG_CR1_SB_MICBIAS_OFFSET, 1, NULL, 0), + + /* Pins */ + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1N"), + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2N"), + + SND_SOC_DAPM_INPUT("LLINEIN"), + SND_SOC_DAPM_INPUT("RLINEIN"), + + SND_SOC_DAPM_OUTPUT("LHPOUT"), + SND_SOC_DAPM_OUTPUT("RHPOUT"), +}; + +static const struct snd_soc_dapm_route jz4725b_codec_dapm_routes[] = { + {"Mic 1", NULL, "MIC1P"}, + {"Mic 1", NULL, "MIC1N"}, + {"Mic 2", NULL, "MIC2P"}, + {"Mic 2", NULL, "MIC2N"}, + + {"Line In", NULL, "LLINEIN"}, + {"Line In", NULL, "RLINEIN"}, + + {"Mixer", "Line In Bypass", "Line In"}, + {"DAC to Mixer", NULL, "DAC"}, + {"Mixer", NULL, "DAC to Mixer"}, + + {"Mixer to ADC", NULL, "Mixer"}, + {"ADC Source", "Mixer", "Mixer to ADC"}, + {"ADC Source", "Line In", "Line In"}, + {"ADC Source", "Mic 1", "Mic 1"}, + {"ADC Source", "Mic 2", "Mic 2"}, + {"ADC", NULL, "ADC Source"}, + + {"Out Stage", NULL, "Mixer"}, + {"HP Out", NULL, "Out Stage"}, + {"LHPOUT", NULL, "HP Out"}, + {"RHPOUT", NULL, "HP Out"}, +}; + +static int jz4725b_codec_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); + struct regmap *map = icdc->regmap; + + switch (level) { + case SND_SOC_BIAS_ON: + regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_SLEEP_OFFSET), 0); + break; + case SND_SOC_BIAS_PREPARE: + /* Enable sound hardware */ + regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_OFFSET), 0); + msleep(224); + break; + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_SLEEP_OFFSET), + BIT(REG_PMR2_SB_SLEEP_OFFSET)); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_OFFSET), + BIT(REG_PMR2_SB_OFFSET)); + break; + } + + return 0; +} + +static int jz4725b_codec_dev_probe(struct snd_soc_component *component) +{ + struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); + struct regmap *map = icdc->regmap; + + clk_prepare_enable(icdc->clk); + + /* Write CONFIGn (n=1 to 8) bits. + * The value 0x0f is specified in the datasheet as a requirement. + */ + regmap_write(map, JZ4725B_CODEC_REG_AICR, + 0xf << REG_AICR_CONFIG1_OFFSET); + regmap_write(map, JZ4725B_CODEC_REG_CCR1, + 0x0 << REG_CCR1_CONFIG4_OFFSET); + + return 0; +} + +static void jz4725b_codec_dev_remove(struct snd_soc_component *component) +{ + struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(icdc->clk); +} + +static const struct snd_soc_component_driver jz4725b_codec = { + .probe = jz4725b_codec_dev_probe, + .remove = jz4725b_codec_dev_remove, + .set_bias_level = jz4725b_codec_set_bias_level, + .controls = jz4725b_codec_controls, + .num_controls = ARRAY_SIZE(jz4725b_codec_controls), + .dapm_widgets = jz4725b_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(jz4725b_codec_dapm_widgets), + .dapm_routes = jz4725b_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(jz4725b_codec_dapm_routes), + .suspend_bias_off = 1, + .use_pmdown_time = 1, +}; + +static const unsigned int jz4725b_codec_sample_rates[] = { + 96000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, + 11025, 9600, 8000, +}; + +static int jz4725b_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct jz_icdc *icdc = snd_soc_component_get_drvdata(dai->component); + unsigned int rate, bit_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + bit_width = 0; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + bit_width = 1; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + bit_width = 2; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 3; + break; + default: + return -EINVAL; + } + + for (rate = 0; rate < ARRAY_SIZE(jz4725b_codec_sample_rates); rate++) { + if (jz4725b_codec_sample_rates[rate] == params_rate(params)) + break; + } + + if (rate == ARRAY_SIZE(jz4725b_codec_sample_rates)) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(icdc->regmap, + JZ4725B_CODEC_REG_CR2, + REG_CR2_DAC_ADWL_MASK, + bit_width << REG_CR2_DAC_ADWL_OFFSET); + + regmap_update_bits(icdc->regmap, + JZ4725B_CODEC_REG_CCR2, + REG_CCR2_DFREQ_MASK, + rate << REG_CCR2_DFREQ_OFFSET); + } else { + regmap_update_bits(icdc->regmap, + JZ4725B_CODEC_REG_CR2, + REG_CR2_ADC_ADWL_MASK, + bit_width << REG_CR2_ADC_ADWL_OFFSET); + + regmap_update_bits(icdc->regmap, + JZ4725B_CODEC_REG_CCR2, + REG_CCR2_AFREQ_MASK, + rate << REG_CCR2_AFREQ_OFFSET); + } + + return 0; +} + +static const struct snd_soc_dai_ops jz4725b_codec_dai_ops = { + .hw_params = jz4725b_codec_hw_params, +}; + +#define JZ_ICDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE) + +static struct snd_soc_dai_driver jz4725b_codec_dai = { + .name = "jz4725b-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_ICDC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_ICDC_FORMATS, + }, + .ops = &jz4725b_codec_dai_ops, +}; + +static bool jz4725b_codec_volatile(struct device *dev, unsigned int reg) +{ + return reg == JZ4725B_CODEC_REG_IFR; +} + +static bool jz4725b_codec_can_access_reg(struct device *dev, unsigned int reg) +{ + return (reg != JZ4725B_CODEC_REG_TR1) && (reg != JZ4725B_CODEC_REG_TR2); +} + +static int jz4725b_codec_io_wait(struct jz_icdc *icdc) +{ + u32 reg; + + return readl_poll_timeout(icdc->base + ICDC_RGADW_OFFSET, reg, + !(reg & ICDC_RGADW_RGWR), 1000, 10000); +} + +static int jz4725b_codec_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct jz_icdc *icdc = context; + unsigned int i; + u32 tmp; + int ret; + + ret = jz4725b_codec_io_wait(icdc); + if (ret) + return ret; + + tmp = readl(icdc->base + ICDC_RGADW_OFFSET); + tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK) + | (reg << ICDC_RGADW_RGADDR_OFFSET); + writel(tmp, icdc->base + ICDC_RGADW_OFFSET); + + /* wait 6+ cycles */ + for (i = 0; i < 6; i++) + *val = readl(icdc->base + ICDC_RGDATA_OFFSET) & + ICDC_RGDATA_RGDOUT_MASK; + + return 0; +} + +static int jz4725b_codec_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct jz_icdc *icdc = context; + int ret; + + ret = jz4725b_codec_io_wait(icdc); + if (ret) + return ret; + + writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val, + icdc->base + ICDC_RGADW_OFFSET); + + ret = jz4725b_codec_io_wait(icdc); + if (ret) + return ret; + + return 0; +} + +static const u8 jz4725b_codec_reg_defaults[] = { + 0x0c, 0xaa, 0x78, 0x00, 0x00, 0xff, 0x03, 0x51, + 0x3f, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0xc0, 0x34, + 0x07, 0x44, 0x1f, 0x00, +}; + +static const struct regmap_config jz4725b_codec_regmap_config = { + .reg_bits = 7, + .val_bits = 8, + + .max_register = JZ4725B_CODEC_REG_AGC5, + .volatile_reg = jz4725b_codec_volatile, + .readable_reg = jz4725b_codec_can_access_reg, + .writeable_reg = jz4725b_codec_can_access_reg, + + .reg_read = jz4725b_codec_reg_read, + .reg_write = jz4725b_codec_reg_write, + + .reg_defaults_raw = jz4725b_codec_reg_defaults, + .num_reg_defaults_raw = ARRAY_SIZE(jz4725b_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int jz4725b_codec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz_icdc *icdc; + struct resource *mem; + int ret; + + icdc = devm_kzalloc(dev, sizeof(*icdc), GFP_KERNEL); + if (!icdc) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + icdc->base = devm_ioremap_resource(dev, mem); + if (IS_ERR(icdc->base)) + return PTR_ERR(icdc->base); + + icdc->regmap = devm_regmap_init(dev, NULL, icdc, + &jz4725b_codec_regmap_config); + if (IS_ERR(icdc->regmap)) + return PTR_ERR(icdc->regmap); + + icdc->clk = devm_clk_get(&pdev->dev, "aic"); + if (IS_ERR(icdc->clk)) + return PTR_ERR(icdc->clk); + + platform_set_drvdata(pdev, icdc); + + ret = devm_snd_soc_register_component(dev, &jz4725b_codec, + &jz4725b_codec_dai, 1); + if (ret) + dev_err(dev, "Failed to register codec\n"); + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id jz4725b_codec_of_matches[] = { + { .compatible = "ingenic,jz4725b-codec", }, + { } +}; +MODULE_DEVICE_TABLE(of, jz4725b_codec_of_matches); +#endif + +static struct platform_driver jz4725b_codec_driver = { + .probe = jz4725b_codec_probe, + .driver = { + .name = "jz4725b-codec", + .of_match_table = of_match_ptr(jz4725b_codec_of_matches), + }, +}; +module_platform_driver(jz4725b_codec_driver); + +MODULE_DESCRIPTION("JZ4725B SoC internal codec driver"); +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 9395b583432c..974e17fa1911 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -1,15 +1,8 @@ -/* - * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ +// SPDX-License-Identifier: GPL-2.0 +// +// JZ4740 CODEC driver +// +// Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> #include <linux/kernel.h> #include <linux/module.h> @@ -353,10 +346,19 @@ static int jz4740_codec_probe(struct platform_device *pdev) return ret; } +#ifdef CONFIG_OF +static const struct of_device_id jz4740_codec_of_matches[] = { + { .compatible = "ingenic,jz4740-codec", }, + { } +}; +MODULE_DEVICE_TABLE(of, jz4740_codec_of_matches); +#endif + static struct platform_driver jz4740_codec_driver = { .probe = jz4740_codec_probe, .driver = { .name = "jz4740-codec", + .of_match_table = of_match_ptr(jz4740_codec_of_matches), }, }; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index c97f21836c66..30c242c38d99 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -314,9 +314,6 @@ static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0); static const DECLARE_TLV_DB_SCALE(max98090_dvg_tlv, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(max98090_dv_tlv, -1500, 100, 0); -static const DECLARE_TLV_DB_SCALE(max98090_sidetone_tlv, -6050, 200, 0); - -static const DECLARE_TLV_DB_SCALE(max98090_alc_tlv, -1500, 100, 0); static const DECLARE_TLV_DB_SCALE(max98090_alcmakeup_tlv, 0, 100, 0); static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0); static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0); @@ -817,18 +814,6 @@ static SOC_ENUM_SINGLE_VIRT_DECL(dmic_mux_enum, dmic_mux_text); static const struct snd_kcontrol_new max98090_dmic_mux = SOC_DAPM_ENUM("DMIC Mux", dmic_mux_enum); -static const char *max98090_micpre_text[] = { "Off", "On" }; - -static SOC_ENUM_SINGLE_DECL(max98090_pa1en_enum, - M98090_REG_MIC1_INPUT_LEVEL, - M98090_MIC_PA1EN_SHIFT, - max98090_micpre_text); - -static SOC_ENUM_SINGLE_DECL(max98090_pa2en_enum, - M98090_REG_MIC2_INPUT_LEVEL, - M98090_MIC_PA2EN_SHIFT, - max98090_micpre_text); - /* LINEA mixer switch */ static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = { SOC_DAPM_SINGLE("IN1 Switch", M98090_REG_LINE_INPUT_CONFIG, diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c index 9c8616a7b61c..528695cd6a1c 100644 --- a/sound/soc/codecs/max98373.c +++ b/sound/soc/codecs/max98373.c @@ -408,7 +408,7 @@ static int max98373_dac_event(struct snd_soc_dapm_widget *w, regmap_update_bits(max98373->regmap, MAX98373_R20FF_GLOBAL_SHDN, MAX98373_GLOBAL_EN_MASK, 0); - max98373->tdm_mode = 0; + max98373->tdm_mode = false; break; default: return 0; @@ -919,9 +919,9 @@ static int max98373_i2c_probe(struct i2c_client *i2c, /* update interleave mode info */ if (device_property_read_bool(&i2c->dev, "maxim,interleave_mode")) - max98373->interleave_mode = 1; + max98373->interleave_mode = true; else - max98373->interleave_mode = 0; + max98373->interleave_mode = false; /* regmap initialization */ diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index de3d44e9199b..8be636fe6552 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -615,7 +615,8 @@ static int max9860_probe(struct i2c_client *i2c) max9860->dvddio_nb.notifier_call = max9860_dvddio_event; - ret = regulator_register_notifier(max9860->dvddio, &max9860->dvddio_nb); + ret = devm_regulator_register_notifier(max9860->dvddio, + &max9860->dvddio_nb); if (ret) dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret); diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 065303a46535..e53d2007f3be 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -505,7 +505,7 @@ static int max98927_dac_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - max98927->tdm_mode = 0; + max98927->tdm_mode = false; break; case SND_SOC_DAPM_POST_PMU: regmap_update_bits(max98927->regmap, @@ -886,11 +886,11 @@ static int max98927_i2c_probe(struct i2c_client *i2c, if (!of_property_read_u32(i2c->dev.of_node, "interleave_mode", &value)) { if (value > 0) - max98927->interleave_mode = 1; + max98927->interleave_mode = true; else - max98927->interleave_mode = 0; + max98927->interleave_mode = false; } else - max98927->interleave_mode = 0; + max98927->interleave_mode = false; /* regmap initialization */ max98927->regmap diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index b7cf7cce95fe..368b6c09474b 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016, The Linux Foundation. All rights reserved. + #include <linux/module.h> #include <linux/err.h> #include <linux/kernel.h> diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c index 3063dedd21cf..a63961861e55 100644 --- a/sound/soc/codecs/msm8916-wcd-digital.c +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -1,14 +1,5 @@ -/* 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016, The Linux Foundation. All rights reserved. #include <linux/module.h> #include <linux/err.h> @@ -220,8 +211,6 @@ static const char *const dec_mux_text[] = { }; static const char *const cic_mux_text[] = { "AMIC", "DMIC" }; -static const char *const rx_mix2_text[] = { "ZERO", "IIR1", "IIR2" }; -static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" }; /* RX1 MIX1 */ static const struct soc_enum rx_mix1_inp_enum[] = { @@ -230,10 +219,6 @@ static const struct soc_enum rx_mix1_inp_enum[] = { SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B2_CTL, 0, 6, rx_mix1_text), }; -/* RX1 MIX2 */ -static const struct soc_enum rx_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( - LPASS_CDC_CONN_RX1_B3_CTL, 0, 3, rx_mix2_text); - /* RX2 MIX1 */ static const struct soc_enum rx2_mix1_inp_enum[] = { SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), @@ -241,10 +226,6 @@ static const struct soc_enum rx2_mix1_inp_enum[] = { SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B2_CTL, 0, 6, rx_mix1_text), }; -/* RX2 MIX2 */ -static const struct soc_enum rx2_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( - LPASS_CDC_CONN_RX2_B3_CTL, 0, 3, rx_mix2_text); - /* RX3 MIX1 */ static const struct soc_enum rx3_mix1_inp_enum[] = { SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c index f73dcd753584..4b3ce01c5a93 100644 --- a/sound/soc/codecs/mt6351.c +++ b/sound/soc/codecs/mt6351.c @@ -1415,8 +1415,6 @@ static const struct snd_soc_dapm_route mt6351_dapm_routes[] = { static int mt6351_codec_init_reg(struct snd_soc_component *cmpnt) { - int ret = 0; - /* Disable CLKSQ 26MHz */ regmap_update_bits(cmpnt->regmap, MT6351_TOP_CLKSQ, 0x0001, 0x0); /* disable AUDGLB */ @@ -1434,7 +1432,7 @@ static int mt6351_codec_init_reg(struct snd_soc_component *cmpnt) /* Reverse the PMIC clock*/ regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2, 0x8000, 0x8000); - return ret; + return 0; } static int mt6351_codec_probe(struct snd_soc_component *cmpnt) diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c new file mode 100644 index 000000000000..d4c4fee6d3d9 --- /dev/null +++ b/sound/soc/codecs/mt6358.c @@ -0,0 +1,2336 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6358.c -- mt6358 ALSA SoC audio codec driver +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include <linux/mfd/mt6397/core.h> +#include <linux/regulator/consumer.h> + +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "mt6358.h" + +enum { + AUDIO_ANALOG_VOLUME_HSOUTL, + AUDIO_ANALOG_VOLUME_HSOUTR, + AUDIO_ANALOG_VOLUME_HPOUTL, + AUDIO_ANALOG_VOLUME_HPOUTR, + AUDIO_ANALOG_VOLUME_LINEOUTL, + AUDIO_ANALOG_VOLUME_LINEOUTR, + AUDIO_ANALOG_VOLUME_MICAMP1, + AUDIO_ANALOG_VOLUME_MICAMP2, + AUDIO_ANALOG_VOLUME_TYPE_MAX +}; + +enum { + MUX_ADC_L, + MUX_ADC_R, + MUX_PGA_L, + MUX_PGA_R, + MUX_MIC_TYPE, + MUX_HP_L, + MUX_HP_R, + MUX_NUM, +}; + +enum { + DEVICE_HP, + DEVICE_LO, + DEVICE_RCV, + DEVICE_MIC1, + DEVICE_MIC2, + DEVICE_NUM +}; + +/* Supply widget subseq */ +enum { + /* common */ + SUPPLY_SEQ_CLK_BUF, + SUPPLY_SEQ_AUD_GLB, + SUPPLY_SEQ_CLKSQ, + SUPPLY_SEQ_VOW_AUD_LPW, + SUPPLY_SEQ_AUD_VOW, + SUPPLY_SEQ_VOW_CLK, + SUPPLY_SEQ_VOW_LDO, + SUPPLY_SEQ_TOP_CK, + SUPPLY_SEQ_TOP_CK_LAST, + SUPPLY_SEQ_AUD_TOP, + SUPPLY_SEQ_AUD_TOP_LAST, + SUPPLY_SEQ_AFE, + /* capture */ + SUPPLY_SEQ_ADC_SUPPLY, +}; + +enum { + CH_L = 0, + CH_R, + NUM_CH, +}; + +#define REG_STRIDE 2 + +struct mt6358_priv { + struct device *dev; + struct regmap *regmap; + + unsigned int dl_rate; + unsigned int ul_rate; + + int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX]; + unsigned int mux_select[MUX_NUM]; + + int dev_counter[DEVICE_NUM]; + + int mtkaif_protocol; + + struct regulator *avdd_reg; +}; + +int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, + int mtkaif_protocol) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + priv->mtkaif_protocol = mtkaif_protocol; + return 0; +} + +static void playback_gpio_set(struct mt6358_priv *priv) +{ + /* set gpio mosi mode */ + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_CLR, + 0x01f8, 0x01f8); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_SET, + 0xffff, 0x0249); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2, + 0xffff, 0x0249); +} + +static void playback_gpio_reset(struct mt6358_priv *priv) +{ + /* set pad_aud_*_mosi to GPIO mode and dir input + * reason: + * pad_aud_dat_mosi*, because the pin is used as boot strap + * don't clean clk/sync, for mtkaif protocol 2 + */ + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_CLR, + 0x01f8, 0x01f8); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2, + 0x01f8, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_DIR0, + 0xf << 8, 0x0); +} + +static void capture_gpio_set(struct mt6358_priv *priv) +{ + /* set gpio miso mode */ + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_CLR, + 0xffff, 0xffff); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_SET, + 0xffff, 0x0249); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, + 0xffff, 0x0249); +} + +static void capture_gpio_reset(struct mt6358_priv *priv) +{ + /* set pad_aud_*_miso to GPIO mode and dir input + * reason: + * pad_aud_clk_miso, because when playback only the miso_clk + * will also have 26m, so will have power leak + * pad_aud_dat_miso*, because the pin is used as boot strap + */ + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_CLR, + 0xffff, 0xffff); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_DIR0, + 0xf << 12, 0x0); +} + +/* use only when not govern by DAPM */ +static int mt6358_set_dcxo(struct mt6358_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, + 0x1 << RG_XO_AUDIO_EN_M_SFT, + (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT); + return 0; +} + +/* use only when not govern by DAPM */ +static int mt6358_set_clksq(struct mt6358_priv *priv, bool enable) +{ + /* audio clk source from internal dcxo */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6, + RG_CLKSQ_IN_SEL_TEST_MASK_SFT, + 0x0); + + /* Enable/disable CLKSQ 26MHz */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6, + RG_CLKSQ_EN_MASK_SFT, + (enable ? 1 : 0) << RG_CLKSQ_EN_SFT); + return 0; +} + +/* use only when not govern by DAPM */ +static int mt6358_set_aud_global_bias(struct mt6358_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + RG_AUDGLB_PWRDN_VA28_MASK_SFT, + (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA28_SFT); + return 0; +} + +/* use only when not govern by DAPM */ +static int mt6358_set_topck(struct mt6358_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, + 0x0066, enable ? 0x0 : 0x66); + return 0; +} + +static int mt6358_mtkaif_tx_enable(struct mt6358_priv *priv) +{ + switch (priv->mtkaif_protocol) { + case MT6358_MTKAIF_PROTOCOL_2_CLK_P2: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6358_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0010); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3800); + regmap_update_bits(priv->regmap, + MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3900); + break; + case MT6358_MTKAIF_PROTOCOL_2: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6358_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0010); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3100); + break; + case MT6358_MTKAIF_PROTOCOL_1: + default: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6358_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0000); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3100); + break; + } + return 0; +} + +static int mt6358_mtkaif_tx_disable(struct mt6358_priv *priv) +{ + /* disable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3000); + return 0; +} + +int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + playback_gpio_set(priv); + capture_gpio_set(priv); + mt6358_mtkaif_tx_enable(priv); + + mt6358_set_dcxo(priv, true); + mt6358_set_aud_global_bias(priv, true); + mt6358_set_clksq(priv, true); + mt6358_set_topck(priv, true); + + /* set dat_miso_loopback on */ + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); + return 0; +} + +int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + /* set dat_miso_loopback off */ + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); + + mt6358_set_topck(priv, false); + mt6358_set_clksq(priv, false); + mt6358_set_aud_global_bias(priv, false); + mt6358_set_dcxo(priv, false); + + mt6358_mtkaif_tx_disable(priv); + playback_gpio_reset(priv); + capture_gpio_reset(priv); + return 0; +} + +int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, + int phase_1, int phase_2) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT, + phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT, + phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT); + return 0; +} + +/* dl pga gain */ +enum { + DL_GAIN_8DB = 0, + DL_GAIN_0DB = 8, + DL_GAIN_N_1DB = 9, + DL_GAIN_N_10DB = 18, + DL_GAIN_N_40DB = 0x1f, +}; + +#define DL_GAIN_N_10DB_REG (DL_GAIN_N_10DB << 7 | DL_GAIN_N_10DB) +#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB) +#define DL_GAIN_REG_MASK 0x0f9f + +static void lo_store_gain(struct mt6358_priv *priv) +{ + unsigned int reg; + unsigned int gain_l, gain_r; + + regmap_read(priv->regmap, MT6358_ZCD_CON1, ®); + gain_l = (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK; + gain_r = (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK; + + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] = gain_l; + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] = gain_r; +} + +static void hp_store_gain(struct mt6358_priv *priv) +{ + unsigned int reg; + unsigned int gain_l, gain_r; + + regmap_read(priv->regmap, MT6358_ZCD_CON2, ®); + gain_l = (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK; + gain_r = (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK; + + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = gain_l; + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = gain_r; +} + +static void hp_zcd_disable(struct mt6358_priv *priv) +{ + regmap_write(priv->regmap, MT6358_ZCD_CON0, 0x0000); +} + +static void hp_main_output_ramp(struct mt6358_priv *priv, bool up) +{ + int i = 0, stage = 0; + int target = 7; + + /* Enable/Reduce HPL/R main output stage step by step */ + for (i = 0; i <= target; i++) { + stage = up ? i : target - i; + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x7 << 8, stage << 8); + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x7 << 11, stage << 11); + usleep_range(100, 150); + } +} + +static void hp_aux_feedback_loop_gain_ramp(struct mt6358_priv *priv, bool up) +{ + int i = 0, stage = 0; + + /* Reduce HP aux feedback loop gain step by step */ + for (i = 0; i <= 0xf; i++) { + stage = up ? i : 0xf - i; + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0xf << 12, stage << 12); + usleep_range(100, 150); + } +} + +static void hp_pull_down(struct mt6358_priv *priv, bool enable) +{ + int i; + + if (enable) { + for (i = 0x0; i <= 0x6; i++) { + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x7, i); + usleep_range(600, 700); + } + } else { + for (i = 0x6; i >= 0x1; i--) { + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x7, i); + usleep_range(600, 700); + } + } +} + +static bool is_valid_hp_pga_idx(int reg_idx) +{ + return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_10DB) || + reg_idx == DL_GAIN_N_40DB; +} + +static void headset_volume_ramp(struct mt6358_priv *priv, + int from, int to) +{ + int offset = 0, count = 1, reg_idx; + + if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to)) + dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n", + __func__, from, to); + + dev_info(priv->dev, "%s(), from %d, to %d\n", + __func__, from, to); + + if (to > from) + offset = to - from; + else + offset = from - to; + + while (offset > 0) { + if (to > from) + reg_idx = from + count; + else + reg_idx = from - count; + + if (is_valid_hp_pga_idx(reg_idx)) { + regmap_update_bits(priv->regmap, + MT6358_ZCD_CON2, + DL_GAIN_REG_MASK, + (reg_idx << 7) | reg_idx); + usleep_range(200, 300); + } + offset--; + count++; + } +} + +static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new mt6358_snd_controls[] = { + /* dl pga gain */ + SOC_DOUBLE_TLV("Headphone Volume", + MT6358_ZCD_CON2, 0, 7, 0x12, 1, + playback_tlv), + SOC_DOUBLE_TLV("Lineout Volume", + MT6358_ZCD_CON1, 0, 7, 0x12, 1, + playback_tlv), + SOC_SINGLE_TLV("Handset Volume", + MT6358_ZCD_CON3, 0, 0x12, 1, + playback_tlv), + /* ul pga gain */ + SOC_DOUBLE_R_TLV("PGA Volume", + MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1, + 8, 4, 0, + pga_tlv), +}; + +/* MUX */ +/* LOL MUX */ +static const char * const lo_in_mux_map[] = { + "Open", "Mute", "Playback", "Test Mode" +}; + +static int lo_in_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(lo_in_mux_map_enum, + MT6358_AUDDEC_ANA_CON7, + RG_AUDLOLMUXINPUTSEL_VAUDP15_SFT, + RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK, + lo_in_mux_map, + lo_in_mux_map_value); + +static const struct snd_kcontrol_new lo_in_mux_control = + SOC_DAPM_ENUM("In Select", lo_in_mux_map_enum); + +/*HP MUX */ +enum { + HP_MUX_OPEN = 0, + HP_MUX_HPSPK, + HP_MUX_HP, + HP_MUX_TEST_MODE, + HP_MUX_HP_IMPEDANCE, + HP_MUX_MASK = 0x7, +}; + +static const char * const hp_in_mux_map[] = { + "Open", + "LoudSPK Playback", + "Audio Playback", + "Test Mode", + "HP Impedance", + "undefined1", + "undefined2", + "undefined3", +}; + +static int hp_in_mux_map_value[] = { + HP_MUX_OPEN, + HP_MUX_HPSPK, + HP_MUX_HP, + HP_MUX_TEST_MODE, + HP_MUX_HP_IMPEDANCE, + HP_MUX_OPEN, + HP_MUX_OPEN, + HP_MUX_OPEN, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(hpl_in_mux_map_enum, + SND_SOC_NOPM, + 0, + HP_MUX_MASK, + hp_in_mux_map, + hp_in_mux_map_value); + +static const struct snd_kcontrol_new hpl_in_mux_control = + SOC_DAPM_ENUM("HPL Select", hpl_in_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hpr_in_mux_map_enum, + SND_SOC_NOPM, + 0, + HP_MUX_MASK, + hp_in_mux_map, + hp_in_mux_map_value); + +static const struct snd_kcontrol_new hpr_in_mux_control = + SOC_DAPM_ENUM("HPR Select", hpr_in_mux_map_enum); + +/* RCV MUX */ +enum { + RCV_MUX_OPEN = 0, + RCV_MUX_MUTE, + RCV_MUX_VOICE_PLAYBACK, + RCV_MUX_TEST_MODE, + RCV_MUX_MASK = 0x3, +}; + +static const char * const rcv_in_mux_map[] = { + "Open", "Mute", "Voice Playback", "Test Mode" +}; + +static int rcv_in_mux_map_value[] = { + RCV_MUX_OPEN, + RCV_MUX_MUTE, + RCV_MUX_VOICE_PLAYBACK, + RCV_MUX_TEST_MODE, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rcv_in_mux_map_enum, + SND_SOC_NOPM, + 0, + RCV_MUX_MASK, + rcv_in_mux_map, + rcv_in_mux_map_value); + +static const struct snd_kcontrol_new rcv_in_mux_control = + SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum); + +/* DAC In MUX */ +static const char * const dac_in_mux_map[] = { + "Normal Path", "Sgen" +}; + +static int dac_in_mux_map_value[] = { + 0x0, 0x1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum, + MT6358_AFE_TOP_CON0, + DL_SINE_ON_SFT, + DL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new dac_in_mux_control = + SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum); + +/* AIF Out MUX */ +static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum, + MT6358_AFE_TOP_CON0, + UL_SINE_ON_SFT, + UL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new aif_out_mux_control = + SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum); + +/* Mic Type MUX */ +enum { + MIC_TYPE_MUX_IDLE = 0, + MIC_TYPE_MUX_ACC, + MIC_TYPE_MUX_DMIC, + MIC_TYPE_MUX_DCC, + MIC_TYPE_MUX_DCC_ECM_DIFF, + MIC_TYPE_MUX_DCC_ECM_SINGLE, + MIC_TYPE_MUX_MASK = 0x7, +}; + +#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \ + (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \ + (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE) + +static const char * const mic_type_mux_map[] = { + "Idle", + "ACC", + "DMIC", + "DCC", + "DCC_ECM_DIFF", + "DCC_ECM_SINGLE", +}; + +static int mic_type_mux_map_value[] = { + MIC_TYPE_MUX_IDLE, + MIC_TYPE_MUX_ACC, + MIC_TYPE_MUX_DMIC, + MIC_TYPE_MUX_DCC, + MIC_TYPE_MUX_DCC_ECM_DIFF, + MIC_TYPE_MUX_DCC_ECM_SINGLE, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(mic_type_mux_map_enum, + SND_SOC_NOPM, + 0, + MIC_TYPE_MUX_MASK, + mic_type_mux_map, + mic_type_mux_map_value); + +static const struct snd_kcontrol_new mic_type_mux_control = + SOC_DAPM_ENUM("Mic Type Select", mic_type_mux_map_enum); + +/* ADC L MUX */ +enum { + ADC_MUX_IDLE = 0, + ADC_MUX_AIN0, + ADC_MUX_PREAMPLIFIER, + ADC_MUX_IDLE1, + ADC_MUX_MASK = 0x3, +}; + +static const char * const adc_left_mux_map[] = { + "Idle", "AIN0", "Left Preamplifier", "Idle_1" +}; + +static int adc_mux_map_value[] = { + ADC_MUX_IDLE, + ADC_MUX_AIN0, + ADC_MUX_PREAMPLIFIER, + ADC_MUX_IDLE1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum, + SND_SOC_NOPM, + 0, + ADC_MUX_MASK, + adc_left_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_left_mux_control = + SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum); + +/* ADC R MUX */ +static const char * const adc_right_mux_map[] = { + "Idle", "AIN0", "Right Preamplifier", "Idle_1" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum, + SND_SOC_NOPM, + 0, + ADC_MUX_MASK, + adc_right_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_right_mux_control = + SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum); + +/* PGA L MUX */ +enum { + PGA_MUX_NONE = 0, + PGA_MUX_AIN0, + PGA_MUX_AIN1, + PGA_MUX_AIN2, + PGA_MUX_MASK = 0x3, +}; + +static const char * const pga_mux_map[] = { + "None", "AIN0", "AIN1", "AIN2" +}; + +static int pga_mux_map_value[] = { + PGA_MUX_NONE, + PGA_MUX_AIN0, + PGA_MUX_AIN1, + PGA_MUX_AIN2, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum, + SND_SOC_NOPM, + 0, + PGA_MUX_MASK, + pga_mux_map, + pga_mux_map_value); + +static const struct snd_kcontrol_new pga_left_mux_control = + SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum); + +/* PGA R MUX */ +static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum, + SND_SOC_NOPM, + 0, + PGA_MUX_MASK, + pga_mux_map, + pga_mux_map_value); + +static const struct snd_kcontrol_new pga_right_mux_control = + SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum); + +static int mt_clksq_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* audio clk source from internal dcxo */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6, + RG_CLKSQ_IN_SEL_TEST_MASK_SFT, + 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_sgen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xCBA1); + /* sdm power on */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0003); + /* sdm fifo enable */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x000B); + + regmap_update_bits(priv->regmap, MT6358_AFE_SGEN_CFG0, + 0xff3f, + 0x0000); + regmap_update_bits(priv->regmap, MT6358_AFE_SGEN_CFG1, + 0xffff, + 0x0001); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0000); + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xcba0); + break; + default: + break; + } + + return 0; +} + +static int mt_aif_in_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_info(priv->dev, "%s(), event 0x%x, rate %d\n", + __func__, event, priv->dl_rate); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + playback_gpio_set(priv); + + /* sdm audio fifo clock power on */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xCBA1); + /* sdm power on */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0003); + /* sdm fifo enable */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x000B); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0000); + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xcba0); + + playback_gpio_reset(priv); + break; + default: + break; + } + + return 0; +} + +static int mtk_hp_enable(struct mt6358_priv *priv) +{ + /* Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, true); + /* release HP CMFB gate rstb */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x1 << 6, 0x1 << 6); + + /* Reduce ESD resistance of AU_REFN */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000); + + /* save target gain to restore after hardware open complete */ + hp_store_gain(priv); + /* Set HPR/HPL gain as minimum (~ -40dB) */ + regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_40DB_REG); + + /* Turn on DA_600K_NCP_VA18 */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001); + /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c); + /* Toggle RG_DIVCKS_CHG */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001); + /* Set NCP soft start mode as default mode: 100us */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003); + /* Enable NCP */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000); + usleep_range(250, 270); + + /* Enable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x1055); + /* Enable NV regulator (-1.2V) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001); + usleep_range(100, 120); + + /* Disable AUD_ZCD */ + hp_zcd_disable(priv); + + /* Disable headphone short-circuit protection */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3000); + + /* Enable IBIST */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + + /* Set HP DR bias current optimization, 010: 6uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900); + /* Set HP & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + /* Set HPP/N STB enhance circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4033); + + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x000c); + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x003c); + /* Enable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0c00); + /* Enable HP driver bias circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30c0); + /* Enable HP driver core circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f0); + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x00fc); + + /* Enable HP main CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0e00); + /* Disable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0200); + + /* Select CMFB resistor bulk to AC mode */ + /* Selec HS/LO cap size (6.5pF default) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000); + + /* Enable HP main output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x00ff); + /* Enable HPR/L main output stage step by step */ + hp_main_output_ramp(priv, true); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, true); + /* Disable HP aux feedback loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf); + + /* apply volume setting */ + headset_volume_ramp(priv, + DL_GAIN_N_10DB, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]); + + /* Disable HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3); + /* Unshort HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3f03); + usleep_range(100, 120); + + /* Enable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x1); + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30ff); + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0xf201); + usleep_range(100, 120); + + /* Switch HPL MUX to audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x32ff); + /* Switch HPR MUX to audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3aff); + + /* Disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); + + return 0; +} + +static int mtk_hp_disable(struct mt6358_priv *priv) +{ + /* Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, true); + + /* HPR/HPL mux to open */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x0f00, 0x0000); + + /* Disable low-noise mode of DAC */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0x0001, 0x0000); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x0); + + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3); + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf); + + /* decrease HPL/R gain to normal gain step by step */ + headset_volume_ramp(priv, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL], + DL_GAIN_N_40DB); + + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fff); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, false); + + /* decrease HPR/L main output stage step by step */ + hp_main_output_ramp(priv, false); + + /* Disable HP main output stage */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3, 0x0); + + /* Enable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0e00); + + /* Disable HP main CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0c00); + + /* Unshort HP main output to HP aux output stage */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x3 << 6, 0x0); + + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + + /* Disable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0000); + + /* Disable HP aux feedback loop */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x3 << 4, 0x0); + + /* Disable HP aux output stage */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x3 << 2, 0x0); + + /* Disable IBIST */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12, + 0x1 << 8, 0x1 << 8); + + /* Disable NV regulator (-1.2V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x1, 0x0); + /* Disable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x0); + /* Disable NCP */ + regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, + 0x1, 0x1); + + /* Increase ESD resistance of AU_REFN */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON2, + 0x1 << 14, 0x0); + + /* Set HP CMFB gate rstb */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x1 << 6, 0x0); + /* disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); + + return 0; +} + +static int mtk_hp_spk_enable(struct mt6358_priv *priv) +{ + /* Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, true); + /* release HP CMFB gate rstb */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x1 << 6, 0x1 << 6); + + /* Reduce ESD resistance of AU_REFN */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000); + + /* save target gain to restore after hardware open complete */ + hp_store_gain(priv); + /* Set HPR/HPL gain to -10dB */ + regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_10DB_REG); + + /* Turn on DA_600K_NCP_VA18 */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001); + /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c); + /* Toggle RG_DIVCKS_CHG */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001); + /* Set NCP soft start mode as default mode: 100us */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003); + /* Enable NCP */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000); + usleep_range(250, 270); + + /* Enable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x1055); + /* Enable NV regulator (-1.2V) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001); + usleep_range(100, 120); + + /* Disable AUD_ZCD */ + hp_zcd_disable(priv); + + /* Disable headphone short-circuit protection */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3000); + + /* Enable IBIST */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + + /* Set HP DR bias current optimization, 010: 6uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900); + /* Set HP & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + /* Set HPP/N STB enhance circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4033); + + /* Disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); + + /* Enable HP driver bias circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30c0); + /* Enable HP driver core circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f0); + /* Enable HP main CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0200); + + /* Select CMFB resistor bulk to AC mode */ + /* Selec HS/LO cap size (6.5pF default) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000); + + /* Enable HP main output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x0003); + /* Enable HPR/L main output stage step by step */ + hp_main_output_ramp(priv, true); + + /* Set LO gain as minimum (~ -40dB) */ + lo_store_gain(priv); + regmap_write(priv->regmap, MT6358_ZCD_CON1, DL_GAIN_N_40DB_REG); + /* apply volume setting */ + headset_volume_ramp(priv, + DL_GAIN_N_10DB, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]); + + /* Set LO STB enhance circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0110); + /* Enable LO driver bias circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0112); + /* Enable LO driver core circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0113); + + /* Set LOL gain to normal gain step by step */ + regmap_update_bits(priv->regmap, MT6358_ZCD_CON1, + RG_AUDLOLGAIN_MASK_SFT, + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] << + RG_AUDLOLGAIN_SFT); + regmap_update_bits(priv->regmap, MT6358_ZCD_CON1, + RG_AUDLORGAIN_MASK_SFT, + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] << + RG_AUDLORGAIN_SFT); + + /* Enable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x1); + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f9); + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0201); + /* Switch LOL MUX to audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x011b); + /* Switch HPL/R MUX to Line-out */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x35f9); + + return 0; +} + +static int mtk_hp_spk_disable(struct mt6358_priv *priv) +{ + /* HPR/HPL mux to open */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x0f00, 0x0000); + /* LOL mux to open */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7, + 0x3 << 2, 0x0000); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x0); + + /* decrease HPL/R gain to normal gain step by step */ + headset_volume_ramp(priv, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL], + DL_GAIN_N_40DB); + + /* decrease LOL gain to minimum gain step by step */ + regmap_update_bits(priv->regmap, MT6358_ZCD_CON1, + DL_GAIN_REG_MASK, DL_GAIN_N_40DB_REG); + + /* decrease HPR/L main output stage step by step */ + hp_main_output_ramp(priv, false); + + /* Disable HP main output stage */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3, 0x0); + + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3); + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf); + + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fff); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, false); + + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + /* Disable LO driver core circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7, + 0x1, 0x0); + + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + /* Disable LO driver bias circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7, + 0x1 << 1, 0x0); + + /* Disable HP aux CMFB loop */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0xff << 8, 0x0000); + + /* Disable IBIST */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12, + 0x1 << 8, 0x1 << 8); + /* Disable NV regulator (-1.2V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x1, 0x0); + /* Disable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, 0x1055, 0x0); + /* Disable NCP */ + regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x1, 0x1); + + /* Set HP CMFB gate rstb */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x1 << 6, 0x0); + /* disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); + + return 0; +} + +static int mt_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + int device = DEVICE_HP; + + dev_info(priv->dev, "%s(), event 0x%x, dev_counter[DEV_HP] %d, mux %u\n", + __func__, + event, + priv->dev_counter[device], + mux); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + priv->dev_counter[device]++; + if (priv->dev_counter[device] > 1) + break; /* already enabled, do nothing */ + else if (priv->dev_counter[device] <= 0) + dev_warn(priv->dev, "%s(), dev_counter[DEV_HP] %d <= 0\n", + __func__, + priv->dev_counter[device]); + + priv->mux_select[MUX_HP_L] = mux; + + if (mux == HP_MUX_HP) + mtk_hp_enable(priv); + else if (mux == HP_MUX_HPSPK) + mtk_hp_spk_enable(priv); + break; + case SND_SOC_DAPM_PRE_PMD: + priv->dev_counter[device]--; + if (priv->dev_counter[device] > 0) { + break; /* still being used, don't close */ + } else if (priv->dev_counter[device] < 0) { + dev_warn(priv->dev, "%s(), dev_counter[DEV_HP] %d < 0\n", + __func__, + priv->dev_counter[device]); + priv->dev_counter[device] = 0; + break; + } + + if (priv->mux_select[MUX_HP_L] == HP_MUX_HP) + mtk_hp_disable(priv); + else if (priv->mux_select[MUX_HP_L] == HP_MUX_HPSPK) + mtk_hp_spk_disable(priv); + + priv->mux_select[MUX_HP_L] = mux; + break; + default: + break; + } + + return 0; +} + +static int mt_rcv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_info(priv->dev, "%s(), event 0x%x, mux %u\n", + __func__, + event, + dapm_kcontrol_get_value(w->kcontrols[0])); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reduce ESD resistance of AU_REFN */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000); + + /* Turn on DA_600K_NCP_VA18 */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001); + /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c); + /* Toggle RG_DIVCKS_CHG */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001); + /* Set NCP soft start mode as default mode: 100us */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003); + /* Enable NCP */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000); + usleep_range(250, 270); + + /* Enable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x1055); + /* Enable NV regulator (-1.2V) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001); + usleep_range(100, 120); + + /* Disable AUD_ZCD */ + hp_zcd_disable(priv); + + /* Disable handset short-circuit protection */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0010); + + /* Enable IBIST */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + /* Set HP DR bias current optimization, 010: 6uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900); + /* Set HP & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + /* Set HS STB enhance circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0090); + + /* Disable HP main CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0000); + /* Select CMFB resistor bulk to AC mode */ + /* Selec HS/LO cap size (6.5pF default) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000); + + /* Enable HS driver bias circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0092); + /* Enable HS driver core circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0093); + + /* Enable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0x1, 0x1); + + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x0009); + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0001); + /* Switch HS MUX to audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x009b); + break; + case SND_SOC_DAPM_PRE_PMD: + /* HS mux to open */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6, + RG_AUDHSMUXINPUTSEL_VAUDP15_MASK_SFT, + RCV_MUX_OPEN); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0x1, 0x0); + + /* decrease HS gain to minimum gain step by step */ + regmap_write(priv->regmap, MT6358_ZCD_CON3, DL_GAIN_N_40DB); + + /* Disable HS driver core circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6, + 0x1, 0x0); + + /* Disable HS driver bias circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6, + 0x1 << 1, 0x0000); + + /* Disable HP aux CMFB loop */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0xff << 8, 0x0); + + /* Enable HP main CMFB Switch */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0xff << 8, 0x2 << 8); + + /* Disable IBIST */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12, + 0x1 << 8, 0x1 << 8); + + /* Disable NV regulator (-1.2V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, + 0x1, 0x0); + /* Disable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x0); + /* Disable NCP */ + regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, + 0x1, 0x1); + break; + default: + break; + } + + return 0; +} + +static int mt_aif_out_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n", + __func__, event, priv->ul_rate); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + capture_gpio_set(priv); + break; + case SND_SOC_DAPM_POST_PMD: + capture_gpio_reset(priv); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x\n", + __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable audio ADC CLKGEN */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0x1 << 5, 0x1 << 5); + /* ADC CLK from CLKGEN (13MHz) */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON3, + 0x0000); + /* Enable LCLDO_ENC 1P8V */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x2500, 0x0100); + /* LCLDO_ENC remote sense */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x2500, 0x2500); + break; + case SND_SOC_DAPM_POST_PMD: + /* LCLDO_ENC remote sense off */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x2500, 0x0100); + /* disable LCLDO_ENC 1P8V */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x2500, 0x0000); + + /* ADC CLK from CLKGEN (13MHz) */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON3, 0x0000); + /* disable audio ADC CLKGEN */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0x1 << 5, 0x0 << 5); + break; + default: + break; + } + + return 0; +} + +static int mt6358_amic_enable(struct mt6358_priv *priv) +{ + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE]; + unsigned int mux_pga_l = priv->mux_select[MUX_PGA_L]; + unsigned int mux_pga_r = priv->mux_select[MUX_PGA_R]; + + dev_info(priv->dev, "%s(), mux, mic %u, pga l %u, pga r %u\n", + __func__, mic_type, mux_pga_l, mux_pga_r); + + if (IS_DCC_BASE(mic_type)) { + /* DCC 50k CLK (from 26M) */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2060); + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2061); + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG1, 0x0100); + } + + /* mic bias 0 */ + if (mux_pga_l == PGA_MUX_AIN0 || mux_pga_l == PGA_MUX_AIN2 || + mux_pga_r == PGA_MUX_AIN0 || mux_pga_r == PGA_MUX_AIN2) { + switch (mic_type) { + case MIC_TYPE_MUX_DCC_ECM_DIFF: + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xff00, 0x7700); + break; + case MIC_TYPE_MUX_DCC_ECM_SINGLE: + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xff00, 0x1100); + break; + default: + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xff00, 0x0000); + break; + } + /* Enable MICBIAS0, MISBIAS0 = 1P9V */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xff, 0x21); + } + + /* mic bias 1 */ + if (mux_pga_l == PGA_MUX_AIN1 || mux_pga_r == PGA_MUX_AIN1) { + /* Enable MICBIAS1, MISBIAS1 = 2P6V */ + if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE) + regmap_write(priv->regmap, + MT6358_AUDENC_ANA_CON10, 0x0161); + else + regmap_write(priv->regmap, + MT6358_AUDENC_ANA_CON10, 0x0061); + } + + if (IS_DCC_BASE(mic_type)) { + /* Audio L/R preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0xf8ff, 0x0004); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xf8ff, 0x0004); + } else { + /* reset reg */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0xf8ff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xf8ff, 0x0000); + } + + if (mux_pga_l != PGA_MUX_NONE) { + /* L preamplifier input sel */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLINPUTSEL_MASK_SFT, + mux_pga_l << RG_AUDPREAMPLINPUTSEL_SFT); + + /* L preamplifier enable */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLON_MASK_SFT, + 0x1 << RG_AUDPREAMPLON_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* L preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMPLDCCEN_SFT); + } + + /* L ADC input sel : L PGA. Enable audio L ADC */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDADCLINPUTSEL_MASK_SFT, + ADC_MUX_PREAMPLIFIER << + RG_AUDADCLINPUTSEL_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDADCLPWRUP_MASK_SFT, + 0x1 << RG_AUDADCLPWRUP_SFT); + } + + if (mux_pga_r != PGA_MUX_NONE) { + /* R preamplifier input sel */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRINPUTSEL_MASK_SFT, + mux_pga_r << RG_AUDPREAMPRINPUTSEL_SFT); + + /* R preamplifier enable */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRON_MASK_SFT, + 0x1 << RG_AUDPREAMPRON_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* R preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMPRDCCEN_SFT); + } + + /* R ADC input sel : R PGA. Enable audio R ADC */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDADCRINPUTSEL_MASK_SFT, + ADC_MUX_PREAMPLIFIER << + RG_AUDADCRINPUTSEL_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDADCRPWRUP_MASK_SFT, + 0x1 << RG_AUDADCRPWRUP_SFT); + } + + if (IS_DCC_BASE(mic_type)) { + usleep_range(100, 150); + /* Audio L preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCPRECHARGE_MASK_SFT, 0x0); + /* Audio R preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCPRECHARGE_MASK_SFT, 0x0); + + /* Short body to ground in PGA */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON3, + 0x1 << 12, 0x0); + } + + /* here to set digital part */ + mt6358_mtkaif_tx_enable(priv); + + /* UL dmic setting off */ + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0000); + + /* UL turn on */ + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0001); + + return 0; +} + +static void mt6358_amic_disable(struct mt6358_priv *priv) +{ + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE]; + unsigned int mux_pga_l = priv->mux_select[MUX_PGA_L]; + unsigned int mux_pga_r = priv->mux_select[MUX_PGA_R]; + + dev_info(priv->dev, "%s(), mux, mic %u, pga l %u, pga r %u\n", + __func__, mic_type, mux_pga_l, mux_pga_r); + + /* UL turn off */ + regmap_update_bits(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, + 0x0001, 0x0000); + + /* disable aud_pad TX fifos */ + mt6358_mtkaif_tx_disable(priv); + + /* L ADC input sel : off, disable L ADC */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0xf000, 0x0000); + /* L preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0x1 << 1, 0x0); + /* L preamplifier input sel : off, L PGA 0 dB gain */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0xfffb, 0x0000); + + /* disable L preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0x1 << 2, 0x0); + + /* R ADC input sel : off, disable R ADC */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xf000, 0x0000); + /* R preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0x1 << 1, 0x0); + /* R preamplifier input sel : off, R PGA 0 dB gain */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0x0ffb, 0x0000); + + /* disable R preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0x1 << 2, 0x0); + + /* mic bias */ + /* Disable MICBIAS0, MISBIAS0 = 1P7V */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000); + + /* Disable MICBIAS1 */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10, + 0x0001, 0x0000); + + if (IS_DCC_BASE(mic_type)) { + /* dcclk_gen_on=1'b0 */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2060); + /* dcclk_pdn=1'b1 */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + /* dcclk_ref_ck_sel=2'b00 */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + /* dcclk_div=11'b00100000011 */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + } +} + +static int mt6358_dmic_enable(struct mt6358_priv *priv) +{ + dev_info(priv->dev, "%s()\n", __func__); + + /* mic bias */ + /* Enable MICBIAS0, MISBIAS0 = 1P9V */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0021); + + /* RG_BANDGAPGEN=1'b0 */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10, + 0x1 << 12, 0x0); + + /* DMIC enable */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON8, 0x0005); + + /* here to set digital part */ + mt6358_mtkaif_tx_enable(priv); + + /* UL dmic setting */ + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0080); + + /* UL turn on */ + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0003); + return 0; +} + +static void mt6358_dmic_disable(struct mt6358_priv *priv) +{ + dev_info(priv->dev, "%s()\n", __func__); + + /* UL turn off */ + regmap_update_bits(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, + 0x0003, 0x0000); + + /* disable aud_pad TX fifos */ + mt6358_mtkaif_tx_disable(priv); + + /* DMIC disable */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON8, 0x0000); + + /* mic bias */ + /* MISBIAS0 = 1P7V */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0001); + + /* RG_BANDGAPGEN=1'b0 */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10, + 0x1 << 12, 0x0); + + /* MICBIA0 disable */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000); +} + +static int mt_mic_type_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", + __func__, event, mux); + + switch (event) { + case SND_SOC_DAPM_WILL_PMU: + priv->mux_select[MUX_MIC_TYPE] = mux; + break; + case SND_SOC_DAPM_PRE_PMU: + switch (mux) { + case MIC_TYPE_MUX_DMIC: + mt6358_dmic_enable(priv); + break; + default: + mt6358_amic_enable(priv); + break; + } + + break; + case SND_SOC_DAPM_POST_PMD: + switch (priv->mux_select[MUX_MIC_TYPE]) { + case MIC_TYPE_MUX_DMIC: + mt6358_dmic_disable(priv); + break; + default: + mt6358_amic_disable(priv); + break; + } + + priv->mux_select[MUX_MIC_TYPE] = mux; + break; + default: + break; + } + + return 0; +} + +static int mt_adc_l_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n", + __func__, event, mux); + + priv->mux_select[MUX_ADC_L] = mux; + + return 0; +} + +static int mt_adc_r_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n", + __func__, event, mux); + + priv->mux_select[MUX_ADC_R] = mux; + + return 0; +} + +static int mt_pga_left_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n", + __func__, event, mux); + + priv->mux_select[MUX_PGA_L] = mux; + + return 0; +} + +static int mt_pga_right_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n", + __func__, event, mux); + + priv->mux_select[MUX_PGA_R] = mux; + + return 0; +} + +static int mt_delay_250_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(250, 270); + break; + case SND_SOC_DAPM_PRE_PMD: + usleep_range(250, 270); + break; + default: + break; + } + + return 0; +} + +/* DAPM Widgets */ +static const struct snd_soc_dapm_widget mt6358_dapm_widgets[] = { + /* Global Supply*/ + SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF, + MT6358_DCXO_CW14, + RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB, + MT6358_AUDDEC_ANA_CON13, + RG_AUDGLB_PWRDN_VA28_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ, + MT6358_AUDENC_ANA_CON6, + RG_CLKSQ_EN_SFT, 0, + mt_clksq_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK, + MT6358_AUD_TOP_CKPDN_CON0, + RG_AUDNCP_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK, + MT6358_AUD_TOP_CKPDN_CON0, + RG_ZCD13M_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST, + MT6358_AUD_TOP_CKPDN_CON0, + RG_AUD_CK_PDN_SFT, 1, + mt_delay_250_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK, + MT6358_AUD_TOP_CKPDN_CON0, + RG_AUDIF_CK_PDN_SFT, 1, NULL, 0), + + /* Digital Clock */ + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST, + MT6358_AUDIO_TOP_CON0, + PDN_AFE_CTL_SFT, 1, + mt_delay_250_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_DAC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_ADC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_I2S_DL_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PWR_CLK_DIS_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_RESERVED_SFT, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* AFE ON */ + SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE, + MT6358_AFE_UL_DL_CON0, AFE_ON_SFT, 0, + NULL, 0), + + /* AIF Rx*/ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "AIF1 Playback", 0, + MT6358_AFE_DL_SRC2_CON0_L, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, + mt_aif_in_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* DL Supply */ + SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* DAC */ + SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control), + + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + /* LOL */ + SND_SOC_DAPM_MUX("LOL Mux", SND_SOC_NOPM, 0, 0, &lo_in_mux_control), + + SND_SOC_DAPM_SUPPLY("LO Stability Enh", MT6358_AUDDEC_ANA_CON7, + RG_LOOUTPUTSTBENH_VAUDP15_SFT, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("LOL Buffer", MT6358_AUDDEC_ANA_CON7, + RG_AUDLOLPWRUP_VAUDP15_SFT, 0, NULL, 0), + + /* Headphone */ + SND_SOC_DAPM_MUX_E("HPL Mux", SND_SOC_NOPM, 0, 0, + &hpl_in_mux_control, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX_E("HPR Mux", SND_SOC_NOPM, 0, 0, + &hpr_in_mux_control, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + + /* Receiver */ + SND_SOC_DAPM_MUX_E("RCV Mux", SND_SOC_NOPM, 0, 0, + &rcv_in_mux_control, + mt_rcv_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("Receiver"), + SND_SOC_DAPM_OUTPUT("Headphone L"), + SND_SOC_DAPM_OUTPUT("Headphone R"), + SND_SOC_DAPM_OUTPUT("Headphone L Ext Spk Amp"), + SND_SOC_DAPM_OUTPUT("Headphone R Ext Spk Amp"), + SND_SOC_DAPM_OUTPUT("LINEOUT L"), + SND_SOC_DAPM_OUTPUT("LINEOUT L HSSPK"), + + /* SGEN */ + SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6358_AFE_SGEN_CFG0, + SGEN_DAC_EN_CTL_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6358_AFE_SGEN_CFG0, + SGEN_MUTE_SW_CTL_SFT, 1, + mt_sgen_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6358_AFE_DL_SRC2_CON0_L, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("SGEN DL"), + + /* Uplinks */ + SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "AIF1 Capture", 0, + SND_SOC_NOPM, 0, 0, + mt_aif_out_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("ADC Supply", SUPPLY_SEQ_ADC_SUPPLY, + SND_SOC_NOPM, 0, 0, + mt_adc_supply_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Uplinks MUX */ + SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0, + &aif_out_mux_control), + + SND_SOC_DAPM_MUX_E("Mic Type Mux", SND_SOC_NOPM, 0, 0, + &mic_type_mux_control, + mt_mic_type_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_WILL_PMU), + + SND_SOC_DAPM_MUX_E("ADC L Mux", SND_SOC_NOPM, 0, 0, + &adc_left_mux_control, + mt_adc_l_event, + SND_SOC_DAPM_WILL_PMU), + SND_SOC_DAPM_MUX_E("ADC R Mux", SND_SOC_NOPM, 0, 0, + &adc_right_mux_control, + mt_adc_r_event, + SND_SOC_DAPM_WILL_PMU), + + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX_E("PGA L Mux", SND_SOC_NOPM, 0, 0, + &pga_left_mux_control, + mt_pga_left_event, + SND_SOC_DAPM_WILL_PMU), + SND_SOC_DAPM_MUX_E("PGA R Mux", SND_SOC_NOPM, 0, 0, + &pga_right_mux_control, + mt_pga_right_event, + SND_SOC_DAPM_WILL_PMU), + + SND_SOC_DAPM_PGA("PGA L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* UL input */ + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), +}; + +static const struct snd_soc_dapm_route mt6358_dapm_routes[] = { + /* Capture */ + {"AIF1TX", NULL, "AIF Out Mux"}, + {"AIF1TX", NULL, "CLK_BUF"}, + {"AIF1TX", NULL, "AUDGLB"}, + {"AIF1TX", NULL, "CLKSQ Audio"}, + + {"AIF1TX", NULL, "AUD_CK"}, + {"AIF1TX", NULL, "AUDIF_CK"}, + + {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"}, + {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"}, + {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"}, + {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"}, + {"AIF1TX", NULL, "AUDIO_TOP_I2S_DL"}, + + {"AIF1TX", NULL, "AFE_ON"}, + + {"AIF Out Mux", NULL, "Mic Type Mux"}, + + {"Mic Type Mux", "ACC", "ADC L"}, + {"Mic Type Mux", "ACC", "ADC R"}, + {"Mic Type Mux", "DCC", "ADC L"}, + {"Mic Type Mux", "DCC", "ADC R"}, + {"Mic Type Mux", "DCC_ECM_DIFF", "ADC L"}, + {"Mic Type Mux", "DCC_ECM_DIFF", "ADC R"}, + {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC L"}, + {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC R"}, + {"Mic Type Mux", "DMIC", "AIN0"}, + {"Mic Type Mux", "DMIC", "AIN2"}, + + {"ADC L", NULL, "ADC L Mux"}, + {"ADC L", NULL, "ADC Supply"}, + {"ADC R", NULL, "ADC R Mux"}, + {"ADC R", NULL, "ADC Supply"}, + + {"ADC L Mux", "Left Preamplifier", "PGA L"}, + + {"ADC R Mux", "Right Preamplifier", "PGA R"}, + + {"PGA L", NULL, "PGA L Mux"}, + {"PGA R", NULL, "PGA R Mux"}, + + {"PGA L Mux", "AIN0", "AIN0"}, + {"PGA L Mux", "AIN1", "AIN1"}, + {"PGA L Mux", "AIN2", "AIN2"}, + + {"PGA R Mux", "AIN0", "AIN0"}, + {"PGA R Mux", "AIN1", "AIN1"}, + {"PGA R Mux", "AIN2", "AIN2"}, + + /* DL Supply */ + {"DL Power Supply", NULL, "CLK_BUF"}, + {"DL Power Supply", NULL, "AUDGLB"}, + {"DL Power Supply", NULL, "CLKSQ Audio"}, + + {"DL Power Supply", NULL, "AUDNCP_CK"}, + {"DL Power Supply", NULL, "ZCD13M_CK"}, + {"DL Power Supply", NULL, "AUD_CK"}, + {"DL Power Supply", NULL, "AUDIF_CK"}, + + /* DL Digital Supply */ + {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"}, + + {"DL Digital Clock", NULL, "AFE_ON"}, + + {"AIF_RX", NULL, "DL Digital Clock"}, + + /* DL Path */ + {"DAC In Mux", "Normal Path", "AIF_RX"}, + + {"DAC In Mux", "Sgen", "SGEN DL"}, + {"SGEN DL", NULL, "SGEN DL SRC"}, + {"SGEN DL", NULL, "SGEN MUTE"}, + {"SGEN DL", NULL, "SGEN DL Enable"}, + {"SGEN DL", NULL, "DL Digital Clock"}, + {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"}, + + {"DACL", NULL, "DAC In Mux"}, + {"DACL", NULL, "DL Power Supply"}, + + {"DACR", NULL, "DAC In Mux"}, + {"DACR", NULL, "DL Power Supply"}, + + /* Lineout Path */ + {"LOL Mux", "Playback", "DACL"}, + + {"LOL Buffer", NULL, "LOL Mux"}, + {"LOL Buffer", NULL, "LO Stability Enh"}, + + {"LINEOUT L", NULL, "LOL Buffer"}, + + /* Headphone Path */ + {"HPL Mux", "Audio Playback", "DACL"}, + {"HPR Mux", "Audio Playback", "DACR"}, + {"HPL Mux", "HP Impedance", "DACL"}, + {"HPR Mux", "HP Impedance", "DACR"}, + {"HPL Mux", "LoudSPK Playback", "DACL"}, + {"HPR Mux", "LoudSPK Playback", "DACR"}, + + {"Headphone L", NULL, "HPL Mux"}, + {"Headphone R", NULL, "HPR Mux"}, + {"Headphone L Ext Spk Amp", NULL, "HPL Mux"}, + {"Headphone R Ext Spk Amp", NULL, "HPR Mux"}, + {"LINEOUT L HSSPK", NULL, "HPL Mux"}, + + /* Receiver Path */ + {"RCV Mux", "Voice Playback", "DACL"}, + {"Receiver", NULL, "RCV Mux"}, +}; + +static int mt6358_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int rate = params_rate(params); + + dev_info(priv->dev, "%s(), substream->stream %d, rate %d, number %d\n", + __func__, + substream->stream, + rate, + substream->number); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->dl_rate = rate; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + priv->ul_rate = rate; + + return 0; +} + +static const struct snd_soc_dai_ops mt6358_codec_dai_ops = { + .hw_params = mt6358_codec_dai_hw_params, +}; + +#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ + SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE) + +static struct snd_soc_dai_driver mt6358_dai_driver[] = { + { + .name = "mt6358-snd-codec-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6358_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000, + .formats = MT6358_FORMATS, + }, + .ops = &mt6358_codec_dai_ops, + }, +}; + +static int mt6358_codec_init_reg(struct mt6358_priv *priv) +{ + int ret = 0; + + /* Disable HeadphoneL/HeadphoneR short circuit protection */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + RG_AUDHPLSCDISABLE_VAUDP15_MASK_SFT, + 0x1 << RG_AUDHPLSCDISABLE_VAUDP15_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + RG_AUDHPRSCDISABLE_VAUDP15_MASK_SFT, + 0x1 << RG_AUDHPRSCDISABLE_VAUDP15_SFT); + /* Disable voice short circuit protection */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6, + RG_AUDHSSCDISABLE_VAUDP15_MASK_SFT, + 0x1 << RG_AUDHSSCDISABLE_VAUDP15_SFT); + /* disable LO buffer left short circuit protection */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7, + RG_AUDLOLSCDISABLE_VAUDP15_MASK_SFT, + 0x1 << RG_AUDLOLSCDISABLE_VAUDP15_SFT); + + /* accdet s/w enable */ + regmap_update_bits(priv->regmap, MT6358_ACCDET_CON13, + 0xFFFF, 0x700E); + + /* gpio miso driving set to 4mA */ + regmap_write(priv->regmap, MT6358_DRV_CON3, 0x8888); + + /* set gpio */ + playback_gpio_reset(priv); + capture_gpio_reset(priv); + + return ret; +} + +static int mt6358_codec_probe(struct snd_soc_component *cmpnt) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int ret; + + snd_soc_component_init_regmap(cmpnt, priv->regmap); + + mt6358_codec_init_reg(priv); + + priv->avdd_reg = devm_regulator_get(priv->dev, "Avdd"); + if (IS_ERR(priv->avdd_reg)) { + dev_err(priv->dev, "%s() have no Avdd supply", __func__); + return PTR_ERR(priv->avdd_reg); + } + + ret = regulator_enable(priv->avdd_reg); + if (ret) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver mt6358_soc_component_driver = { + .probe = mt6358_codec_probe, + .controls = mt6358_snd_controls, + .num_controls = ARRAY_SIZE(mt6358_snd_controls), + .dapm_widgets = mt6358_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt6358_dapm_widgets), + .dapm_routes = mt6358_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt6358_dapm_routes), +}; + +static int mt6358_platform_driver_probe(struct platform_device *pdev) +{ + struct mt6358_priv *priv; + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + + priv = devm_kzalloc(&pdev->dev, + sizeof(struct mt6358_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, priv); + + priv->dev = &pdev->dev; + + priv->regmap = mt6397->regmap; + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + dev_info(priv->dev, "%s(), dev name %s\n", + __func__, dev_name(&pdev->dev)); + + return devm_snd_soc_register_component(&pdev->dev, + &mt6358_soc_component_driver, + mt6358_dai_driver, + ARRAY_SIZE(mt6358_dai_driver)); +} + +static const struct of_device_id mt6358_of_match[] = { + {.compatible = "mediatek,mt6358-sound",}, + {} +}; +MODULE_DEVICE_TABLE(of, mt6358_of_match); + +static struct platform_driver mt6358_platform_driver = { + .driver = { + .name = "mt6358-sound", + .of_match_table = mt6358_of_match, + }, + .probe = mt6358_platform_driver_probe, +}; + +module_platform_driver(mt6358_platform_driver) + +/* Module information */ +MODULE_DESCRIPTION("MT6358 ALSA SoC codec driver"); +MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/mt6358.h b/sound/soc/codecs/mt6358.h new file mode 100644 index 000000000000..a5953315eaa2 --- /dev/null +++ b/sound/soc/codecs/mt6358.h @@ -0,0 +1,2314 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt6358.h -- mt6358 ALSA SoC audio codec driver + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef __MT6358_H__ +#define __MT6358_H__ + +/* Reg bit define */ +/* MT6358_DCXO_CW14 */ +#define RG_XO_AUDIO_EN_M_SFT 13 + +/* MT6358_DCXO_CW13 */ +#define RG_XO_VOW_EN_SFT 8 + +/* MT6358_AUD_TOP_CKPDN_CON0 */ +#define RG_VOW13M_CK_PDN_SFT 13 +#define RG_VOW13M_CK_PDN_MASK 0x1 +#define RG_VOW13M_CK_PDN_MASK_SFT (0x1 << 13) +#define RG_VOW32K_CK_PDN_SFT 12 +#define RG_VOW32K_CK_PDN_MASK 0x1 +#define RG_VOW32K_CK_PDN_MASK_SFT (0x1 << 12) +#define RG_AUD_INTRP_CK_PDN_SFT 8 +#define RG_AUD_INTRP_CK_PDN_MASK 0x1 +#define RG_AUD_INTRP_CK_PDN_MASK_SFT (0x1 << 8) +#define RG_PAD_AUD_CLK_MISO_CK_PDN_SFT 7 +#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK 0x1 +#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK_SFT (0x1 << 7) +#define RG_AUDNCP_CK_PDN_SFT 6 +#define RG_AUDNCP_CK_PDN_MASK 0x1 +#define RG_AUDNCP_CK_PDN_MASK_SFT (0x1 << 6) +#define RG_ZCD13M_CK_PDN_SFT 5 +#define RG_ZCD13M_CK_PDN_MASK 0x1 +#define RG_ZCD13M_CK_PDN_MASK_SFT (0x1 << 5) +#define RG_AUDIF_CK_PDN_SFT 2 +#define RG_AUDIF_CK_PDN_MASK 0x1 +#define RG_AUDIF_CK_PDN_MASK_SFT (0x1 << 2) +#define RG_AUD_CK_PDN_SFT 1 +#define RG_AUD_CK_PDN_MASK 0x1 +#define RG_AUD_CK_PDN_MASK_SFT (0x1 << 1) +#define RG_ACCDET_CK_PDN_SFT 0 +#define RG_ACCDET_CK_PDN_MASK 0x1 +#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_CKPDN_CON0_SET */ +#define RG_AUD_TOP_CKPDN_CON0_SET_SFT 0 +#define RG_AUD_TOP_CKPDN_CON0_SET_MASK 0x3fff +#define RG_AUD_TOP_CKPDN_CON0_SET_MASK_SFT (0x3fff << 0) + +/* MT6358_AUD_TOP_CKPDN_CON0_CLR */ +#define RG_AUD_TOP_CKPDN_CON0_CLR_SFT 0 +#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK 0x3fff +#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK_SFT (0x3fff << 0) + +/* MT6358_AUD_TOP_CKSEL_CON0 */ +#define RG_AUDIF_CK_CKSEL_SFT 3 +#define RG_AUDIF_CK_CKSEL_MASK 0x1 +#define RG_AUDIF_CK_CKSEL_MASK_SFT (0x1 << 3) +#define RG_AUD_CK_CKSEL_SFT 2 +#define RG_AUD_CK_CKSEL_MASK 0x1 +#define RG_AUD_CK_CKSEL_MASK_SFT (0x1 << 2) + +/* MT6358_AUD_TOP_CKSEL_CON0_SET */ +#define RG_AUD_TOP_CKSEL_CON0_SET_SFT 0 +#define RG_AUD_TOP_CKSEL_CON0_SET_MASK 0xf +#define RG_AUD_TOP_CKSEL_CON0_SET_MASK_SFT (0xf << 0) + +/* MT6358_AUD_TOP_CKSEL_CON0_CLR */ +#define RG_AUD_TOP_CKSEL_CON0_CLR_SFT 0 +#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK 0xf +#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK_SFT (0xf << 0) + +/* MT6358_AUD_TOP_CKTST_CON0 */ +#define RG_VOW13M_CK_TSTSEL_SFT 9 +#define RG_VOW13M_CK_TSTSEL_MASK 0x1 +#define RG_VOW13M_CK_TSTSEL_MASK_SFT (0x1 << 9) +#define RG_VOW13M_CK_TST_DIS_SFT 8 +#define RG_VOW13M_CK_TST_DIS_MASK 0x1 +#define RG_VOW13M_CK_TST_DIS_MASK_SFT (0x1 << 8) +#define RG_AUD26M_CK_TSTSEL_SFT 4 +#define RG_AUD26M_CK_TSTSEL_MASK 0x1 +#define RG_AUD26M_CK_TSTSEL_MASK_SFT (0x1 << 4) +#define RG_AUDIF_CK_TSTSEL_SFT 3 +#define RG_AUDIF_CK_TSTSEL_MASK 0x1 +#define RG_AUDIF_CK_TSTSEL_MASK_SFT (0x1 << 3) +#define RG_AUD_CK_TSTSEL_SFT 2 +#define RG_AUD_CK_TSTSEL_MASK 0x1 +#define RG_AUD_CK_TSTSEL_MASK_SFT (0x1 << 2) +#define RG_AUD26M_CK_TST_DIS_SFT 0 +#define RG_AUD26M_CK_TST_DIS_MASK 0x1 +#define RG_AUD26M_CK_TST_DIS_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_CLK_HWEN_CON0 */ +#define RG_AUD_INTRP_CK_PDN_HWEN_SFT 0 +#define RG_AUD_INTRP_CK_PDN_HWEN_MASK 0x1 +#define RG_AUD_INTRP_CK_PDN_HWEN_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_CLK_HWEN_CON0_SET */ +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_SFT 0 +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK 0xffff +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK_SFT (0xffff << 0) + +/* MT6358_AUD_TOP_CLK_HWEN_CON0_CLR */ +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_SFT 0 +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK 0xffff +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK_SFT (0xffff << 0) + +/* MT6358_AUD_TOP_RST_CON0 */ +#define RG_AUDNCP_RST_SFT 3 +#define RG_AUDNCP_RST_MASK 0x1 +#define RG_AUDNCP_RST_MASK_SFT (0x1 << 3) +#define RG_ZCD_RST_SFT 2 +#define RG_ZCD_RST_MASK 0x1 +#define RG_ZCD_RST_MASK_SFT (0x1 << 2) +#define RG_ACCDET_RST_SFT 1 +#define RG_ACCDET_RST_MASK 0x1 +#define RG_ACCDET_RST_MASK_SFT (0x1 << 1) +#define RG_AUDIO_RST_SFT 0 +#define RG_AUDIO_RST_MASK 0x1 +#define RG_AUDIO_RST_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_RST_CON0_SET */ +#define RG_AUD_TOP_RST_CON0_SET_SFT 0 +#define RG_AUD_TOP_RST_CON0_SET_MASK 0xf +#define RG_AUD_TOP_RST_CON0_SET_MASK_SFT (0xf << 0) + +/* MT6358_AUD_TOP_RST_CON0_CLR */ +#define RG_AUD_TOP_RST_CON0_CLR_SFT 0 +#define RG_AUD_TOP_RST_CON0_CLR_MASK 0xf +#define RG_AUD_TOP_RST_CON0_CLR_MASK_SFT (0xf << 0) + +/* MT6358_AUD_TOP_RST_BANK_CON0 */ +#define BANK_AUDZCD_SWRST_SFT 2 +#define BANK_AUDZCD_SWRST_MASK 0x1 +#define BANK_AUDZCD_SWRST_MASK_SFT (0x1 << 2) +#define BANK_AUDIO_SWRST_SFT 1 +#define BANK_AUDIO_SWRST_MASK 0x1 +#define BANK_AUDIO_SWRST_MASK_SFT (0x1 << 1) +#define BANK_ACCDET_SWRST_SFT 0 +#define BANK_ACCDET_SWRST_MASK 0x1 +#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_INT_CON0 */ +#define RG_INT_EN_AUDIO_SFT 0 +#define RG_INT_EN_AUDIO_MASK 0x1 +#define RG_INT_EN_AUDIO_MASK_SFT (0x1 << 0) +#define RG_INT_EN_ACCDET_SFT 5 +#define RG_INT_EN_ACCDET_MASK 0x1 +#define RG_INT_EN_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_EN_ACCDET_EINT0_SFT 6 +#define RG_INT_EN_ACCDET_EINT0_MASK 0x1 +#define RG_INT_EN_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_EN_ACCDET_EINT1_SFT 7 +#define RG_INT_EN_ACCDET_EINT1_MASK 0x1 +#define RG_INT_EN_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +/* MT6358_AUD_TOP_INT_CON0_SET */ +#define RG_AUD_INT_CON0_SET_SFT 0 +#define RG_AUD_INT_CON0_SET_MASK 0xffff +#define RG_AUD_INT_CON0_SET_MASK_SFT (0xffff << 0) + +/* MT6358_AUD_TOP_INT_CON0_CLR */ +#define RG_AUD_INT_CON0_CLR_SFT 0 +#define RG_AUD_INT_CON0_CLR_MASK 0xffff +#define RG_AUD_INT_CON0_CLR_MASK_SFT (0xffff << 0) + +/* MT6358_AUD_TOP_INT_MASK_CON0 */ +#define RG_INT_MASK_AUDIO_SFT 0 +#define RG_INT_MASK_AUDIO_MASK 0x1 +#define RG_INT_MASK_AUDIO_MASK_SFT (0x1 << 0) +#define RG_INT_MASK_ACCDET_SFT 5 +#define RG_INT_MASK_ACCDET_MASK 0x1 +#define RG_INT_MASK_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_MASK_ACCDET_EINT0_SFT 6 +#define RG_INT_MASK_ACCDET_EINT0_MASK 0x1 +#define RG_INT_MASK_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_MASK_ACCDET_EINT1_SFT 7 +#define RG_INT_MASK_ACCDET_EINT1_MASK 0x1 +#define RG_INT_MASK_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +/* MT6358_AUD_TOP_INT_MASK_CON0_SET */ +#define RG_AUD_INT_MASK_CON0_SET_SFT 0 +#define RG_AUD_INT_MASK_CON0_SET_MASK 0xff +#define RG_AUD_INT_MASK_CON0_SET_MASK_SFT (0xff << 0) + +/* MT6358_AUD_TOP_INT_MASK_CON0_CLR */ +#define RG_AUD_INT_MASK_CON0_CLR_SFT 0 +#define RG_AUD_INT_MASK_CON0_CLR_MASK 0xff +#define RG_AUD_INT_MASK_CON0_CLR_MASK_SFT (0xff << 0) + +/* MT6358_AUD_TOP_INT_STATUS0 */ +#define RG_INT_STATUS_AUDIO_SFT 0 +#define RG_INT_STATUS_AUDIO_MASK 0x1 +#define RG_INT_STATUS_AUDIO_MASK_SFT (0x1 << 0) +#define RG_INT_STATUS_ACCDET_SFT 5 +#define RG_INT_STATUS_ACCDET_MASK 0x1 +#define RG_INT_STATUS_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_STATUS_ACCDET_EINT0_SFT 6 +#define RG_INT_STATUS_ACCDET_EINT0_MASK 0x1 +#define RG_INT_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_STATUS_ACCDET_EINT1_SFT 7 +#define RG_INT_STATUS_ACCDET_EINT1_MASK 0x1 +#define RG_INT_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +/* MT6358_AUD_TOP_INT_RAW_STATUS0 */ +#define RG_INT_RAW_STATUS_AUDIO_SFT 0 +#define RG_INT_RAW_STATUS_AUDIO_MASK 0x1 +#define RG_INT_RAW_STATUS_AUDIO_MASK_SFT (0x1 << 0) +#define RG_INT_RAW_STATUS_ACCDET_SFT 5 +#define RG_INT_RAW_STATUS_ACCDET_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_RAW_STATUS_ACCDET_EINT0_SFT 6 +#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_RAW_STATUS_ACCDET_EINT1_SFT 7 +#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +/* MT6358_AUD_TOP_INT_MISC_CON0 */ +#define RG_AUD_TOP_INT_POLARITY_SFT 0 +#define RG_AUD_TOP_INT_POLARITY_MASK 0x1 +#define RG_AUD_TOP_INT_POLARITY_MASK_SFT (0x1 << 0) + +/* MT6358_AUDNCP_CLKDIV_CON0 */ +#define RG_DIVCKS_CHG_SFT 0 +#define RG_DIVCKS_CHG_MASK 0x1 +#define RG_DIVCKS_CHG_MASK_SFT (0x1 << 0) + +/* MT6358_AUDNCP_CLKDIV_CON1 */ +#define RG_DIVCKS_ON_SFT 0 +#define RG_DIVCKS_ON_MASK 0x1 +#define RG_DIVCKS_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AUDNCP_CLKDIV_CON2 */ +#define RG_DIVCKS_PRG_SFT 0 +#define RG_DIVCKS_PRG_MASK 0x1ff +#define RG_DIVCKS_PRG_MASK_SFT (0x1ff << 0) + +/* MT6358_AUDNCP_CLKDIV_CON3 */ +#define RG_DIVCKS_PWD_NCP_SFT 0 +#define RG_DIVCKS_PWD_NCP_MASK 0x1 +#define RG_DIVCKS_PWD_NCP_MASK_SFT (0x1 << 0) + +/* MT6358_AUDNCP_CLKDIV_CON4 */ +#define RG_DIVCKS_PWD_NCP_ST_SEL_SFT 0 +#define RG_DIVCKS_PWD_NCP_ST_SEL_MASK 0x3 +#define RG_DIVCKS_PWD_NCP_ST_SEL_MASK_SFT (0x3 << 0) + +/* MT6358_AUD_TOP_MON_CON0 */ +#define RG_AUD_TOP_MON_SEL_SFT 0 +#define RG_AUD_TOP_MON_SEL_MASK 0x7 +#define RG_AUD_TOP_MON_SEL_MASK_SFT (0x7 << 0) +#define RG_AUD_CLK_INT_MON_FLAG_SEL_SFT 3 +#define RG_AUD_CLK_INT_MON_FLAG_SEL_MASK 0xff +#define RG_AUD_CLK_INT_MON_FLAG_SEL_MASK_SFT (0xff << 3) +#define RG_AUD_CLK_INT_MON_FLAG_EN_SFT 11 +#define RG_AUD_CLK_INT_MON_FLAG_EN_MASK 0x1 +#define RG_AUD_CLK_INT_MON_FLAG_EN_MASK_SFT (0x1 << 11) + +/* MT6358_AUDIO_DIG_DSN_ID */ +#define AUDIO_DIG_ANA_ID_SFT 0 +#define AUDIO_DIG_ANA_ID_MASK 0xff +#define AUDIO_DIG_ANA_ID_MASK_SFT (0xff << 0) +#define AUDIO_DIG_DIG_ID_SFT 8 +#define AUDIO_DIG_DIG_ID_MASK 0xff +#define AUDIO_DIG_DIG_ID_MASK_SFT (0xff << 8) + +/* MT6358_AUDIO_DIG_DSN_REV0 */ +#define AUDIO_DIG_ANA_MINOR_REV_SFT 0 +#define AUDIO_DIG_ANA_MINOR_REV_MASK 0xf +#define AUDIO_DIG_ANA_MINOR_REV_MASK_SFT (0xf << 0) +#define AUDIO_DIG_ANA_MAJOR_REV_SFT 4 +#define AUDIO_DIG_ANA_MAJOR_REV_MASK 0xf +#define AUDIO_DIG_ANA_MAJOR_REV_MASK_SFT (0xf << 4) +#define AUDIO_DIG_DIG_MINOR_REV_SFT 8 +#define AUDIO_DIG_DIG_MINOR_REV_MASK 0xf +#define AUDIO_DIG_DIG_MINOR_REV_MASK_SFT (0xf << 8) +#define AUDIO_DIG_DIG_MAJOR_REV_SFT 12 +#define AUDIO_DIG_DIG_MAJOR_REV_MASK 0xf +#define AUDIO_DIG_DIG_MAJOR_REV_MASK_SFT (0xf << 12) + +/* MT6358_AUDIO_DIG_DSN_DBI */ +#define AUDIO_DIG_DSN_CBS_SFT 0 +#define AUDIO_DIG_DSN_CBS_MASK 0x3 +#define AUDIO_DIG_DSN_CBS_MASK_SFT (0x3 << 0) +#define AUDIO_DIG_DSN_BIX_SFT 2 +#define AUDIO_DIG_DSN_BIX_MASK 0x3 +#define AUDIO_DIG_DSN_BIX_MASK_SFT (0x3 << 2) +#define AUDIO_DIG_ESP_SFT 8 +#define AUDIO_DIG_ESP_MASK 0xff +#define AUDIO_DIG_ESP_MASK_SFT (0xff << 8) + +/* MT6358_AUDIO_DIG_DSN_DXI */ +#define AUDIO_DIG_DSN_FPI_SFT 0 +#define AUDIO_DIG_DSN_FPI_MASK 0xff +#define AUDIO_DIG_DSN_FPI_MASK_SFT (0xff << 0) + +/* MT6358_AFE_UL_DL_CON0 */ +#define AFE_UL_LR_SWAP_SFT 15 +#define AFE_UL_LR_SWAP_MASK 0x1 +#define AFE_UL_LR_SWAP_MASK_SFT (0x1 << 15) +#define AFE_DL_LR_SWAP_SFT 14 +#define AFE_DL_LR_SWAP_MASK 0x1 +#define AFE_DL_LR_SWAP_MASK_SFT (0x1 << 14) +#define AFE_ON_SFT 0 +#define AFE_ON_MASK 0x1 +#define AFE_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_DL_SRC2_CON0_L */ +#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_UL_SRC_CON0_H */ +#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11) +#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8) +#define C_TWO_DIGITAL_MIC_CTL_SFT 7 +#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1 +#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7) + +/* MT6358_AFE_UL_SRC_CON0_L */ +#define DMIC_LOW_POWER_MODE_CTL_SFT 14 +#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3 +#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14) +#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5) +#define UL_LOOP_BACK_MODE_CTL_SFT 2 +#define UL_LOOP_BACK_MODE_CTL_MASK 0x1 +#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2) +#define UL_SDM_3_LEVEL_CTL_SFT 1 +#define UL_SDM_3_LEVEL_CTL_MASK 0x1 +#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1) +#define UL_SRC_ON_TMP_CTL_SFT 0 +#define UL_SRC_ON_TMP_CTL_MASK 0x1 +#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_TOP_CON0 */ +#define MTKAIF_SINE_ON_SFT 2 +#define MTKAIF_SINE_ON_MASK 0x1 +#define MTKAIF_SINE_ON_MASK_SFT (0x1 << 2) +#define UL_SINE_ON_SFT 1 +#define UL_SINE_ON_MASK 0x1 +#define UL_SINE_ON_MASK_SFT (0x1 << 1) +#define DL_SINE_ON_SFT 0 +#define DL_SINE_ON_MASK 0x1 +#define DL_SINE_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AUDIO_TOP_CON0 */ +#define PDN_AFE_CTL_SFT 7 +#define PDN_AFE_CTL_MASK 0x1 +#define PDN_AFE_CTL_MASK_SFT (0x1 << 7) +#define PDN_DAC_CTL_SFT 6 +#define PDN_DAC_CTL_MASK 0x1 +#define PDN_DAC_CTL_MASK_SFT (0x1 << 6) +#define PDN_ADC_CTL_SFT 5 +#define PDN_ADC_CTL_MASK 0x1 +#define PDN_ADC_CTL_MASK_SFT (0x1 << 5) +#define PDN_I2S_DL_CTL_SFT 3 +#define PDN_I2S_DL_CTL_MASK 0x1 +#define PDN_I2S_DL_CTL_MASK_SFT (0x1 << 3) +#define PWR_CLK_DIS_CTL_SFT 2 +#define PWR_CLK_DIS_CTL_MASK 0x1 +#define PWR_CLK_DIS_CTL_MASK_SFT (0x1 << 2) +#define PDN_AFE_TESTMODEL_CTL_SFT 1 +#define PDN_AFE_TESTMODEL_CTL_MASK 0x1 +#define PDN_AFE_TESTMODEL_CTL_MASK_SFT (0x1 << 1) +#define PDN_RESERVED_SFT 0 +#define PDN_RESERVED_MASK 0x1 +#define PDN_RESERVED_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_MON_DEBUG0 */ +#define AUDIO_SYS_TOP_MON_SWAP_SFT 14 +#define AUDIO_SYS_TOP_MON_SWAP_MASK 0x3 +#define AUDIO_SYS_TOP_MON_SWAP_MASK_SFT (0x3 << 14) +#define AUDIO_SYS_TOP_MON_SEL_SFT 8 +#define AUDIO_SYS_TOP_MON_SEL_MASK 0x1f +#define AUDIO_SYS_TOP_MON_SEL_MASK_SFT (0x1f << 8) +#define AFE_MON_SEL_SFT 0 +#define AFE_MON_SEL_MASK 0xff +#define AFE_MON_SEL_MASK_SFT (0xff << 0) + +/* MT6358_AFUNC_AUD_CON0 */ +#define CCI_AUD_ANACK_SEL_SFT 15 +#define CCI_AUD_ANACK_SEL_MASK 0x1 +#define CCI_AUD_ANACK_SEL_MASK_SFT (0x1 << 15) +#define CCI_AUDIO_FIFO_WPTR_SFT 12 +#define CCI_AUDIO_FIFO_WPTR_MASK 0x7 +#define CCI_AUDIO_FIFO_WPTR_MASK_SFT (0x7 << 12) +#define CCI_SCRAMBLER_CG_EN_SFT 11 +#define CCI_SCRAMBLER_CG_EN_MASK 0x1 +#define CCI_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 11) +#define CCI_LCH_INV_SFT 10 +#define CCI_LCH_INV_MASK 0x1 +#define CCI_LCH_INV_MASK_SFT (0x1 << 10) +#define CCI_RAND_EN_SFT 9 +#define CCI_RAND_EN_MASK 0x1 +#define CCI_RAND_EN_MASK_SFT (0x1 << 9) +#define CCI_SPLT_SCRMB_CLK_ON_SFT 8 +#define CCI_SPLT_SCRMB_CLK_ON_MASK 0x1 +#define CCI_SPLT_SCRMB_CLK_ON_MASK_SFT (0x1 << 8) +#define CCI_SPLT_SCRMB_ON_SFT 7 +#define CCI_SPLT_SCRMB_ON_MASK 0x1 +#define CCI_SPLT_SCRMB_ON_MASK_SFT (0x1 << 7) +#define CCI_AUD_IDAC_TEST_EN_SFT 6 +#define CCI_AUD_IDAC_TEST_EN_MASK 0x1 +#define CCI_AUD_IDAC_TEST_EN_MASK_SFT (0x1 << 6) +#define CCI_ZERO_PAD_DISABLE_SFT 5 +#define CCI_ZERO_PAD_DISABLE_MASK 0x1 +#define CCI_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 5) +#define CCI_AUD_SPLIT_TEST_EN_SFT 4 +#define CCI_AUD_SPLIT_TEST_EN_MASK 0x1 +#define CCI_AUD_SPLIT_TEST_EN_MASK_SFT (0x1 << 4) +#define CCI_AUD_SDM_MUTEL_SFT 3 +#define CCI_AUD_SDM_MUTEL_MASK 0x1 +#define CCI_AUD_SDM_MUTEL_MASK_SFT (0x1 << 3) +#define CCI_AUD_SDM_MUTER_SFT 2 +#define CCI_AUD_SDM_MUTER_MASK 0x1 +#define CCI_AUD_SDM_MUTER_MASK_SFT (0x1 << 2) +#define CCI_AUD_SDM_7BIT_SEL_SFT 1 +#define CCI_AUD_SDM_7BIT_SEL_MASK 0x1 +#define CCI_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 1) +#define CCI_SCRAMBLER_EN_SFT 0 +#define CCI_SCRAMBLER_EN_MASK 0x1 +#define CCI_SCRAMBLER_EN_MASK_SFT (0x1 << 0) + +/* MT6358_AFUNC_AUD_CON1 */ +#define AUD_SDM_TEST_L_SFT 8 +#define AUD_SDM_TEST_L_MASK 0xff +#define AUD_SDM_TEST_L_MASK_SFT (0xff << 8) +#define AUD_SDM_TEST_R_SFT 0 +#define AUD_SDM_TEST_R_MASK 0xff +#define AUD_SDM_TEST_R_MASK_SFT (0xff << 0) + +/* MT6358_AFUNC_AUD_CON2 */ +#define CCI_AUD_DAC_ANA_MUTE_SFT 7 +#define CCI_AUD_DAC_ANA_MUTE_MASK 0x1 +#define CCI_AUD_DAC_ANA_MUTE_MASK_SFT (0x1 << 7) +#define CCI_AUD_DAC_ANA_RSTB_SEL_SFT 6 +#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK 0x1 +#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK_SFT (0x1 << 6) +#define CCI_AUDIO_FIFO_CLKIN_INV_SFT 4 +#define CCI_AUDIO_FIFO_CLKIN_INV_MASK 0x1 +#define CCI_AUDIO_FIFO_CLKIN_INV_MASK_SFT (0x1 << 4) +#define CCI_AUDIO_FIFO_ENABLE_SFT 3 +#define CCI_AUDIO_FIFO_ENABLE_MASK 0x1 +#define CCI_AUDIO_FIFO_ENABLE_MASK_SFT (0x1 << 3) +#define CCI_ACD_MODE_SFT 2 +#define CCI_ACD_MODE_MASK 0x1 +#define CCI_ACD_MODE_MASK_SFT (0x1 << 2) +#define CCI_AFIFO_CLK_PWDB_SFT 1 +#define CCI_AFIFO_CLK_PWDB_MASK 0x1 +#define CCI_AFIFO_CLK_PWDB_MASK_SFT (0x1 << 1) +#define CCI_ACD_FUNC_RSTB_SFT 0 +#define CCI_ACD_FUNC_RSTB_MASK 0x1 +#define CCI_ACD_FUNC_RSTB_MASK_SFT (0x1 << 0) + +/* MT6358_AFUNC_AUD_CON3 */ +#define SDM_ANA13M_TESTCK_SEL_SFT 15 +#define SDM_ANA13M_TESTCK_SEL_MASK 0x1 +#define SDM_ANA13M_TESTCK_SEL_MASK_SFT (0x1 << 15) +#define SDM_ANA13M_TESTCK_SRC_SEL_SFT 12 +#define SDM_ANA13M_TESTCK_SRC_SEL_MASK 0x7 +#define SDM_ANA13M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12) +#define SDM_TESTCK_SRC_SEL_SFT 8 +#define SDM_TESTCK_SRC_SEL_MASK 0x7 +#define SDM_TESTCK_SRC_SEL_MASK_SFT (0x7 << 8) +#define DIGMIC_TESTCK_SRC_SEL_SFT 4 +#define DIGMIC_TESTCK_SRC_SEL_MASK 0x7 +#define DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 4) +#define DIGMIC_TESTCK_SEL_SFT 0 +#define DIGMIC_TESTCK_SEL_MASK 0x1 +#define DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 0) + +/* MT6358_AFUNC_AUD_CON4 */ +#define UL_FIFO_WCLK_INV_SFT 8 +#define UL_FIFO_WCLK_INV_MASK 0x1 +#define UL_FIFO_WCLK_INV_MASK_SFT (0x1 << 8) +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6 +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6) +#define UL_FIFO_WDATA_TESTEN_SFT 5 +#define UL_FIFO_WDATA_TESTEN_MASK 0x1 +#define UL_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5) +#define UL_FIFO_WDATA_TESTSRC_SEL_SFT 4 +#define UL_FIFO_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4) +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3 +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1 +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3) +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0 +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7 +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0) + +/* MT6358_AFUNC_AUD_CON5 */ +#define R_AUD_DAC_POS_LARGE_MONO_SFT 8 +#define R_AUD_DAC_POS_LARGE_MONO_MASK 0xff +#define R_AUD_DAC_POS_LARGE_MONO_MASK_SFT (0xff << 8) +#define R_AUD_DAC_NEG_LARGE_MONO_SFT 0 +#define R_AUD_DAC_NEG_LARGE_MONO_MASK 0xff +#define R_AUD_DAC_NEG_LARGE_MONO_MASK_SFT (0xff << 0) + +/* MT6358_AFUNC_AUD_CON6 */ +#define R_AUD_DAC_POS_SMALL_MONO_SFT 12 +#define R_AUD_DAC_POS_SMALL_MONO_MASK 0xf +#define R_AUD_DAC_POS_SMALL_MONO_MASK_SFT (0xf << 12) +#define R_AUD_DAC_NEG_SMALL_MONO_SFT 8 +#define R_AUD_DAC_NEG_SMALL_MONO_MASK 0xf +#define R_AUD_DAC_NEG_SMALL_MONO_MASK_SFT (0xf << 8) +#define R_AUD_DAC_POS_TINY_MONO_SFT 6 +#define R_AUD_DAC_POS_TINY_MONO_MASK 0x3 +#define R_AUD_DAC_POS_TINY_MONO_MASK_SFT (0x3 << 6) +#define R_AUD_DAC_NEG_TINY_MONO_SFT 4 +#define R_AUD_DAC_NEG_TINY_MONO_MASK 0x3 +#define R_AUD_DAC_NEG_TINY_MONO_MASK_SFT (0x3 << 4) +#define R_AUD_DAC_MONO_SEL_SFT 3 +#define R_AUD_DAC_MONO_SEL_MASK 0x1 +#define R_AUD_DAC_MONO_SEL_MASK_SFT (0x1 << 3) +#define R_AUD_DAC_SW_RSTB_SFT 0 +#define R_AUD_DAC_SW_RSTB_MASK 0x1 +#define R_AUD_DAC_SW_RSTB_MASK_SFT (0x1 << 0) + +/* MT6358_AFUNC_AUD_MON0 */ +#define AUD_SCR_OUT_L_SFT 8 +#define AUD_SCR_OUT_L_MASK 0xff +#define AUD_SCR_OUT_L_MASK_SFT (0xff << 8) +#define AUD_SCR_OUT_R_SFT 0 +#define AUD_SCR_OUT_R_MASK 0xff +#define AUD_SCR_OUT_R_MASK_SFT (0xff << 0) + +/* MT6358_AUDRC_TUNE_MON0 */ +#define ASYNC_TEST_OUT_BCK_SFT 15 +#define ASYNC_TEST_OUT_BCK_MASK 0x1 +#define ASYNC_TEST_OUT_BCK_MASK_SFT (0x1 << 15) +#define RGS_AUDRCTUNE1READ_SFT 8 +#define RGS_AUDRCTUNE1READ_MASK 0x1f +#define RGS_AUDRCTUNE1READ_MASK_SFT (0x1f << 8) +#define RGS_AUDRCTUNE0READ_SFT 0 +#define RGS_AUDRCTUNE0READ_MASK 0x1f +#define RGS_AUDRCTUNE0READ_MASK_SFT (0x1f << 0) + +/* MT6358_AFE_ADDA_MTKAIF_FIFO_CFG0 */ +#define AFE_RESERVED_SFT 1 +#define AFE_RESERVED_MASK 0x7fff +#define AFE_RESERVED_MASK_SFT (0x7fff << 1) +#define RG_MTKAIF_RXIF_FIFO_INTEN_SFT 0 +#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK 0x1 +#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 */ +#define MTKAIF_RXIF_WR_FULL_STATUS_SFT 1 +#define MTKAIF_RXIF_WR_FULL_STATUS_MASK 0x1 +#define MTKAIF_RXIF_WR_FULL_STATUS_MASK_SFT (0x1 << 1) +#define MTKAIF_RXIF_RD_EMPTY_STATUS_SFT 0 +#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK 0x1 +#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_ADDA_MTKAIF_MON0 */ +#define MTKAIFTX_V3_SYNC_OUT_SFT 14 +#define MTKAIFTX_V3_SYNC_OUT_MASK 0x1 +#define MTKAIFTX_V3_SYNC_OUT_MASK_SFT (0x1 << 14) +#define MTKAIFTX_V3_SDATA_OUT2_SFT 13 +#define MTKAIFTX_V3_SDATA_OUT2_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT2_MASK_SFT (0x1 << 13) +#define MTKAIFTX_V3_SDATA_OUT1_SFT 12 +#define MTKAIFTX_V3_SDATA_OUT1_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT1_MASK_SFT (0x1 << 12) +#define MTKAIF_RXIF_FIFO_STATUS_SFT 0 +#define MTKAIF_RXIF_FIFO_STATUS_MASK 0xfff +#define MTKAIF_RXIF_FIFO_STATUS_MASK_SFT (0xfff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_MON1 */ +#define MTKAIFRX_V3_SYNC_IN_SFT 14 +#define MTKAIFRX_V3_SYNC_IN_MASK 0x1 +#define MTKAIFRX_V3_SYNC_IN_MASK_SFT (0x1 << 14) +#define MTKAIFRX_V3_SDATA_IN2_SFT 13 +#define MTKAIFRX_V3_SDATA_IN2_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN2_MASK_SFT (0x1 << 13) +#define MTKAIFRX_V3_SDATA_IN1_SFT 12 +#define MTKAIFRX_V3_SDATA_IN1_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN1_MASK_SFT (0x1 << 12) +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_SFT 11 +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK 0x1 +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 11) +#define MTKAIF_RXIF_INVALID_FLAG_SFT 8 +#define MTKAIF_RXIF_INVALID_FLAG_MASK 0x1 +#define MTKAIF_RXIF_INVALID_FLAG_MASK_SFT (0x1 << 8) +#define MTKAIF_RXIF_INVALID_CYCLE_SFT 0 +#define MTKAIF_RXIF_INVALID_CYCLE_MASK 0xff +#define MTKAIF_RXIF_INVALID_CYCLE_MASK_SFT (0xff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_MON2 */ +#define MTKAIF_TXIF_IN_CH2_SFT 8 +#define MTKAIF_TXIF_IN_CH2_MASK 0xff +#define MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8) +#define MTKAIF_TXIF_IN_CH1_SFT 0 +#define MTKAIF_TXIF_IN_CH1_MASK 0xff +#define MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_MON3 */ +#define MTKAIF_RXIF_OUT_CH2_SFT 8 +#define MTKAIF_RXIF_OUT_CH2_MASK 0xff +#define MTKAIF_RXIF_OUT_CH2_MASK_SFT (0xff << 8) +#define MTKAIF_RXIF_OUT_CH1_SFT 0 +#define MTKAIF_RXIF_OUT_CH1_MASK 0xff +#define MTKAIF_RXIF_OUT_CH1_MASK_SFT (0xff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_CFG0 */ +#define RG_MTKAIF_RXIF_CLKINV_SFT 15 +#define RG_MTKAIF_RXIF_CLKINV_MASK 0x1 +#define RG_MTKAIF_RXIF_CLKINV_MASK_SFT (0x1 << 15) +#define RG_MTKAIF_RXIF_PROTOCOL2_SFT 8 +#define RG_MTKAIF_RXIF_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 8) +#define RG_MTKAIF_BYPASS_SRC_MODE_SFT 6 +#define RG_MTKAIF_BYPASS_SRC_MODE_MASK 0x3 +#define RG_MTKAIF_BYPASS_SRC_MODE_MASK_SFT (0x3 << 6) +#define RG_MTKAIF_BYPASS_SRC_TEST_SFT 5 +#define RG_MTKAIF_BYPASS_SRC_TEST_MASK 0x1 +#define RG_MTKAIF_BYPASS_SRC_TEST_MASK_SFT (0x1 << 5) +#define RG_MTKAIF_TXIF_PROTOCOL2_SFT 4 +#define RG_MTKAIF_TXIF_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4) +#define RG_MTKAIF_PMIC_TXIF_8TO5_SFT 2 +#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1 +#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 2) +#define RG_MTKAIF_LOOPBACK_TEST2_SFT 1 +#define RG_MTKAIF_LOOPBACK_TEST2_MASK 0x1 +#define RG_MTKAIF_LOOPBACK_TEST2_MASK_SFT (0x1 << 1) +#define RG_MTKAIF_LOOPBACK_TEST1_SFT 0 +#define RG_MTKAIF_LOOPBACK_TEST1_MASK 0x1 +#define RG_MTKAIF_LOOPBACK_TEST1_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_ADDA_MTKAIF_RX_CFG0 */ +#define RG_MTKAIF_RXIF_VOICE_MODE_SFT 12 +#define RG_MTKAIF_RXIF_VOICE_MODE_MASK 0xf +#define RG_MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 12) +#define RG_MTKAIF_RXIF_DATA_BIT_SFT 8 +#define RG_MTKAIF_RXIF_DATA_BIT_MASK 0x7 +#define RG_MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8) +#define RG_MTKAIF_RXIF_FIFO_RSP_SFT 4 +#define RG_MTKAIF_RXIF_FIFO_RSP_MASK 0x7 +#define RG_MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RXIF_DETECT_ON_SFT 3 +#define RG_MTKAIF_RXIF_DETECT_ON_MASK 0x1 +#define RG_MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 3) +#define RG_MTKAIF_RXIF_DATA_MODE_SFT 0 +#define RG_MTKAIF_RXIF_DATA_MODE_MASK 0x1 +#define RG_MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_ADDA_MTKAIF_RX_CFG1 */ +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_SFT 12 +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK 0xf +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12) +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8 +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8) +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_SFT 4 +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK 0xf +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4) +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_SFT 0 +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK 0xf +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK_SFT (0xf << 0) + +/* MT6358_AFE_ADDA_MTKAIF_RX_CFG2 */ +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_SFT 12 +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK 0x1 +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 12) +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_SFT 0 +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK 0xfff +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_RX_CFG3 */ +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_SFT 7 +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK 0x1 +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK_SFT (0x1 << 7) +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4 +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7 +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 3 +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 3) + +/* MT6358_AFE_ADDA_MTKAIF_TX_CFG1 */ +#define RG_MTKAIF_SYNC_WORD2_SFT 4 +#define RG_MTKAIF_SYNC_WORD2_MASK 0x7 +#define RG_MTKAIF_SYNC_WORD2_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_SYNC_WORD1_SFT 0 +#define RG_MTKAIF_SYNC_WORD1_MASK 0x7 +#define RG_MTKAIF_SYNC_WORD1_MASK_SFT (0x7 << 0) + +/* MT6358_AFE_SGEN_CFG0 */ +#define SGEN_AMP_DIV_CH1_CTL_SFT 12 +#define SGEN_AMP_DIV_CH1_CTL_MASK 0xf +#define SGEN_AMP_DIV_CH1_CTL_MASK_SFT (0xf << 12) +#define SGEN_DAC_EN_CTL_SFT 7 +#define SGEN_DAC_EN_CTL_MASK 0x1 +#define SGEN_DAC_EN_CTL_MASK_SFT (0x1 << 7) +#define SGEN_MUTE_SW_CTL_SFT 6 +#define SGEN_MUTE_SW_CTL_MASK 0x1 +#define SGEN_MUTE_SW_CTL_MASK_SFT (0x1 << 6) +#define R_AUD_SDM_MUTE_L_SFT 5 +#define R_AUD_SDM_MUTE_L_MASK 0x1 +#define R_AUD_SDM_MUTE_L_MASK_SFT (0x1 << 5) +#define R_AUD_SDM_MUTE_R_SFT 4 +#define R_AUD_SDM_MUTE_R_MASK 0x1 +#define R_AUD_SDM_MUTE_R_MASK_SFT (0x1 << 4) + +/* MT6358_AFE_SGEN_CFG1 */ +#define C_SGEN_RCH_INV_5BIT_SFT 15 +#define C_SGEN_RCH_INV_5BIT_MASK 0x1 +#define C_SGEN_RCH_INV_5BIT_MASK_SFT (0x1 << 15) +#define C_SGEN_RCH_INV_8BIT_SFT 14 +#define C_SGEN_RCH_INV_8BIT_MASK 0x1 +#define C_SGEN_RCH_INV_8BIT_MASK_SFT (0x1 << 14) +#define SGEN_FREQ_DIV_CH1_CTL_SFT 0 +#define SGEN_FREQ_DIV_CH1_CTL_MASK 0x1f +#define SGEN_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 0) + +/* MT6358_AFE_ADC_ASYNC_FIFO_CFG */ +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_SFT 5 +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1 +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5) +#define RG_UL_ASYNC_FIFO_SOFT_RST_SFT 4 +#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK 0x1 +#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4) +#define RG_AMIC_UL_ADC_CLK_SEL_SFT 1 +#define RG_AMIC_UL_ADC_CLK_SEL_MASK 0x1 +#define RG_AMIC_UL_ADC_CLK_SEL_MASK_SFT (0x1 << 1) + +/* MT6358_AFE_DCCLK_CFG0 */ +#define DCCLK_DIV_SFT 5 +#define DCCLK_DIV_MASK 0x7ff +#define DCCLK_DIV_MASK_SFT (0x7ff << 5) +#define DCCLK_INV_SFT 4 +#define DCCLK_INV_MASK 0x1 +#define DCCLK_INV_MASK_SFT (0x1 << 4) +#define DCCLK_PDN_SFT 1 +#define DCCLK_PDN_MASK 0x1 +#define DCCLK_PDN_MASK_SFT (0x1 << 1) +#define DCCLK_GEN_ON_SFT 0 +#define DCCLK_GEN_ON_MASK 0x1 +#define DCCLK_GEN_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_DCCLK_CFG1 */ +#define RESYNC_SRC_SEL_SFT 10 +#define RESYNC_SRC_SEL_MASK 0x3 +#define RESYNC_SRC_SEL_MASK_SFT (0x3 << 10) +#define RESYNC_SRC_CK_INV_SFT 9 +#define RESYNC_SRC_CK_INV_MASK 0x1 +#define RESYNC_SRC_CK_INV_MASK_SFT (0x1 << 9) +#define DCCLK_RESYNC_BYPASS_SFT 8 +#define DCCLK_RESYNC_BYPASS_MASK 0x1 +#define DCCLK_RESYNC_BYPASS_MASK_SFT (0x1 << 8) +#define DCCLK_PHASE_SEL_SFT 4 +#define DCCLK_PHASE_SEL_MASK 0xf +#define DCCLK_PHASE_SEL_MASK_SFT (0xf << 4) + +/* MT6358_AUDIO_DIG_CFG */ +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT 15 +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT (0x1 << 15) +#define RG_AUD_PAD_TOP_PHASE_MODE2_SFT 8 +#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT (0x7f << 8) +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT 7 +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT (0x1 << 7) +#define RG_AUD_PAD_TOP_PHASE_MODE_SFT 0 +#define RG_AUD_PAD_TOP_PHASE_MODE_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT (0x7f << 0) + +/* MT6358_AFE_AUD_PAD_TOP */ +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_SFT 12 +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK 0x7 +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK_SFT (0x7 << 12) +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_SFT 11 +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK 0x1 +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK_SFT (0x1 << 11) +#define RG_AUD_PAD_TOP_TX_FIFO_ON_SFT 8 +#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK 0x1 +#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK_SFT (0x1 << 8) + +/* MT6358_AFE_AUD_PAD_TOP_MON */ +#define ADDA_AUD_PAD_TOP_MON_SFT 0 +#define ADDA_AUD_PAD_TOP_MON_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_AUD_PAD_TOP_MON1 */ +#define ADDA_AUD_PAD_TOP_MON1_SFT 0 +#define ADDA_AUD_PAD_TOP_MON1_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON1_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_DL_NLE_CFG */ +#define NLE_RCH_HPGAIN_SEL_SFT 10 +#define NLE_RCH_HPGAIN_SEL_MASK 0x1 +#define NLE_RCH_HPGAIN_SEL_MASK_SFT (0x1 << 10) +#define NLE_RCH_CH_SEL_SFT 9 +#define NLE_RCH_CH_SEL_MASK 0x1 +#define NLE_RCH_CH_SEL_MASK_SFT (0x1 << 9) +#define NLE_RCH_ON_SFT 8 +#define NLE_RCH_ON_MASK 0x1 +#define NLE_RCH_ON_MASK_SFT (0x1 << 8) +#define NLE_LCH_HPGAIN_SEL_SFT 2 +#define NLE_LCH_HPGAIN_SEL_MASK 0x1 +#define NLE_LCH_HPGAIN_SEL_MASK_SFT (0x1 << 2) +#define NLE_LCH_CH_SEL_SFT 1 +#define NLE_LCH_CH_SEL_MASK 0x1 +#define NLE_LCH_CH_SEL_MASK_SFT (0x1 << 1) +#define NLE_LCH_ON_SFT 0 +#define NLE_LCH_ON_MASK 0x1 +#define NLE_LCH_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_DL_NLE_MON */ +#define NLE_MONITOR_SFT 0 +#define NLE_MONITOR_MASK 0x3fff +#define NLE_MONITOR_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_CG_EN_MON */ +#define CK_CG_EN_MON_SFT 0 +#define CK_CG_EN_MON_MASK 0x3f +#define CK_CG_EN_MON_MASK_SFT (0x3f << 0) + +/* MT6358_AFE_VOW_TOP */ +#define PDN_VOW_SFT 15 +#define PDN_VOW_MASK 0x1 +#define PDN_VOW_MASK_SFT (0x1 << 15) +#define VOW_1P6M_800K_SEL_SFT 14 +#define VOW_1P6M_800K_SEL_MASK 0x1 +#define VOW_1P6M_800K_SEL_MASK_SFT (0x1 << 14) +#define VOW_DIGMIC_ON_SFT 13 +#define VOW_DIGMIC_ON_MASK 0x1 +#define VOW_DIGMIC_ON_MASK_SFT (0x1 << 13) +#define VOW_CK_DIV_RST_SFT 12 +#define VOW_CK_DIV_RST_MASK 0x1 +#define VOW_CK_DIV_RST_MASK_SFT (0x1 << 12) +#define VOW_ON_SFT 11 +#define VOW_ON_MASK 0x1 +#define VOW_ON_MASK_SFT (0x1 << 11) +#define VOW_DIGMIC_CK_PHASE_SEL_SFT 8 +#define VOW_DIGMIC_CK_PHASE_SEL_MASK 0x7 +#define VOW_DIGMIC_CK_PHASE_SEL_MASK_SFT (0x7 << 8) +#define MAIN_DMIC_CK_VOW_SEL_SFT 7 +#define MAIN_DMIC_CK_VOW_SEL_MASK 0x1 +#define MAIN_DMIC_CK_VOW_SEL_MASK_SFT (0x1 << 7) +#define VOW_SDM_3_LEVEL_SFT 6 +#define VOW_SDM_3_LEVEL_MASK 0x1 +#define VOW_SDM_3_LEVEL_MASK_SFT (0x1 << 6) +#define VOW_LOOP_BACK_MODE_SFT 5 +#define VOW_LOOP_BACK_MODE_MASK 0x1 +#define VOW_LOOP_BACK_MODE_MASK_SFT (0x1 << 5) +#define VOW_INTR_SOURCE_SEL_SFT 4 +#define VOW_INTR_SOURCE_SEL_MASK 0x1 +#define VOW_INTR_SOURCE_SEL_MASK_SFT (0x1 << 4) +#define VOW_INTR_CLR_SFT 3 +#define VOW_INTR_CLR_MASK 0x1 +#define VOW_INTR_CLR_MASK_SFT (0x1 << 3) +#define S_N_VALUE_RST_SFT 2 +#define S_N_VALUE_RST_MASK 0x1 +#define S_N_VALUE_RST_MASK_SFT (0x1 << 2) +#define SAMPLE_BASE_MODE_SFT 1 +#define SAMPLE_BASE_MODE_MASK 0x1 +#define SAMPLE_BASE_MODE_MASK_SFT (0x1 << 1) +#define VOW_INTR_FLAG_SFT 0 +#define VOW_INTR_FLAG_MASK 0x1 +#define VOW_INTR_FLAG_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_VOW_CFG0 */ +#define AMPREF_SFT 0 +#define AMPREF_MASK 0xffff +#define AMPREF_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_CFG1 */ +#define TIMERINI_SFT 0 +#define TIMERINI_MASK 0xffff +#define TIMERINI_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_CFG2 */ +#define B_DEFAULT_SFT 12 +#define B_DEFAULT_MASK 0x7 +#define B_DEFAULT_MASK_SFT (0x7 << 12) +#define A_DEFAULT_SFT 8 +#define A_DEFAULT_MASK 0x7 +#define A_DEFAULT_MASK_SFT (0x7 << 8) +#define B_INI_SFT 4 +#define B_INI_MASK 0x7 +#define B_INI_MASK_SFT (0x7 << 4) +#define A_INI_SFT 0 +#define A_INI_MASK 0x7 +#define A_INI_MASK_SFT (0x7 << 0) + +/* MT6358_AFE_VOW_CFG3 */ +#define K_BETA_RISE_SFT 12 +#define K_BETA_RISE_MASK 0xf +#define K_BETA_RISE_MASK_SFT (0xf << 12) +#define K_BETA_FALL_SFT 8 +#define K_BETA_FALL_MASK 0xf +#define K_BETA_FALL_MASK_SFT (0xf << 8) +#define K_ALPHA_RISE_SFT 4 +#define K_ALPHA_RISE_MASK 0xf +#define K_ALPHA_RISE_MASK_SFT (0xf << 4) +#define K_ALPHA_FALL_SFT 0 +#define K_ALPHA_FALL_MASK 0xf +#define K_ALPHA_FALL_MASK_SFT (0xf << 0) + +/* MT6358_AFE_VOW_CFG4 */ +#define VOW_TXIF_SCK_INV_SFT 15 +#define VOW_TXIF_SCK_INV_MASK 0x1 +#define VOW_TXIF_SCK_INV_MASK_SFT (0x1 << 15) +#define VOW_ADC_TESTCK_SRC_SEL_SFT 12 +#define VOW_ADC_TESTCK_SRC_SEL_MASK 0x7 +#define VOW_ADC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12) +#define VOW_ADC_TESTCK_SEL_SFT 11 +#define VOW_ADC_TESTCK_SEL_MASK 0x1 +#define VOW_ADC_TESTCK_SEL_MASK_SFT (0x1 << 11) +#define VOW_ADC_CLK_INV_SFT 10 +#define VOW_ADC_CLK_INV_MASK 0x1 +#define VOW_ADC_CLK_INV_MASK_SFT (0x1 << 10) +#define VOW_TXIF_MONO_SFT 9 +#define VOW_TXIF_MONO_MASK 0x1 +#define VOW_TXIF_MONO_MASK_SFT (0x1 << 9) +#define VOW_TXIF_SCK_DIV_SFT 4 +#define VOW_TXIF_SCK_DIV_MASK 0x1f +#define VOW_TXIF_SCK_DIV_MASK_SFT (0x1f << 4) +#define K_GAMMA_SFT 0 +#define K_GAMMA_MASK 0xf +#define K_GAMMA_MASK_SFT (0xf << 0) + +/* MT6358_AFE_VOW_CFG5 */ +#define N_MIN_SFT 0 +#define N_MIN_MASK 0xffff +#define N_MIN_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_CFG6 */ +#define RG_WINDOW_SIZE_SEL_SFT 12 +#define RG_WINDOW_SIZE_SEL_MASK 0x1 +#define RG_WINDOW_SIZE_SEL_MASK_SFT (0x1 << 12) +#define RG_FLR_BYPASS_SFT 11 +#define RG_FLR_BYPASS_MASK 0x1 +#define RG_FLR_BYPASS_MASK_SFT (0x1 << 11) +#define RG_FLR_RATIO_SFT 8 +#define RG_FLR_RATIO_MASK 0x7 +#define RG_FLR_RATIO_MASK_SFT (0x7 << 8) +#define RG_BUCK_DVFS_DONE_SW_CTL_SFT 7 +#define RG_BUCK_DVFS_DONE_SW_CTL_MASK 0x1 +#define RG_BUCK_DVFS_DONE_SW_CTL_MASK_SFT (0x1 << 7) +#define RG_BUCK_DVFS_DONE_HW_MODE_SFT 6 +#define RG_BUCK_DVFS_DONE_HW_MODE_MASK 0x1 +#define RG_BUCK_DVFS_DONE_HW_MODE_MASK_SFT (0x1 << 6) +#define RG_BUCK_DVFS_HW_CNT_THR_SFT 0 +#define RG_BUCK_DVFS_HW_CNT_THR_MASK 0x3f +#define RG_BUCK_DVFS_HW_CNT_THR_MASK_SFT (0x3f << 0) + +/* MT6358_AFE_VOW_MON0 */ +#define VOW_DOWNCNT_SFT 0 +#define VOW_DOWNCNT_MASK 0xffff +#define VOW_DOWNCNT_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_MON1 */ +#define K_TMP_MON_SFT 10 +#define K_TMP_MON_MASK 0xf +#define K_TMP_MON_MASK_SFT (0xf << 10) +#define SLT_COUNTER_MON_SFT 7 +#define SLT_COUNTER_MON_MASK 0x7 +#define SLT_COUNTER_MON_MASK_SFT (0x7 << 7) +#define VOW_B_SFT 4 +#define VOW_B_MASK 0x7 +#define VOW_B_MASK_SFT (0x7 << 4) +#define VOW_A_SFT 1 +#define VOW_A_MASK 0x7 +#define VOW_A_MASK_SFT (0x7 << 1) +#define SECOND_CNT_START_SFT 0 +#define SECOND_CNT_START_MASK 0x1 +#define SECOND_CNT_START_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_VOW_MON2 */ +#define VOW_S_L_SFT 0 +#define VOW_S_L_MASK 0xffff +#define VOW_S_L_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_MON3 */ +#define VOW_S_H_SFT 0 +#define VOW_S_H_MASK 0xffff +#define VOW_S_H_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_MON4 */ +#define VOW_N_L_SFT 0 +#define VOW_N_L_MASK 0xffff +#define VOW_N_L_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_MON5 */ +#define VOW_N_H_SFT 0 +#define VOW_N_H_MASK 0xffff +#define VOW_N_H_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_SN_INI_CFG */ +#define VOW_SN_INI_CFG_EN_SFT 15 +#define VOW_SN_INI_CFG_EN_MASK 0x1 +#define VOW_SN_INI_CFG_EN_MASK_SFT (0x1 << 15) +#define VOW_SN_INI_CFG_VAL_SFT 0 +#define VOW_SN_INI_CFG_VAL_MASK 0x7fff +#define VOW_SN_INI_CFG_VAL_MASK_SFT (0x7fff << 0) + +/* MT6358_AFE_VOW_TGEN_CFG0 */ +#define VOW_TGEN_EN_SFT 15 +#define VOW_TGEN_EN_MASK 0x1 +#define VOW_TGEN_EN_MASK_SFT (0x1 << 15) +#define VOW_TGEN_MUTE_SW_SFT 14 +#define VOW_TGEN_MUTE_SW_MASK 0x1 +#define VOW_TGEN_MUTE_SW_MASK_SFT (0x1 << 14) +#define VOW_TGEN_FREQ_DIV_SFT 0 +#define VOW_TGEN_FREQ_DIV_MASK 0x3fff +#define VOW_TGEN_FREQ_DIV_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_POSDIV_CFG0 */ +#define BUCK_DVFS_DONE_SFT 15 +#define BUCK_DVFS_DONE_MASK 0x1 +#define BUCK_DVFS_DONE_MASK_SFT (0x1 << 15) +#define VOW_32K_MODE_SFT 13 +#define VOW_32K_MODE_MASK 0x1 +#define VOW_32K_MODE_MASK_SFT (0x1 << 13) +#define RG_BUCK_CLK_DIV_SFT 8 +#define RG_BUCK_CLK_DIV_MASK 0x1f +#define RG_BUCK_CLK_DIV_MASK_SFT (0x1f << 8) +#define RG_A1P6M_EN_SEL_SFT 7 +#define RG_A1P6M_EN_SEL_MASK 0x1 +#define RG_A1P6M_EN_SEL_MASK_SFT (0x1 << 7) +#define VOW_CLK_SEL_SFT 6 +#define VOW_CLK_SEL_MASK 0x1 +#define VOW_CLK_SEL_MASK_SFT (0x1 << 6) +#define VOW_INTR_SW_MODE_SFT 5 +#define VOW_INTR_SW_MODE_MASK 0x1 +#define VOW_INTR_SW_MODE_MASK_SFT (0x1 << 5) +#define VOW_INTR_SW_VAL_SFT 4 +#define VOW_INTR_SW_VAL_MASK 0x1 +#define VOW_INTR_SW_VAL_MASK_SFT (0x1 << 4) +#define VOW_CIC_MODE_SEL_SFT 2 +#define VOW_CIC_MODE_SEL_MASK 0x3 +#define VOW_CIC_MODE_SEL_MASK_SFT (0x3 << 2) +#define RG_VOW_POSDIV_SFT 0 +#define RG_VOW_POSDIV_MASK 0x3 +#define RG_VOW_POSDIV_MASK_SFT (0x3 << 0) + +/* MT6358_AFE_VOW_HPF_CFG0 */ +#define VOW_HPF_DC_TEST_SFT 12 +#define VOW_HPF_DC_TEST_MASK 0xf +#define VOW_HPF_DC_TEST_MASK_SFT (0xf << 12) +#define VOW_IRQ_LATCH_SNR_EN_SFT 10 +#define VOW_IRQ_LATCH_SNR_EN_MASK 0x1 +#define VOW_IRQ_LATCH_SNR_EN_MASK_SFT (0x1 << 10) +#define VOW_DMICCLK_PDN_SFT 9 +#define VOW_DMICCLK_PDN_MASK 0x1 +#define VOW_DMICCLK_PDN_MASK_SFT (0x1 << 9) +#define VOW_POSDIVCLK_PDN_SFT 8 +#define VOW_POSDIVCLK_PDN_MASK 0x1 +#define VOW_POSDIVCLK_PDN_MASK_SFT (0x1 << 8) +#define RG_BASELINE_ALPHA_ORDER_SFT 4 +#define RG_BASELINE_ALPHA_ORDER_MASK 0xf +#define RG_BASELINE_ALPHA_ORDER_MASK_SFT (0xf << 4) +#define RG_MTKAIF_HPF_BYPASS_SFT 2 +#define RG_MTKAIF_HPF_BYPASS_MASK 0x1 +#define RG_MTKAIF_HPF_BYPASS_MASK_SFT (0x1 << 2) +#define RG_SNRDET_HPF_BYPASS_SFT 1 +#define RG_SNRDET_HPF_BYPASS_MASK 0x1 +#define RG_SNRDET_HPF_BYPASS_MASK_SFT (0x1 << 1) +#define RG_HPF_ON_SFT 0 +#define RG_HPF_ON_MASK 0x1 +#define RG_HPF_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG0 */ +#define RG_PERIODIC_EN_SFT 15 +#define RG_PERIODIC_EN_MASK 0x1 +#define RG_PERIODIC_EN_MASK_SFT (0x1 << 15) +#define RG_PERIODIC_CNT_CLR_SFT 14 +#define RG_PERIODIC_CNT_CLR_MASK 0x1 +#define RG_PERIODIC_CNT_CLR_MASK_SFT (0x1 << 14) +#define RG_PERIODIC_CNT_PERIOD_SFT 0 +#define RG_PERIODIC_CNT_PERIOD_MASK 0x3fff +#define RG_PERIODIC_CNT_PERIOD_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG1 */ +#define RG_PERIODIC_CNT_SET_SFT 15 +#define RG_PERIODIC_CNT_SET_MASK 0x1 +#define RG_PERIODIC_CNT_SET_MASK_SFT (0x1 << 15) +#define RG_PERIODIC_CNT_PAUSE_SFT 14 +#define RG_PERIODIC_CNT_PAUSE_MASK 0x1 +#define RG_PERIODIC_CNT_PAUSE_MASK_SFT (0x1 << 14) +#define RG_PERIODIC_CNT_SET_VALUE_SFT 0 +#define RG_PERIODIC_CNT_SET_VALUE_MASK 0x3fff +#define RG_PERIODIC_CNT_SET_VALUE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG2 */ +#define AUDPREAMPLON_PERIODIC_MODE_SFT 15 +#define AUDPREAMPLON_PERIODIC_MODE_MASK 0x1 +#define AUDPREAMPLON_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDPREAMPLON_PERIODIC_INVERSE_SFT 14 +#define AUDPREAMPLON_PERIODIC_INVERSE_MASK 0x1 +#define AUDPREAMPLON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDPREAMPLON_PERIODIC_ON_CYCLE_SFT 0 +#define AUDPREAMPLON_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDPREAMPLON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG3 */ +#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_SFT 15 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_MASK 0x1 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_SFT 14 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_MASK 0x1 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_SFT 0 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG4 */ +#define AUDADCLPWRUP_PERIODIC_MODE_SFT 15 +#define AUDADCLPWRUP_PERIODIC_MODE_MASK 0x1 +#define AUDADCLPWRUP_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDADCLPWRUP_PERIODIC_INVERSE_SFT 14 +#define AUDADCLPWRUP_PERIODIC_INVERSE_MASK 0x1 +#define AUDADCLPWRUP_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_SFT 0 +#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG5 */ +#define AUDGLBVOWLPWEN_PERIODIC_MODE_SFT 15 +#define AUDGLBVOWLPWEN_PERIODIC_MODE_MASK 0x1 +#define AUDGLBVOWLPWEN_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_SFT 14 +#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_MASK 0x1 +#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_SFT 0 +#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG6 */ +#define AUDDIGMICEN_PERIODIC_MODE_SFT 15 +#define AUDDIGMICEN_PERIODIC_MODE_MASK 0x1 +#define AUDDIGMICEN_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDDIGMICEN_PERIODIC_INVERSE_SFT 14 +#define AUDDIGMICEN_PERIODIC_INVERSE_MASK 0x1 +#define AUDDIGMICEN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDDIGMICEN_PERIODIC_ON_CYCLE_SFT 0 +#define AUDDIGMICEN_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDDIGMICEN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG7 */ +#define AUDPWDBMICBIAS0_PERIODIC_MODE_SFT 15 +#define AUDPWDBMICBIAS0_PERIODIC_MODE_MASK 0x1 +#define AUDPWDBMICBIAS0_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_SFT 14 +#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_MASK 0x1 +#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_SFT 0 +#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG8 */ +#define AUDPWDBMICBIAS1_PERIODIC_MODE_SFT 15 +#define AUDPWDBMICBIAS1_PERIODIC_MODE_MASK 0x1 +#define AUDPWDBMICBIAS1_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_SFT 14 +#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_MASK 0x1 +#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_SFT 0 +#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG9 */ +#define XO_VOW_CK_EN_PERIODIC_MODE_SFT 15 +#define XO_VOW_CK_EN_PERIODIC_MODE_MASK 0x1 +#define XO_VOW_CK_EN_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define XO_VOW_CK_EN_PERIODIC_INVERSE_SFT 14 +#define XO_VOW_CK_EN_PERIODIC_INVERSE_MASK 0x1 +#define XO_VOW_CK_EN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_SFT 0 +#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_MASK 0x3fff +#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG10 */ +#define AUDGLB_PWRDN_PERIODIC_MODE_SFT 15 +#define AUDGLB_PWRDN_PERIODIC_MODE_MASK 0x1 +#define AUDGLB_PWRDN_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDGLB_PWRDN_PERIODIC_INVERSE_SFT 14 +#define AUDGLB_PWRDN_PERIODIC_INVERSE_MASK 0x1 +#define AUDGLB_PWRDN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_SFT 0 +#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG11 */ +#define VOW_ON_PERIODIC_MODE_SFT 15 +#define VOW_ON_PERIODIC_MODE_MASK 0x1 +#define VOW_ON_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define VOW_ON_PERIODIC_INVERSE_SFT 14 +#define VOW_ON_PERIODIC_INVERSE_MASK 0x1 +#define VOW_ON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define VOW_ON_PERIODIC_ON_CYCLE_SFT 0 +#define VOW_ON_PERIODIC_ON_CYCLE_MASK 0x3fff +#define VOW_ON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG12 */ +#define DMIC_ON_PERIODIC_MODE_SFT 15 +#define DMIC_ON_PERIODIC_MODE_MASK 0x1 +#define DMIC_ON_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define DMIC_ON_PERIODIC_INVERSE_SFT 14 +#define DMIC_ON_PERIODIC_INVERSE_MASK 0x1 +#define DMIC_ON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define DMIC_ON_PERIODIC_ON_CYCLE_SFT 0 +#define DMIC_ON_PERIODIC_ON_CYCLE_MASK 0x3fff +#define DMIC_ON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG13 */ +#define PDN_VOW_F32K_CK_SFT 15 +#define PDN_VOW_F32K_CK_MASK 0x1 +#define PDN_VOW_F32K_CK_MASK_SFT (0x1 << 15) +#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG14 */ +#define VOW_SNRDET_PERIODIC_CFG_SFT 15 +#define VOW_SNRDET_PERIODIC_CFG_MASK 0x1 +#define VOW_SNRDET_PERIODIC_CFG_MASK_SFT (0x1 << 15) +#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG15 */ +#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG16 */ +#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG17 */ +#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG18 */ +#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG19 */ +#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG20 */ +#define CLKSQ_EN_VOW_PERIODIC_MODE_SFT 15 +#define CLKSQ_EN_VOW_PERIODIC_MODE_MASK 0x1 +#define CLKSQ_EN_VOW_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_SFT 0 +#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG21 */ +#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG22 */ +#define VOW_ON_PERIODIC_OFF_CYCLE_SFT 0 +#define VOW_ON_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define VOW_ON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG23 */ +#define DMIC_ON_PERIODIC_OFF_CYCLE_SFT 0 +#define DMIC_ON_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define DMIC_ON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_MON0 */ +#define VOW_PERIODIC_MON_SFT 0 +#define VOW_PERIODIC_MON_MASK 0xffff +#define VOW_PERIODIC_MON_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_PERIODIC_MON1 */ +#define VOW_PERIODIC_COUNT_MON_SFT 0 +#define VOW_PERIODIC_COUNT_MON_MASK 0xffff +#define VOW_PERIODIC_COUNT_MON_MASK_SFT (0xffff << 0) + +/* MT6358_AUDENC_DSN_ID */ +#define AUDENC_ANA_ID_SFT 0 +#define AUDENC_ANA_ID_MASK 0xff +#define AUDENC_ANA_ID_MASK_SFT (0xff << 0) +#define AUDENC_DIG_ID_SFT 8 +#define AUDENC_DIG_ID_MASK 0xff +#define AUDENC_DIG_ID_MASK_SFT (0xff << 8) + +/* MT6358_AUDENC_DSN_REV0 */ +#define AUDENC_ANA_MINOR_REV_SFT 0 +#define AUDENC_ANA_MINOR_REV_MASK 0xf +#define AUDENC_ANA_MINOR_REV_MASK_SFT (0xf << 0) +#define AUDENC_ANA_MAJOR_REV_SFT 4 +#define AUDENC_ANA_MAJOR_REV_MASK 0xf +#define AUDENC_ANA_MAJOR_REV_MASK_SFT (0xf << 4) +#define AUDENC_DIG_MINOR_REV_SFT 8 +#define AUDENC_DIG_MINOR_REV_MASK 0xf +#define AUDENC_DIG_MINOR_REV_MASK_SFT (0xf << 8) +#define AUDENC_DIG_MAJOR_REV_SFT 12 +#define AUDENC_DIG_MAJOR_REV_MASK 0xf +#define AUDENC_DIG_MAJOR_REV_MASK_SFT (0xf << 12) + +/* MT6358_AUDENC_DSN_DBI */ +#define AUDENC_DSN_CBS_SFT 0 +#define AUDENC_DSN_CBS_MASK 0x3 +#define AUDENC_DSN_CBS_MASK_SFT (0x3 << 0) +#define AUDENC_DSN_BIX_SFT 2 +#define AUDENC_DSN_BIX_MASK 0x3 +#define AUDENC_DSN_BIX_MASK_SFT (0x3 << 2) +#define AUDENC_DSN_ESP_SFT 8 +#define AUDENC_DSN_ESP_MASK 0xff +#define AUDENC_DSN_ESP_MASK_SFT (0xff << 8) + +/* MT6358_AUDENC_DSN_FPI */ +#define AUDENC_DSN_FPI_SFT 0 +#define AUDENC_DSN_FPI_MASK 0xff +#define AUDENC_DSN_FPI_MASK_SFT (0xff << 0) + +/* MT6358_AUDENC_ANA_CON0 */ +#define RG_AUDPREAMPLON_SFT 0 +#define RG_AUDPREAMPLON_MASK 0x1 +#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPLDCCEN_SFT 1 +#define RG_AUDPREAMPLDCCEN_MASK 0x1 +#define RG_AUDPREAMPLDCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPLDCPRECHARGE_SFT 2 +#define RG_AUDPREAMPLDCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMPLDCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMPLPGATEST_SFT 3 +#define RG_AUDPREAMPLPGATEST_MASK 0x1 +#define RG_AUDPREAMPLPGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMPLVSCALE_SFT 4 +#define RG_AUDPREAMPLVSCALE_MASK 0x3 +#define RG_AUDPREAMPLVSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMPLINPUTSEL_SFT 6 +#define RG_AUDPREAMPLINPUTSEL_MASK 0x3 +#define RG_AUDPREAMPLINPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMPLGAIN_SFT 8 +#define RG_AUDPREAMPLGAIN_MASK 0x7 +#define RG_AUDPREAMPLGAIN_MASK_SFT (0x7 << 8) +#define RG_AUDADCLPWRUP_SFT 12 +#define RG_AUDADCLPWRUP_MASK 0x1 +#define RG_AUDADCLPWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADCLINPUTSEL_SFT 13 +#define RG_AUDADCLINPUTSEL_MASK 0x3 +#define RG_AUDADCLINPUTSEL_MASK_SFT (0x3 << 13) + +/* MT6358_AUDENC_ANA_CON1 */ +#define RG_AUDPREAMPRON_SFT 0 +#define RG_AUDPREAMPRON_MASK 0x1 +#define RG_AUDPREAMPRON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPRDCCEN_SFT 1 +#define RG_AUDPREAMPRDCCEN_MASK 0x1 +#define RG_AUDPREAMPRDCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPRDCPRECHARGE_SFT 2 +#define RG_AUDPREAMPRDCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMPRDCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMPRPGATEST_SFT 3 +#define RG_AUDPREAMPRPGATEST_MASK 0x1 +#define RG_AUDPREAMPRPGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMPRVSCALE_SFT 4 +#define RG_AUDPREAMPRVSCALE_MASK 0x3 +#define RG_AUDPREAMPRVSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMPRINPUTSEL_SFT 6 +#define RG_AUDPREAMPRINPUTSEL_MASK 0x3 +#define RG_AUDPREAMPRINPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMPRGAIN_SFT 8 +#define RG_AUDPREAMPRGAIN_MASK 0x7 +#define RG_AUDPREAMPRGAIN_MASK_SFT (0x7 << 8) +#define RG_AUDIO_VOW_EN_SFT 11 +#define RG_AUDIO_VOW_EN_MASK 0x1 +#define RG_AUDIO_VOW_EN_MASK_SFT (0x1 << 11) +#define RG_AUDADCRPWRUP_SFT 12 +#define RG_AUDADCRPWRUP_MASK 0x1 +#define RG_AUDADCRPWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADCRINPUTSEL_SFT 13 +#define RG_AUDADCRINPUTSEL_MASK 0x3 +#define RG_AUDADCRINPUTSEL_MASK_SFT (0x3 << 13) +#define RG_CLKSQ_EN_VOW_SFT 15 +#define RG_CLKSQ_EN_VOW_MASK 0x1 +#define RG_CLKSQ_EN_VOW_MASK_SFT (0x1 << 15) + +/* MT6358_AUDENC_ANA_CON2 */ +#define RG_AUDULHALFBIAS_SFT 0 +#define RG_AUDULHALFBIAS_MASK 0x1 +#define RG_AUDULHALFBIAS_MASK_SFT (0x1 << 0) +#define RG_AUDGLBVOWLPWEN_SFT 1 +#define RG_AUDGLBVOWLPWEN_MASK 0x1 +#define RG_AUDGLBVOWLPWEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPLPEN_SFT 2 +#define RG_AUDPREAMPLPEN_MASK 0x1 +#define RG_AUDPREAMPLPEN_MASK_SFT (0x1 << 2) +#define RG_AUDADC1STSTAGELPEN_SFT 3 +#define RG_AUDADC1STSTAGELPEN_MASK 0x1 +#define RG_AUDADC1STSTAGELPEN_MASK_SFT (0x1 << 3) +#define RG_AUDADC2NDSTAGELPEN_SFT 4 +#define RG_AUDADC2NDSTAGELPEN_MASK 0x1 +#define RG_AUDADC2NDSTAGELPEN_MASK_SFT (0x1 << 4) +#define RG_AUDADCFLASHLPEN_SFT 5 +#define RG_AUDADCFLASHLPEN_MASK 0x1 +#define RG_AUDADCFLASHLPEN_MASK_SFT (0x1 << 5) +#define RG_AUDPREAMPIDDTEST_SFT 6 +#define RG_AUDPREAMPIDDTEST_MASK 0x3 +#define RG_AUDPREAMPIDDTEST_MASK_SFT (0x3 << 6) +#define RG_AUDADC1STSTAGEIDDTEST_SFT 8 +#define RG_AUDADC1STSTAGEIDDTEST_MASK 0x3 +#define RG_AUDADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8) +#define RG_AUDADC2NDSTAGEIDDTEST_SFT 10 +#define RG_AUDADC2NDSTAGEIDDTEST_MASK 0x3 +#define RG_AUDADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10) +#define RG_AUDADCREFBUFIDDTEST_SFT 12 +#define RG_AUDADCREFBUFIDDTEST_MASK 0x3 +#define RG_AUDADCREFBUFIDDTEST_MASK_SFT (0x3 << 12) +#define RG_AUDADCFLASHIDDTEST_SFT 14 +#define RG_AUDADCFLASHIDDTEST_MASK 0x3 +#define RG_AUDADCFLASHIDDTEST_MASK_SFT (0x3 << 14) + +/* MT6358_AUDENC_ANA_CON3 */ +#define RG_AUDADCDAC0P25FS_SFT 0 +#define RG_AUDADCDAC0P25FS_MASK 0x1 +#define RG_AUDADCDAC0P25FS_MASK_SFT (0x1 << 0) +#define RG_AUDADCCLKSEL_SFT 1 +#define RG_AUDADCCLKSEL_MASK 0x1 +#define RG_AUDADCCLKSEL_MASK_SFT (0x1 << 1) +#define RG_AUDADCCLKSOURCE_SFT 2 +#define RG_AUDADCCLKSOURCE_MASK 0x3 +#define RG_AUDADCCLKSOURCE_MASK_SFT (0x3 << 2) +#define RG_AUDPREAMPAAFEN_SFT 8 +#define RG_AUDPREAMPAAFEN_MASK 0x1 +#define RG_AUDPREAMPAAFEN_MASK_SFT (0x1 << 8) +#define RG_DCCVCMBUFLPMODSEL_SFT 9 +#define RG_DCCVCMBUFLPMODSEL_MASK 0x1 +#define RG_DCCVCMBUFLPMODSEL_MASK_SFT (0x1 << 9) +#define RG_DCCVCMBUFLPSWEN_SFT 10 +#define RG_DCCVCMBUFLPSWEN_MASK 0x1 +#define RG_DCCVCMBUFLPSWEN_MASK_SFT (0x1 << 10) +#define RG_CMSTBENH_SFT 11 +#define RG_CMSTBENH_MASK 0x1 +#define RG_CMSTBENH_MASK_SFT (0x1 << 11) +#define RG_PGABODYSW_SFT 12 +#define RG_PGABODYSW_MASK 0x1 +#define RG_PGABODYSW_MASK_SFT (0x1 << 12) + +/* MT6358_AUDENC_ANA_CON4 */ +#define RG_AUDADC1STSTAGESDENB_SFT 0 +#define RG_AUDADC1STSTAGESDENB_MASK 0x1 +#define RG_AUDADC1STSTAGESDENB_MASK_SFT (0x1 << 0) +#define RG_AUDADC2NDSTAGERESET_SFT 1 +#define RG_AUDADC2NDSTAGERESET_MASK 0x1 +#define RG_AUDADC2NDSTAGERESET_MASK_SFT (0x1 << 1) +#define RG_AUDADC3RDSTAGERESET_SFT 2 +#define RG_AUDADC3RDSTAGERESET_MASK 0x1 +#define RG_AUDADC3RDSTAGERESET_MASK_SFT (0x1 << 2) +#define RG_AUDADCFSRESET_SFT 3 +#define RG_AUDADCFSRESET_MASK 0x1 +#define RG_AUDADCFSRESET_MASK_SFT (0x1 << 3) +#define RG_AUDADCWIDECM_SFT 4 +#define RG_AUDADCWIDECM_MASK 0x1 +#define RG_AUDADCWIDECM_MASK_SFT (0x1 << 4) +#define RG_AUDADCNOPATEST_SFT 5 +#define RG_AUDADCNOPATEST_MASK 0x1 +#define RG_AUDADCNOPATEST_MASK_SFT (0x1 << 5) +#define RG_AUDADCBYPASS_SFT 6 +#define RG_AUDADCBYPASS_MASK 0x1 +#define RG_AUDADCBYPASS_MASK_SFT (0x1 << 6) +#define RG_AUDADCFFBYPASS_SFT 7 +#define RG_AUDADCFFBYPASS_MASK 0x1 +#define RG_AUDADCFFBYPASS_MASK_SFT (0x1 << 7) +#define RG_AUDADCDACFBCURRENT_SFT 8 +#define RG_AUDADCDACFBCURRENT_MASK 0x1 +#define RG_AUDADCDACFBCURRENT_MASK_SFT (0x1 << 8) +#define RG_AUDADCDACIDDTEST_SFT 9 +#define RG_AUDADCDACIDDTEST_MASK 0x3 +#define RG_AUDADCDACIDDTEST_MASK_SFT (0x3 << 9) +#define RG_AUDADCDACNRZ_SFT 11 +#define RG_AUDADCDACNRZ_MASK 0x1 +#define RG_AUDADCDACNRZ_MASK_SFT (0x1 << 11) +#define RG_AUDADCNODEM_SFT 12 +#define RG_AUDADCNODEM_MASK 0x1 +#define RG_AUDADCNODEM_MASK_SFT (0x1 << 12) +#define RG_AUDADCDACTEST_SFT 13 +#define RG_AUDADCDACTEST_MASK 0x1 +#define RG_AUDADCDACTEST_MASK_SFT (0x1 << 13) + +/* MT6358_AUDENC_ANA_CON5 */ +#define RG_AUDRCTUNEL_SFT 0 +#define RG_AUDRCTUNEL_MASK 0x1f +#define RG_AUDRCTUNEL_MASK_SFT (0x1f << 0) +#define RG_AUDRCTUNELSEL_SFT 5 +#define RG_AUDRCTUNELSEL_MASK 0x1 +#define RG_AUDRCTUNELSEL_MASK_SFT (0x1 << 5) +#define RG_AUDRCTUNER_SFT 8 +#define RG_AUDRCTUNER_MASK 0x1f +#define RG_AUDRCTUNER_MASK_SFT (0x1f << 8) +#define RG_AUDRCTUNERSEL_SFT 13 +#define RG_AUDRCTUNERSEL_MASK 0x1 +#define RG_AUDRCTUNERSEL_MASK_SFT (0x1 << 13) + +/* MT6358_AUDENC_ANA_CON6 */ +#define RG_CLKSQ_EN_SFT 0 +#define RG_CLKSQ_EN_MASK 0x1 +#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0) +#define RG_CLKSQ_IN_SEL_TEST_SFT 1 +#define RG_CLKSQ_IN_SEL_TEST_MASK 0x1 +#define RG_CLKSQ_IN_SEL_TEST_MASK_SFT (0x1 << 1) +#define RG_CM_REFGENSEL_SFT 2 +#define RG_CM_REFGENSEL_MASK 0x1 +#define RG_CM_REFGENSEL_MASK_SFT (0x1 << 2) +#define RG_AUDSPARE_SFT 4 +#define RG_AUDSPARE_MASK 0xf +#define RG_AUDSPARE_MASK_SFT (0xf << 4) +#define RG_AUDENCSPARE_SFT 8 +#define RG_AUDENCSPARE_MASK 0x3f +#define RG_AUDENCSPARE_MASK_SFT (0x3f << 8) + +/* MT6358_AUDENC_ANA_CON7 */ +#define RG_AUDENCSPARE2_SFT 0 +#define RG_AUDENCSPARE2_MASK 0xff +#define RG_AUDENCSPARE2_MASK_SFT (0xff << 0) + +/* MT6358_AUDENC_ANA_CON8 */ +#define RG_AUDDIGMICEN_SFT 0 +#define RG_AUDDIGMICEN_MASK 0x1 +#define RG_AUDDIGMICEN_MASK_SFT (0x1 << 0) +#define RG_AUDDIGMICBIAS_SFT 1 +#define RG_AUDDIGMICBIAS_MASK 0x3 +#define RG_AUDDIGMICBIAS_MASK_SFT (0x3 << 1) +#define RG_DMICHPCLKEN_SFT 3 +#define RG_DMICHPCLKEN_MASK 0x1 +#define RG_DMICHPCLKEN_MASK_SFT (0x1 << 3) +#define RG_AUDDIGMICPDUTY_SFT 4 +#define RG_AUDDIGMICPDUTY_MASK 0x3 +#define RG_AUDDIGMICPDUTY_MASK_SFT (0x3 << 4) +#define RG_AUDDIGMICNDUTY_SFT 6 +#define RG_AUDDIGMICNDUTY_MASK 0x3 +#define RG_AUDDIGMICNDUTY_MASK_SFT (0x3 << 6) +#define RG_DMICMONEN_SFT 8 +#define RG_DMICMONEN_MASK 0x1 +#define RG_DMICMONEN_MASK_SFT (0x1 << 8) +#define RG_DMICMONSEL_SFT 9 +#define RG_DMICMONSEL_MASK 0x7 +#define RG_DMICMONSEL_MASK_SFT (0x7 << 9) +#define RG_AUDSPAREVMIC_SFT 12 +#define RG_AUDSPAREVMIC_MASK 0xf +#define RG_AUDSPAREVMIC_MASK_SFT (0xf << 12) + +/* MT6358_AUDENC_ANA_CON9 */ +#define RG_AUDPWDBMICBIAS0_SFT 0 +#define RG_AUDPWDBMICBIAS0_MASK 0x1 +#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS0BYPASSEN_SFT 1 +#define RG_AUDMICBIAS0BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS0BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS0LOWPEN_SFT 2 +#define RG_AUDMICBIAS0LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS0LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS0VREF_SFT 4 +#define RG_AUDMICBIAS0VREF_MASK 0x7 +#define RG_AUDMICBIAS0VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS0DCSW0P1EN_SFT 8 +#define RG_AUDMICBIAS0DCSW0P1EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0P1EN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS0DCSW0P2EN_SFT 9 +#define RG_AUDMICBIAS0DCSW0P2EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0P2EN_MASK_SFT (0x1 << 9) +#define RG_AUDMICBIAS0DCSW0NEN_SFT 10 +#define RG_AUDMICBIAS0DCSW0NEN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0NEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIAS0DCSW2P1EN_SFT 12 +#define RG_AUDMICBIAS0DCSW2P1EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2P1EN_MASK_SFT (0x1 << 12) +#define RG_AUDMICBIAS0DCSW2P2EN_SFT 13 +#define RG_AUDMICBIAS0DCSW2P2EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2P2EN_MASK_SFT (0x1 << 13) +#define RG_AUDMICBIAS0DCSW2NEN_SFT 14 +#define RG_AUDMICBIAS0DCSW2NEN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2NEN_MASK_SFT (0x1 << 14) + +/* MT6358_AUDENC_ANA_CON10 */ +#define RG_AUDPWDBMICBIAS1_SFT 0 +#define RG_AUDPWDBMICBIAS1_MASK 0x1 +#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS1BYPASSEN_SFT 1 +#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS1LOWPEN_SFT 2 +#define RG_AUDMICBIAS1LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS1VREF_SFT 4 +#define RG_AUDMICBIAS1VREF_MASK 0x7 +#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS1DCSW1PEN_SFT 8 +#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS1DCSW1NEN_SFT 9 +#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9) +#define RG_BANDGAPGEN_SFT 12 +#define RG_BANDGAPGEN_MASK 0x1 +#define RG_BANDGAPGEN_MASK_SFT (0x1 << 12) +#define RG_MTEST_EN_SFT 13 +#define RG_MTEST_EN_MASK 0x1 +#define RG_MTEST_EN_MASK_SFT (0x1 << 13) +#define RG_MTEST_SEL_SFT 14 +#define RG_MTEST_SEL_MASK 0x1 +#define RG_MTEST_SEL_MASK_SFT (0x1 << 14) +#define RG_MTEST_CURRENT_SFT 15 +#define RG_MTEST_CURRENT_MASK 0x1 +#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 15) + +/* MT6358_AUDENC_ANA_CON11 */ +#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0) +#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1) +#define RG_AUDACCDETVIN1PULLLOW_SFT 2 +#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1 +#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 2) +#define RG_AUDACCDETVTHACAL_SFT 4 +#define RG_AUDACCDETVTHACAL_MASK 0x1 +#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4) +#define RG_AUDACCDETVTHBCAL_SFT 5 +#define RG_AUDACCDETVTHBCAL_MASK 0x1 +#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5) +#define RG_AUDACCDETTVDET_SFT 6 +#define RG_AUDACCDETTVDET_MASK 0x1 +#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6) +#define RG_ACCDETSEL_SFT 7 +#define RG_ACCDETSEL_MASK 0x1 +#define RG_ACCDETSEL_MASK_SFT (0x1 << 7) +#define RG_SWBUFMODSEL_SFT 8 +#define RG_SWBUFMODSEL_MASK 0x1 +#define RG_SWBUFMODSEL_MASK_SFT (0x1 << 8) +#define RG_SWBUFSWEN_SFT 9 +#define RG_SWBUFSWEN_MASK 0x1 +#define RG_SWBUFSWEN_MASK_SFT (0x1 << 9) +#define RG_EINTCOMPVTH_SFT 10 +#define RG_EINTCOMPVTH_MASK 0x1 +#define RG_EINTCOMPVTH_MASK_SFT (0x1 << 10) +#define RG_EINTCONFIGACCDET_SFT 11 +#define RG_EINTCONFIGACCDET_MASK 0x1 +#define RG_EINTCONFIGACCDET_MASK_SFT (0x1 << 11) +#define RG_EINTHIRENB_SFT 12 +#define RG_EINTHIRENB_MASK 0x1 +#define RG_EINTHIRENB_MASK_SFT (0x1 << 12) +#define RG_ACCDET2AUXRESBYPASS_SFT 13 +#define RG_ACCDET2AUXRESBYPASS_MASK 0x1 +#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13) +#define RG_ACCDET2AUXBUFFERBYPASS_SFT 14 +#define RG_ACCDET2AUXBUFFERBYPASS_MASK 0x1 +#define RG_ACCDET2AUXBUFFERBYPASS_MASK_SFT (0x1 << 14) +#define RG_ACCDET2AUXSWEN_SFT 15 +#define RG_ACCDET2AUXSWEN_MASK 0x1 +#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 15) + +/* MT6358_AUDENC_ANA_CON12 */ +#define RGS_AUDRCTUNELREAD_SFT 0 +#define RGS_AUDRCTUNELREAD_MASK 0x1f +#define RGS_AUDRCTUNELREAD_MASK_SFT (0x1f << 0) +#define RGS_AUDRCTUNERREAD_SFT 8 +#define RGS_AUDRCTUNERREAD_MASK 0x1f +#define RGS_AUDRCTUNERREAD_MASK_SFT (0x1f << 8) + +/* MT6358_AUDDEC_DSN_ID */ +#define AUDDEC_ANA_ID_SFT 0 +#define AUDDEC_ANA_ID_MASK 0xff +#define AUDDEC_ANA_ID_MASK_SFT (0xff << 0) +#define AUDDEC_DIG_ID_SFT 8 +#define AUDDEC_DIG_ID_MASK 0xff +#define AUDDEC_DIG_ID_MASK_SFT (0xff << 8) + +/* MT6358_AUDDEC_DSN_REV0 */ +#define AUDDEC_ANA_MINOR_REV_SFT 0 +#define AUDDEC_ANA_MINOR_REV_MASK 0xf +#define AUDDEC_ANA_MINOR_REV_MASK_SFT (0xf << 0) +#define AUDDEC_ANA_MAJOR_REV_SFT 4 +#define AUDDEC_ANA_MAJOR_REV_MASK 0xf +#define AUDDEC_ANA_MAJOR_REV_MASK_SFT (0xf << 4) +#define AUDDEC_DIG_MINOR_REV_SFT 8 +#define AUDDEC_DIG_MINOR_REV_MASK 0xf +#define AUDDEC_DIG_MINOR_REV_MASK_SFT (0xf << 8) +#define AUDDEC_DIG_MAJOR_REV_SFT 12 +#define AUDDEC_DIG_MAJOR_REV_MASK 0xf +#define AUDDEC_DIG_MAJOR_REV_MASK_SFT (0xf << 12) + +/* MT6358_AUDDEC_DSN_DBI */ +#define AUDDEC_DSN_CBS_SFT 0 +#define AUDDEC_DSN_CBS_MASK 0x3 +#define AUDDEC_DSN_CBS_MASK_SFT (0x3 << 0) +#define AUDDEC_DSN_BIX_SFT 2 +#define AUDDEC_DSN_BIX_MASK 0x3 +#define AUDDEC_DSN_BIX_MASK_SFT (0x3 << 2) +#define AUDDEC_DSN_ESP_SFT 8 +#define AUDDEC_DSN_ESP_MASK 0xff +#define AUDDEC_DSN_ESP_MASK_SFT (0xff << 8) + +/* MT6358_AUDDEC_DSN_FPI */ +#define AUDDEC_DSN_FPI_SFT 0 +#define AUDDEC_DSN_FPI_MASK 0xff +#define AUDDEC_DSN_FPI_MASK_SFT (0xff << 0) + +/* MT6358_AUDDEC_ANA_CON0 */ +#define RG_AUDDACLPWRUP_VAUDP15_SFT 0 +#define RG_AUDDACLPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDDACLPWRUP_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_AUDDACRPWRUP_VAUDP15_SFT 1 +#define RG_AUDDACRPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDDACRPWRUP_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUD_DAC_PWR_UP_VA28_SFT 2 +#define RG_AUD_DAC_PWR_UP_VA28_MASK 0x1 +#define RG_AUD_DAC_PWR_UP_VA28_MASK_SFT (0x1 << 2) +#define RG_AUD_DAC_PWL_UP_VA28_SFT 3 +#define RG_AUD_DAC_PWL_UP_VA28_MASK 0x1 +#define RG_AUD_DAC_PWL_UP_VA28_MASK_SFT (0x1 << 3) +#define RG_AUDHPLPWRUP_VAUDP15_SFT 4 +#define RG_AUDHPLPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPLPWRUP_VAUDP15_MASK_SFT (0x1 << 4) +#define RG_AUDHPRPWRUP_VAUDP15_SFT 5 +#define RG_AUDHPRPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPRPWRUP_VAUDP15_MASK_SFT (0x1 << 5) +#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_SFT 6 +#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_MASK 0x1 +#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_SFT 7 +#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_MASK 0x1 +#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 7) +#define RG_AUDHPLMUXINPUTSEL_VAUDP15_SFT 8 +#define RG_AUDHPLMUXINPUTSEL_VAUDP15_MASK 0x3 +#define RG_AUDHPLMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 8) +#define RG_AUDHPRMUXINPUTSEL_VAUDP15_SFT 10 +#define RG_AUDHPRMUXINPUTSEL_VAUDP15_MASK 0x3 +#define RG_AUDHPRMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 10) +#define RG_AUDHPLSCDISABLE_VAUDP15_SFT 12 +#define RG_AUDHPLSCDISABLE_VAUDP15_MASK 0x1 +#define RG_AUDHPLSCDISABLE_VAUDP15_MASK_SFT (0x1 << 12) +#define RG_AUDHPRSCDISABLE_VAUDP15_SFT 13 +#define RG_AUDHPRSCDISABLE_VAUDP15_MASK 0x1 +#define RG_AUDHPRSCDISABLE_VAUDP15_MASK_SFT (0x1 << 13) +#define RG_AUDHPLBSCCURRENT_VAUDP15_SFT 14 +#define RG_AUDHPLBSCCURRENT_VAUDP15_MASK 0x1 +#define RG_AUDHPLBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 14) +#define RG_AUDHPRBSCCURRENT_VAUDP15_SFT 15 +#define RG_AUDHPRBSCCURRENT_VAUDP15_MASK 0x1 +#define RG_AUDHPRBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 15) + +/* MT6358_AUDDEC_ANA_CON1 */ +#define RG_AUDHPLOUTPWRUP_VAUDP15_SFT 0 +#define RG_AUDHPLOUTPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPLOUTPWRUP_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_AUDHPROUTPWRUP_VAUDP15_SFT 1 +#define RG_AUDHPROUTPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPROUTPWRUP_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_SFT 2 +#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_MASK_SFT (0x1 << 2) +#define RG_AUDHPROUTAUXPWRUP_VAUDP15_SFT 3 +#define RG_AUDHPROUTAUXPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPROUTAUXPWRUP_VAUDP15_MASK_SFT (0x1 << 3) +#define RG_HPLAUXFBRSW_EN_VAUDP15_SFT 4 +#define RG_HPLAUXFBRSW_EN_VAUDP15_MASK 0x1 +#define RG_HPLAUXFBRSW_EN_VAUDP15_MASK_SFT (0x1 << 4) +#define RG_HPRAUXFBRSW_EN_VAUDP15_SFT 5 +#define RG_HPRAUXFBRSW_EN_VAUDP15_MASK 0x1 +#define RG_HPRAUXFBRSW_EN_VAUDP15_MASK_SFT (0x1 << 5) +#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_SFT 6 +#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_MASK 0x1 +#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_SFT 7 +#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_MASK 0x1 +#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_MASK_SFT (0x1 << 7) +#define RG_HPLOUTSTGCTRL_VAUDP15_SFT 8 +#define RG_HPLOUTSTGCTRL_VAUDP15_MASK 0x7 +#define RG_HPLOUTSTGCTRL_VAUDP15_MASK_SFT (0x7 << 8) +#define RG_HPROUTSTGCTRL_VAUDP15_SFT 11 +#define RG_HPROUTSTGCTRL_VAUDP15_MASK 0x7 +#define RG_HPROUTSTGCTRL_VAUDP15_MASK_SFT (0x7 << 11) + +/* MT6358_AUDDEC_ANA_CON2 */ +#define RG_HPLOUTPUTSTBENH_VAUDP15_SFT 0 +#define RG_HPLOUTPUTSTBENH_VAUDP15_MASK 0x7 +#define RG_HPLOUTPUTSTBENH_VAUDP15_MASK_SFT (0x7 << 0) +#define RG_HPROUTPUTSTBENH_VAUDP15_SFT 4 +#define RG_HPROUTPUTSTBENH_VAUDP15_MASK 0x7 +#define RG_HPROUTPUTSTBENH_VAUDP15_MASK_SFT (0x7 << 4) +#define RG_AUDHPSTARTUP_VAUDP15_SFT 13 +#define RG_AUDHPSTARTUP_VAUDP15_MASK 0x1 +#define RG_AUDHPSTARTUP_VAUDP15_MASK_SFT (0x1 << 13) +#define RG_AUDREFN_DERES_EN_VAUDP15_SFT 14 +#define RG_AUDREFN_DERES_EN_VAUDP15_MASK 0x1 +#define RG_AUDREFN_DERES_EN_VAUDP15_MASK_SFT (0x1 << 14) +#define RG_HPPSHORT2VCM_VAUDP15_SFT 15 +#define RG_HPPSHORT2VCM_VAUDP15_MASK 0x1 +#define RG_HPPSHORT2VCM_VAUDP15_MASK_SFT (0x1 << 15) + +/* MT6358_AUDDEC_ANA_CON3 */ +#define RG_HPINPUTSTBENH_VAUDP15_SFT 13 +#define RG_HPINPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_HPINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 13) +#define RG_HPINPUTRESET0_VAUDP15_SFT 14 +#define RG_HPINPUTRESET0_VAUDP15_MASK 0x1 +#define RG_HPINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 14) +#define RG_HPOUTPUTRESET0_VAUDP15_SFT 15 +#define RG_HPOUTPUTRESET0_VAUDP15_MASK 0x1 +#define RG_HPOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 15) + +/* MT6358_AUDDEC_ANA_CON4 */ +#define RG_ABIDEC_RSVD0_VAUDP28_SFT 0 +#define RG_ABIDEC_RSVD0_VAUDP28_MASK 0xff +#define RG_ABIDEC_RSVD0_VAUDP28_MASK_SFT (0xff << 0) + +/* MT6358_AUDDEC_ANA_CON5 */ +#define RG_AUDHPDECMGAINADJ_VAUDP15_SFT 0 +#define RG_AUDHPDECMGAINADJ_VAUDP15_MASK 0x7 +#define RG_AUDHPDECMGAINADJ_VAUDP15_MASK_SFT (0x7 << 0) +#define RG_AUDHPDEDMGAINADJ_VAUDP15_SFT 4 +#define RG_AUDHPDEDMGAINADJ_VAUDP15_MASK 0x7 +#define RG_AUDHPDEDMGAINADJ_VAUDP15_MASK_SFT (0x7 << 4) + +/* MT6358_AUDDEC_ANA_CON6 */ +#define RG_AUDHSPWRUP_VAUDP15_SFT 0 +#define RG_AUDHSPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHSPWRUP_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_AUDHSPWRUP_IBIAS_VAUDP15_SFT 1 +#define RG_AUDHSPWRUP_IBIAS_VAUDP15_MASK 0x1 +#define RG_AUDHSPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUDHSMUXINPUTSEL_VAUDP15_SFT 2 +#define RG_AUDHSMUXINPUTSEL_VAUDP15_MASK 0x3 +#define RG_AUDHSMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 2) +#define RG_AUDHSSCDISABLE_VAUDP15_SFT 4 +#define RG_AUDHSSCDISABLE_VAUDP15_MASK 0x1 +#define RG_AUDHSSCDISABLE_VAUDP15_MASK_SFT (0x1 << 4) +#define RG_AUDHSBSCCURRENT_VAUDP15_SFT 5 +#define RG_AUDHSBSCCURRENT_VAUDP15_MASK 0x1 +#define RG_AUDHSBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 5) +#define RG_AUDHSSTARTUP_VAUDP15_SFT 6 +#define RG_AUDHSSTARTUP_VAUDP15_MASK 0x1 +#define RG_AUDHSSTARTUP_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_HSOUTPUTSTBENH_VAUDP15_SFT 7 +#define RG_HSOUTPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_HSOUTPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 7) +#define RG_HSINPUTSTBENH_VAUDP15_SFT 8 +#define RG_HSINPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_HSINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 8) +#define RG_HSINPUTRESET0_VAUDP15_SFT 9 +#define RG_HSINPUTRESET0_VAUDP15_MASK 0x1 +#define RG_HSINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 9) +#define RG_HSOUTPUTRESET0_VAUDP15_SFT 10 +#define RG_HSOUTPUTRESET0_VAUDP15_MASK 0x1 +#define RG_HSOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 10) +#define RG_HSOUT_SHORTVCM_VAUDP15_SFT 11 +#define RG_HSOUT_SHORTVCM_VAUDP15_MASK 0x1 +#define RG_HSOUT_SHORTVCM_VAUDP15_MASK_SFT (0x1 << 11) + +/* MT6358_AUDDEC_ANA_CON7 */ +#define RG_AUDLOLPWRUP_VAUDP15_SFT 0 +#define RG_AUDLOLPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDLOLPWRUP_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_SFT 1 +#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_MASK 0x1 +#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUDLOLMUXINPUTSEL_VAUDP15_SFT 2 +#define RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK 0x3 +#define RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 2) +#define RG_AUDLOLSCDISABLE_VAUDP15_SFT 4 +#define RG_AUDLOLSCDISABLE_VAUDP15_MASK 0x1 +#define RG_AUDLOLSCDISABLE_VAUDP15_MASK_SFT (0x1 << 4) +#define RG_AUDLOLBSCCURRENT_VAUDP15_SFT 5 +#define RG_AUDLOLBSCCURRENT_VAUDP15_MASK 0x1 +#define RG_AUDLOLBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 5) +#define RG_AUDLOSTARTUP_VAUDP15_SFT 6 +#define RG_AUDLOSTARTUP_VAUDP15_MASK 0x1 +#define RG_AUDLOSTARTUP_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_LOINPUTSTBENH_VAUDP15_SFT 7 +#define RG_LOINPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_LOINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 7) +#define RG_LOOUTPUTSTBENH_VAUDP15_SFT 8 +#define RG_LOOUTPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_LOOUTPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 8) +#define RG_LOINPUTRESET0_VAUDP15_SFT 9 +#define RG_LOINPUTRESET0_VAUDP15_MASK 0x1 +#define RG_LOINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 9) +#define RG_LOOUTPUTRESET0_VAUDP15_SFT 10 +#define RG_LOOUTPUTRESET0_VAUDP15_MASK 0x1 +#define RG_LOOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 10) +#define RG_LOOUT_SHORTVCM_VAUDP15_SFT 11 +#define RG_LOOUT_SHORTVCM_VAUDP15_MASK 0x1 +#define RG_LOOUT_SHORTVCM_VAUDP15_MASK_SFT (0x1 << 11) + +/* MT6358_AUDDEC_ANA_CON8 */ +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_SFT 0 +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_MASK 0xf +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_MASK_SFT (0xf << 0) +#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_SFT 4 +#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_MASK 0x3 +#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_MASK_SFT (0x3 << 4) +#define RG_AUDTRIMBUF_EN_VAUDP15_SFT 6 +#define RG_AUDTRIMBUF_EN_VAUDP15_MASK 0x1 +#define RG_AUDTRIMBUF_EN_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_SFT 8 +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_MASK 0x3 +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_MASK_SFT (0x3 << 8) +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_SFT 10 +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_MASK 0x3 +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_MASK_SFT (0x3 << 10) +#define RG_AUDHPSPKDET_EN_VAUDP15_SFT 12 +#define RG_AUDHPSPKDET_EN_VAUDP15_MASK 0x1 +#define RG_AUDHPSPKDET_EN_VAUDP15_MASK_SFT (0x1 << 12) + +/* MT6358_AUDDEC_ANA_CON9 */ +#define RG_ABIDEC_RSVD0_VA28_SFT 0 +#define RG_ABIDEC_RSVD0_VA28_MASK 0xff +#define RG_ABIDEC_RSVD0_VA28_MASK_SFT (0xff << 0) +#define RG_ABIDEC_RSVD0_VAUDP15_SFT 8 +#define RG_ABIDEC_RSVD0_VAUDP15_MASK 0xff +#define RG_ABIDEC_RSVD0_VAUDP15_MASK_SFT (0xff << 8) + +/* MT6358_AUDDEC_ANA_CON10 */ +#define RG_ABIDEC_RSVD1_VAUDP15_SFT 0 +#define RG_ABIDEC_RSVD1_VAUDP15_MASK 0xff +#define RG_ABIDEC_RSVD1_VAUDP15_MASK_SFT (0xff << 0) +#define RG_ABIDEC_RSVD2_VAUDP15_SFT 8 +#define RG_ABIDEC_RSVD2_VAUDP15_MASK 0xff +#define RG_ABIDEC_RSVD2_VAUDP15_MASK_SFT (0xff << 8) + +/* MT6358_AUDDEC_ANA_CON11 */ +#define RG_AUDZCDMUXSEL_VAUDP15_SFT 0 +#define RG_AUDZCDMUXSEL_VAUDP15_MASK 0x7 +#define RG_AUDZCDMUXSEL_VAUDP15_MASK_SFT (0x7 << 0) +#define RG_AUDZCDCLKSEL_VAUDP15_SFT 3 +#define RG_AUDZCDCLKSEL_VAUDP15_MASK 0x1 +#define RG_AUDZCDCLKSEL_VAUDP15_MASK_SFT (0x1 << 3) +#define RG_AUDBIASADJ_0_VAUDP15_SFT 7 +#define RG_AUDBIASADJ_0_VAUDP15_MASK 0x1ff +#define RG_AUDBIASADJ_0_VAUDP15_MASK_SFT (0x1ff << 7) + +/* MT6358_AUDDEC_ANA_CON12 */ +#define RG_AUDBIASADJ_1_VAUDP15_SFT 0 +#define RG_AUDBIASADJ_1_VAUDP15_MASK 0xff +#define RG_AUDBIASADJ_1_VAUDP15_MASK_SFT (0xff << 0) +#define RG_AUDIBIASPWRDN_VAUDP15_SFT 8 +#define RG_AUDIBIASPWRDN_VAUDP15_MASK 0x1 +#define RG_AUDIBIASPWRDN_VAUDP15_MASK_SFT (0x1 << 8) + +/* MT6358_AUDDEC_ANA_CON13 */ +#define RG_RSTB_DECODER_VA28_SFT 0 +#define RG_RSTB_DECODER_VA28_MASK 0x1 +#define RG_RSTB_DECODER_VA28_MASK_SFT (0x1 << 0) +#define RG_SEL_DECODER_96K_VA28_SFT 1 +#define RG_SEL_DECODER_96K_VA28_MASK 0x1 +#define RG_SEL_DECODER_96K_VA28_MASK_SFT (0x1 << 1) +#define RG_SEL_DELAY_VCORE_SFT 2 +#define RG_SEL_DELAY_VCORE_MASK 0x1 +#define RG_SEL_DELAY_VCORE_MASK_SFT (0x1 << 2) +#define RG_AUDGLB_PWRDN_VA28_SFT 4 +#define RG_AUDGLB_PWRDN_VA28_MASK 0x1 +#define RG_AUDGLB_PWRDN_VA28_MASK_SFT (0x1 << 4) +#define RG_RSTB_ENCODER_VA28_SFT 5 +#define RG_RSTB_ENCODER_VA28_MASK 0x1 +#define RG_RSTB_ENCODER_VA28_MASK_SFT (0x1 << 5) +#define RG_SEL_ENCODER_96K_VA28_SFT 6 +#define RG_SEL_ENCODER_96K_VA28_MASK 0x1 +#define RG_SEL_ENCODER_96K_VA28_MASK_SFT (0x1 << 6) + +/* MT6358_AUDDEC_ANA_CON14 */ +#define RG_HCLDO_EN_VA18_SFT 0 +#define RG_HCLDO_EN_VA18_MASK 0x1 +#define RG_HCLDO_EN_VA18_MASK_SFT (0x1 << 0) +#define RG_HCLDO_PDDIS_EN_VA18_SFT 1 +#define RG_HCLDO_PDDIS_EN_VA18_MASK 0x1 +#define RG_HCLDO_PDDIS_EN_VA18_MASK_SFT (0x1 << 1) +#define RG_HCLDO_REMOTE_SENSE_VA18_SFT 2 +#define RG_HCLDO_REMOTE_SENSE_VA18_MASK 0x1 +#define RG_HCLDO_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 2) +#define RG_LCLDO_EN_VA18_SFT 4 +#define RG_LCLDO_EN_VA18_MASK 0x1 +#define RG_LCLDO_EN_VA18_MASK_SFT (0x1 << 4) +#define RG_LCLDO_PDDIS_EN_VA18_SFT 5 +#define RG_LCLDO_PDDIS_EN_VA18_MASK 0x1 +#define RG_LCLDO_PDDIS_EN_VA18_MASK_SFT (0x1 << 5) +#define RG_LCLDO_REMOTE_SENSE_VA18_SFT 6 +#define RG_LCLDO_REMOTE_SENSE_VA18_MASK 0x1 +#define RG_LCLDO_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 6) +#define RG_LCLDO_ENC_EN_VA28_SFT 8 +#define RG_LCLDO_ENC_EN_VA28_MASK 0x1 +#define RG_LCLDO_ENC_EN_VA28_MASK_SFT (0x1 << 8) +#define RG_LCLDO_ENC_PDDIS_EN_VA28_SFT 9 +#define RG_LCLDO_ENC_PDDIS_EN_VA28_MASK 0x1 +#define RG_LCLDO_ENC_PDDIS_EN_VA28_MASK_SFT (0x1 << 9) +#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_SFT 10 +#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_MASK 0x1 +#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_MASK_SFT (0x1 << 10) +#define RG_VA33REFGEN_EN_VA18_SFT 12 +#define RG_VA33REFGEN_EN_VA18_MASK 0x1 +#define RG_VA33REFGEN_EN_VA18_MASK_SFT (0x1 << 12) +#define RG_VA28REFGEN_EN_VA28_SFT 13 +#define RG_VA28REFGEN_EN_VA28_MASK 0x1 +#define RG_VA28REFGEN_EN_VA28_MASK_SFT (0x1 << 13) +#define RG_HCLDO_VOSEL_VA18_SFT 14 +#define RG_HCLDO_VOSEL_VA18_MASK 0x1 +#define RG_HCLDO_VOSEL_VA18_MASK_SFT (0x1 << 14) +#define RG_LCLDO_VOSEL_VA18_SFT 15 +#define RG_LCLDO_VOSEL_VA18_MASK 0x1 +#define RG_LCLDO_VOSEL_VA18_MASK_SFT (0x1 << 15) + +/* MT6358_AUDDEC_ANA_CON15 */ +#define RG_NVREG_EN_VAUDP15_SFT 0 +#define RG_NVREG_EN_VAUDP15_MASK 0x1 +#define RG_NVREG_EN_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_NVREG_PULL0V_VAUDP15_SFT 1 +#define RG_NVREG_PULL0V_VAUDP15_MASK 0x1 +#define RG_NVREG_PULL0V_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUDPMU_RSD0_VAUDP15_SFT 4 +#define RG_AUDPMU_RSD0_VAUDP15_MASK 0xf +#define RG_AUDPMU_RSD0_VAUDP15_MASK_SFT (0xf << 4) +#define RG_AUDPMU_RSD0_VA18_SFT 8 +#define RG_AUDPMU_RSD0_VA18_MASK 0xf +#define RG_AUDPMU_RSD0_VA18_MASK_SFT (0xf << 8) +#define RG_AUDPMU_RSD0_VA28_SFT 12 +#define RG_AUDPMU_RSD0_VA28_MASK 0xf +#define RG_AUDPMU_RSD0_VA28_MASK_SFT (0xf << 12) + +/* MT6358_ZCD_CON0 */ +#define RG_AUDZCDENABLE_SFT 0 +#define RG_AUDZCDENABLE_MASK 0x1 +#define RG_AUDZCDENABLE_MASK_SFT (0x1 << 0) +#define RG_AUDZCDGAINSTEPTIME_SFT 1 +#define RG_AUDZCDGAINSTEPTIME_MASK 0x7 +#define RG_AUDZCDGAINSTEPTIME_MASK_SFT (0x7 << 1) +#define RG_AUDZCDGAINSTEPSIZE_SFT 4 +#define RG_AUDZCDGAINSTEPSIZE_MASK 0x3 +#define RG_AUDZCDGAINSTEPSIZE_MASK_SFT (0x3 << 4) +#define RG_AUDZCDTIMEOUTMODESEL_SFT 6 +#define RG_AUDZCDTIMEOUTMODESEL_MASK 0x1 +#define RG_AUDZCDTIMEOUTMODESEL_MASK_SFT (0x1 << 6) + +/* MT6358_ZCD_CON1 */ +#define RG_AUDLOLGAIN_SFT 0 +#define RG_AUDLOLGAIN_MASK 0x1f +#define RG_AUDLOLGAIN_MASK_SFT (0x1f << 0) +#define RG_AUDLORGAIN_SFT 7 +#define RG_AUDLORGAIN_MASK 0x1f +#define RG_AUDLORGAIN_MASK_SFT (0x1f << 7) + +/* MT6358_ZCD_CON2 */ +#define RG_AUDHPLGAIN_SFT 0 +#define RG_AUDHPLGAIN_MASK 0x1f +#define RG_AUDHPLGAIN_MASK_SFT (0x1f << 0) +#define RG_AUDHPRGAIN_SFT 7 +#define RG_AUDHPRGAIN_MASK 0x1f +#define RG_AUDHPRGAIN_MASK_SFT (0x1f << 7) + +/* MT6358_ZCD_CON3 */ +#define RG_AUDHSGAIN_SFT 0 +#define RG_AUDHSGAIN_MASK 0x1f +#define RG_AUDHSGAIN_MASK_SFT (0x1f << 0) + +/* MT6358_ZCD_CON4 */ +#define RG_AUDIVLGAIN_SFT 0 +#define RG_AUDIVLGAIN_MASK 0x7 +#define RG_AUDIVLGAIN_MASK_SFT (0x7 << 0) +#define RG_AUDIVRGAIN_SFT 8 +#define RG_AUDIVRGAIN_MASK 0x7 +#define RG_AUDIVRGAIN_MASK_SFT (0x7 << 8) + +/* MT6358_ZCD_CON5 */ +#define RG_AUDINTGAIN1_SFT 0 +#define RG_AUDINTGAIN1_MASK 0x3f +#define RG_AUDINTGAIN1_MASK_SFT (0x3f << 0) +#define RG_AUDINTGAIN2_SFT 8 +#define RG_AUDINTGAIN2_MASK 0x3f +#define RG_AUDINTGAIN2_MASK_SFT (0x3f << 8) + +/* audio register */ +#define MT6358_DRV_CON3 0x3c +#define MT6358_GPIO_DIR0 0x88 + +#define MT6358_GPIO_MODE2 0xd8 /* mosi */ +#define MT6358_GPIO_MODE2_SET 0xda +#define MT6358_GPIO_MODE2_CLR 0xdc + +#define MT6358_GPIO_MODE3 0xde /* miso */ +#define MT6358_GPIO_MODE3_SET 0xe0 +#define MT6358_GPIO_MODE3_CLR 0xe2 + +#define MT6358_TOP_CKPDN_CON0 0x10c +#define MT6358_TOP_CKPDN_CON0_SET 0x10e +#define MT6358_TOP_CKPDN_CON0_CLR 0x110 + +#define MT6358_TOP_CKHWEN_CON0 0x12a +#define MT6358_TOP_CKHWEN_CON0_SET 0x12c +#define MT6358_TOP_CKHWEN_CON0_CLR 0x12e + +#define MT6358_OTP_CON0 0x38a +#define MT6358_OTP_CON8 0x39a +#define MT6358_OTP_CON11 0x3a0 +#define MT6358_OTP_CON12 0x3a2 +#define MT6358_OTP_CON13 0x3a4 + +#define MT6358_DCXO_CW13 0x7aa +#define MT6358_DCXO_CW14 0x7ac + +#define MT6358_AUXADC_CON10 0x11a0 + +/* audio register */ +#define MT6358_AUD_TOP_ID 0x2200 +#define MT6358_AUD_TOP_REV0 0x2202 +#define MT6358_AUD_TOP_DBI 0x2204 +#define MT6358_AUD_TOP_DXI 0x2206 +#define MT6358_AUD_TOP_CKPDN_TPM0 0x2208 +#define MT6358_AUD_TOP_CKPDN_TPM1 0x220a +#define MT6358_AUD_TOP_CKPDN_CON0 0x220c +#define MT6358_AUD_TOP_CKPDN_CON0_SET 0x220e +#define MT6358_AUD_TOP_CKPDN_CON0_CLR 0x2210 +#define MT6358_AUD_TOP_CKSEL_CON0 0x2212 +#define MT6358_AUD_TOP_CKSEL_CON0_SET 0x2214 +#define MT6358_AUD_TOP_CKSEL_CON0_CLR 0x2216 +#define MT6358_AUD_TOP_CKTST_CON0 0x2218 +#define MT6358_AUD_TOP_CLK_HWEN_CON0 0x221a +#define MT6358_AUD_TOP_CLK_HWEN_CON0_SET 0x221c +#define MT6358_AUD_TOP_CLK_HWEN_CON0_CLR 0x221e +#define MT6358_AUD_TOP_RST_CON0 0x2220 +#define MT6358_AUD_TOP_RST_CON0_SET 0x2222 +#define MT6358_AUD_TOP_RST_CON0_CLR 0x2224 +#define MT6358_AUD_TOP_RST_BANK_CON0 0x2226 +#define MT6358_AUD_TOP_INT_CON0 0x2228 +#define MT6358_AUD_TOP_INT_CON0_SET 0x222a +#define MT6358_AUD_TOP_INT_CON0_CLR 0x222c +#define MT6358_AUD_TOP_INT_MASK_CON0 0x222e +#define MT6358_AUD_TOP_INT_MASK_CON0_SET 0x2230 +#define MT6358_AUD_TOP_INT_MASK_CON0_CLR 0x2232 +#define MT6358_AUD_TOP_INT_STATUS0 0x2234 +#define MT6358_AUD_TOP_INT_RAW_STATUS0 0x2236 +#define MT6358_AUD_TOP_INT_MISC_CON0 0x2238 +#define MT6358_AUDNCP_CLKDIV_CON0 0x223a +#define MT6358_AUDNCP_CLKDIV_CON1 0x223c +#define MT6358_AUDNCP_CLKDIV_CON2 0x223e +#define MT6358_AUDNCP_CLKDIV_CON3 0x2240 +#define MT6358_AUDNCP_CLKDIV_CON4 0x2242 +#define MT6358_AUD_TOP_MON_CON0 0x2244 +#define MT6358_AUDIO_DIG_DSN_ID 0x2280 +#define MT6358_AUDIO_DIG_DSN_REV0 0x2282 +#define MT6358_AUDIO_DIG_DSN_DBI 0x2284 +#define MT6358_AUDIO_DIG_DSN_DXI 0x2286 +#define MT6358_AFE_UL_DL_CON0 0x2288 +#define MT6358_AFE_DL_SRC2_CON0_L 0x228a +#define MT6358_AFE_UL_SRC_CON0_H 0x228c +#define MT6358_AFE_UL_SRC_CON0_L 0x228e +#define MT6358_AFE_TOP_CON0 0x2290 +#define MT6358_AUDIO_TOP_CON0 0x2292 +#define MT6358_AFE_MON_DEBUG0 0x2294 +#define MT6358_AFUNC_AUD_CON0 0x2296 +#define MT6358_AFUNC_AUD_CON1 0x2298 +#define MT6358_AFUNC_AUD_CON2 0x229a +#define MT6358_AFUNC_AUD_CON3 0x229c +#define MT6358_AFUNC_AUD_CON4 0x229e +#define MT6358_AFUNC_AUD_CON5 0x22a0 +#define MT6358_AFUNC_AUD_CON6 0x22a2 +#define MT6358_AFUNC_AUD_MON0 0x22a4 +#define MT6358_AUDRC_TUNE_MON0 0x22a6 +#define MT6358_AFE_ADDA_MTKAIF_FIFO_CFG0 0x22a8 +#define MT6358_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 0x22aa +#define MT6358_AFE_ADDA_MTKAIF_MON0 0x22ac +#define MT6358_AFE_ADDA_MTKAIF_MON1 0x22ae +#define MT6358_AFE_ADDA_MTKAIF_MON2 0x22b0 +#define MT6358_AFE_ADDA_MTKAIF_MON3 0x22b2 +#define MT6358_AFE_ADDA_MTKAIF_CFG0 0x22b4 +#define MT6358_AFE_ADDA_MTKAIF_RX_CFG0 0x22b6 +#define MT6358_AFE_ADDA_MTKAIF_RX_CFG1 0x22b8 +#define MT6358_AFE_ADDA_MTKAIF_RX_CFG2 0x22ba +#define MT6358_AFE_ADDA_MTKAIF_RX_CFG3 0x22bc +#define MT6358_AFE_ADDA_MTKAIF_TX_CFG1 0x22be +#define MT6358_AFE_SGEN_CFG0 0x22c0 +#define MT6358_AFE_SGEN_CFG1 0x22c2 +#define MT6358_AFE_ADC_ASYNC_FIFO_CFG 0x22c4 +#define MT6358_AFE_DCCLK_CFG0 0x22c6 +#define MT6358_AFE_DCCLK_CFG1 0x22c8 +#define MT6358_AUDIO_DIG_CFG 0x22ca +#define MT6358_AFE_AUD_PAD_TOP 0x22cc +#define MT6358_AFE_AUD_PAD_TOP_MON 0x22ce +#define MT6358_AFE_AUD_PAD_TOP_MON1 0x22d0 +#define MT6358_AFE_DL_NLE_CFG 0x22d2 +#define MT6358_AFE_DL_NLE_MON 0x22d4 +#define MT6358_AFE_CG_EN_MON 0x22d6 +#define MT6358_AUDIO_DIG_2ND_DSN_ID 0x2300 +#define MT6358_AUDIO_DIG_2ND_DSN_REV0 0x2302 +#define MT6358_AUDIO_DIG_2ND_DSN_DBI 0x2304 +#define MT6358_AUDIO_DIG_2ND_DSN_DXI 0x2306 +#define MT6358_AFE_PMIC_NEWIF_CFG3 0x2308 +#define MT6358_AFE_VOW_TOP 0x230a +#define MT6358_AFE_VOW_CFG0 0x230c +#define MT6358_AFE_VOW_CFG1 0x230e +#define MT6358_AFE_VOW_CFG2 0x2310 +#define MT6358_AFE_VOW_CFG3 0x2312 +#define MT6358_AFE_VOW_CFG4 0x2314 +#define MT6358_AFE_VOW_CFG5 0x2316 +#define MT6358_AFE_VOW_CFG6 0x2318 +#define MT6358_AFE_VOW_MON0 0x231a +#define MT6358_AFE_VOW_MON1 0x231c +#define MT6358_AFE_VOW_MON2 0x231e +#define MT6358_AFE_VOW_MON3 0x2320 +#define MT6358_AFE_VOW_MON4 0x2322 +#define MT6358_AFE_VOW_MON5 0x2324 +#define MT6358_AFE_VOW_SN_INI_CFG 0x2326 +#define MT6358_AFE_VOW_TGEN_CFG0 0x2328 +#define MT6358_AFE_VOW_POSDIV_CFG0 0x232a +#define MT6358_AFE_VOW_HPF_CFG0 0x232c +#define MT6358_AFE_VOW_PERIODIC_CFG0 0x232e +#define MT6358_AFE_VOW_PERIODIC_CFG1 0x2330 +#define MT6358_AFE_VOW_PERIODIC_CFG2 0x2332 +#define MT6358_AFE_VOW_PERIODIC_CFG3 0x2334 +#define MT6358_AFE_VOW_PERIODIC_CFG4 0x2336 +#define MT6358_AFE_VOW_PERIODIC_CFG5 0x2338 +#define MT6358_AFE_VOW_PERIODIC_CFG6 0x233a +#define MT6358_AFE_VOW_PERIODIC_CFG7 0x233c +#define MT6358_AFE_VOW_PERIODIC_CFG8 0x233e +#define MT6358_AFE_VOW_PERIODIC_CFG9 0x2340 +#define MT6358_AFE_VOW_PERIODIC_CFG10 0x2342 +#define MT6358_AFE_VOW_PERIODIC_CFG11 0x2344 +#define MT6358_AFE_VOW_PERIODIC_CFG12 0x2346 +#define MT6358_AFE_VOW_PERIODIC_CFG13 0x2348 +#define MT6358_AFE_VOW_PERIODIC_CFG14 0x234a +#define MT6358_AFE_VOW_PERIODIC_CFG15 0x234c +#define MT6358_AFE_VOW_PERIODIC_CFG16 0x234e +#define MT6358_AFE_VOW_PERIODIC_CFG17 0x2350 +#define MT6358_AFE_VOW_PERIODIC_CFG18 0x2352 +#define MT6358_AFE_VOW_PERIODIC_CFG19 0x2354 +#define MT6358_AFE_VOW_PERIODIC_CFG20 0x2356 +#define MT6358_AFE_VOW_PERIODIC_CFG21 0x2358 +#define MT6358_AFE_VOW_PERIODIC_CFG22 0x235a +#define MT6358_AFE_VOW_PERIODIC_CFG23 0x235c +#define MT6358_AFE_VOW_PERIODIC_MON0 0x235e +#define MT6358_AFE_VOW_PERIODIC_MON1 0x2360 +#define MT6358_AUDENC_DSN_ID 0x2380 +#define MT6358_AUDENC_DSN_REV0 0x2382 +#define MT6358_AUDENC_DSN_DBI 0x2384 +#define MT6358_AUDENC_DSN_FPI 0x2386 +#define MT6358_AUDENC_ANA_CON0 0x2388 +#define MT6358_AUDENC_ANA_CON1 0x238a +#define MT6358_AUDENC_ANA_CON2 0x238c +#define MT6358_AUDENC_ANA_CON3 0x238e +#define MT6358_AUDENC_ANA_CON4 0x2390 +#define MT6358_AUDENC_ANA_CON5 0x2392 +#define MT6358_AUDENC_ANA_CON6 0x2394 +#define MT6358_AUDENC_ANA_CON7 0x2396 +#define MT6358_AUDENC_ANA_CON8 0x2398 +#define MT6358_AUDENC_ANA_CON9 0x239a +#define MT6358_AUDENC_ANA_CON10 0x239c +#define MT6358_AUDENC_ANA_CON11 0x239e +#define MT6358_AUDENC_ANA_CON12 0x23a0 +#define MT6358_AUDDEC_DSN_ID 0x2400 +#define MT6358_AUDDEC_DSN_REV0 0x2402 +#define MT6358_AUDDEC_DSN_DBI 0x2404 +#define MT6358_AUDDEC_DSN_FPI 0x2406 +#define MT6358_AUDDEC_ANA_CON0 0x2408 +#define MT6358_AUDDEC_ANA_CON1 0x240a +#define MT6358_AUDDEC_ANA_CON2 0x240c +#define MT6358_AUDDEC_ANA_CON3 0x240e +#define MT6358_AUDDEC_ANA_CON4 0x2410 +#define MT6358_AUDDEC_ANA_CON5 0x2412 +#define MT6358_AUDDEC_ANA_CON6 0x2414 +#define MT6358_AUDDEC_ANA_CON7 0x2416 +#define MT6358_AUDDEC_ANA_CON8 0x2418 +#define MT6358_AUDDEC_ANA_CON9 0x241a +#define MT6358_AUDDEC_ANA_CON10 0x241c +#define MT6358_AUDDEC_ANA_CON11 0x241e +#define MT6358_AUDDEC_ANA_CON12 0x2420 +#define MT6358_AUDDEC_ANA_CON13 0x2422 +#define MT6358_AUDDEC_ANA_CON14 0x2424 +#define MT6358_AUDDEC_ANA_CON15 0x2426 +#define MT6358_AUDDEC_ELR_NUM 0x2428 +#define MT6358_AUDDEC_ELR_0 0x242a +#define MT6358_AUDZCD_DSN_ID 0x2480 +#define MT6358_AUDZCD_DSN_REV0 0x2482 +#define MT6358_AUDZCD_DSN_DBI 0x2484 +#define MT6358_AUDZCD_DSN_FPI 0x2486 +#define MT6358_ZCD_CON0 0x2488 +#define MT6358_ZCD_CON1 0x248a +#define MT6358_ZCD_CON2 0x248c +#define MT6358_ZCD_CON3 0x248e +#define MT6358_ZCD_CON4 0x2490 +#define MT6358_ZCD_CON5 0x2492 +#define MT6358_ACCDET_CON13 0x2522 + +#define MT6358_MAX_REGISTER MT6358_ZCD_CON5 + +enum { + MT6358_MTKAIF_PROTOCOL_1 = 0, + MT6358_MTKAIF_PROTOCOL_2, + MT6358_MTKAIF_PROTOCOL_2_CLK_P2, +}; + +/* set only during init */ +int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, + int mtkaif_protocol); +int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt); +int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt); +int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, + int phase_1, int phase_2); +#endif /* __MT6358_H__ */ diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 468d5143e2c4..87ed3dc496dc 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -807,7 +807,7 @@ static const struct snd_soc_dapm_route nau8824_dapm_routes[] = { static bool nau8824_is_jack_inserted(struct nau8824 *nau8824) { struct snd_soc_jack *jack = nau8824->jack; - bool insert = FALSE; + bool insert = false; if (nau8824->irq && jack) insert = jack->status & SND_JACK_HEADPHONE; diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 7bbcbf5f05c8..47e65cf99879 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -351,6 +351,7 @@ static void nau8825_hpvol_ramp(struct nau8825 *nau8825, * Computes log10 of a value; the result is round off to 3 decimal. This func- * tion takes reference to dvb-math. The source code locates as the following. * Linux/drivers/media/dvb-core/dvb_math.c + * @value: input for log10 * * return log10(value) * 1000 */ diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index 6714aa8d9026..32b26f1c2282 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -18,12 +18,39 @@ static int pcm3060_set_sysclk(struct snd_soc_dai *dai, int clk_id, { struct snd_soc_component *comp = dai->component; struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); + unsigned int reg; + unsigned int val; if (dir != SND_SOC_CLOCK_IN) { dev_err(comp->dev, "unsupported sysclock dir: %d\n", dir); return -EINVAL; } + switch (clk_id) { + case PCM3060_CLK_DEF: + val = 0; + break; + + case PCM3060_CLK1: + val = (dai->id == PCM3060_DAI_ID_DAC ? PCM3060_REG_CSEL : 0); + break; + + case PCM3060_CLK2: + val = (dai->id == PCM3060_DAI_ID_DAC ? 0 : PCM3060_REG_CSEL); + break; + + default: + dev_err(comp->dev, "unsupported sysclock id: %d\n", clk_id); + return -EINVAL; + } + + if (dai->id == PCM3060_DAI_ID_DAC) + reg = PCM3060_REG67; + else + reg = PCM3060_REG72; + + regmap_update_bits(priv->regmap, reg, PCM3060_REG_CSEL, val); + priv->dai[dai->id].sclk_freq = freq; return 0; @@ -287,6 +314,14 @@ int pcm3060_probe(struct device *dev) int rc; struct pcm3060_priv *priv = dev_get_drvdata(dev); + /* soft reset */ + rc = regmap_update_bits(priv->regmap, PCM3060_REG64, + PCM3060_REG_MRST, 0); + if (rc) { + dev_err(dev, "failed to reset component, rc=%d\n", rc); + return rc; + } + if (dev->of_node) pcm3060_parse_dt(dev->of_node, priv); diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h index 6a027b4a845d..75931c9a9d85 100644 --- a/sound/soc/codecs/pcm3060.h +++ b/sound/soc/codecs/pcm3060.h @@ -17,6 +17,11 @@ extern const struct regmap_config pcm3060_regmap; #define PCM3060_DAI_ID_ADC 1 #define PCM3060_DAI_IDS_NUM 2 +/* ADC and DAC can be clocked from separate or same sources CLK1 and CLK2 */ +#define PCM3060_CLK_DEF 0 /* default: CLK1->ADC, CLK2->DAC */ +#define PCM3060_CLK1 1 +#define PCM3060_CLK2 2 + struct pcm3060_priv_dai { bool is_master; unsigned int sclk_freq; diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 4cc24a5d5c31..62d05b01711f 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -55,6 +55,7 @@ struct pcm512x_priv { unsigned long overclock_dsp; int mute; struct mutex mutex; + unsigned int bclk_ratio; }; /* @@ -915,16 +916,21 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, int fssp; int gpio; - lrclk_div = snd_soc_params_to_frame_size(params); - if (lrclk_div == 0) { - dev_err(dev, "No LRCLK?\n"); - return -EINVAL; + if (pcm512x->bclk_ratio > 0) { + lrclk_div = pcm512x->bclk_ratio; + } else { + lrclk_div = snd_soc_params_to_frame_size(params); + + if (lrclk_div == 0) { + dev_err(dev, "No LRCLK?\n"); + return -EINVAL; + } } if (!pcm512x->pll_out) { sck_rate = clk_get_rate(pcm512x->sclk); - bclk_div = params->rate_den * 64 / lrclk_div; - bclk_rate = DIV_ROUND_CLOSEST(sck_rate, bclk_div); + bclk_rate = params_rate(params) * lrclk_div; + bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate); mck_rate = sck_rate; } else { @@ -1383,6 +1389,19 @@ static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int pcm512x_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + if (ratio > 256) + return -EINVAL; + + pcm512x->bclk_ratio = ratio; + + return 0; +} + static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_component *component = dai->component; @@ -1435,6 +1454,7 @@ static const struct snd_soc_dai_ops pcm512x_dai_ops = { .hw_params = pcm512x_hw_params, .set_fmt = pcm512x_set_fmt, .digital_mute = pcm512x_digital_mute, + .set_bclk_ratio = pcm512x_set_bclk_ratio, }; static struct snd_soc_dai_driver pcm512x_dai = { @@ -1520,8 +1540,9 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2; for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) { - ret = regulator_register_notifier(pcm512x->supplies[i].consumer, - &pcm512x->supply_nb[i]); + ret = devm_regulator_register_notifier( + pcm512x->supplies[i].consumer, + &pcm512x->supply_nb[i]); if (ret != 0) { dev_err(dev, "Failed to register regulator notifier: %d\n", diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c new file mode 100644 index 000000000000..24f8f86d58e9 --- /dev/null +++ b/sound/soc/codecs/rk3328_codec.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rk3328 ALSA SoC Audio driver +// +// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> +#include "rk3328_codec.h" + +/* + * volume setting + * 0: -39dB + * 26: 0dB + * 31: 6dB + * Step: 1.5dB + */ +#define OUT_VOLUME (0x18) +#define RK3328_GRF_SOC_CON2 (0x0408) +#define RK3328_GRF_SOC_CON10 (0x0428) +#define INITIAL_FREQ (11289600) + +struct rk3328_codec_priv { + struct regmap *regmap; + struct regmap *grf; + struct clk *mclk; + struct clk *pclk; + unsigned int sclk; + int spk_depop_time; /* msec */ +}; + +static const struct reg_default rk3328_codec_reg_defaults[] = { + { CODEC_RESET, 0x03 }, + { DAC_INIT_CTRL1, 0x00 }, + { DAC_INIT_CTRL2, 0x50 }, + { DAC_INIT_CTRL3, 0x0e }, + { DAC_PRECHARGE_CTRL, 0x01 }, + { DAC_PWR_CTRL, 0x00 }, + { DAC_CLK_CTRL, 0x00 }, + { HPMIX_CTRL, 0x00 }, + { HPOUT_CTRL, 0x00 }, + { HPOUTL_GAIN_CTRL, 0x00 }, + { HPOUTR_GAIN_CTRL, 0x00 }, + { HPOUT_POP_CTRL, 0x11 }, +}; + +static int rk3328_codec_reset(struct rk3328_codec_priv *rk3328) +{ + regmap_write(rk3328->regmap, CODEC_RESET, 0x00); + mdelay(10); + regmap_write(rk3328->regmap, CODEC_RESET, 0x03); + + return 0; +} + +static int rk3328_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + val = PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + val = PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1, + PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + val = DAC_MODE_PCM; + break; + case SND_SOC_DAIFMT_I2S: + val = DAC_MODE_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val = DAC_MODE_RJM; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = DAC_MODE_LJM; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, + DAC_MODE_MASK, val); + + return 0; +} + +static void rk3328_analog_output(struct rk3328_codec_priv *rk3328, int mute) +{ + unsigned int val = BIT(17); + + if (mute) + val |= BIT(1); + + regmap_write(rk3328->grf, RK3328_GRF_SOC_CON10, val); +} + +static int rk3328_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + if (mute) + val = HPOUTL_MUTE | HPOUTR_MUTE; + else + val = HPOUTL_UNMUTE | HPOUTR_UNMUTE; + + regmap_update_bits(rk3328->regmap, HPOUT_CTRL, + HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val); + + return 0; +} + +static int rk3328_codec_power_on(struct rk3328_codec_priv *rk3328, int wait_ms) +{ + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE); + mdelay(10); + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_CURRENT_ALL_MASK, + DAC_CHARGE_CURRENT_ALL_ON); + mdelay(wait_ms); + + return 0; +} + +static int rk3328_codec_power_off(struct rk3328_codec_priv *rk3328, int wait_ms) +{ + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE); + mdelay(10); + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_CURRENT_ALL_MASK, + DAC_CHARGE_CURRENT_ALL_ON); + mdelay(wait_ms); + + return 0; +} + +static const struct rk3328_reg_msk_val playback_open_list[] = { + { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON }, + { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK, + DACL_PATH_REFV_ON | DACR_PATH_REFV_ON }, + { DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_MASK | HPOUTR_ZERO_CROSSING_MASK, + HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON }, + { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK, + HPOUTR_POP_WORK | HPOUTL_POP_WORK }, + { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN }, + { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK, + HPMIXL_INIT_EN | HPMIXR_INIT_EN }, + { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN }, + { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK, + HPOUTL_INIT_EN | HPOUTR_INIT_EN }, + { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK, + DACL_REFV_ON | DACR_REFV_ON }, + { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK, + DACL_CLK_ON | DACR_CLK_ON }, + { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON }, + { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK, + DACL_INIT_ON | DACR_INIT_ON }, + { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK, + DACL_SELECT | DACR_SELECT }, + { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK, + HPMIXL_INIT2_EN | HPMIXR_INIT2_EN }, + { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, + HPOUTL_UNMUTE | HPOUTR_UNMUTE }, +}; + +static int rk3328_codec_open_playback(struct rk3328_codec_priv *rk3328) +{ + int i; + + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_CURRENT_ALL_MASK, + DAC_CHARGE_CURRENT_I); + + for (i = 0; i < ARRAY_SIZE(playback_open_list); i++) { + regmap_update_bits(rk3328->regmap, + playback_open_list[i].reg, + playback_open_list[i].msk, + playback_open_list[i].val); + mdelay(1); + } + + msleep(rk3328->spk_depop_time); + rk3328_analog_output(rk3328, 1); + + regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL, + HPOUTL_GAIN_MASK, OUT_VOLUME); + regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL, + HPOUTR_GAIN_MASK, OUT_VOLUME); + + return 0; +} + +static const struct rk3328_reg_msk_val playback_close_list[] = { + { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK, + HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS }, + { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK, + DACL_UNSELECT | DACR_UNSELECT }, + { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, + HPOUTL_MUTE | HPOUTR_MUTE }, + { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK, + HPOUTL_INIT_DIS | HPOUTR_INIT_DIS }, + { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS }, + { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS }, + { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF }, + { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK, + DACL_CLK_OFF | DACR_CLK_OFF }, + { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK, + DACL_REFV_OFF | DACR_REFV_OFF }, + { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK, + HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE }, + { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK, + DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF }, + { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF }, + { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK, + HPMIXL_INIT_DIS | HPMIXR_INIT_DIS }, + { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK, + DACL_INIT_OFF | DACR_INIT_OFF }, +}; + +static int rk3328_codec_close_playback(struct rk3328_codec_priv *rk3328) +{ + size_t i; + + rk3328_analog_output(rk3328, 0); + + regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL, + HPOUTL_GAIN_MASK, 0); + regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL, + HPOUTR_GAIN_MASK, 0); + + for (i = 0; i < ARRAY_SIZE(playback_close_list); i++) { + regmap_update_bits(rk3328->regmap, + playback_close_list[i].reg, + playback_close_list[i].msk, + playback_close_list[i].val); + mdelay(1); + } + + /* Workaround for silence when changed Fs 48 -> 44.1kHz */ + rk3328_codec_reset(rk3328); + + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_CURRENT_ALL_MASK, + DAC_CHARGE_CURRENT_ALL_ON); + + return 0; +} + +static int rk3328_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + unsigned int val = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + val = DAC_VDL_16BITS; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = DAC_VDL_20BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = DAC_VDL_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + val = DAC_VDL_32BITS; + break; + default: + return -EINVAL; + } + regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val); + + val = DAC_WL_32BITS | DAC_RST_DIS; + regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3, + DAC_WL_MASK | DAC_RST_MASK, val); + + return 0; +} + +static int rk3328_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + + return rk3328_codec_open_playback(rk3328); +} + +static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + + rk3328_codec_close_playback(rk3328); +} + +static const struct snd_soc_dai_ops rk3328_dai_ops = { + .hw_params = rk3328_hw_params, + .set_fmt = rk3328_set_dai_fmt, + .digital_mute = rk3328_digital_mute, + .startup = rk3328_pcm_startup, + .shutdown = rk3328_pcm_shutdown, +}; + +static struct snd_soc_dai_driver rk3328_dai[] = { + { + .name = "rk3328-hifi", + .id = RK3328_HIFI, + .playback = { + .stream_name = "HIFI Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + }, + .capture = { + .stream_name = "HIFI Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + }, + .ops = &rk3328_dai_ops, + }, +}; + +static int rk3328_codec_probe(struct snd_soc_component *component) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(component); + + rk3328_codec_reset(rk3328); + rk3328_codec_power_on(rk3328, 0); + + return 0; +} + +static void rk3328_codec_remove(struct snd_soc_component *component) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(component); + + rk3328_codec_close_playback(rk3328); + rk3328_codec_power_off(rk3328, 0); +} + +static const struct snd_soc_component_driver soc_codec_rk3328 = { + .probe = rk3328_codec_probe, + .remove = rk3328_codec_remove, +}; + +static bool rk3328_codec_write_read_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CODEC_RESET: + case DAC_INIT_CTRL1: + case DAC_INIT_CTRL2: + case DAC_INIT_CTRL3: + case DAC_PRECHARGE_CTRL: + case DAC_PWR_CTRL: + case DAC_CLK_CTRL: + case HPMIX_CTRL: + case DAC_SELECT: + case HPOUT_CTRL: + case HPOUTL_GAIN_CTRL: + case HPOUTR_GAIN_CTRL: + case HPOUT_POP_CTRL: + return true; + default: + return false; + } +} + +static bool rk3328_codec_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CODEC_RESET: + return true; + default: + return false; + } +} + +static const struct regmap_config rk3328_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = HPOUT_POP_CTRL, + .writeable_reg = rk3328_codec_write_read_reg, + .readable_reg = rk3328_codec_write_read_reg, + .volatile_reg = rk3328_codec_volatile_reg, + .reg_defaults = rk3328_codec_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int rk3328_platform_probe(struct platform_device *pdev) +{ + struct device_node *rk3328_np = pdev->dev.of_node; + struct rk3328_codec_priv *rk3328; + struct resource *res; + struct regmap *grf; + void __iomem *base; + int ret = 0; + + rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL); + if (!rk3328) + return -ENOMEM; + + grf = syscon_regmap_lookup_by_phandle(rk3328_np, + "rockchip,grf"); + if (IS_ERR(grf)) { + dev_err(&pdev->dev, "missing 'rockchip,grf'\n"); + return PTR_ERR(grf); + } + rk3328->grf = grf; + /* enable i2s_acodec_en */ + regmap_write(grf, RK3328_GRF_SOC_CON2, + (BIT(14) << 16 | BIT(14))); + + ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms", + &rk3328->spk_depop_time); + if (ret < 0) { + dev_info(&pdev->dev, "spk_depop_time use default value.\n"); + rk3328->spk_depop_time = 200; + } + + rk3328_analog_output(rk3328, 0); + + rk3328->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(rk3328->mclk)) + return PTR_ERR(rk3328->mclk); + + ret = clk_prepare_enable(rk3328->mclk); + if (ret) + return ret; + clk_set_rate(rk3328->mclk, INITIAL_FREQ); + + rk3328->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(rk3328->pclk)) { + dev_err(&pdev->dev, "can't get acodec pclk\n"); + return PTR_ERR(rk3328->pclk); + } + + ret = clk_prepare_enable(rk3328->pclk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable acodec pclk\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &rk3328_codec_regmap_config); + if (IS_ERR(rk3328->regmap)) + return PTR_ERR(rk3328->regmap); + + platform_set_drvdata(pdev, rk3328); + + return devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328, + rk3328_dai, + ARRAY_SIZE(rk3328_dai)); +} + +static const struct of_device_id rk3328_codec_of_match[] = { + { .compatible = "rockchip,rk3328-codec", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk3328_codec_of_match); + +static struct platform_driver rk3328_codec_driver = { + .driver = { + .name = "rk3328-codec", + .of_match_table = of_match_ptr(rk3328_codec_of_match), + }, + .probe = rk3328_platform_probe, +}; +module_platform_driver(rk3328_codec_driver); + +MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>"); +MODULE_DESCRIPTION("ASoC rk3328 codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rk3328_codec.h b/sound/soc/codecs/rk3328_codec.h new file mode 100644 index 000000000000..655103586241 --- /dev/null +++ b/sound/soc/codecs/rk3328_codec.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rk3328 ALSA SoC Audio driver + * + * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. + */ + +#ifndef _RK3328_CODEC_H +#define _RK3328_CODEC_H + +#include <linux/bitfield.h> + +/* codec register */ +#define CODEC_RESET (0x00 << 2) +#define DAC_INIT_CTRL1 (0x03 << 2) +#define DAC_INIT_CTRL2 (0x04 << 2) +#define DAC_INIT_CTRL3 (0x05 << 2) +#define DAC_PRECHARGE_CTRL (0x22 << 2) +#define DAC_PWR_CTRL (0x23 << 2) +#define DAC_CLK_CTRL (0x24 << 2) +#define HPMIX_CTRL (0x25 << 2) +#define DAC_SELECT (0x26 << 2) +#define HPOUT_CTRL (0x27 << 2) +#define HPOUTL_GAIN_CTRL (0x28 << 2) +#define HPOUTR_GAIN_CTRL (0x29 << 2) +#define HPOUT_POP_CTRL (0x2a << 2) + +/* REG00: CODEC_RESET */ +#define PWR_RST_BYPASS_DIS (0x0 << 6) +#define PWR_RST_BYPASS_EN (0x1 << 6) +#define DIG_CORE_RST (0x0 << 1) +#define DIG_CORE_WORK (0x1 << 1) +#define SYS_RST (0x0 << 0) +#define SYS_WORK (0x1 << 0) + +/* REG03: DAC_INIT_CTRL1 */ +#define PIN_DIRECTION_MASK BIT(5) +#define PIN_DIRECTION_IN (0x0 << 5) +#define PIN_DIRECTION_OUT (0x1 << 5) +#define DAC_I2S_MODE_MASK BIT(4) +#define DAC_I2S_MODE_SLAVE (0x0 << 4) +#define DAC_I2S_MODE_MASTER (0x1 << 4) + +/* REG04: DAC_INIT_CTRL2 */ +#define DAC_I2S_LRP_MASK BIT(7) +#define DAC_I2S_LRP_NORMAL (0x0 << 7) +#define DAC_I2S_LRP_REVERSAL (0x1 << 7) +#define DAC_VDL_MASK GENMASK(6, 5) +#define DAC_VDL_16BITS (0x0 << 5) +#define DAC_VDL_20BITS (0x1 << 5) +#define DAC_VDL_24BITS (0x2 << 5) +#define DAC_VDL_32BITS (0x3 << 5) +#define DAC_MODE_MASK GENMASK(4, 3) +#define DAC_MODE_RJM (0x0 << 3) +#define DAC_MODE_LJM (0x1 << 3) +#define DAC_MODE_I2S (0x2 << 3) +#define DAC_MODE_PCM (0x3 << 3) +#define DAC_LR_SWAP_MASK BIT(2) +#define DAC_LR_SWAP_DIS (0x0 << 2) +#define DAC_LR_SWAP_EN (0x1 << 2) + +/* REG05: DAC_INIT_CTRL3 */ +#define DAC_WL_MASK GENMASK(3, 2) +#define DAC_WL_16BITS (0x0 << 2) +#define DAC_WL_20BITS (0x1 << 2) +#define DAC_WL_24BITS (0x2 << 2) +#define DAC_WL_32BITS (0x3 << 2) +#define DAC_RST_MASK BIT(1) +#define DAC_RST_EN (0x0 << 1) +#define DAC_RST_DIS (0x1 << 1) +#define DAC_BCP_MASK BIT(0) +#define DAC_BCP_NORMAL (0x0 << 0) +#define DAC_BCP_REVERSAL (0x1 << 0) + +/* REG22: DAC_PRECHARGE_CTRL */ +#define DAC_CHARGE_XCHARGE_MASK BIT(7) +#define DAC_CHARGE_DISCHARGE (0x0 << 7) +#define DAC_CHARGE_PRECHARGE (0x1 << 7) +#define DAC_CHARGE_CURRENT_64I_MASK BIT(6) +#define DAC_CHARGE_CURRENT_64I (0x1 << 6) +#define DAC_CHARGE_CURRENT_32I_MASK BIT(5) +#define DAC_CHARGE_CURRENT_32I (0x1 << 5) +#define DAC_CHARGE_CURRENT_16I_MASK BIT(4) +#define DAC_CHARGE_CURRENT_16I (0x1 << 4) +#define DAC_CHARGE_CURRENT_08I_MASK BIT(3) +#define DAC_CHARGE_CURRENT_08I (0x1 << 3) +#define DAC_CHARGE_CURRENT_04I_MASK BIT(2) +#define DAC_CHARGE_CURRENT_04I (0x1 << 2) +#define DAC_CHARGE_CURRENT_02I_MASK BIT(1) +#define DAC_CHARGE_CURRENT_02I (0x1 << 1) +#define DAC_CHARGE_CURRENT_I_MASK BIT(0) +#define DAC_CHARGE_CURRENT_I (0x1 << 0) +#define DAC_CHARGE_CURRENT_ALL_MASK GENMASK(6, 0) +#define DAC_CHARGE_CURRENT_ALL_OFF 0x00 +#define DAC_CHARGE_CURRENT_ALL_ON 0x7f + +/* REG23: DAC_PWR_CTRL */ +#define DAC_PWR_MASK BIT(6) +#define DAC_PWR_OFF (0x0 << 6) +#define DAC_PWR_ON (0x1 << 6) +#define DACL_PATH_REFV_MASK BIT(5) +#define DACL_PATH_REFV_OFF (0x0 << 5) +#define DACL_PATH_REFV_ON (0x1 << 5) +#define HPOUTL_ZERO_CROSSING_MASK BIT(4) +#define HPOUTL_ZERO_CROSSING_OFF (0x0 << 4) +#define HPOUTL_ZERO_CROSSING_ON (0x1 << 4) +#define DACR_PATH_REFV_MASK BIT(1) +#define DACR_PATH_REFV_OFF (0x0 << 1) +#define DACR_PATH_REFV_ON (0x1 << 1) +#define HPOUTR_ZERO_CROSSING_MASK BIT(0) +#define HPOUTR_ZERO_CROSSING_OFF (0x0 << 0) +#define HPOUTR_ZERO_CROSSING_ON (0x1 << 0) + +/* REG24: DAC_CLK_CTRL */ +#define DACL_REFV_MASK BIT(7) +#define DACL_REFV_OFF (0x0 << 7) +#define DACL_REFV_ON (0x1 << 7) +#define DACL_CLK_MASK BIT(6) +#define DACL_CLK_OFF (0x0 << 6) +#define DACL_CLK_ON (0x1 << 6) +#define DACL_MASK BIT(5) +#define DACL_OFF (0x0 << 5) +#define DACL_ON (0x1 << 5) +#define DACL_INIT_MASK BIT(4) +#define DACL_INIT_OFF (0x0 << 4) +#define DACL_INIT_ON (0x1 << 4) +#define DACR_REFV_MASK BIT(3) +#define DACR_REFV_OFF (0x0 << 3) +#define DACR_REFV_ON (0x1 << 3) +#define DACR_CLK_MASK BIT(2) +#define DACR_CLK_OFF (0x0 << 2) +#define DACR_CLK_ON (0x1 << 2) +#define DACR_MASK BIT(1) +#define DACR_OFF (0x0 << 1) +#define DACR_ON (0x1 << 1) +#define DACR_INIT_MASK BIT(0) +#define DACR_INIT_OFF (0x0 << 0) +#define DACR_INIT_ON (0x1 << 0) + +/* REG25: HPMIX_CTRL*/ +#define HPMIXL_MASK BIT(6) +#define HPMIXL_DIS (0x0 << 6) +#define HPMIXL_EN (0x1 << 6) +#define HPMIXL_INIT_MASK BIT(5) +#define HPMIXL_INIT_DIS (0x0 << 5) +#define HPMIXL_INIT_EN (0x1 << 5) +#define HPMIXL_INIT2_MASK BIT(4) +#define HPMIXL_INIT2_DIS (0x0 << 4) +#define HPMIXL_INIT2_EN (0x1 << 4) +#define HPMIXR_MASK BIT(2) +#define HPMIXR_DIS (0x0 << 2) +#define HPMIXR_EN (0x1 << 2) +#define HPMIXR_INIT_MASK BIT(1) +#define HPMIXR_INIT_DIS (0x0 << 1) +#define HPMIXR_INIT_EN (0x1 << 1) +#define HPMIXR_INIT2_MASK BIT(0) +#define HPMIXR_INIT2_DIS (0x0 << 0) +#define HPMIXR_INIT2_EN (0x1 << 0) + +/* REG26: DAC_SELECT */ +#define DACL_SELECT_MASK BIT(4) +#define DACL_UNSELECT (0x0 << 4) +#define DACL_SELECT (0x1 << 4) +#define DACR_SELECT_MASK BIT(0) +#define DACR_UNSELECT (0x0 << 0) +#define DACR_SELECT (0x1 << 0) + +/* REG27: HPOUT_CTRL */ +#define HPOUTL_MASK BIT(7) +#define HPOUTL_DIS (0x0 << 7) +#define HPOUTL_EN (0x1 << 7) +#define HPOUTL_INIT_MASK BIT(6) +#define HPOUTL_INIT_DIS (0x0 << 6) +#define HPOUTL_INIT_EN (0x1 << 6) +#define HPOUTL_MUTE_MASK BIT(5) +#define HPOUTL_MUTE (0x0 << 5) +#define HPOUTL_UNMUTE (0x1 << 5) +#define HPOUTR_MASK BIT(4) +#define HPOUTR_DIS (0x0 << 4) +#define HPOUTR_EN (0x1 << 4) +#define HPOUTR_INIT_MASK BIT(3) +#define HPOUTR_INIT_DIS (0x0 << 3) +#define HPOUTR_INIT_EN (0x1 << 3) +#define HPOUTR_MUTE_MASK BIT(2) +#define HPOUTR_MUTE (0x0 << 2) +#define HPOUTR_UNMUTE (0x1 << 2) + +/* REG28: HPOUTL_GAIN_CTRL */ +#define HPOUTL_GAIN_MASK GENMASK(4, 0) + +/* REG29: HPOUTR_GAIN_CTRL */ +#define HPOUTR_GAIN_MASK GENMASK(4, 0) + +/* REG2a: HPOUT_POP_CTRL */ +#define HPOUTR_POP_MASK GENMASK(5, 4) +#define HPOUTR_POP_XCHARGE (0x1 << 4) +#define HPOUTR_POP_WORK (0x2 << 4) +#define HPOUTL_POP_MASK GENMASK(1, 0) +#define HPOUTL_POP_XCHARGE (0x1 << 0) +#define HPOUTL_POP_WORK (0x2 << 0) + +#define RK3328_HIFI 0 + +struct rk3328_reg_msk_val { + unsigned int reg; + unsigned int msk; + unsigned int val; +}; + +#endif diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c index 8f571cf8edd4..c0d729b45277 100644 --- a/sound/soc/codecs/rl6347a.c +++ b/sound/soc/codecs/rl6347a.c @@ -64,8 +64,8 @@ int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value) struct i2c_client *client = context; struct i2c_msg xfer[2]; int ret; - __be32 be_reg; - unsigned int index, vid, buf = 0x0; + __be32 be_reg, buf = 0x0; + unsigned int index, vid; /* handle index registers */ if (reg <= 0xff) { diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index e2855ab9a2c6..adf59039a3b6 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -353,6 +353,7 @@ static void rt274_index_sync(struct snd_soc_component *component) static int rt274_jack_detect(struct rt274_priv *rt274, bool *hp, bool *mic) { unsigned int buf; + int ret; *hp = false; *mic = false; @@ -360,9 +361,15 @@ static int rt274_jack_detect(struct rt274_priv *rt274, bool *hp, bool *mic) if (!rt274->component) return -EINVAL; - regmap_read(rt274->regmap, RT274_GET_HP_SENSE, &buf); + ret = regmap_read(rt274->regmap, RT274_GET_HP_SENSE, &buf); + if (ret) + return ret; + *hp = buf & 0x80000000; - regmap_read(rt274->regmap, RT274_GET_MIC_SENSE, &buf); + ret = regmap_read(rt274->regmap, RT274_GET_MIC_SENSE, &buf); + if (ret) + return ret; + *mic = buf & 0x80000000; pr_debug("*hp = %d *mic = %d\n", *hp, *mic); @@ -381,10 +388,10 @@ static void rt274_jack_detect_work(struct work_struct *work) if (rt274_jack_detect(rt274, &hp, &mic) < 0) return; - if (hp == true) + if (hp) status |= SND_JACK_HEADPHONE; - if (mic == true) + if (mic) status |= SND_JACK_MICROPHONE; snd_soc_jack_report(rt274->jack, status, @@ -955,10 +962,10 @@ static irqreturn_t rt274_irq(int irq, void *data) ret = rt274_jack_detect(rt274, &hp, &mic); if (ret == 0) { - if (hp == true) + if (hp) status |= SND_JACK_HEADPHONE; - if (mic == true) + if (mic) status |= SND_JACK_MICROPHONE; snd_soc_jack_report(rt274->jack, status, diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 0b0f748bffbe..c9457c247a03 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -296,10 +296,10 @@ static void rt286_jack_detect_work(struct work_struct *work) rt286_jack_detect(rt286, &hp, &mic); - if (hp == true) + if (hp) status |= SND_JACK_HEADPHONE; - if (mic == true) + if (mic) status |= SND_JACK_MICROPHONE; snd_soc_jack_report(rt286->jack, status, @@ -924,10 +924,10 @@ static irqreturn_t rt286_irq(int irq, void *data) /* Clear IRQ */ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x1, 0x1); - if (hp == true) + if (hp) status |= SND_JACK_HEADPHONE; - if (mic == true) + if (mic) status |= SND_JACK_MICROPHONE; snd_soc_jack_report(rt286->jack, status, diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 06cdba4edfe2..bcf5bab31969 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -314,10 +314,10 @@ static void rt298_jack_detect_work(struct work_struct *work) if (rt298_jack_detect(rt298, &hp, &mic) < 0) return; - if (hp == true) + if (hp) status |= SND_JACK_HEADPHONE; - if (mic == true) + if (mic) status |= SND_JACK_MICROPHONE; snd_soc_jack_report(rt298->jack, status, @@ -345,10 +345,10 @@ int rt298_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *j regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); rt298_jack_detect(rt298, &hp, &mic); - if (hp == true) + if (hp) status |= SND_JACK_HEADPHONE; - if (mic == true) + if (mic) status |= SND_JACK_MICROPHONE; snd_soc_jack_report(rt298->jack, status, @@ -989,10 +989,10 @@ static irqreturn_t rt298_irq(int irq, void *data) regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x1, 0x1); if (ret == 0) { - if (hp == true) + if (hp) status |= SND_JACK_HEADPHONE; - if (mic == true) + if (mic) status |= SND_JACK_MICROPHONE; snd_soc_jack_report(rt298->jack, status, diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index a67de68b6da6..f9ad6e36ab16 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -489,6 +489,7 @@ static const struct snd_kcontrol_new rt5514_sto2_dmic_mux = /** * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic. * + * @component: only used for dev_warn * @rate: base clock rate. * * Choose divider parameter that gives the highest possible DMIC frequency in diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index fc530481a6e4..b3580ecadecf 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -977,11 +977,11 @@ static int rt5640_hp_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: rt5640_pmu_depop(component); - rt5640->hp_mute = 0; + rt5640->hp_mute = false; break; case SND_SOC_DAPM_PRE_PMD: - rt5640->hp_mute = 1; + rt5640->hp_mute = true; msleep(70); break; @@ -2822,7 +2822,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_DUMMY1, RT5640_MCLK_DET, RT5640_MCLK_DET); - rt5640->hp_mute = 1; + rt5640->hp_mute = true; rt5640->irq = i2c->irq; INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work); INIT_WORK(&rt5640->jack_work, rt5640_jack_work); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index be674688dc40..9a0751978090 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -401,6 +401,11 @@ struct rt5645_eq_param_s { unsigned short val; }; +struct rt5645_eq_param_s_be16 { + __be16 reg; + __be16 val; +}; + static const char *const rt5645_supply_names[] = { "avdd", "cpvdd", @@ -672,8 +677,8 @@ static int rt5645_hweq_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); - struct rt5645_eq_param_s *eq_param = - (struct rt5645_eq_param_s *)ucontrol->value.bytes.data; + struct rt5645_eq_param_s_be16 *eq_param = + (struct rt5645_eq_param_s_be16 *)ucontrol->value.bytes.data; int i; for (i = 0; i < RT5645_HWEQ_NUM; i++) { @@ -698,36 +703,33 @@ static int rt5645_hweq_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); - struct rt5645_eq_param_s *eq_param = - (struct rt5645_eq_param_s *)ucontrol->value.bytes.data; + struct rt5645_eq_param_s_be16 *eq_param = + (struct rt5645_eq_param_s_be16 *)ucontrol->value.bytes.data; int i; for (i = 0; i < RT5645_HWEQ_NUM; i++) { - eq_param[i].reg = be16_to_cpu(eq_param[i].reg); - eq_param[i].val = be16_to_cpu(eq_param[i].val); + rt5645->eq_param[i].reg = be16_to_cpu(eq_param[i].reg); + rt5645->eq_param[i].val = be16_to_cpu(eq_param[i].val); } /* The final setting of the table should be RT5645_EQ_CTRL2 */ for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) { - if (eq_param[i].reg == 0) + if (rt5645->eq_param[i].reg == 0) continue; - else if (eq_param[i].reg != RT5645_EQ_CTRL2) + else if (rt5645->eq_param[i].reg != RT5645_EQ_CTRL2) return 0; else break; } for (i = 0; i < RT5645_HWEQ_NUM; i++) { - if (!rt5645_validate_hweq(eq_param[i].reg) && - eq_param[i].reg != 0) + if (!rt5645_validate_hweq(rt5645->eq_param[i].reg) && + rt5645->eq_param[i].reg != 0) return 0; - else if (eq_param[i].reg == 0) + else if (rt5645->eq_param[i].reg == 0) break; } - memcpy(rt5645->eq_param, eq_param, - RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s)); - return 0; } @@ -1288,30 +1290,6 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5645_dac_r2_mux = SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum); - -/* INL/R source */ -static const char * const rt5645_inl_src[] = { - "IN2P", "MonoP" -}; - -static SOC_ENUM_SINGLE_DECL( - rt5645_inl_enum, RT5645_INL1_INR1_VOL, - RT5645_INL_SEL_SFT, rt5645_inl_src); - -static const struct snd_kcontrol_new rt5645_inl_mux = - SOC_DAPM_ENUM("INL source", rt5645_inl_enum); - -static const char * const rt5645_inr_src[] = { - "IN2N", "MonoN" -}; - -static SOC_ENUM_SINGLE_DECL( - rt5645_inr_enum, RT5645_INL1_INR1_VOL, - RT5645_INR_SEL_SFT, rt5645_inr_src); - -static const struct snd_kcontrol_new rt5645_inr_mux = - SOC_DAPM_ENUM("INR source", rt5645_inr_enum); - /* Stereo1 ADC source */ /* MX-27 [12] */ static const char * const rt5645_stereo_adc1_src[] = { @@ -1611,18 +1589,6 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5645_if2_adc_in_mux = SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum); -/* MX-2F [1:0] */ -static const char * const rt5645_if3_adc_in_src[] = { - "IF_ADC1", "IF_ADC2", "VAD_ADC" -}; - -static SOC_ENUM_SINGLE_DECL( - rt5645_if3_adc_in_enum, RT5645_DIG_INF1_DATA, - RT5645_IF3_ADC_IN_SFT, rt5645_if3_adc_in_src); - -static const struct snd_kcontrol_new rt5645_if3_adc_in_mux = - SOC_DAPM_ENUM("IF3 ADC IN source", rt5645_if3_adc_in_enum); - /* MX-31 [15] [13] [11] [9] */ static const char * const rt5645_pdm_src[] = { "Mono DAC", "Stereo DAC" diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index b7ba64350a07..29b2d60076b0 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/platform_device.h> @@ -747,11 +748,11 @@ static int rt5651_hp_event(struct snd_soc_dapm_widget *w, RT5651_HP_CP_PD | RT5651_HP_SG_EN); regmap_update_bits(rt5651->regmap, RT5651_PR_BASE + RT5651_CHPUMP_INT_REG1, 0x0700, 0x0400); - rt5651->hp_mute = 0; + rt5651->hp_mute = false; break; case SND_SOC_DAPM_PRE_PMD: - rt5651->hp_mute = 1; + rt5651->hp_mute = true; usleep_range(70000, 75000); break; @@ -1621,6 +1622,12 @@ static bool rt5651_jack_inserted(struct snd_soc_component *component) struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); int val; + if (rt5651->gpiod_hp_det) { + val = gpiod_get_value_cansleep(rt5651->gpiod_hp_det); + dev_dbg(component->dev, "jack-detect gpio %d\n", val); + return val; + } + val = snd_soc_component_read32(component, RT5651_INT_IRQ_ST); dev_dbg(component->dev, "irq status %#04x\n", val); @@ -1761,6 +1768,13 @@ static int rt5651_detect_headset(struct snd_soc_component *component) return SND_JACK_HEADPHONE; } +static bool rt5651_support_button_press(struct rt5651_priv *rt5651) +{ + /* Button press support only works with internal jack-detection */ + return (rt5651->hp_jack->status & SND_JACK_MICROPHONE) && + rt5651->gpiod_hp_det == NULL; +} + static void rt5651_jack_detect_work(struct work_struct *work) { struct rt5651_priv *rt5651 = @@ -1785,15 +1799,15 @@ static void rt5651_jack_detect_work(struct work_struct *work) WARN_ON(rt5651->ovcd_irq_enabled); rt5651_enable_micbias1_for_ovcd(component); report = rt5651_detect_headset(component); - if (report == SND_JACK_HEADSET) { + dev_dbg(component->dev, "detect report %#02x\n", report); + snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); + if (rt5651_support_button_press(rt5651)) { /* Enable ovcd IRQ for button press detect. */ rt5651_enable_micbias1_ovcd_irq(component); } else { /* No more need for overcurrent detect. */ rt5651_disable_micbias1_for_ovcd(component); } - dev_dbg(component->dev, "detect report %#02x\n", report); - snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); } else if (rt5651->ovcd_irq_enabled && rt5651_micbias1_ovcd(component)) { dev_dbg(component->dev, "OVCD IRQ\n"); @@ -1837,16 +1851,20 @@ static void rt5651_cancel_work(void *data) } static void rt5651_enable_jack_detect(struct snd_soc_component *component, - struct snd_soc_jack *hp_jack) + struct snd_soc_jack *hp_jack, + struct gpio_desc *gpiod_hp_det) { struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); - - /* IRQ output on GPIO1 */ - snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1, - RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ); + bool using_internal_jack_detect = true; /* Select jack detect source */ switch (rt5651->jd_src) { + case RT5651_JD_NULL: + rt5651->gpiod_hp_det = gpiod_hp_det; + if (!rt5651->gpiod_hp_det) + return; /* No jack detect */ + using_internal_jack_detect = false; + break; case RT5651_JD1_1: snd_soc_component_update_bits(component, RT5651_JD_CTRL2, RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_1); @@ -1865,16 +1883,20 @@ static void rt5651_enable_jack_detect(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1, RT5651_JD2_IRQ_EN, RT5651_JD2_IRQ_EN); break; - case RT5651_JD_NULL: - return; default: dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n"); return; } - /* Enable jack detect power */ - snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, - RT5651_PWR_JD_M, RT5651_PWR_JD_M); + if (using_internal_jack_detect) { + /* IRQ output on GPIO1 */ + snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1, + RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ); + + /* Enable jack detect power */ + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_JD_M, RT5651_PWR_JD_M); + } /* Set OVCD threshold current and scale-factor */ snd_soc_component_write(component, RT5651_PR_BASE + RT5651_BIAS_CUR4, @@ -1903,7 +1925,7 @@ static void rt5651_enable_jack_detect(struct snd_soc_component *component, RT5651_MB1_OC_STKY_MASK, RT5651_MB1_OC_STKY_EN); rt5651->hp_jack = hp_jack; - if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + if (rt5651_support_button_press(rt5651)) { rt5651_enable_micbias1_for_ovcd(component); rt5651_enable_micbias1_ovcd_irq(component); } @@ -1920,7 +1942,7 @@ static void rt5651_disable_jack_detect(struct snd_soc_component *component) disable_irq(rt5651->irq); rt5651_cancel_work(rt5651); - if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + if (rt5651_support_button_press(rt5651)) { rt5651_disable_micbias1_ovcd_irq(component); rt5651_disable_micbias1_for_ovcd(component); snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0); @@ -1933,7 +1955,7 @@ static int rt5651_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data) { if (jack) - rt5651_enable_jack_detect(component, jack); + rt5651_enable_jack_detect(component, jack, data); else rt5651_disable_jack_detect(component); @@ -2138,6 +2160,7 @@ MODULE_DEVICE_TABLE(of, rt5651_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id rt5651_acpi_match[] = { { "10EC5651", 0 }, + { "10EC5640", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match); @@ -2158,6 +2181,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, { struct rt5651_priv *rt5651; int ret; + int err; rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651), GFP_KERNEL); @@ -2174,7 +2198,10 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, return ret; } - regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret); + err = regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret); + if (err) + return err; + if (ret != RT5651_DEVICE_ID_VALUE) { dev_err(&i2c->dev, "Device with ID register %#x is not rt5651\n", ret); @@ -2189,7 +2216,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); rt5651->irq = i2c->irq; - rt5651->hp_mute = 1; + rt5651->hp_mute = true; INIT_DELAYED_WORK(&rt5651->bp_work, rt5651_button_press_work); INIT_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work); diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h index ac6de6fb5414..41fcb8b5eb40 100644 --- a/sound/soc/codecs/rt5651.h +++ b/sound/soc/codecs/rt5651.h @@ -2073,6 +2073,7 @@ struct rt5651_priv { struct regmap *regmap; /* Jack and button detect data */ struct snd_soc_jack *hp_jack; + struct gpio_desc *gpiod_hp_det; struct work_struct jack_detect_work; struct delayed_work bp_work; bool ovcd_irq_enabled; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 453328c988c0..9a037108b1ae 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -1057,20 +1057,6 @@ static const struct snd_kcontrol_new rt5670_lout_mix[] = { RT5670_M_OV_R_LM_SFT, 1, 1), }; -static const struct snd_kcontrol_new rt5670_hpl_mix[] = { - SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_HPO_MIXER, - RT5670_M_DACL1_HML_SFT, 1, 1), - SOC_DAPM_SINGLE("INL1 Switch", RT5670_HPO_MIXER, - RT5670_M_INL1_HML_SFT, 1, 1), -}; - -static const struct snd_kcontrol_new rt5670_hpr_mix[] = { - SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_HPO_MIXER, - RT5670_M_DACR1_HMR_SFT, 1, 1), - SOC_DAPM_SINGLE("INR1 Switch", RT5670_HPO_MIXER, - RT5670_M_INR1_HMR_SFT, 1, 1), -}; - static const struct snd_kcontrol_new lout_l_enable_control = SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5670_LOUT1, RT5670_L_MUTE_SFT, 1, 1); @@ -1196,24 +1182,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc2_enum, RT5670_STO2_ADC_MIXER, static const struct snd_kcontrol_new rt5670_sto2_adc_2_mux = SOC_DAPM_ENUM("Stereo2 ADC 2 Mux", rt5670_stereo2_adc2_enum); - -/* MX-27 MX26 [10] */ -static const char * const rt5670_stereo_adc_src[] = { - "ADC1L ADC2R", "ADC3" -}; - -static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc_enum, RT5670_STO1_ADC_MIXER, - RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src); - -static const struct snd_kcontrol_new rt5670_sto_adc_mux = - SOC_DAPM_ENUM("Stereo1 ADC source", rt5670_stereo1_adc_enum); - -static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc_enum, RT5670_STO2_ADC_MIXER, - RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src); - -static const struct snd_kcontrol_new rt5670_sto2_adc_mux = - SOC_DAPM_ENUM("Stereo2 ADC source", rt5670_stereo2_adc_enum); - /* MX-27 MX-26 [9:8] */ static const char * const rt5670_stereo_dmic_src[] = { "DMIC1", "DMIC2", "DMIC3" @@ -1231,17 +1199,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_dmic_enum, RT5670_STO2_ADC_MIXER, static const struct snd_kcontrol_new rt5670_sto2_dmic_mux = SOC_DAPM_ENUM("Stereo2 DMIC source", rt5670_stereo2_dmic_enum); -/* MX-27 [0] */ -static const char * const rt5670_stereo_dmic3_src[] = { - "DMIC3", "PDM ADC" -}; - -static SOC_ENUM_SINGLE_DECL(rt5670_stereo_dmic3_enum, RT5670_STO1_ADC_MIXER, - RT5670_DMIC3_SRC_SFT, rt5670_stereo_dmic3_src); - -static const struct snd_kcontrol_new rt5670_sto_dmic3_mux = - SOC_DAPM_ENUM("Stereo DMIC3 source", rt5670_stereo_dmic3_enum); - /* Mono ADC source */ /* MX-28 [12] */ static const char * const rt5670_mono_adc_l1_src[] = { @@ -1334,17 +1291,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_in_enum, RT5670_DIG_INF1_DATA, static const struct snd_kcontrol_new rt5670_if2_adc_in_mux = SOC_DAPM_ENUM("IF2 ADC IN source", rt5670_if2_adc_in_enum); -/* MX-30 [5:4] */ -static const char * const rt5670_if4_adc_in_src[] = { - "IF_ADC1", "IF_ADC2", "IF_ADC3" -}; - -static SOC_ENUM_SINGLE_DECL(rt5670_if4_adc_in_enum, RT5670_DIG_INF2_DATA, - RT5670_IF4_ADC_IN_SFT, rt5670_if4_adc_in_src); - -static const struct snd_kcontrol_new rt5670_if4_adc_in_mux = - SOC_DAPM_ENUM("IF4 ADC IN source", rt5670_if4_adc_in_enum); - /* MX-31 [15] [13] [11] [9] */ static const char * const rt5670_pdm_src[] = { "Mono DAC", "Stereo DAC" diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 9b7a1833d331..6fc70e441458 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -547,7 +547,7 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg) * @rt5677: Private Data. * @addr: Address index. * @value: Address data. - * + * @opcode: opcode value * * Returns 0 for success or negative error code. */ @@ -602,7 +602,7 @@ err: /** * rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode. - * rt5677: Private Data. + * @rt5677: Private Data. * @addr: Address index. * @value: Address data. * @@ -651,7 +651,7 @@ err: /** * rt5677_dsp_mode_i2c_write - Write register on DSP mode. - * rt5677: Private Data. + * @rt5677: Private Data. * @reg: Register index. * @value: Register data. * @@ -667,7 +667,7 @@ static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677, /** * rt5677_dsp_mode_i2c_read - Read register on DSP mode. - * @codec: SoC audio codec device. + * @rt5677: Private Data * @reg: Register index. * @value: Register data. * diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index a9b91bcfcc09..9d5acd2d04ab 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -43,6 +43,12 @@ static const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = { "VBAT", }; +static const struct rt5682_platform_data i2s_default_platform_data = { + .dmic1_data_pin = RT5682_DMIC1_DATA_GPIO2, + .dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3, + .jd_src = RT5682_JD1, +}; + struct rt5682_priv { struct snd_soc_component *component; struct rt5682_platform_data pdata; @@ -2536,6 +2542,8 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, rt5682); + rt5682->pdata = i2s_default_platform_data; + if (pdata) rt5682->pdata = *pdata; else diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index add18d6d77da..a6a4748c97f9 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -116,6 +116,13 @@ enum { I2S_LRCLK_STRENGTH_HIGH, }; +enum { + I2S_SCLK_STRENGTH_DISABLE, + I2S_SCLK_STRENGTH_LOW, + I2S_SCLK_STRENGTH_MEDIUM, + I2S_SCLK_STRENGTH_HIGH, +}; + /* sgtl5000 private structure in codec */ struct sgtl5000_priv { int sysclk; /* sysclk rate */ @@ -129,6 +136,7 @@ struct sgtl5000_priv { u8 micbias_resistor; u8 micbias_voltage; u8 lrclk_strength; + u8 sclk_strength; }; /* @@ -1302,7 +1310,9 @@ static int sgtl5000_probe(struct snd_soc_component *component) SGTL5000_DAC_MUTE_RIGHT | SGTL5000_DAC_MUTE_LEFT); - reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | 0x5f); + reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | + (sgtl5000->sclk_strength) << SGTL5000_PAD_I2S_SCLK_SHIFT | + 0x1f); snd_soc_component_write(component, SGTL5000_CHIP_PAD_STRENGTH, reg); snd_soc_component_write(component, SGTL5000_CHIP_ANA_CTRL, @@ -1542,6 +1552,13 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, sgtl5000->lrclk_strength = value; } + sgtl5000->sclk_strength = I2S_SCLK_STRENGTH_LOW; + if (!of_property_read_u32(np, "sclk-strength", &value)) { + if (value > I2S_SCLK_STRENGTH_HIGH) + value = I2S_SCLK_STRENGTH_LOW; + sgtl5000->sclk_strength = value; + } + /* Ensure sgtl5000 will start with sane register values */ sgtl5000_fill_defaults(client); diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 501a4e73b185..464a4d7873bb 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -1,31 +1,17 @@ -/* - * File: sound/soc/codecs/ssm2602.c - * Author: Cliff Cai <Cliff.Cai@analog.com> - * - * Created: Tue June 06 2008 - * Description: Driver for ssm2602 sound chip - * - * Modified: - * Copyright 2008 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - +// SPDX-License-Identifier: GPL-2.0-or-later +// +// File: sound/soc/codecs/ssm2602.c +// Author: Cliff Cai <Cliff.Cai@analog.com> +// +// Created: Tue June 06 2008 +// Description: Driver for ssm2602 sound chip +// +// Modified: +// Copyright 2008 Analog Devices Inc. +// +// Bugs: Enter bugs at http://blackfin.uclinux.org/ + +#include <linux/delay.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -111,7 +97,6 @@ SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1, SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0), -SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1), }; /* Output Mixer */ @@ -121,10 +106,31 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), }; +static const struct snd_kcontrol_new mic_ctl = + SOC_DAPM_SINGLE("Switch", SSM2602_APANA, 1, 1, 1); + /* Input mux */ static const struct snd_kcontrol_new ssm2602_input_mux_controls = SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]); +static int ssm2602_mic_switch_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + /* + * According to the ssm2603 data sheet (control register sequencing), + * the digital core should be activated only after all necessary bits + * in the power register are enabled, and a delay determined by the + * decoupling capacitor on the VMID pin has passed. If the digital core + * is activated too early, or even before the ADC is powered up, audible + * artifacts appear at the beginning and end of the recorded signal. + * + * In practice, audible artifacts disappear well over 500 ms. + */ + msleep(500); + + return 0; +} + static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), @@ -146,6 +152,9 @@ SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1), +SND_SOC_DAPM_SWITCH_E("Mic Switch", SSM2602_APANA, 1, 1, &mic_ctl, + ssm2602_mic_switch_event, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_OUTPUT("LHPOUT"), SND_SOC_DAPM_OUTPUT("RHPOUT"), SND_SOC_DAPM_INPUT("MICIN"), @@ -178,9 +187,11 @@ static const struct snd_soc_dapm_route ssm2602_routes[] = { {"LHPOUT", NULL, "Output Mixer"}, {"Input Mux", "Line", "Line Input"}, - {"Input Mux", "Mic", "Mic Bias"}, + {"Input Mux", "Mic", "Mic Switch"}, {"ADC", NULL, "Input Mux"}, + {"Mic Switch", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "MICIN"}, }; diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index c6048d95c6d3..c544a1e35f5e 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1274,8 +1274,9 @@ static int aic31xx_codec_probe(struct snd_soc_component *component) aic31xx->disable_nb[i].nb.notifier_call = aic31xx_regulator_event; aic31xx->disable_nb[i].aic31xx = aic31xx; - ret = regulator_register_notifier(aic31xx->supplies[i].consumer, - &aic31xx->disable_nb[i].nb); + ret = devm_regulator_register_notifier( + aic31xx->supplies[i].consumer, + &aic31xx->disable_nb[i].nb); if (ret) { dev_err(component->dev, "Failed to request regulator notifier: %d\n", @@ -1298,19 +1299,8 @@ static int aic31xx_codec_probe(struct snd_soc_component *component) return 0; } -static void aic31xx_codec_remove(struct snd_soc_component *component) -{ - struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); - int i; - - for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) - regulator_unregister_notifier(aic31xx->supplies[i].consumer, - &aic31xx->disable_nb[i].nb); -} - static const struct snd_soc_component_driver soc_codec_driver_aic31xx = { .probe = aic31xx_codec_probe, - .remove = aic31xx_codec_remove, .set_bias_level = aic31xx_set_bias_level, .controls = common31xx_snd_controls, .num_controls = ARRAY_SIZE(common31xx_snd_controls), diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index f03195d2ab2e..96f1526cb258 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -79,6 +79,32 @@ struct aic32x4_priv { struct device *dev; }; +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Change Mic Bias Registor */ + snd_soc_component_update_bits(component, AIC32X4_MICBIAS, + AIC32x4_MICBIAS_MASK, + AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, AIC32X4_MICBIAS, + AIC32x4_MICBIAS_MASK, 0); + printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n", + __func__); + break; + } + + return 0; +} + + static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -450,7 +476,9 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, in3r_to_lmixer_controls), - SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("HPL"), SND_SOC_DAPM_OUTPUT("HPR"), @@ -942,6 +970,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) if (gpio_is_valid(aic32x4->rstn_gpio)) { ndelay(10); gpio_set_value(aic32x4->rstn_gpio, 1); + mdelay(1); } snd_soc_component_write(component, AIC32X4_RESET, 0x01); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index e9df49edbf19..c2d74025bf4b 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -195,6 +195,7 @@ int aic32x4_remove(struct device *dev); /* AIC32X4_MICBIAS */ #define AIC32X4_MICBIAS_LDOIN BIT(3) #define AIC32X4_MICBIAS_2075V 0x60 +#define AIC32x4_MICBIAS_MASK GENMASK(6, 3) /* AIC32X4_LMICPGANIN */ #define AIC32X4_LMICPGANIN_IN2R_10K 0x10 diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 6aa0edf8c5ef..283583d1db60 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1615,13 +1615,14 @@ static int aic3x_probe(struct snd_soc_component *component) for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) { aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event; aic3x->disable_nb[i].aic3x = aic3x; - ret = regulator_register_notifier(aic3x->supplies[i].consumer, - &aic3x->disable_nb[i].nb); + ret = devm_regulator_register_notifier( + aic3x->supplies[i].consumer, + &aic3x->disable_nb[i].nb); if (ret) { dev_err(component->dev, "Failed to request regulator notifier: %d\n", ret); - goto err_notif; + return ret; } } @@ -1679,29 +1680,11 @@ static int aic3x_probe(struct snd_soc_component *component) aic3x_add_widgets(component); return 0; - -err_notif: - while (i--) - regulator_unregister_notifier(aic3x->supplies[i].consumer, - &aic3x->disable_nb[i].nb); - return ret; -} - -static void aic3x_remove(struct snd_soc_component *component) -{ - struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); - int i; - - list_del(&aic3x->list); - for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) - regulator_unregister_notifier(aic3x->supplies[i].consumer, - &aic3x->disable_nb[i].nb); } static const struct snd_soc_component_driver soc_component_dev_aic3x = { .set_bias_level = aic3x_set_bias_level, .probe = aic3x_probe, - .remove = aic3x_remove, .controls = aic3x_snd_controls, .num_controls = ARRAY_SIZE(aic3x_snd_controls), .dapm_widgets = aic3x_dapm_widgets, diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index 7396a6e5277e..27b8c6ba72fa 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -389,7 +389,7 @@ static int dac_event(struct snd_soc_dapm_widget *w, mutex_lock(&tscs42xx->coeff_ram_lock); - if (tscs42xx->coeff_ram_synced == false) { + if (!tscs42xx->coeff_ram_synced) { ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00, COEFF_RAM_COEFF_COUNT); if (ret < 0) diff --git a/sound/soc/codecs/wcd-clsh-v2.c b/sound/soc/codecs/wcd-clsh-v2.c new file mode 100644 index 000000000000..c397d713f01a --- /dev/null +++ b/sound/soc/codecs/wcd-clsh-v2.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +// Copyright (c) 2017-2018, Linaro Limited + +#include <linux/slab.h> +#include <sound/soc.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include "wcd9335.h" +#include "wcd-clsh-v2.h" + +struct wcd_clsh_ctrl { + int state; + int mode; + int flyback_users; + int buck_users; + int clsh_users; + int codec_version; + struct snd_soc_component *comp; +}; + +/* Class-H registers for codecs from and above WCD9335 */ +#define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0xB, 0x42) +#define WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK BIT(6) +#define WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE BIT(6) +#define WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE 0 +#define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0xB, 0x56) +#define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0xB, 0x6A) +#define WCD9XXX_A_CDC_CLSH_K1_MSB WCD9335_REG(0xC, 0x08) +#define WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK GENMASK(3, 0) +#define WCD9XXX_A_CDC_CLSH_K1_LSB WCD9335_REG(0xC, 0x09) +#define WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK GENMASK(7, 0) +#define WCD9XXX_A_ANA_RX_SUPPLIES WCD9335_REG(0x6, 0x08) +#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK BIT(1) +#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H 0 +#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB BIT(1) +#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK BIT(2) +#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA BIT(2) +#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT 0 +#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK BIT(3) +#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA BIT(3) +#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT 0 +#define WCD9XXX_A_ANA_RX_VNEG_EN_MASK BIT(6) +#define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT 6 +#define WCD9XXX_A_ANA_RX_VNEG_ENABLE BIT(6) +#define WCD9XXX_A_ANA_RX_VNEG_DISABLE 0 +#define WCD9XXX_A_ANA_RX_VPOS_EN_MASK BIT(7) +#define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT 7 +#define WCD9XXX_A_ANA_RX_VPOS_ENABLE BIT(7) +#define WCD9XXX_A_ANA_RX_VPOS_DISABLE 0 +#define WCD9XXX_A_ANA_HPH WCD9335_REG(0x6, 0x09) +#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK GENMASK(3, 2) +#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA 0x08 +#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP 0x04 +#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL 0x0 +#define WCD9XXX_A_CDC_CLSH_CRC WCD9335_REG(0xC, 0x01) +#define WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK BIT(0) +#define WCD9XXX_A_CDC_CLSH_CRC_CLK_ENABLE BIT(0) +#define WCD9XXX_A_CDC_CLSH_CRC_CLK_DISABLE 0 +#define WCD9XXX_FLYBACK_EN WCD9335_REG(0x6, 0xA4) +#define WCD9XXX_FLYBACK_EN_DELAY_SEL_MASK GENMASK(6, 5) +#define WCD9XXX_FLYBACK_EN_DELAY_26P25_US 0x40 +#define WCD9XXX_FLYBACK_EN_RESET_BY_EXT_MASK BIT(4) +#define WCD9XXX_FLYBACK_EN_PWDN_WITHOUT_DELAY BIT(4) +#define WCD9XXX_FLYBACK_EN_PWDN_WITH_DELAY 0 +#define WCD9XXX_RX_BIAS_FLYB_BUFF WCD9335_REG(0x6, 0xC7) +#define WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4) +#define WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(0, 3) +#define WCD9XXX_HPH_L_EN WCD9335_REG(0x6, 0xD3) +#define WCD9XXX_HPH_CONST_SEL_L_MASK GENMASK(7, 3) +#define WCD9XXX_HPH_CONST_SEL_BYPASS 0 +#define WCD9XXX_HPH_CONST_SEL_LP_PATH 0x40 +#define WCD9XXX_HPH_CONST_SEL_HQ_PATH 0x80 +#define WCD9XXX_HPH_R_EN WCD9335_REG(0x6, 0xD6) +#define WCD9XXX_HPH_REFBUFF_UHQA_CTL WCD9335_REG(0x6, 0xDD) +#define WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK GENMASK(2, 0) +#define WCD9XXX_CLASSH_CTRL_VCL_2 WCD9335_REG(0x6, 0x9B) +#define WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK GENMASK(5, 4) +#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM 0x20 +#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM 0x0 +#define WCD9XXX_CDC_RX1_RX_PATH_CTL WCD9335_REG(0xB, 0x55) +#define WCD9XXX_CDC_RX2_RX_PATH_CTL WCD9335_REG(0xB, 0x69) +#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0xD, 0x41) +#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0) +#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_11P3_EN_MASK BIT(1) +#define WCD9XXX_CLASSH_CTRL_CCL_1 WCD9335_REG(0x6, 0x9C) +#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK GENMASK(7, 4) +#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA 0x50 +#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA 0x30 + +#define CLSH_REQ_ENABLE true +#define CLSH_REQ_DISABLE false +#define WCD_USLEEP_RANGE 50 + +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, +}; + +static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl, + bool enable) +{ + struct snd_soc_component *comp = ctrl->comp; + + if ((enable && ++ctrl->clsh_users == 1) || + (!enable && --ctrl->clsh_users == 0)) + snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC, + WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK, + enable); + if (ctrl->clsh_users < 0) + ctrl->clsh_users = 0; +} + +static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp) +{ + return snd_soc_component_read32(comp, WCD9XXX_A_CDC_CLSH_CRC) & + WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK; +} + +static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp, + int mode) +{ + /* set to HIFI */ + if (mode == CLS_H_HIFI) + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK, + WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA); + else + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK, + WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT); +} + +static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp, + int mode) +{ + /* set to HIFI */ + if (mode == CLS_H_HIFI) + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK, + WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA); + else + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK, + WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT); +} + +static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl, + int mode, + bool enable) +{ + struct snd_soc_component *comp = ctrl->comp; + + /* enable/disable buck */ + if ((enable && (++ctrl->buck_users == 1)) || + (!enable && (--ctrl->buck_users == 0))) + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VPOS_EN_MASK, + enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT); + /* + * 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 wcd_clsh_ctrl *ctrl, + int mode, + bool enable) +{ + struct snd_soc_component *comp = ctrl->comp; + + /* enable/disable flyback */ + if ((enable && (++ctrl->flyback_users == 1)) || + (!enable && (--ctrl->flyback_users == 0))) { + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VNEG_EN_MASK, + enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + } + /* + * 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 wcd_clsh_ctrl *ctrl, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + int val = 0; + + switch (mode) { + case CLS_H_NORMAL: + case CLS_AB: + val = WCD9XXX_HPH_CONST_SEL_BYPASS; + break; + case CLS_H_HIFI: + val = WCD9XXX_HPH_CONST_SEL_HQ_PATH; + break; + case CLS_H_LP: + val = WCD9XXX_HPH_CONST_SEL_LP_PATH; + break; + } + + snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN, + WCD9XXX_HPH_CONST_SEL_L_MASK, + val); + + snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN, + WCD9XXX_HPH_CONST_SEL_L_MASK, + val); +} + +static void wcd_clsh_set_hph_mode(struct snd_soc_component *comp, + int mode) +{ + int val = 0, gain = 0, res_val; + int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; + + res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM; + switch (mode) { + case CLS_H_NORMAL: + res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM; + val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL; + gain = DAC_GAIN_0DB; + ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; + break; + case CLS_AB: + val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL; + gain = DAC_GAIN_0DB; + ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; + break; + case CLS_H_HIFI: + val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA; + gain = DAC_GAIN_M0P2DB; + ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; + break; + case CLS_H_LP: + val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP; + ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA; + break; + } + + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH, + WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val); + snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2, + WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK, + res_val); + if (mode != CLS_H_LP) + snd_soc_component_update_bits(comp, + WCD9XXX_HPH_REFBUFF_UHQA_CTL, + WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK, + gain); + snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1, + WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK, + ipeak); +} + +static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp, + int mode) +{ + + snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF, + WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A); + snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF, + WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A); + /* 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_component *comp, + int mode) +{ + if (mode == CLS_AB) + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK, + WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB); + else + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK, + WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H); +} + +static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (mode != CLS_AB) { + dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n", + __func__, mode); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(comp, mode); + wcd_clsh_set_buck_mode(comp, mode); + wcd_clsh_set_flyback_mode(comp, mode); + wcd_clsh_flyback_ctrl(ctrl, mode, true); + wcd_clsh_set_flyback_current(comp, mode); + wcd_clsh_buck_ctrl(ctrl, mode, true); + } else { + wcd_clsh_buck_ctrl(ctrl, mode, false); + wcd_clsh_flyback_ctrl(ctrl, mode, false); + wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (mode == CLS_H_NORMAL) { + dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB) { + wcd_enable_clsh_block(ctrl, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_CLSH_K1_MSB, + WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK, + 0x00); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_CLSH_K1_LSB, + WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK, + 0xC0); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); + } + wcd_clsh_set_buck_regulator_mode(comp, mode); + wcd_clsh_set_flyback_mode(comp, mode); + wcd_clsh_flyback_ctrl(ctrl, mode, true); + wcd_clsh_set_flyback_current(comp, mode); + wcd_clsh_set_buck_mode(comp, mode); + wcd_clsh_buck_ctrl(ctrl, mode, true); + wcd_clsh_set_hph_mode(comp, mode); + wcd_clsh_set_gain_path(ctrl, mode); + } else { + wcd_clsh_set_hph_mode(comp, CLS_H_NORMAL); + + if (mode != CLS_AB) { + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); + wcd_enable_clsh_block(ctrl, false); + } + /* buck and flyback set to default mode and disable */ + wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (mode == CLS_H_NORMAL) { + dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB) { + wcd_enable_clsh_block(ctrl, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_CLSH_K1_MSB, + WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK, + 0x00); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_CLSH_K1_LSB, + WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK, + 0xC0); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); + } + wcd_clsh_set_buck_regulator_mode(comp, mode); + wcd_clsh_set_flyback_mode(comp, mode); + wcd_clsh_flyback_ctrl(ctrl, mode, true); + wcd_clsh_set_flyback_current(comp, mode); + wcd_clsh_set_buck_mode(comp, mode); + wcd_clsh_buck_ctrl(ctrl, mode, true); + wcd_clsh_set_hph_mode(comp, mode); + wcd_clsh_set_gain_path(ctrl, mode); + } else { + wcd_clsh_set_hph_mode(comp, CLS_H_NORMAL); + + if (mode != CLS_AB) { + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); + wcd_enable_clsh_block(ctrl, false); + } + /* set buck and flyback to Default Mode */ + wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (mode != CLS_H_NORMAL) { + dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n", + __func__, mode); + return; + } + + if (is_enable) { + wcd_enable_clsh_block(ctrl, true); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); + wcd_clsh_set_buck_mode(comp, mode); + wcd_clsh_set_flyback_mode(comp, mode); + wcd_clsh_flyback_ctrl(ctrl, mode, true); + wcd_clsh_set_flyback_current(comp, mode); + wcd_clsh_buck_ctrl(ctrl, mode, true); + } else { + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); + wcd_enable_clsh_block(ctrl, false); + wcd_clsh_buck_ctrl(ctrl, mode, false); + wcd_clsh_flyback_ctrl(ctrl, mode, false); + wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); + } +} + +static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + switch (req_state) { + case WCD_CLSH_STATE_EAR: + wcd_clsh_state_ear(ctrl, req_state, is_enable, mode); + break; + case WCD_CLSH_STATE_HPHL: + wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode); + break; + case WCD_CLSH_STATE_HPHR: + wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode); + break; + break; + case WCD_CLSH_STATE_LO: + wcd_clsh_state_lo(ctrl, req_state, is_enable, mode); + break; + default: + break; + } + + return 0; +} + +/* + * 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(int 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_LO: + return true; + default: + return false; + }; +} + +/* + * Function: wcd_clsh_fsm + * Params: ctrl, 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. ctrl will contain current + * class h state information + */ +int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, + enum wcd_clsh_event clsh_event, + int nstate, + enum wcd_clsh_mode mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (nstate == ctrl->state) + return 0; + + if (!wcd_clsh_is_state_valid(nstate)) { + dev_err(comp->dev, "Class-H not a valid new state:\n"); + return -EINVAL; + } + + switch (clsh_event) { + case WCD_CLSH_EVENT_PRE_DAC: + _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode); + break; + case WCD_CLSH_EVENT_POST_PA: + _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode); + break; + } + + ctrl->state = nstate; + ctrl->mode = mode; + + return 0; +} + +int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl) +{ + return ctrl->state; +} + +struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp, + int version) +{ + struct wcd_clsh_ctrl *ctrl; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return ERR_PTR(-ENOMEM); + + ctrl->state = WCD_CLSH_STATE_IDLE; + ctrl->comp = comp; + + return ctrl; +} + +void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl) +{ + kfree(ctrl); +} diff --git a/sound/soc/codecs/wcd-clsh-v2.h b/sound/soc/codecs/wcd-clsh-v2.h new file mode 100644 index 000000000000..a902f9893467 --- /dev/null +++ b/sound/soc/codecs/wcd-clsh-v2.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _WCD_CLSH_V2_H_ +#define _WCD_CLSH_V2_H_ +#include <sound/soc.h> + +enum wcd_clsh_event { + WCD_CLSH_EVENT_PRE_DAC = 1, + WCD_CLSH_EVENT_POST_PA, +}; + +/* + * 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 0 +#define WCD_CLSH_STATE_EAR BIT(0) +#define WCD_CLSH_STATE_HPHL BIT(1) +#define WCD_CLSH_STATE_HPHR BIT(2) +#define WCD_CLSH_STATE_LO BIT(3) +#define WCD_CLSH_STATE_MAX 4 +#define NUM_CLSH_STATES_V2 BIT(WCD_CLSH_STATE_MAX) + +enum wcd_clsh_mode { + CLS_H_NORMAL = 0, /* Class-H Default */ + CLS_H_HIFI, /* Class-H HiFi */ + CLS_H_LP, /* Class-H Low Power */ + CLS_AB, /* Class-AB */ + CLS_H_LOHIFI, /* LoHIFI */ + CLS_NONE, /* None of the above modes */ +}; + +struct wcd_clsh_ctrl; + +extern struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc( + struct snd_soc_component *component, + int version); +extern void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl); +extern int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl); +extern int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, + enum wcd_clsh_event event, + int state, + enum wcd_clsh_mode mode); + +#endif /* _WCD_CLSH_V2_H_ */ diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c new file mode 100644 index 000000000000..981f88a5f615 --- /dev/null +++ b/sound/soc/codecs/wcd9335.c @@ -0,0 +1,5244 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +// Copyright (c) 2017-2018, Linaro Limited + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/bitops.h> +#include <linux/regulator/consumer.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/slimbus.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <sound/tlv.h> +#include <sound/info.h> +#include "wcd9335.h" +#include "wcd-clsh-v2.h" + +#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_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* slave port water mark level + * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes) + */ +#define SLAVE_PORT_WATER_MARK_6BYTES 0 +#define SLAVE_PORT_WATER_MARK_9BYTES 1 +#define SLAVE_PORT_WATER_MARK_12BYTES 2 +#define SLAVE_PORT_WATER_MARK_15BYTES 3 +#define SLAVE_PORT_WATER_MARK_SHIFT 1 +#define SLAVE_PORT_ENABLE 1 +#define SLAVE_PORT_DISABLE 0 +#define WCD9335_SLIM_WATER_MARK_VAL \ + ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \ + (SLAVE_PORT_ENABLE)) + +#define WCD9335_SLIM_NUM_PORT_REG 3 +#define WCD9335_SLIM_PGD_PORT_INT_TX_EN0 (WCD9335_SLIM_PGD_PORT_INT_EN0 + 2) + +#define WCD9335_MCLK_CLK_12P288MHZ 12288000 +#define WCD9335_MCLK_CLK_9P6MHZ 9600000 + +#define WCD9335_SLIM_CLOSE_TIMEOUT 1000 +#define WCD9335_SLIM_IRQ_OVERFLOW (1 << 0) +#define WCD9335_SLIM_IRQ_UNDERFLOW (1 << 1) +#define WCD9335_SLIM_IRQ_PORT_CLOSED (1 << 2) + +#define WCD9335_NUM_INTERPOLATORS 9 +#define WCD9335_RX_START 16 +#define WCD9335_SLIM_CH_START 128 +#define WCD9335_MAX_MICBIAS 4 +#define WCD9335_MAX_VALID_ADC_MUX 13 +#define WCD9335_INVALID_ADC_MUX 9 + +#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 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_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 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 WCD9335_SLIM_RX_CH(p) \ + {.port = p + WCD9335_RX_START, .shift = p,} + +#define WCD9335_SLIM_TX_CH(p) \ + {.port = p, .shift = p,} + +/* vout step value */ +#define WCD9335_CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25) + +#define WCD9335_INTERPOLATOR_PATH(id) \ + {"RX INT" #id "_1 MIX1 INP0", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_2 MUX", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_2 MUX", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_2 MUX", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_2 MUX", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_2 MUX", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_2 MUX", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_2 MUX", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_2 MUX", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP0"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP1"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP2"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_2 MUX"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_1 MIX1"}, \ + {"RX INT" #id " MIX2", NULL, "RX INT" #id " SEC MIX"}, \ + {"RX INT" #id " INTERP", NULL, "RX INT" #id " MIX2"} + +#define WCD9335_ADC_MUX_PATH(id) \ + {"AIF1_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \ + {"AIF2_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \ + {"AIF3_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \ + {"SLIM TX" #id " MUX", "DEC" #id, "ADC MUX" #id}, \ + {"ADC MUX" #id, "DMIC", "DMIC MUX" #id}, \ + {"ADC MUX" #id, "AMIC", "AMIC MUX" #id}, \ + {"DMIC MUX" #id, "DMIC0", "DMIC0"}, \ + {"DMIC MUX" #id, "DMIC1", "DMIC1"}, \ + {"DMIC MUX" #id, "DMIC2", "DMIC2"}, \ + {"DMIC MUX" #id, "DMIC3", "DMIC3"}, \ + {"DMIC MUX" #id, "DMIC4", "DMIC4"}, \ + {"DMIC MUX" #id, "DMIC5", "DMIC5"}, \ + {"AMIC MUX" #id, "ADC1", "ADC1"}, \ + {"AMIC MUX" #id, "ADC2", "ADC2"}, \ + {"AMIC MUX" #id, "ADC3", "ADC3"}, \ + {"AMIC MUX" #id, "ADC4", "ADC4"}, \ + {"AMIC MUX" #id, "ADC5", "ADC5"}, \ + {"AMIC MUX" #id, "ADC6", "ADC6"} + +enum { + WCD9335_RX0 = 0, + WCD9335_RX1, + WCD9335_RX2, + WCD9335_RX3, + WCD9335_RX4, + WCD9335_RX5, + WCD9335_RX6, + WCD9335_RX7, + WCD9335_RX8, + WCD9335_RX9, + WCD9335_RX10, + WCD9335_RX11, + WCD9335_RX12, + WCD9335_RX_MAX, +}; + +enum { + WCD9335_TX0 = 0, + WCD9335_TX1, + WCD9335_TX2, + WCD9335_TX3, + WCD9335_TX4, + WCD9335_TX5, + WCD9335_TX6, + WCD9335_TX7, + WCD9335_TX8, + WCD9335_TX9, + WCD9335_TX10, + WCD9335_TX11, + WCD9335_TX12, + WCD9335_TX13, + WCD9335_TX14, + WCD9335_TX15, + WCD9335_TX_MAX, +}; + +enum { + SIDO_SOURCE_INTERNAL = 0, + SIDO_SOURCE_RCO_BG, +}; + +enum wcd9335_sido_voltage { + SIDO_VOLTAGE_SVS_MV = 950, + SIDO_VOLTAGE_NOMINAL_MV = 1100, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + NUM_CODEC_DAIS, +}; + +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 { + 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 { + 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, + +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3, + INTERP_LO4, + INTERP_SPKR1, + INTERP_SPKR2, +}; + +enum wcd_clock_type { + WCD_CLK_OFF, + WCD_CLK_RCO, + WCD_CLK_MCLK, +}; + +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, +}; + +struct wcd9335_slim_ch { + u32 ch_num; + u16 port; + u16 shift; + struct list_head list; +}; + +struct wcd_slim_codec_dai_data { + struct list_head slim_ch_list; + struct slim_stream_config sconfig; + struct slim_stream_runtime *sruntime; +}; + +struct wcd9335_codec { + struct device *dev; + struct clk *mclk; + struct clk *native_clk; + u32 mclk_rate; + u8 version; + + struct slim_device *slim; + struct slim_device *slim_ifc_dev; + struct regmap *regmap; + struct regmap *if_regmap; + struct regmap_irq_chip_data *irq_data; + + struct wcd9335_slim_ch rx_chs[WCD9335_RX_MAX]; + struct wcd9335_slim_ch tx_chs[WCD9335_TX_MAX]; + u32 num_rx_port; + u32 num_tx_port; + + int sido_input_src; + enum wcd9335_sido_voltage sido_voltage; + + struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS]; + struct snd_soc_component *component; + + int master_bias_users; + int clk_mclk_users; + int clk_rco_users; + int sido_ccl_cnt; + enum wcd_clock_type clk_type; + + struct wcd_clsh_ctrl *clsh_ctrl; + u32 hph_mode; + int prim_int_users[WCD9335_NUM_INTERPOLATORS]; + + int comp_enabled[COMPANDER_MAX]; + + int intr1; + int reset_gpio; + struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; + + unsigned int rx_port_value; + unsigned int tx_port_value; + int hph_l_gain; + int hph_r_gain; + u32 rx_bias_count; + + /*TX*/ + int micb_ref[WCD9335_MAX_MICBIAS]; + int pullup_ref[WCD9335_MAX_MICBIAS]; + + int dmic_0_1_clk_cnt; + int dmic_2_3_clk_cnt; + int dmic_4_5_clk_cnt; + int dmic_sample_rate; + int mad_dmic_sample_rate; + + int native_clk_users; +}; + +struct wcd9335_irq { + int irq; + irqreturn_t (*handler)(int irq, void *data); + char *name; +}; + +static const struct wcd9335_slim_ch wcd9335_tx_chs[WCD9335_TX_MAX] = { + WCD9335_SLIM_TX_CH(0), + WCD9335_SLIM_TX_CH(1), + WCD9335_SLIM_TX_CH(2), + WCD9335_SLIM_TX_CH(3), + WCD9335_SLIM_TX_CH(4), + WCD9335_SLIM_TX_CH(5), + WCD9335_SLIM_TX_CH(6), + WCD9335_SLIM_TX_CH(7), + WCD9335_SLIM_TX_CH(8), + WCD9335_SLIM_TX_CH(9), + WCD9335_SLIM_TX_CH(10), + WCD9335_SLIM_TX_CH(11), + WCD9335_SLIM_TX_CH(12), + WCD9335_SLIM_TX_CH(13), + WCD9335_SLIM_TX_CH(14), + WCD9335_SLIM_TX_CH(15), +}; + +static const struct wcd9335_slim_ch wcd9335_rx_chs[WCD9335_RX_MAX] = { + WCD9335_SLIM_RX_CH(0), /* 16 */ + WCD9335_SLIM_RX_CH(1), /* 17 */ + WCD9335_SLIM_RX_CH(2), + WCD9335_SLIM_RX_CH(3), + WCD9335_SLIM_RX_CH(4), + WCD9335_SLIM_RX_CH(5), + WCD9335_SLIM_RX_CH(6), + WCD9335_SLIM_RX_CH(7), + WCD9335_SLIM_RX_CH(8), + WCD9335_SLIM_RX_CH(9), + WCD9335_SLIM_RX_CH(10), + WCD9335_SLIM_RX_CH(11), + WCD9335_SLIM_RX_CH(12), +}; + +struct interp_sample_rate { + int rate; + int rate_val; +}; + +static struct interp_sample_rate int_mix_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +static struct interp_sample_rate int_prim_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 */ +}; + +struct wcd9335_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +static const struct wcd9335_reg_mask_val wcd9335_codec_reg_init[] = { + /* 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_DIFF_LO_COM_PA_FREQ, 0x70, 0x40}, + {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}, + {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, 0x5A}, + {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}, +}; + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const char * const rx_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_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 rx_hph_mode_mux_text[] = { + "Class H Invalid", "Class-H Hi-Fi", "Class-H Low Power", "Class-AB", + "Class-H Hi-Fi Low Power" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", +}; + +static const char * const 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 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 DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); +static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0); + +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 soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct soc_enum slim_rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text); + +static const struct soc_enum rx_int0_2_mux_chain_enum = + SOC_ENUM_SINGLE(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 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_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 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_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_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 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 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 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_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 tx_dmic_mux0 = + SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum); + +static const struct snd_kcontrol_new tx_dmic_mux1 = + SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum); + +static const struct snd_kcontrol_new tx_dmic_mux2 = + SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum); + +static const struct snd_kcontrol_new tx_dmic_mux3 = + SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum); + +static const struct snd_kcontrol_new tx_dmic_mux4 = + SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum); + +static const struct snd_kcontrol_new tx_dmic_mux5 = + SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum); + +static const struct snd_kcontrol_new tx_dmic_mux6 = + SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum); + +static const struct snd_kcontrol_new tx_dmic_mux7 = + SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum); + +static const struct snd_kcontrol_new tx_dmic_mux8 = + SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic_mux0 = + SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum); + +static const struct snd_kcontrol_new tx_amic_mux1 = + SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum); + +static const struct snd_kcontrol_new tx_amic_mux2 = + SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum); + +static const struct snd_kcontrol_new tx_amic_mux3 = + SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum); + +static const struct snd_kcontrol_new tx_amic_mux4 = + SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum); + +static const struct snd_kcontrol_new tx_amic_mux5 = + SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum); + +static const struct snd_kcontrol_new tx_amic_mux6 = + SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum); + +static const struct snd_kcontrol_new tx_amic_mux7 = + SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum); + +static const struct snd_kcontrol_new tx_amic_mux8 = + SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum); + +static const struct snd_kcontrol_new 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 int slim_rx_mux_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev); + + ucontrol->value.enumerated.item[0] = wcd->rx_port_value; + + return 0; +} + +static int slim_rx_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(w->dapm->dev); + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 port_id = w->shift; + + wcd->rx_port_value = ucontrol->value.enumerated.item[0]; + + switch (wcd->rx_port_value) { + case 0: + list_del_init(&wcd->rx_chs[port_id].list); + break; + case 1: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF1_PB].slim_ch_list); + break; + case 2: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF2_PB].slim_ch_list); + break; + case 3: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF3_PB].slim_ch_list); + break; + case 4: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF4_PB].slim_ch_list); + break; + default: + dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value); + goto err; + } + + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value, + e, update); + + return 0; +err: + return -EINVAL; +} + +static int slim_tx_mixer_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev); + + ucontrol->value.integer.value[0] = wcd->tx_port_value; + + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(widget->dapm->dev); + struct snd_soc_dapm_update *update = NULL; + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int enable = ucontrol->value.integer.value[0]; + int dai_id = widget->shift; + int port_id = mixer->shift; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(wcd->tx_port_value & BIT(port_id))) { + wcd->tx_port_value |= BIT(port_id); + list_add_tail(&wcd->tx_chs[port_id].list, + &wcd->dai[dai_id].slim_ch_list); + } else if (!enable && (wcd->tx_port_value & BIT(port_id))) { + wcd->tx_port_value &= ~BIT(port_id); + list_del_init(&wcd->tx_chs[port_id].list); + } + break; + default: + dev_err(wcd->dev, "Unknown AIF %d\n", dai_id); + return -EINVAL; + } + + snd_soc_dapm_mixer_update_power(widget->dapm, kc, enable, update); + + return 0; +} + +static const struct snd_kcontrol_new slim_rx_mux[WCD9335_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 aif1_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9335_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9335_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9335_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9335_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9335_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, WCD9335_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9335_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9335_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9335_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9335_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, WCD9335_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static int wcd9335_put_dec_enum(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct soc_enum *e = (struct soc_enum *)kc->private_value; + unsigned int val, reg, sel; + + val = ucontrol->value.enumerated.item[0]; + + switch (e->reg) { + case WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + reg = WCD9335_CDC_TX0_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + reg = WCD9335_CDC_TX1_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + reg = WCD9335_CDC_TX2_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + reg = WCD9335_CDC_TX3_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0: + reg = WCD9335_CDC_TX4_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0: + reg = WCD9335_CDC_TX5_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0: + reg = WCD9335_CDC_TX6_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0: + reg = WCD9335_CDC_TX7_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0: + reg = WCD9335_CDC_TX8_TX_PATH_CFG0; + break; + default: + return -EINVAL; + } + + /* AMIC: 0, DMIC: 1 */ + sel = val ? WCD9335_CDC_TX_ADC_AMIC_SEL : WCD9335_CDC_TX_ADC_DMIC_SEL; + snd_soc_component_update_bits(component, reg, + WCD9335_CDC_TX_ADC_AMIC_DMIC_SEL_MASK, + sel); + + return snd_soc_dapm_put_enum_double(kc, ucontrol); +} + +static int wcd9335_int_dem_inp_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_component *component; + int reg, val; + + component = snd_soc_dapm_kcontrol_component(kc); + val = ucontrol->value.enumerated.item[0]; + + if (e->reg == WCD9335_CDC_RX0_RX_PATH_SEC0) + reg = WCD9335_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX1_RX_PATH_SEC0) + reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX2_RX_PATH_SEC0) + reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + else + return -EINVAL; + + /* Set Look Ahead Delay */ + snd_soc_component_update_bits(component, reg, + WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN_MASK, + val ? WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN : 0); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kc, ucontrol); +} + +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, + wcd9335_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, + wcd9335_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, + wcd9335_int_dem_inp_mux_put); + +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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_put_dec_enum); + +static int wcd9335_set_mix_interpolator_rate(struct snd_soc_dai *dai, + int rate_val, + u32 rate) +{ + struct snd_soc_component *component = dai->component; + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + struct wcd9335_slim_ch *ch; + int val, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + for (j = 0; j < WCD9335_NUM_INTERPOLATORS; j++) { + val = snd_soc_component_read32(component, + WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(j)) & + WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if (val == (ch->shift + INTn_2_INP_SEL_RX0)) + snd_soc_component_update_bits(component, + WCD9335_CDC_RX_PATH_MIX_CTL(j), + WCD9335_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + + return 0; +} + +static int wcd9335_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_val, + u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + struct wcd9335_slim_ch *ch; + u8 cfg0, cfg1, inp0_sel, inp1_sel, inp2_sel; + int inp, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + inp = ch->shift + INTn_1_MIX_INP_SEL_RX0; + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < WCD9335_NUM_INTERPOLATORS; j++) { + cfg0 = snd_soc_component_read32(comp, + WCD9335_CDC_RX_INP_MUX_RX_INT_CFG0(j)); + cfg1 = snd_soc_component_read32(comp, + WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(j)); + + inp0_sel = cfg0 & + WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp1_sel = (cfg0 >> 4) & + WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp2_sel = (cfg1 >> 4) & + WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if ((inp0_sel == inp) || (inp1_sel == inp) || + (inp2_sel == inp)) { + /* rate is in Hz */ + if ((j == 0) && (rate == 44100)) + dev_info(wcd->dev, + "Cannot set 44.1KHz on INT0\n"); + else + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX_PATH_CTL(j), + WCD9335_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + } + + return 0; +} + +static int wcd9335_set_interpolator_rate(struct snd_soc_dai *dai, u32 rate) +{ + int i; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_rate_val); i++) { + if (rate == int_mix_rate_val[i].rate) { + wcd9335_set_mix_interpolator_rate(dai, + int_mix_rate_val[i].rate_val, rate); + break; + } + } + + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_rate_val); i++) { + if (rate == int_prim_rate_val[i].rate) { + wcd9335_set_prim_interpolator_rate(dai, + int_prim_rate_val[i].rate_val, rate); + break; + } + } + + return 0; +} + +static int wcd9335_slim_set_hw_params(struct wcd9335_codec *wcd, + struct wcd_slim_codec_dai_data *dai_data, + int direction) +{ + struct list_head *slim_ch_list = &dai_data->slim_ch_list; + struct slim_stream_config *cfg = &dai_data->sconfig; + struct wcd9335_slim_ch *ch; + u16 payload = 0; + int ret, i; + + cfg->ch_count = 0; + cfg->direction = direction; + cfg->port_mask = 0; + + /* Configure slave interface device */ + list_for_each_entry(ch, slim_ch_list, list) { + cfg->ch_count++; + payload |= 1 << ch->shift; + cfg->port_mask |= BIT(ch->port); + } + + cfg->chs = kcalloc(cfg->ch_count, sizeof(unsigned int), GFP_KERNEL); + if (!cfg->chs) + return -ENOMEM; + + i = 0; + list_for_each_entry(ch, slim_ch_list, list) { + cfg->chs[i++] = ch->ch_num; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + /* write to interface device */ + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_RX_PORT_MULTI_CHNL_0(ch->port), + payload); + + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_RX_PORT_CFG(ch->port), + WCD9335_SLIM_WATER_MARK_VAL); + if (ret < 0) + goto err; + } else { + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_0(ch->port), + payload & 0x00FF); + if (ret < 0) + goto err; + + /* ports 8,9 */ + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_1(ch->port), + (payload & 0xFF00)>>8); + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_TX_PORT_CFG(ch->port), + WCD9335_SLIM_WATER_MARK_VAL); + + if (ret < 0) + goto err; + } + } + + dai_data->sruntime = slim_stream_allocate(wcd->slim, "WCD9335-SLIM"); + + return 0; + +err: + dev_err(wcd->dev, "Error Setting slim hw params\n"); + kfree(cfg->chs); + cfg->chs = NULL; + + return ret; +} + +static int wcd9335_set_decimator_rate(struct snd_soc_dai *dai, + u8 rate_val, u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp); + u8 shift = 0, shift_val = 0, tx_mux_sel; + struct wcd9335_slim_ch *ch; + int tx_port, tx_port_reg; + int decimator = -1; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + tx_port = ch->port; + if ((tx_port == 12) || (tx_port >= 14)) { + dev_err(wcd->dev, "Invalid SLIM TX%u port DAI ID:%d\n", + 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; + } else { + return -EINVAL; + } + + tx_mux_sel = snd_soc_component_read32(comp, 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) { + snd_soc_component_update_bits(comp, + WCD9335_CDC_TX_PATH_CTL(decimator), + WCD9335_CDC_TX_PATH_CTL_PCM_RATE_MASK, + rate_val); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_err(wcd->dev, "RX_MIX_TX%u going to SLIM TX%u\n", + tx_port, tx_port); + } else { + dev_err(wcd->dev, "ERROR: Invalid decimator: %d\n", + decimator); + return -EINVAL; + } + } + + return 0; +} + +static int wcd9335_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd9335_codec *wcd; + int ret, tx_fs_rate = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wcd9335_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(wcd->dev, "cannot set sample rate: %u\n", + params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16 ... 24: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + } + break; + + case SNDRV_PCM_STREAM_CAPTURE: + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(wcd->dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + + }; + + ret = wcd9335_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(wcd->dev, "Cannot set TX Decimator rate\n"); + return ret; + } + switch (params_width(params)) { + case 16 ... 32: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + break; + default: + dev_err(wcd->dev, "Invalid stream type %d\n", + substream->stream); + return -EINVAL; + }; + + wcd->dai[dai->id].sconfig.rate = params_rate(params); + wcd9335_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream); + + return 0; +} + +static int wcd9335_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct wcd_slim_codec_dai_data *dai_data; + struct wcd9335_codec *wcd; + struct slim_stream_config *cfg; + + wcd = snd_soc_component_get_drvdata(dai->component); + + dai_data = &wcd->dai[dai->id]; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cfg = &dai_data->sconfig; + slim_stream_prepare(dai_data->sruntime, cfg); + slim_stream_enable(dai_data->sruntime); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + slim_stream_unprepare(dai_data->sruntime); + slim_stream_disable(dai_data->sruntime); + break; + default: + break; + } + + return 0; +} + +static int wcd9335_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 wcd9335_codec *wcd; + int i; + + wcd = snd_soc_component_get_drvdata(dai->component); + + if (!tx_slot || !rx_slot) { + dev_err(wcd->dev, "Invalid tx_slot=%p, rx_slot=%p\n", + tx_slot, rx_slot); + return -EINVAL; + } + + wcd->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd->rx_chs[i].list); + } + + wcd->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd->tx_chs[i].list); + } + + return 0; +} + +static int wcd9335_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 wcd9335_slim_ch *ch; + struct wcd9335_codec *wcd; + int i = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(wcd->dev, "Invalid rx_slot %p or rx_num %p\n", + rx_slot, rx_num); + return -EINVAL; + } + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + rx_slot[i++] = ch->ch_num; + + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + if (!tx_slot || !tx_num) { + dev_err(wcd->dev, "Invalid tx_slot %p or tx_num %p\n", + tx_slot, tx_num); + return -EINVAL; + } + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + tx_slot[i++] = ch->ch_num; + + *tx_num = i; + break; + default: + dev_err(wcd->dev, "Invalid DAI ID %x\n", dai->id); + break; + } + + return 0; +} + +static struct snd_soc_dai_ops wcd9335_dai_ops = { + .hw_params = wcd9335_hw_params, + .trigger = wcd9335_trigger, + .set_channel_map = wcd9335_set_channel_map, + .get_channel_map = wcd9335_get_channel_map, +}; + +static struct snd_soc_dai_driver wcd9335_slim_dais[] = { + [0] = { + .name = "wcd9335_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = WCD9335_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd9335_dai_ops, + }, + [1] = { + .name = "wcd9335_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9335_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd9335_dai_ops, + }, + [2] = { + .name = "wcd9335_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = WCD9335_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd9335_dai_ops, + }, + [3] = { + .name = "wcd9335_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9335_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd9335_dai_ops, + }, + [4] = { + .name = "wcd9335_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = WCD9335_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd9335_dai_ops, + }, + [5] = { + .name = "wcd9335_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD9335_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd9335_dai_ops, + }, + [6] = { + .name = "wcd9335_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = WCD9335_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd9335_dai_ops, + }, +}; + +static int wcd9335_get_compander(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + int comp = ((struct soc_mixer_control *)kc->private_value)->shift; + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.integer.value[0] = wcd->comp_enabled[comp]; + return 0; +} + +static int wcd9335_set_compander(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int comp = ((struct soc_mixer_control *) kc->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + int sel; + + wcd->comp_enabled[comp] = value; + sel = value ? WCD9335_HPH_GAIN_SRC_SEL_COMPANDER : + WCD9335_HPH_GAIN_SRC_SEL_REGISTER; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_component_update_bits(component, WCD9335_HPH_L_EN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, sel); + break; + case COMPANDER_2: + snd_soc_component_update_bits(component, WCD9335_HPH_R_EN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, sel); + break; + case COMPANDER_5: + snd_soc_component_update_bits(component, WCD9335_SE_LO_LO3_GAIN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, sel); + break; + case COMPANDER_6: + snd_soc_component_update_bits(component, WCD9335_SE_LO_LO4_GAIN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, sel); + break; + default: + break; + }; + + return 0; +} + +static int wcd9335_rx_hph_mode_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.enumerated.item[0] = wcd->hph_mode; + + return 0; +} + +static int wcd9335_rx_hph_mode_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (mode_val == 0) { + dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n"); + mode_val = CLS_H_HIFI; + } + wcd->hph_mode = mode_val; + + return 0; +} + +static const struct snd_kcontrol_new wcd9335_snd_controls[] = { + /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD9335_CDC_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), + 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), + SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume", + WCD9335_CDC_RX1_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume", + WCD9335_CDC_RX2_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume", + WCD9335_CDC_RX3_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume", + WCD9335_CDC_RX4_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX5 Mix Digital Volume", + WCD9335_CDC_RX5_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX6 Mix Digital Volume", + WCD9335_CDC_RX6_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume", + WCD9335_CDC_RX7_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume", + WCD9335_CDC_RX8_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + 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("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP5 Switch", SND_SOC_NOPM, COMPANDER_5, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP6 Switch", SND_SOC_NOPM, COMPANDER_6, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd9335_rx_hph_mode_get, wcd9335_rx_hph_mode_put), + + /* Gain Controls */ + SOC_SINGLE_TLV("EAR PA Volume", WCD9335_ANA_EAR, 4, 4, 1, + ear_pa_gain), + 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), + + 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), +}; + +static const struct snd_soc_dapm_route wcd9335_audio_map[] = { + {"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"}, + + WCD9335_INTERPOLATOR_PATH(0), + WCD9335_INTERPOLATOR_PATH(1), + WCD9335_INTERPOLATOR_PATH(2), + WCD9335_INTERPOLATOR_PATH(3), + WCD9335_INTERPOLATOR_PATH(4), + WCD9335_INTERPOLATOR_PATH(5), + WCD9335_INTERPOLATOR_PATH(6), + WCD9335_INTERPOLATOR_PATH(7), + WCD9335_INTERPOLATOR_PATH(8), + + /* EAR PA */ + {"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"}, + + /* HPHL */ + {"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"}, + + /* HPHR */ + {"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"}, + + /* LINEOUT1 */ + {"RX INT3 DAC", NULL, "RX INT3 INTERP"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + /* LINEOUT2 */ + {"RX INT4 DAC", NULL, "RX INT4 INTERP"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + /* LINEOUT3 */ + {"RX INT5 DAC", NULL, "RX INT5 INTERP"}, + {"RX INT5 DAC", NULL, "RX_BIAS"}, + {"LINEOUT3 PA", NULL, "RX INT5 DAC"}, + {"LINEOUT3", NULL, "LINEOUT3 PA"}, + + /* LINEOUT4 */ + {"RX INT6 DAC", NULL, "RX INT6 INTERP"}, + {"RX INT6 DAC", NULL, "RX_BIAS"}, + {"LINEOUT4 PA", NULL, "RX INT6 DAC"}, + {"LINEOUT4", NULL, "LINEOUT4 PA"}, + + /* SLIMBUS Connections */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + /* ADC Mux */ + WCD9335_ADC_MUX_PATH(0), + WCD9335_ADC_MUX_PATH(1), + WCD9335_ADC_MUX_PATH(2), + WCD9335_ADC_MUX_PATH(3), + WCD9335_ADC_MUX_PATH(4), + WCD9335_ADC_MUX_PATH(5), + WCD9335_ADC_MUX_PATH(6), + WCD9335_ADC_MUX_PATH(7), + WCD9335_ADC_MUX_PATH(8), + + /* ADC Connections */ + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4"}, + {"ADC5", NULL, "AMIC5"}, + {"ADC6", NULL, "AMIC6"}, +}; + +static int wcd9335_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_reg; + + if ((micb_index < 0) || (micb_index > WCD9335_MAX_MICBIAS - 1)) { + dev_err(wcd->dev, "Invalid micbias index, micb_ind:%d\n", + 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; + break; + case MIC_BIAS_3: + micb_reg = WCD9335_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD9335_ANA_MICB4; + break; + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd->pullup_ref[micb_index]++; + if ((wcd->pullup_ref[micb_index] == 1) && + (wcd->micb_ref[micb_index] == 0)) + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + wcd->pullup_ref[micb_index]--; + if ((wcd->pullup_ref[micb_index] == 0) && + (wcd->micb_ref[micb_index] == 0)) + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x00); + break; + case MICB_ENABLE: + wcd->micb_ref[micb_index]++; + if (wcd->micb_ref[micb_index] == 1) + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x40); + break; + case MICB_DISABLE: + wcd->micb_ref[micb_index]--; + if ((wcd->micb_ref[micb_index] == 0) && + (wcd->pullup_ref[micb_index] > 0)) + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x80); + else if ((wcd->micb_ref[micb_index] == 0) && + (wcd->pullup_ref[micb_index] == 0)) { + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x00); + } + break; + }; + + return 0; +} + +static int __wcd9335_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int micb_num; + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) + micb_num = MIC_BIAS_4; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * MIC BIAS can also be requested by MBHC, + * so use ref count to handle micbias pullup + * and enable requests + */ + wcd9335_micbias_control(comp, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd9335_micbias_control(comp, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +static int wcd9335_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + return __wcd9335_codec_enable_micbias(w, event); +} + +static void wcd9335_codec_set_tx_hold(struct snd_soc_component *comp, + 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_component_update_bits(comp, WCD9335_ANA_AMIC2, mask, + val); + break; + case WCD9335_ANA_AMIC3: + case WCD9335_ANA_AMIC4: + snd_soc_component_update_bits(comp, WCD9335_ANA_AMIC4, mask, + val); + break; + case WCD9335_ANA_AMIC5: + case WCD9335_ANA_AMIC6: + snd_soc_component_update_bits(comp, WCD9335_ANA_AMIC6, mask, + val); + break; + default: + dev_err(comp->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int wcd9335_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd9335_codec_set_tx_hold(comp, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int wcd9335_codec_find_amic_input(struct snd_soc_component *comp, + int adc_mux_n) +{ + int mux_sel, reg, mreg; + + 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) { + reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + 2 * adc_mux_n; + mreg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + 2 * adc_mux_n; + mux_sel = snd_soc_component_read32(comp, reg) & 0x3; + } else { + reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + adc_mux_n - 4; + mreg = reg; + mux_sel = snd_soc_component_read32(comp, reg) >> 6; + } + + if (mux_sel != WCD9335_CDC_TX_INP_MUX_SEL_AMIC) + return 0; + + return snd_soc_component_read32(comp, mreg) & 0x07; +} + +static u16 wcd9335_codec_get_amic_pwlvl_reg(struct snd_soc_component *comp, + int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = 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_err(comp->dev, "invalid amic: %d\n", amic); + break; + } + + return pwr_level_reg; +} + +static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_coff_freq; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(comp->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = 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 = wcd9335_codec_find_amic_input(comp, decimator); + if (amic_n) + pwr_level_reg = wcd9335_codec_get_amic_pwlvl_reg(comp, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_component_read32(comp, pwr_level_reg) & + WCD9335_AMIC_PWR_LVL_MASK) >> + WCD9335_AMIC_PWR_LVL_SHIFT) { + case WCD9335_AMIC_PWR_LEVEL_LP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_LP); + break; + + case WCD9335_AMIC_PWR_LEVEL_HP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_HP); + break; + case WCD9335_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_DF); + break; + } + } + hpf_coff_freq = (snd_soc_component_read32(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + if (hpf_coff_freq != CF_MIN_3DB_150HZ) + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + /* Enable TX PGA Mute */ + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + 0x10, 0x10); + /* Enable APC */ + snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(comp, hpf_gate_reg, 0x01, 0x00); + + if (decimator == 0) { + snd_soc_component_write(comp, + WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_component_write(comp, + WCD9335_MBHC_ZDET_RAMP_CTL, 0xA3); + snd_soc_component_write(comp, + WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_component_write(comp, + WCD9335_MBHC_ZDET_RAMP_CTL, 0x03); + } + + snd_soc_component_update_bits(comp, hpf_gate_reg, + 0x01, 0x01); + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + 0x10, 0x00); + snd_soc_component_write(comp, tx_gain_ctl_reg, + snd_soc_component_read32(comp, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_coff_freq = (snd_soc_component_read32(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x10); + snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x00); + if (hpf_coff_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_coff_freq << 5); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00); + break; + }; +out: + kfree(wname); + return ret; +} + +static u8 wcd9335_get_dmic_clk_val(struct snd_soc_component *component, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_err(component->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 == WCD9335_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(component->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(component->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 wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(comp->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(wcd->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(wcd->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(wcd->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(comp->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_rate_val = + wcd9335_get_dmic_clk_val(comp, + wcd->mclk_rate, + wcd->dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_component_update_bits(comp, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + wcd9335_get_dmic_clk_val(comp, + wcd->mclk_rate, + wcd->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_component_update_bits(comp, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + }; + + return 0; +} + +static void wcd9335_codec_enable_int_port(struct wcd_slim_codec_dai_data *dai, + struct snd_soc_component *component) +{ + int port_num = 0; + unsigned short reg = 0; + unsigned int val = 0; + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + struct wcd9335_slim_ch *ch; + + list_for_each_entry(ch, &dai->slim_ch_list, list) { + if (ch->port >= WCD9335_RX_START) { + port_num = ch->port - WCD9335_RX_START; + reg = WCD9335_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + } else { + port_num = ch->port; + reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + } + + regmap_read(wcd->if_regmap, reg, &val); + if (!(val & BIT(port_num % 8))) + regmap_write(wcd->if_regmap, reg, + val | BIT(port_num % 8)); + } +} + +static int wcd9335_codec_enable_slim(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp); + struct wcd_slim_codec_dai_data *dai = &wcd->dai[w->shift]; + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wcd9335_codec_enable_int_port(dai, comp); + break; + case SND_SOC_DAPM_POST_PMD: + kfree(dai->sconfig.chs); + + break; + } + + return ret; +} + +static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + int offset_val = 0; + int val = 0; + + 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(comp->dev, "%s: No gain register avail for %s\n", + __func__, w->name); + return 0; + }; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read32(comp, gain_reg); + val += offset_val; + snd_soc_component_write(comp, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + break; + }; + + return 0; +} + +static u16 wcd9335_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL; + + 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 wcd9335_codec_hd2_control(struct snd_soc_component *component, + u16 prim_int_reg, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + 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_component_update_bits(component, hd2_scale_reg, + WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P2500); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK, + WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_2); + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD9335_CDC_RX_PATH_CFG_HD2_ENABLE); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD9335_CDC_RX_PATH_CFG_HD2_DISABLE); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK, + WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_1); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000); + } +} + +static int wcd9335_codec_enable_prim_interpolator( + struct snd_soc_component *comp, + u16 reg, int event) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + u16 ind = 0; + int prim_int_reg = wcd9335_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd->prim_int_users[ind]++; + if (wcd->prim_int_users[ind] == 1) { + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_ENABLE); + wcd9335_codec_hd2_control(comp, prim_int_reg, event); + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_CLK_EN_MASK, + WCD9335_CDC_RX_CLK_ENABLE); + } + + if ((reg != prim_int_reg) && + ((snd_soc_component_read32(comp, prim_int_reg)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK)) + snd_soc_component_update_bits(comp, reg, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + wcd->prim_int_users[ind]--; + if (wcd->prim_int_users[ind] == 0) { + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_CLK_EN_MASK, + WCD9335_CDC_RX_CLK_DISABLE); + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_RESET_MASK, + WCD9335_CDC_RX_RESET_ENABLE); + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_RESET_MASK, + WCD9335_CDC_RX_RESET_DISABLE); + wcd9335_codec_hd2_control(comp, prim_int_reg, event); + } + break; + }; + + return 0; +} + +static int wcd9335_config_compander(struct snd_soc_component *component, + int interp_n, int event) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + if (!wcd->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD9335_CDC_COMPANDER1_CTL(comp); + rx_path_cfg0_reg = WCD9335_CDC_RX1_RX_PATH_CFG(comp); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_CLK_EN_MASK, + WCD9335_CDC_COMPANDER_CLK_ENABLE); + /* Reset comander */ + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_SOFT_RST_MASK, + WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_SOFT_RST_MASK, + WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE); + /* Enables DRE in this path */ + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK, + WCD9335_CDC_RX_PATH_CFG_CMP_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_HALT_MASK, + WCD9335_CDC_COMPANDER_HALT); + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK, + WCD9335_CDC_RX_PATH_CFG_CMP_DISABLE); + + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_SOFT_RST_MASK, + WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_SOFT_RST_MASK, + WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_CLK_EN_MASK, + WCD9335_CDC_COMPANDER_CLK_DISABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_HALT_MASK, + WCD9335_CDC_COMPANDER_NOHALT); + } + + return 0; +} + +static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + 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(comp->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reset if needed */ + wcd9335_codec_enable_prim_interpolator(comp, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + wcd9335_config_compander(comp, w->shift, event); + val = snd_soc_component_read32(comp, gain_reg); + val += offset_val; + snd_soc_component_write(comp, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + wcd9335_config_compander(comp, w->shift, event); + wcd9335_codec_enable_prim_interpolator(comp, reg, event); + break; + }; + + return 0; +} + +static void wcd9335_codec_hph_mode_gain_opt(struct snd_soc_component *component, + u8 gain) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + 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_component_read32(component, WCD9335_ANA_HPH); + is_hphl_pa = hph_pa_status >> 7; + is_hphr_pa = (hph_pa_status & 0x40) >> 6; + + hph_l_en = snd_soc_component_read32(component, WCD9335_HPH_L_EN); + hph_r_en = snd_soc_component_read32(component, 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_component_write(component, WCD9335_HPH_L_EN, l_val); + wcd->hph_l_gain = hph_l_en & 0x1F; + } + + if ((r_val != hph_r_en) && !is_hphr_pa) { + snd_soc_component_write(component, WCD9335_HPH_R_EN, r_val); + wcd->hph_r_gain = hph_r_en & 0x1F; + } +} + +static void wcd9335_codec_hph_lohifi_config(struct snd_soc_component *comp, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD9335_RX_BIAS_HPH_PA, + WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK, + 0x06); + snd_soc_component_update_bits(comp, + WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, + 0xF0, 0x40); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1, + WCD9335_HPH_PA_GM3_IB_SCALE_MASK, + 0x0C); + wcd9335_codec_hph_mode_gain_opt(comp, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500); + snd_soc_component_write(comp, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, + 0x8A); + snd_soc_component_update_bits(comp, WCD9335_RX_BIAS_HPH_PA, + WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK, + 0x0A); + } +} + +static void wcd9335_codec_hph_lp_config(struct snd_soc_component *comp, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1, + WCD9335_HPH_PA_GM3_IB_SCALE_MASK, + 0x0C); + wcd9335_codec_hph_mode_gain_opt(comp, 0x10); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK, + WCD9335_HPH_PA_CTL2_FORCE_PSRREH_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK, + WCD9335_HPH_PA_CTL2_HPH_PSRR_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_RDAC_LDO_CTL, + WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_MASK, + WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_V_N1P60); + snd_soc_component_update_bits(comp, WCD9335_HPH_RDAC_LDO_CTL, + WCD9335_HPH_RDAC_1P65_LD_OUTCTL_MASK, + WCD9335_HPH_RDAC_1P65_LD_OUTCTL_V_N1P60); + snd_soc_component_update_bits(comp, + WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x0F, 0x01); + snd_soc_component_update_bits(comp, + WCD9335_RX_BIAS_HPH_RDAC_LDO, 0xF0, 0x10); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_write(comp, WCD9335_RX_BIAS_HPH_RDAC_LDO, + 0x88); + snd_soc_component_write(comp, WCD9335_HPH_RDAC_LDO_CTL, + 0x33); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK, + WCD9335_HPH_PA_CTL2_HPH_PSRR_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK, + WCD9335_HPH_PA_CTL2_FORCE_PSRREH_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500); + snd_soc_component_update_bits(comp, WCD9335_HPH_R_EN, + WCD9335_HPH_CONST_SEL_L_MASK, + WCD9335_HPH_CONST_SEL_L_HQ_PATH); + snd_soc_component_update_bits(comp, WCD9335_HPH_L_EN, + WCD9335_HPH_CONST_SEL_L_MASK, + WCD9335_HPH_CONST_SEL_L_HQ_PATH); + } +} + +static void wcd9335_codec_hph_hifi_config(struct snd_soc_component *comp, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1, + WCD9335_HPH_PA_GM3_IB_SCALE_MASK, + 0x0C); + wcd9335_codec_hph_mode_gain_opt(comp, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500); + } +} + +static void wcd9335_codec_hph_mode_config(struct snd_soc_component *component, + int event, int mode) +{ + switch (mode) { + case CLS_H_LP: + wcd9335_codec_hph_lp_config(component, event); + break; + case CLS_H_LOHIFI: + wcd9335_codec_hph_lohifi_config(component, event); + break; + case CLS_H_HIFI: + wcd9335_codec_hph_hifi_config(component, event); + break; + } +} + +static int wcd9335_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Read DEM INP Select */ + dem_inp = snd_soc_component_read32(comp, + 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(comp->dev, "Incorrect DEM Input\n"); + return -EINVAL; + } + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + wcd9335_codec_hph_mode_config(comp, event, hph_mode); + + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_ctrl_get_state(wcd->clsh_ctrl) & + WCD_CLSH_STATE_HPHR)) + wcd9335_codec_hph_mode_config(comp, event, hph_mode); + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + break; + }; + + return ret; +} + +static int wcd9335_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, CLS_AB); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, CLS_AB); + break; + } + + return 0; +} + +static int wcd9335_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + break; + }; + + return ret; +} + +static void wcd9335_codec_hph_post_pa_config(struct wcd9335_codec *wcd, + int mode, int event) +{ + u8 scale_val = 0; + + 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; + } + break; + case SND_SOC_DAPM_PRE_PMD: + scale_val = 0x6; + break; + } + + if (scale_val) + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_PA_CTL1, + WCD9335_HPH_PA_GM3_IB_SCALE_MASK, + scale_val << 1); + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (wcd->comp_enabled[COMPANDER_1] || + wcd->comp_enabled[COMPANDER_2]) { + /* GAIN Source Selection */ + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_L_EN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, + WCD9335_HPH_GAIN_SRC_SEL_COMPANDER); + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_R_EN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, + WCD9335_HPH_GAIN_SRC_SEL_COMPANDER); + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_AUTO_CHOP, + WCD9335_HPH_AUTO_CHOP_MASK, + WCD9335_HPH_AUTO_CHOP_FORCE_ENABLE); + } + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_L_EN, + WCD9335_HPH_PA_GAIN_MASK, + wcd->hph_l_gain); + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_R_EN, + WCD9335_HPH_PA_GAIN_MASK, + wcd->hph_r_gain); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_AUTO_CHOP, + WCD9335_HPH_AUTO_CHOP_MASK, + WCD9335_HPH_AUTO_CHOP_ENABLE_BY_CMPDR_GAIN); +} + +static int wcd9335_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + + /* Read DEM INP Select */ + dem_inp = snd_soc_component_read32(comp, + WCD9335_CDC_RX2_RX_PATH_SEC0) & + WCD9335_CDC_RX_PATH_DEM_INP_SEL_MASK; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(comp->dev, "DEM Input not set correctly, hph_mode: %d\n", + hph_mode); + return -EINVAL; + } + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + wcd9335_codec_hph_mode_config(comp, event, hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_ctrl_get_state(wcd->clsh_ctrl) & + WCD_CLSH_STATE_HPHL)) + wcd9335_codec_hph_mode_config(comp, event, hph_mode); + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + break; + }; + + return ret; +} + +static int wcd9335_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(7000, 7100); + + wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX1_RX_PATH_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read32(comp, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK) + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + + break; + case SND_SOC_DAPM_PRE_PMD: + wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + break; + }; + + return ret; +} + +static int wcd9335_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int vol_reg = 0, mix_vol_reg = 0; + int ret = 0; + + if (w->reg == WCD9335_ANA_LO_1_2) { + if (w->shift == 7) { + vol_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + mix_vol_reg = WCD9335_CDC_RX3_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + vol_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + mix_vol_reg = WCD9335_CDC_RX4_RX_PATH_MIX_CTL; + } + } else if (w->reg == WCD9335_ANA_LO_3_4) { + if (w->shift == 7) { + vol_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + mix_vol_reg = WCD9335_CDC_RX5_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + vol_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + mix_vol_reg = WCD9335_CDC_RX6_RX_PATH_MIX_CTL; + } + } else { + dev_err(comp->dev, "Error enabling lineout PA\n"); + 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_component_update_bits(comp, vol_reg, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read32(comp, mix_vol_reg)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK) + snd_soc_component_update_bits(comp, mix_vol_reg, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + break; + }; + + return ret; +} + +static void wcd9335_codec_init_flyback(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, WCD9335_HPH_L_EN, + WCD9335_HPH_CONST_SEL_L_MASK, + WCD9335_HPH_CONST_SEL_L_BYPASS); + snd_soc_component_update_bits(component, WCD9335_HPH_R_EN, + WCD9335_HPH_CONST_SEL_L_MASK, + WCD9335_HPH_CONST_SEL_L_BYPASS); + snd_soc_component_update_bits(component, WCD9335_RX_BIAS_FLYB_BUFF, + WCD9335_RX_BIAS_FLYB_VPOS_5_UA_MASK, + WCD9335_RX_BIAS_FLYB_I_0P0_UA); + snd_soc_component_update_bits(component, WCD9335_RX_BIAS_FLYB_BUFF, + WCD9335_RX_BIAS_FLYB_VNEG_5_UA_MASK, + WCD9335_RX_BIAS_FLYB_I_0P0_UA); +} + +static int wcd9335_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd->rx_bias_count++; + if (wcd->rx_bias_count == 1) { + wcd9335_codec_init_flyback(comp); + snd_soc_component_update_bits(comp, + WCD9335_ANA_RX_SUPPLIES, + WCD9335_ANA_RX_BIAS_ENABLE_MASK, + WCD9335_ANA_RX_BIAS_ENABLE); + } + break; + case SND_SOC_DAPM_POST_PMD: + wcd->rx_bias_count--; + if (!wcd->rx_bias_count) + snd_soc_component_update_bits(comp, + WCD9335_ANA_RX_SUPPLIES, + WCD9335_ANA_RX_BIAS_ENABLE_MASK, + WCD9335_ANA_RX_BIAS_DISABLE); + break; + }; + + return 0; +} + +static int wcd9335_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(7000, 7100); + wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX2_RX_PATH_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read32(comp, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK) + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + + break; + + case SND_SOC_DAPM_PRE_PMD: + wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + break; + }; + + return ret; +} + +static int wcd9335_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int ret = 0; + + 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_component_update_bits(comp, + WCD9335_CDC_RX0_RX_PATH_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read32(comp, + WCD9335_CDC_RX0_RX_PATH_MIX_CTL)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK) + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + break; + }; + + return ret; +} + +static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) +{ + struct wcd9335_codec *wcd = data; + unsigned long status = 0; + int i, j, port_id; + unsigned int val, int_val = 0; + irqreturn_t ret = IRQ_NONE; + bool tx; + unsigned short reg = 0; + + for (i = WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + regmap_read(wcd->if_regmap, i, &val); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + regmap_read(wcd->if_regmap, + WCD9335_SLIM_PGD_PORT_INT_RX_SOURCE0 + j, &val); + if (val) { + if (!tx) + reg = WCD9335_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read( + wcd->if_regmap, reg, &int_val); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + + if (val & WCD9335_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(wcd->dev, + "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + + if (val & WCD9335_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(wcd->dev, + "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + + if ((val & WCD9335_SLIM_IRQ_OVERFLOW) || + (val & WCD9335_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = WCD9335_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read( + wcd->if_regmap, reg, &int_val); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + regmap_write(wcd->if_regmap, + reg, int_val); + } + } + + regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_PORT_INT_CLR_RX_0 + (j / 8), + BIT(j % 8)); + ret = IRQ_HANDLED; + } + + return ret; +} + +static struct wcd9335_irq wcd9335_irqs[] = { + { + .irq = WCD9335_IRQ_SLIMBUS, + .handler = wcd9335_slimbus_irq, + .name = "SLIM Slave", + }, +}; + +static int wcd9335_setup_irqs(struct wcd9335_codec *wcd) +{ + int irq, ret, i; + + for (i = 0; i < ARRAY_SIZE(wcd9335_irqs); i++) { + irq = regmap_irq_get_virq(wcd->irq_data, wcd9335_irqs[i].irq); + if (irq < 0) { + dev_err(wcd->dev, "Failed to get %s\n", + wcd9335_irqs[i].name); + return irq; + } + + ret = devm_request_threaded_irq(wcd->dev, irq, NULL, + wcd9335_irqs[i].handler, + IRQF_TRIGGER_RISING, + wcd9335_irqs[i].name, wcd); + if (ret) { + dev_err(wcd->dev, "Failed to request %s\n", + wcd9335_irqs[i].name); + return ret; + } + } + + /* enable interrupts on all slave ports */ + for (i = 0; i < WCD9335_SLIM_NUM_PORT_REG; i++) + regmap_write(wcd->if_regmap, WCD9335_SLIM_PGD_PORT_INT_EN0 + i, + 0xFF); + + return ret; +} + +static void wcd9335_cdc_sido_ccl_enable(struct wcd9335_codec *wcd, + bool ccl_flag) +{ + struct snd_soc_component *comp = wcd->component; + + if (ccl_flag) { + if (++wcd->sido_ccl_cnt == 1) + snd_soc_component_write(comp, WCD9335_SIDO_SIDO_CCL_10, + WCD9335_SIDO_SIDO_CCL_DEF_VALUE); + } else { + if (wcd->sido_ccl_cnt == 0) { + dev_err(wcd->dev, "sido_ccl already disabled\n"); + return; + } + if (--wcd->sido_ccl_cnt == 0) + snd_soc_component_write(comp, WCD9335_SIDO_SIDO_CCL_10, + WCD9335_SIDO_SIDO_CCL_10_ICHARG_PWR_SEL_C320FF); + } +} + +static int wcd9335_enable_master_bias(struct wcd9335_codec *wcd) +{ + wcd->master_bias_users++; + if (wcd->master_bias_users == 1) { + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_EN_MASK, + WCD9335_ANA_BIAS_ENABLE); + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_PRECHRG_EN_MASK, + WCD9335_ANA_BIAS_PRECHRG_ENABLE); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_PRECHRG_EN_MASK, + WCD9335_ANA_BIAS_PRECHRG_DISABLE); + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_PRECHRG_CTL_MODE, + WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL); + } + + return 0; +} + +static int wcd9335_enable_mclk(struct wcd9335_codec *wcd) +{ + /* Enable mclk requires master bias to be enabled first */ + if (wcd->master_bias_users <= 0) + return -EINVAL; + + if (((wcd->clk_mclk_users == 0) && (wcd->clk_type == WCD_CLK_MCLK)) || + ((wcd->clk_mclk_users > 0) && (wcd->clk_type != WCD_CLK_MCLK))) { + dev_err(wcd->dev, "Error enabling MCLK, clk_type: %d\n", + wcd->clk_type); + return -EINVAL; + } + + if (++wcd->clk_mclk_users == 1) { + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK, + WCD9335_ANA_CLK_EXT_CLKBUF_ENABLE); + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_MCLK_SRC_MASK, + WCD9335_ANA_CLK_MCLK_SRC_EXTERNAL); + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_MCLK_EN_MASK, + WCD9335_ANA_CLK_MCLK_ENABLE); + regmap_update_bits(wcd->regmap, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_EN_MASK, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_ENABLE); + regmap_update_bits(wcd->regmap, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + WCD9335_CDC_CLK_RST_CTRL_MCLK_EN_MASK, + WCD9335_CDC_CLK_RST_CTRL_MCLK_ENABLE); + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + } + + wcd->clk_type = WCD_CLK_MCLK; + + return 0; +} + +static int wcd9335_disable_mclk(struct wcd9335_codec *wcd) +{ + if (wcd->clk_mclk_users <= 0) + return -EINVAL; + + if (--wcd->clk_mclk_users == 0) { + if (wcd->clk_rco_users > 0) { + /* MCLK to RCO switch */ + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_MCLK_SRC_MASK, + WCD9335_ANA_CLK_MCLK_SRC_RCO); + wcd->clk_type = WCD_CLK_RCO; + } else { + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_MCLK_EN_MASK, + WCD9335_ANA_CLK_MCLK_DISABLE); + wcd->clk_type = WCD_CLK_OFF; + } + + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK, + WCD9335_ANA_CLK_EXT_CLKBUF_DISABLE); + } + + return 0; +} + +static int wcd9335_disable_master_bias(struct wcd9335_codec *wcd) +{ + if (wcd->master_bias_users <= 0) + return -EINVAL; + + wcd->master_bias_users--; + if (wcd->master_bias_users == 0) { + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_EN_MASK, + WCD9335_ANA_BIAS_DISABLE); + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_PRECHRG_CTL_MODE, + WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL); + } + return 0; +} + +static int wcd9335_cdc_req_mclk_enable(struct wcd9335_codec *wcd, + bool enable) +{ + int ret = 0; + + if (enable) { + wcd9335_cdc_sido_ccl_enable(wcd, true); + ret = clk_prepare_enable(wcd->mclk); + if (ret) { + dev_err(wcd->dev, "%s: ext clk enable failed\n", + __func__); + goto err; + } + /* get BG */ + wcd9335_enable_master_bias(wcd); + /* get MCLK */ + wcd9335_enable_mclk(wcd); + + } else { + /* put MCLK */ + wcd9335_disable_mclk(wcd); + /* put BG */ + wcd9335_disable_master_bias(wcd); + clk_disable_unprepare(wcd->mclk); + wcd9335_cdc_sido_ccl_enable(wcd, false); + } +err: + return ret; +} + +static void wcd9335_codec_apply_sido_voltage(struct wcd9335_codec *wcd, + enum wcd9335_sido_voltage req_mv) +{ + struct snd_soc_component *comp = wcd->component; + int vout_d_val; + + if (req_mv == wcd->sido_voltage) + return; + + /* compute the vout_d step value */ + vout_d_val = WCD9335_CALCULATE_VOUT_D(req_mv) & + WCD9335_ANA_BUCK_VOUT_MASK; + snd_soc_component_write(comp, WCD9335_ANA_BUCK_VOUT_D, vout_d_val); + snd_soc_component_update_bits(comp, WCD9335_ANA_BUCK_CTL, + WCD9335_ANA_BUCK_CTL_RAMP_START_MASK, + WCD9335_ANA_BUCK_CTL_RAMP_START_ENABLE); + + /* 1 msec sleep required after SIDO Vout_D voltage change */ + usleep_range(1000, 1100); + wcd->sido_voltage = req_mv; + snd_soc_component_update_bits(comp, WCD9335_ANA_BUCK_CTL, + WCD9335_ANA_BUCK_CTL_RAMP_START_MASK, + WCD9335_ANA_BUCK_CTL_RAMP_START_DISABLE); +} + +static int wcd9335_codec_update_sido_voltage(struct wcd9335_codec *wcd, + enum wcd9335_sido_voltage req_mv) +{ + int ret = 0; + + /* enable mclk before setting SIDO voltage */ + ret = wcd9335_cdc_req_mclk_enable(wcd, true); + if (ret) { + dev_err(wcd->dev, "Ext clk enable failed\n"); + goto err; + } + + wcd9335_codec_apply_sido_voltage(wcd, req_mv); + wcd9335_cdc_req_mclk_enable(wcd, false); + +err: + return ret; +} + +static int _wcd9335_codec_enable_mclk(struct snd_soc_component *component, + int enable) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int ret; + + if (enable) { + ret = wcd9335_cdc_req_mclk_enable(wcd, true); + if (ret) + return ret; + + wcd9335_codec_apply_sido_voltage(wcd, + SIDO_VOLTAGE_NOMINAL_MV); + } else { + wcd9335_codec_update_sido_voltage(wcd, + wcd->sido_voltage); + wcd9335_cdc_req_mclk_enable(wcd, false); + } + + return 0; +} + +static int wcd9335_codec_enable_mclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return _wcd9335_codec_enable_mclk(comp, true); + case SND_SOC_DAPM_POST_PMD: + return _wcd9335_codec_enable_mclk(comp, false); + } + + return 0; +} + +static const struct snd_soc_dapm_widget wcd9335_dapm_widgets[] = { + /* TODO SPK1 & SPK2 OUT*/ + 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("LINEOUT3"), + SND_SOC_DAPM_OUTPUT("LINEOUT4"), + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, WCD9335_RX0, 0, + &slim_rx_mux[WCD9335_RX0]), + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, WCD9335_RX1, 0, + &slim_rx_mux[WCD9335_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, WCD9335_RX2, 0, + &slim_rx_mux[WCD9335_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, WCD9335_RX3, 0, + &slim_rx_mux[WCD9335_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, WCD9335_RX4, 0, + &slim_rx_mux[WCD9335_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, WCD9335_RX5, 0, + &slim_rx_mux[WCD9335_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, WCD9335_RX6, 0, + &slim_rx_mux[WCD9335_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, WCD9335_RX7, 0, + &slim_rx_mux[WCD9335_RX7]), + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + 5, 0, &rx_int0_2_mux, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + 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, NULL, 0), + 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, NULL, 0), + 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, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + 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 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 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 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("RX INT8 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int0_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int2_dem_inp_mux), + + SND_SOC_DAPM_MUX_E("RX INT0 INTERP", SND_SOC_NOPM, + INTERP_EAR, 0, &rx_int0_interp_mux, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_mclk, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* TX */ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + SND_SOC_DAPM_INPUT("AMIC6"), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, 0, 0, + wcd9335_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, WCD9335_ANA_AMIC1, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9335_ANA_AMIC2, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9335_ANA_AMIC3, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9335_ANA_AMIC4, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC5", NULL, WCD9335_ANA_AMIC5, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC6", NULL, WCD9335_ANA_AMIC6, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_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, + wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux0), + SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux1), + SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux2), + SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux3), + SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux4), + SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux5), + SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux6), + SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux7), + SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux8), + + SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0, + &tx_amic_mux0), + SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0, + &tx_amic_mux1), + SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0, + &tx_amic_mux2), + SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0, + &tx_amic_mux3), + SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0, + &tx_amic_mux4), + SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0, + &tx_amic_mux5), + SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0, + &tx_amic_mux6), + SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0, + &tx_amic_mux7), + SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0, + &tx_amic_mux8), + + SND_SOC_DAPM_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 TX0 MUX", SND_SOC_NOPM, WCD9335_TX0, 0, + &sb_tx0_mux), + SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, WCD9335_TX1, 0, + &sb_tx1_mux), + SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, WCD9335_TX2, 0, + &sb_tx2_mux), + SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, WCD9335_TX3, 0, + &sb_tx3_mux), + SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, WCD9335_TX4, 0, + &sb_tx4_mux), + SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, WCD9335_TX5, 0, + &sb_tx5_mux), + SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, WCD9335_TX6, 0, + &sb_tx6_mux), + SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, WCD9335_TX7, 0, + &sb_tx7_mux), + SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, WCD9335_TX8, 0, + &sb_tx8_mux), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9335_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_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, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +}; + +static void wcd9335_enable_sido_buck(struct snd_soc_component *component) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + + snd_soc_component_update_bits(component, WCD9335_ANA_RCO, + WCD9335_ANA_RCO_BG_EN_MASK, + WCD9335_ANA_RCO_BG_ENABLE); + snd_soc_component_update_bits(component, WCD9335_ANA_BUCK_CTL, + WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_MASK, + WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_EXT); + /* 100us sleep needed after IREF settings */ + usleep_range(100, 110); + snd_soc_component_update_bits(component, WCD9335_ANA_BUCK_CTL, + WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_MASK, + WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT); + /* 100us sleep needed after VREF settings */ + usleep_range(100, 110); + wcd->sido_input_src = SIDO_SOURCE_RCO_BG; +} + +static int wcd9335_enable_efuse_sensing(struct snd_soc_component *comp) +{ + _wcd9335_codec_enable_mclk(comp, true); + snd_soc_component_update_bits(comp, + WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, + WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK, + WCD9335_CHIP_TIER_CTRL_EFUSE_ENABLE); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + + if (!(snd_soc_component_read32(comp, + WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & + WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK)) + WARN(1, "%s: Efuse sense is not complete\n", __func__); + + wcd9335_enable_sido_buck(comp); + _wcd9335_codec_enable_mclk(comp, false); + + return 0; +} + +static void wcd9335_codec_init(struct snd_soc_component *component) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int i; + + /* ungate MCLK and set clk rate */ + regmap_update_bits(wcd->regmap, WCD9335_CODEC_RPM_CLK_GATE, + WCD9335_CODEC_RPM_CLK_GATE_MCLK_GATE_MASK, 0); + + regmap_update_bits(wcd->regmap, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ); + + for (i = 0; i < ARRAY_SIZE(wcd9335_codec_reg_init); i++) + snd_soc_component_update_bits(component, + wcd9335_codec_reg_init[i].reg, + wcd9335_codec_reg_init[i].mask, + wcd9335_codec_reg_init[i].val); + + wcd9335_enable_efuse_sensing(component); +} + +static int wcd9335_codec_probe(struct snd_soc_component *component) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int i; + + snd_soc_component_init_regmap(component, wcd->regmap); + /* Class-H Init*/ + wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, wcd->version); + if (IS_ERR(wcd->clsh_ctrl)) + return PTR_ERR(wcd->clsh_ctrl); + + /* Default HPH Mode to Class-H HiFi */ + wcd->hph_mode = CLS_H_HIFI; + wcd->component = component; + + wcd9335_codec_init(component); + + for (i = 0; i < NUM_CODEC_DAIS; i++) + INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list); + + return wcd9335_setup_irqs(wcd); +} + +static void wcd9335_codec_remove(struct snd_soc_component *comp) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + wcd_clsh_ctrl_free(wcd->clsh_ctrl); + free_irq(regmap_irq_get_virq(wcd->irq_data, WCD9335_IRQ_SLIMBUS), wcd); +} + +static int wcd9335_codec_set_sysclk(struct snd_soc_component *comp, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + wcd->mclk_rate = freq; + + if (wcd->mclk_rate == WCD9335_MCLK_CLK_12P288MHZ) + snd_soc_component_update_bits(comp, + WCD9335_CODEC_RPM_CLK_MCLK_CFG, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ); + else if (wcd->mclk_rate == WCD9335_MCLK_CLK_9P6MHZ) + snd_soc_component_update_bits(comp, + WCD9335_CODEC_RPM_CLK_MCLK_CFG, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ); + + return clk_set_rate(wcd->mclk, freq); +} + +static const struct snd_soc_component_driver wcd9335_component_drv = { + .probe = wcd9335_codec_probe, + .remove = wcd9335_codec_remove, + .set_sysclk = wcd9335_codec_set_sysclk, + .controls = wcd9335_snd_controls, + .num_controls = ARRAY_SIZE(wcd9335_snd_controls), + .dapm_widgets = wcd9335_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd9335_dapm_widgets), + .dapm_routes = wcd9335_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd9335_audio_map), +}; + +static int wcd9335_probe(struct wcd9335_codec *wcd) +{ + struct device *dev = wcd->dev; + + memcpy(wcd->rx_chs, wcd9335_rx_chs, sizeof(wcd9335_rx_chs)); + memcpy(wcd->tx_chs, wcd9335_tx_chs, sizeof(wcd9335_tx_chs)); + + wcd->sido_input_src = SIDO_SOURCE_INTERNAL; + wcd->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV; + + return devm_snd_soc_register_component(dev, &wcd9335_component_drv, + wcd9335_slim_dais, + ARRAY_SIZE(wcd9335_slim_dais)); +} + +static const struct regmap_range_cfg wcd9335_ranges[] = { + { + .name = "WCD9335", + .range_min = 0x0, + .range_max = WCD9335_MAX_REGISTER, + .selector_reg = WCD9335_REG(0x0, 0), + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0x0, + .window_len = 0x1000, + }, +}; + +static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD9335_INTR_PIN1_STATUS0...WCD9335_INTR_PIN2_CLEAR3: + 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_ANA_RCO: + case WCD9335_ANA_BIAS: + return true; + default: + return false; + } +} + +static struct regmap_config wcd9335_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = WCD9335_MAX_REGISTER, + .can_multi_write = true, + .ranges = wcd9335_ranges, + .num_ranges = ARRAY_SIZE(wcd9335_ranges), + .volatile_reg = wcd9335_is_volatile_register, +}; + +static const struct regmap_range_cfg wcd9335_ifc_ranges[] = { + { + .name = "WCD9335-IFC-DEV", + .range_min = 0x0, + .range_max = WCD9335_REG(0, 0x7ff), + .selector_reg = WCD9335_REG(0, 0x0), + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0x0, + .window_len = 0x1000, + }, +}; + +static struct regmap_config wcd9335_ifc_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .can_multi_write = true, + .max_register = WCD9335_REG(0, 0x7FF), + .ranges = wcd9335_ifc_ranges, + .num_ranges = ARRAY_SIZE(wcd9335_ifc_ranges), +}; + +static const struct regmap_irq wcd9335_codec_irqs[] = { + /* INTR_REG 0 */ + [WCD9335_IRQ_SLIMBUS] = { + .reg_offset = 0, + .mask = BIT(0), + .type = { + .type_reg_offset = 0, + .types_supported = IRQ_TYPE_EDGE_BOTH, + .type_reg_mask = BIT(0), + }, + }, +}; + +static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = { + .name = "wcd9335_pin1_irq", + .status_base = WCD9335_INTR_PIN1_STATUS0, + .mask_base = WCD9335_INTR_PIN1_MASK0, + .ack_base = WCD9335_INTR_PIN1_CLEAR0, + .type_base = WCD9335_INTR_LEVEL0, + .num_type_reg = 4, + .num_regs = 4, + .irqs = wcd9335_codec_irqs, + .num_irqs = ARRAY_SIZE(wcd9335_codec_irqs), +}; + +static int wcd9335_parse_dt(struct wcd9335_codec *wcd) +{ + struct device *dev = wcd->dev; + struct device_node *np = dev->of_node; + int ret; + + wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); + if (wcd->reset_gpio < 0) { + dev_err(dev, "Reset GPIO missing from DT\n"); + return wcd->reset_gpio; + } + + wcd->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(wcd->mclk)) { + dev_err(dev, "mclk not found\n"); + return PTR_ERR(wcd->mclk); + } + + wcd->native_clk = devm_clk_get(dev, "slimbus"); + if (IS_ERR(wcd->native_clk)) { + dev_err(dev, "slimbus clock not found\n"); + return PTR_ERR(wcd->native_clk); + } + + wcd->supplies[0].supply = "vdd-buck"; + wcd->supplies[1].supply = "vdd-buck-sido"; + wcd->supplies[2].supply = "vdd-tx"; + wcd->supplies[3].supply = "vdd-rx"; + wcd->supplies[4].supply = "vdd-io"; + + ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, wcd->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: err = %d\n", ret); + return ret; + } + + return 0; +} + +static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) +{ + struct device *dev = wcd->dev; + int ret; + + ret = regulator_bulk_enable(WCD9335_MAX_SUPPLY, wcd->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: err = %d\n", ret); + return ret; + } + + /* + * 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 + * Toggle the reset line to make sure the reset pulse is + * correctly applied + */ + usleep_range(600, 650); + + gpio_direction_output(wcd->reset_gpio, 0); + msleep(20); + gpio_set_value(wcd->reset_gpio, 1); + msleep(20); + + return 0; +} + +static int wcd9335_bring_up(struct wcd9335_codec *wcd) +{ + struct regmap *rm = wcd->regmap; + int val, byte0; + + regmap_read(rm, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val); + regmap_read(rm, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0); + + if ((val < 0) || (byte0 < 0)) { + dev_err(wcd->dev, "WCD9335 CODEC version detection fail!\n"); + return -EINVAL; + } + + if (byte0 == 0x1) { + dev_info(wcd->dev, "WCD9335 CODEC version is v2.0\n"); + wcd->version = WCD9335_VERSION_2_0; + regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(rm, WCD9335_SIDO_SIDO_TEST_2, 0x00); + regmap_write(rm, WCD9335_SIDO_SIDO_CCL_8, 0x6F); + regmap_write(rm, WCD9335_BIAS_VBG_FINE_ADJ, 0x65); + regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else { + dev_err(wcd->dev, "WCD9335 CODEC version not supported\n"); + return -EINVAL; + } + + return 0; +} + +static int wcd9335_irq_init(struct wcd9335_codec *wcd) +{ + int ret; + + /* + * INTR1 consists of all possible interrupt sources Ear OCP, + * HPH OCP, MBHC, MAD, VBAT, and SVA + * INTR2 is a subset of first interrupt sources MAD, VBAT, and SVA + */ + wcd->intr1 = of_irq_get_byname(wcd->dev->of_node, "intr1"); + if (wcd->intr1 < 0) { + if (wcd->intr1 != -EPROBE_DEFER) + dev_err(wcd->dev, "Unable to configure IRQ\n"); + + return wcd->intr1; + } + + ret = devm_regmap_add_irq_chip(wcd->dev, wcd->regmap, wcd->intr1, + IRQF_TRIGGER_HIGH, 0, + &wcd9335_regmap_irq1_chip, &wcd->irq_data); + if (ret) + dev_err(wcd->dev, "Failed to register IRQ chip: %d\n", ret); + + return ret; +} + +static int wcd9335_slim_probe(struct slim_device *slim) +{ + struct device *dev = &slim->dev; + struct wcd9335_codec *wcd; + int ret; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + wcd->dev = dev; + ret = wcd9335_parse_dt(wcd); + if (ret) { + dev_err(dev, "Error parsing DT: %d\n", ret); + return ret; + } + + ret = wcd9335_power_on_reset(wcd); + if (ret) + return ret; + + dev_set_drvdata(dev, wcd); + + return 0; +} + +static int wcd9335_slim_status(struct slim_device *sdev, + enum slim_device_status status) +{ + struct device *dev = &sdev->dev; + struct device_node *ifc_dev_np; + struct wcd9335_codec *wcd; + int ret; + + wcd = dev_get_drvdata(dev); + + ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0); + if (!ifc_dev_np) { + dev_err(dev, "No Interface device found\n"); + return -EINVAL; + } + + wcd->slim = sdev; + wcd->slim_ifc_dev = of_slim_get_device(sdev->ctrl, ifc_dev_np); + if (!wcd->slim_ifc_dev) { + dev_err(dev, "Unable to get SLIM Interface device\n"); + return -EINVAL; + } + + slim_get_logical_addr(wcd->slim_ifc_dev); + + wcd->regmap = regmap_init_slimbus(sdev, &wcd9335_regmap_config); + if (IS_ERR(wcd->regmap)) { + dev_err(dev, "Failed to allocate slim register map\n"); + return PTR_ERR(wcd->regmap); + } + + wcd->if_regmap = regmap_init_slimbus(wcd->slim_ifc_dev, + &wcd9335_ifc_regmap_config); + if (IS_ERR(wcd->if_regmap)) { + dev_err(dev, "Failed to allocate ifc register map\n"); + return PTR_ERR(wcd->if_regmap); + } + + ret = wcd9335_bring_up(wcd); + if (ret) { + dev_err(dev, "Failed to bringup WCD9335\n"); + return ret; + } + + ret = wcd9335_irq_init(wcd); + if (ret) + return ret; + + wcd9335_probe(wcd); + + return ret; +} + +static const struct slim_device_id wcd9335_slim_id[] = { + {SLIM_MANF_ID_QCOM, SLIM_PROD_CODE_WCD9335, 0x1, 0x0}, + {} +}; +MODULE_DEVICE_TABLE(slim, wcd9335_slim_id); + +static struct slim_driver wcd9335_slim_driver = { + .driver = { + .name = "wcd9335-slim", + }, + .probe = wcd9335_slim_probe, + .device_status = wcd9335_slim_status, + .id_table = wcd9335_slim_id, +}; + +module_slim_driver(wcd9335_slim_driver); +MODULE_DESCRIPTION("WCD9335 slim driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("slim:217:1a0:*"); diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h new file mode 100644 index 000000000000..4d9be2496c30 --- /dev/null +++ b/sound/soc/codecs/wcd9335.h @@ -0,0 +1,640 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __WCD9335_H__ +#define __WCD9335_H__ + +/* + * WCD9335 register base can change according to the mode it works in + * in slimbus mode the reg base starts from 0x800 + * in i2s/i2c mode the reg base is 0x0 + */ +#define WCD9335_REG(pg, r) ((pg << 12) | (r) | 0x800) +#define WCD9335_REG_OFFSET(r) (r & 0xFF) +#define WCD9335_PAGE_OFFSET(r) ((r >> 12) & 0xFF) + +/* Page-0 Registers */ +#define WCD9335_PAGE0_PAGE_REGISTER WCD9335_REG(0x00, 0x000) +#define WCD9335_CODEC_RPM_CLK_GATE WCD9335_REG(0x00, 0x002) +#define WCD9335_CODEC_RPM_CLK_GATE_MCLK_GATE_MASK GENMASK(1, 0) +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG WCD9335_REG(0x00, 0x003) +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ BIT(0) +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ BIT(0) +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK GENMASK(1, 0) +#define WCD9335_CODEC_RPM_RST_CTL WCD9335_REG(0x00, 0x009) +#define WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL WCD9335_REG(0x00, 0x011) +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0 WCD9335_REG(0x00, 0x021) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_CTL WCD9335_REG(0x00, 0x025) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_SSTATE_MASK GENMASK(4, 1) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK BIT(0) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_ENABLE BIT(0) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 WCD9335_REG(0x00, 0x029) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS WCD9335_REG(0x00, 0x039) +#define WCD9335_INTR_CFG WCD9335_REG(0x00, 0x081) +#define WCD9335_INTR_CLR_COMMIT WCD9335_REG(0x00, 0x082) +#define WCD9335_INTR_PIN1_MASK0 WCD9335_REG(0x00, 0x089) +#define WCD9335_INTR_PIN1_MASK1 WCD9335_REG(0x00, 0x08a) +#define WCD9335_INTR_PIN1_MASK2 WCD9335_REG(0x00, 0x08b) +#define WCD9335_INTR_PIN1_MASK3 WCD9335_REG(0x00, 0x08c) +#define WCD9335_INTR_PIN1_STATUS0 WCD9335_REG(0x00, 0x091) +#define WCD9335_INTR_PIN1_STATUS1 WCD9335_REG(0x00, 0x092) +#define WCD9335_INTR_PIN1_STATUS2 WCD9335_REG(0x00, 0x093) +#define WCD9335_INTR_PIN1_STATUS3 WCD9335_REG(0x00, 0x094) +#define WCD9335_INTR_PIN1_CLEAR0 WCD9335_REG(0x00, 0x099) +#define WCD9335_INTR_PIN1_CLEAR1 WCD9335_REG(0x00, 0x09a) +#define WCD9335_INTR_PIN1_CLEAR2 WCD9335_REG(0x00, 0x09b) +#define WCD9335_INTR_PIN1_CLEAR3 WCD9335_REG(0x00, 0x09c) +#define WCD9335_INTR_PIN2_MASK0 WCD9335_REG(0x00, 0x0a1) +#define WCD9335_INTR_PIN2_MASK1 WCD9335_REG(0x00, 0x0a2) +#define WCD9335_INTR_PIN2_MASK2 WCD9335_REG(0x00, 0x0a3) +#define WCD9335_INTR_PIN2_MASK3 WCD9335_REG(0x00, 0x0a4) +#define WCD9335_INTR_PIN2_STATUS0 WCD9335_REG(0x00, 0x0a9) +#define WCD9335_INTR_PIN2_STATUS1 WCD9335_REG(0x00, 0x0aa) +#define WCD9335_INTR_PIN2_STATUS2 WCD9335_REG(0x00, 0x0ab) +#define WCD9335_INTR_PIN2_STATUS3 WCD9335_REG(0x00, 0x0ac) +#define WCD9335_INTR_PIN2_CLEAR0 WCD9335_REG(0x00, 0x0b1) +#define WCD9335_INTR_PIN2_CLEAR1 WCD9335_REG(0x00, 0x0b2) +#define WCD9335_INTR_PIN2_CLEAR2 WCD9335_REG(0x00, 0x0b3) +#define WCD9335_INTR_PIN2_CLEAR3 WCD9335_REG(0x00, 0x0b4) +#define WCD9335_INTR_LEVEL0 WCD9335_REG(0x00, 0x0e1) +#define WCD9335_INTR_LEVEL1 WCD9335_REG(0x00, 0x0e2) +#define WCD9335_INTR_LEVEL2 WCD9335_REG(0x00, 0x0e3) +#define WCD9335_INTR_LEVEL3 WCD9335_REG(0x00, 0x0e4) + +/* Page-1 Registers */ +#define WCD9335_CPE_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x001) +#define WCD9335_CPE_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x002) +#define WCD9335_CPE_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x003) +#define WCD9335_CPE_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x004) +#define WCD9335_CPE_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x005) +#define WCD9335_CPE_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x006) +#define WCD9335_CPE_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x007) +#define WCD9335_CPE_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x008) +#define WCD9335_CPE_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x009) +#define WCD9335_CPE_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x00a) +#define WCD9335_CPE_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x00b) +#define WCD9335_CPE_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x00c) +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x00d) +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x00e) +#define WCD9335_CPE_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x00f) +#define WCD9335_CPE_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x010) +#define WCD9335_CPE_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x011) +#define WCD9335_CPE_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x012) +#define WCD9335_CPE_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x013) +#define WCD9335_CPE_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x014) +#define WCD9335_CPE_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x015) +#define WCD9335_CPE_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x016) +#define WCD9335_CPE_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x017) +#define WCD9335_CPE_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x018) +#define WCD9335_CPE_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x019) +#define WCD9335_CPE_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x01a) +#define WCD9335_CPE_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x01b) +#define WCD9335_CPE_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x01c) +#define WCD9335_CPE_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x01d) +#define WCD9335_CPE_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x01e) +#define WCD9335_CPE_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x01f) +#define WCD9335_CPE_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x020) +#define WCD9335_CPE_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x021) +#define WCD9335_CPE_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x022) +#define WCD9335_CPE_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x023) +#define WCD9335_CPE_FLL_FLL_MODE WCD9335_REG(0x01, 0x024) +#define WCD9335_CPE_FLL_STATUS_0 WCD9335_REG(0x01, 0x025) +#define WCD9335_CPE_FLL_STATUS_1 WCD9335_REG(0x01, 0x026) +#define WCD9335_CPE_FLL_STATUS_2 WCD9335_REG(0x01, 0x027) +#define WCD9335_CPE_FLL_STATUS_3 WCD9335_REG(0x01, 0x028) +#define WCD9335_I2S_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x041) +#define WCD9335_I2S_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x042) +#define WCD9335_I2S_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x043) +#define WCD9335_I2S_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x044) +#define WCD9335_I2S_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x045) +#define WCD9335_I2S_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x046) +#define WCD9335_I2S_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x047) +#define WCD9335_I2S_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x048) +#define WCD9335_I2S_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x049) +#define WCD9335_I2S_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x04a) +#define WCD9335_I2S_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x04b) +#define WCD9335_I2S_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x04c) +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x04d) +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x04e) +#define WCD9335_I2S_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x04f) +#define WCD9335_I2S_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x050) +#define WCD9335_I2S_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x051) +#define WCD9335_I2S_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x052) +#define WCD9335_I2S_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x053) +#define WCD9335_I2S_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x054) +#define WCD9335_I2S_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x055) +#define WCD9335_I2S_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x056) +#define WCD9335_I2S_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x057) +#define WCD9335_I2S_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x058) +#define WCD9335_I2S_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x059) +#define WCD9335_I2S_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x05a) +#define WCD9335_I2S_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x05b) +#define WCD9335_I2S_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x05c) +#define WCD9335_I2S_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x05d) +#define WCD9335_I2S_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x05e) +#define WCD9335_I2S_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x05f) +#define WCD9335_I2S_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x060) +#define WCD9335_I2S_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x061) +#define WCD9335_I2S_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x062) +#define WCD9335_I2S_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x063) +#define WCD9335_I2S_FLL_FLL_MODE WCD9335_REG(0x01, 0x064) +#define WCD9335_I2S_FLL_STATUS_0 WCD9335_REG(0x01, 0x065) +#define WCD9335_I2S_FLL_STATUS_1 WCD9335_REG(0x01, 0x066) +#define WCD9335_I2S_FLL_STATUS_2 WCD9335_REG(0x01, 0x067) +#define WCD9335_I2S_FLL_STATUS_3 WCD9335_REG(0x01, 0x068) +#define WCD9335_SB_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x081) +#define WCD9335_SB_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x082) +#define WCD9335_SB_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x083) +#define WCD9335_SB_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x084) +#define WCD9335_SB_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x085) +#define WCD9335_SB_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x086) +#define WCD9335_SB_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x087) +#define WCD9335_SB_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x088) +#define WCD9335_SB_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x089) +#define WCD9335_SB_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x08a) +#define WCD9335_SB_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x08b) +#define WCD9335_SB_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x08c) +#define WCD9335_SB_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x08d) +#define WCD9335_SB_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x08e) +#define WCD9335_SB_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x08f) +#define WCD9335_SB_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x090) +#define WCD9335_SB_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x091) +#define WCD9335_SB_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x092) +#define WCD9335_SB_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x093) +#define WCD9335_SB_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x094) +#define WCD9335_SB_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x095) +#define WCD9335_SB_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x096) +#define WCD9335_SB_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x097) +#define WCD9335_SB_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x098) +#define WCD9335_SB_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x099) +#define WCD9335_SB_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x09a) +#define WCD9335_SB_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x09b) +#define WCD9335_SB_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x09c) +#define WCD9335_SB_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x09d) +#define WCD9335_SB_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x09e) +#define WCD9335_SB_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x09f) +#define WCD9335_SB_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x0a0) +#define WCD9335_SB_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x0a1) +#define WCD9335_SB_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x0a2) +#define WCD9335_SB_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x0a3) +#define WCD9335_SB_FLL_FLL_MODE WCD9335_REG(0x01, 0x0a4) +#define WCD9335_SB_FLL_STATUS_0 WCD9335_REG(0x01, 0x0a5) +#define WCD9335_SB_FLL_STATUS_1 WCD9335_REG(0x01, 0x0a6) +#define WCD9335_SB_FLL_STATUS_2 WCD9335_REG(0x01, 0x0a7) +#define WCD9335_SB_FLL_STATUS_3 WCD9335_REG(0x01, 0x0a8) + +/* Page-2 Registers */ +#define WCD9335_PAGE2_PAGE_REGISTER WCD9335_REG(0x02, 0x000) +#define WCD9335_CPE_SS_DMIC0_CTL WCD9335_REG(0x02, 0x063) +#define WCD9335_CPE_SS_DMIC1_CTL WCD9335_REG(0x02, 0x064) +#define WCD9335_CPE_SS_DMIC2_CTL WCD9335_REG(0x02, 0x065) +#define WCD9335_CPE_SS_DMIC_CFG WCD9335_REG(0x02, 0x066) +#define WCD9335_SOC_MAD_AUDIO_CTL_2 WCD9335_REG(0x02, 0x084) + +/* Page-6 Registers */ +#define WCD9335_PAGE6_PAGE_REGISTER WCD9335_REG(0x06, 0x000) +#define WCD9335_ANA_BIAS WCD9335_REG(0x06, 0x001) +#define WCD9335_ANA_BIAS_EN_MASK BIT(7) +#define WCD9335_ANA_BIAS_ENABLE BIT(7) +#define WCD9335_ANA_BIAS_DISABLE 0 +#define WCD9335_ANA_BIAS_PRECHRG_EN_MASK BIT(6) +#define WCD9335_ANA_BIAS_PRECHRG_ENABLE BIT(6) +#define WCD9335_ANA_BIAS_PRECHRG_DISABLE 0 +#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE BIT(5) +#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_AUTO BIT(5) +#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL 0 +#define WCD9335_ANA_CLK_TOP WCD9335_REG(0x06, 0x002) +#define WCD9335_ANA_CLK_MCLK_EN_MASK BIT(2) +#define WCD9335_ANA_CLK_MCLK_ENABLE BIT(2) +#define WCD9335_ANA_CLK_MCLK_DISABLE 0 +#define WCD9335_ANA_CLK_MCLK_SRC_MASK BIT(3) +#define WCD9335_ANA_CLK_MCLK_SRC_RCO BIT(3) +#define WCD9335_ANA_CLK_MCLK_SRC_EXTERNAL 0 +#define WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK BIT(7) +#define WCD9335_ANA_CLK_EXT_CLKBUF_ENABLE BIT(7) +#define WCD9335_ANA_CLK_EXT_CLKBUF_DISABLE 0 +#define WCD9335_ANA_RCO WCD9335_REG(0x06, 0x003) +#define WCD9335_ANA_RCO_BG_EN_MASK BIT(7) +#define WCD9335_ANA_RCO_BG_ENABLE BIT(7) +#define WCD9335_ANA_BUCK_VOUT_D WCD9335_REG(0x06, 0x005) +#define WCD9335_ANA_BUCK_VOUT_MASK GENMASK(7, 0) +#define WCD9335_ANA_BUCK_CTL WCD9335_REG(0x06, 0x006) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_MASK BIT(1) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_EXT BIT(1) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_INT 0 +#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_MASK BIT(2) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT BIT(2) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_INT 0 +#define WCD9335_ANA_BUCK_CTL_RAMP_START_MASK BIT(7) +#define WCD9335_ANA_BUCK_CTL_RAMP_START_ENABLE BIT(7) +#define WCD9335_ANA_BUCK_CTL_RAMP_START_DISABLE 0 +#define WCD9335_ANA_RX_SUPPLIES WCD9335_REG(0x06, 0x008) +#define WCD9335_ANA_RX_BIAS_ENABLE_MASK BIT(0) +#define WCD9335_ANA_RX_BIAS_ENABLE BIT(0) +#define WCD9335_ANA_RX_BIAS_DISABLE 0 +#define WCD9335_ANA_HPH WCD9335_REG(0x06, 0x009) +#define WCD9335_ANA_EAR WCD9335_REG(0x06, 0x00a) +#define WCD9335_ANA_LO_1_2 WCD9335_REG(0x06, 0x00b) +#define WCD9335_ANA_LO_3_4 WCD9335_REG(0x06, 0x00c) +#define WCD9335_ANA_AMIC1 WCD9335_REG(0x06, 0x00e) +#define WCD9335_ANA_AMIC2 WCD9335_REG(0x06, 0x00f) +#define WCD9335_ANA_AMIC3 WCD9335_REG(0x06, 0x010) +#define WCD9335_ANA_AMIC4 WCD9335_REG(0x06, 0x011) +#define WCD9335_ANA_AMIC5 WCD9335_REG(0x06, 0x012) +#define WCD9335_ANA_AMIC6 WCD9335_REG(0x06, 0x013) +#define WCD9335_ANA_MBHC_MECH WCD9335_REG(0x06, 0x014) +#define WCD9335_MBHC_L_DET_EN_MASK BIT(7) +#define WCD9335_MBHC_L_DET_EN BIT(7) +#define WCD9335_MBHC_GND_DET_EN_MASK BIT(6) +#define WCD9335_MBHC_MECH_DETECT_TYPE_MASK BIT(5) +#define WCD9335_MBHC_MECH_DETECT_TYPE_SHIFT 5 +#define WCD9335_MBHC_HPHL_PLUG_TYPE_MASK BIT(4) +#define WCD9335_MBHC_HPHL_PLUG_TYPE_NO BIT(4) +#define WCD9335_MBHC_GND_PLUG_TYPE_MASK BIT(3) +#define WCD9335_MBHC_GND_PLUG_TYPE_NO BIT(3) +#define WCD9335_MBHC_HSL_PULLUP_COMP_EN BIT(2) +#define WCD9335_MBHC_HPHL_100K_TO_GND_EN BIT(0) + +#define WCD9335_ANA_MBHC_ELECT WCD9335_REG(0x06, 0x015) +#define WCD9335_ANA_MBHC_BD_ISRC_CTL_MASK GENMASK(6, 4) +#define WCD9335_ANA_MBHC_BD_ISRC_100UA GENMASK(5, 4) +#define WCD9335_ANA_MBHC_BD_ISRC_OFF 0 +#define WCD9335_ANA_MBHC_BIAS_EN_MASK BIT(0) +#define WCD9335_ANA_MBHC_BIAS_EN BIT(0) +#define WCD9335_ANA_MBHC_ZDET WCD9335_REG(0x06, 0x016) +#define WCD9335_ANA_MBHC_RESULT_1 WCD9335_REG(0x06, 0x017) +#define WCD9335_ANA_MBHC_RESULT_2 WCD9335_REG(0x06, 0x018) +#define WCD9335_ANA_MBHC_RESULT_3 WCD9335_REG(0x06, 0x019) +#define WCD9335_MBHC_BTN_RESULT_MASK GENMASK(2, 0) +#define WCD9335_ANA_MBHC_BTN0 WCD9335_REG(0x06, 0x01a) +#define WCD9335_ANA_MBHC_BTN1 WCD9335_REG(0x06, 0x01b) +#define WCD9335_ANA_MBHC_BTN2 WCD9335_REG(0x06, 0x01c) +#define WCD9335_ANA_MBHC_BTN3 WCD9335_REG(0x06, 0x01d) +#define WCD9335_ANA_MBHC_BTN4 WCD9335_REG(0x06, 0x01e) +#define WCD9335_ANA_MBHC_BTN5 WCD9335_REG(0x06, 0x01f) +#define WCD9335_ANA_MBHC_BTN6 WCD9335_REG(0x06, 0x020) +#define WCD9335_ANA_MBHC_BTN7 WCD9335_REG(0x06, 0x021) +#define WCD9335_ANA_MICB1 WCD9335_REG(0x06, 0x022) +#define WCD9335_ANA_MICB2 WCD9335_REG(0x06, 0x023) +#define WCD9335_ANA_MICB2_ENABLE BIT(6) +#define WCD9335_ANA_MICB2_RAMP WCD9335_REG(0x06, 0x024) +#define WCD9335_ANA_MICB3 WCD9335_REG(0x06, 0x025) +#define WCD9335_ANA_MICB4 WCD9335_REG(0x06, 0x026) +#define WCD9335_ANA_VBADC WCD9335_REG(0x06, 0x027) +#define WCD9335_BIAS_VBG_FINE_ADJ WCD9335_REG(0x06, 0x029) +#define WCD9335_RCO_CTRL_2 WCD9335_REG(0x06, 0x02f) +#define WCD9335_SIDO_SIDO_CCL_2 WCD9335_REG(0x06, 0x042) +#define WCD9335_SIDO_SIDO_CCL_4 WCD9335_REG(0x06, 0x044) +#define WCD9335_SIDO_SIDO_CCL_8 WCD9335_REG(0x06, 0x048) +#define WCD9335_SIDO_SIDO_CCL_10 WCD9335_REG(0x06, 0x04a) +#define WCD9335_SIDO_SIDO_CCL_10_ICHARG_PWR_SEL_C320FF 0x2 +/* Comparator 1 and 2 Bias current at 1P0UA with start pulse width of C320FF */ +#define WCD9335_SIDO_SIDO_CCL_DEF_VALUE 0x6e +#define WCD9335_SIDO_SIDO_TEST_2 WCD9335_REG(0x06, 0x055) +#define WCD9335_MBHC_CTL_1 WCD9335_REG(0x06, 0x056) +#define WCD9335_MBHC_BTN_DBNC_MASK GENMASK(1, 0) +#define WCD9335_MBHC_BTN_DBNC_T_16_MS 0x2 +#define WCD9335_MBHC_CTL_RCO_EN_MASK BIT(7) +#define WCD9335_MBHC_CTL_RCO_EN BIT(7) + +#define WCD9335_MBHC_CTL_2 WCD9335_REG(0x06, 0x057) +#define WCD9335_MBHC_HS_VREF_CTL_MASK GENMASK(1, 0) +#define WCD9335_MBHC_HS_VREF_1P5_V 0x1 +#define WCD9335_MBHC_PLUG_DETECT_CTL WCD9335_REG(0x06, 0x058) +#define WCD9335_MBHC_HSDET_PULLUP_CTL_MASK GENMASK(7, 6) +#define WCD9335_MBHC_HSDET_PULLUP_CTL_SHIFT 6 +#define WCD9335_MBHC_HSDET_PULLUP_CTL_1_2P0_UA 0x80 +#define WCD9335_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS 0x6 + +#define WCD9335_MBHC_ZDET_RAMP_CTL WCD9335_REG(0x06, 0x05a) +#define WCD9335_VBADC_IBIAS_FE WCD9335_REG(0x06, 0x05e) +#define WCD9335_FLYBACK_CTRL_1 WCD9335_REG(0x06, 0x0b1) +#define WCD9335_RX_BIAS_HPH_PA WCD9335_REG(0x06, 0x0bb) +#define WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK GENMASK(3, 0) +#define WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2 WCD9335_REG(0x06, 0x0bc) +#define WCD9335_RX_BIAS_HPH_RDAC_LDO WCD9335_REG(0x06, 0x0bd) +#define WCD9335_RX_BIAS_FLYB_BUFF WCD9335_REG(0x06, 0x0c7) +#define WCD9335_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(3, 0) +#define WCD9335_RX_BIAS_FLYB_I_0P0_UA 0 +#define WCD9335_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4) +#define WCD9335_RX_BIAS_FLYB_MID_RST WCD9335_REG(0x06, 0x0c8) +#define WCD9335_HPH_CNP_WG_CTL WCD9335_REG(0x06, 0x0cc) +#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK GENMASK(2, 0) +#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500 0x2 +#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000 0x3 +#define WCD9335_HPH_OCP_CTL WCD9335_REG(0x06, 0x0ce) +#define WCD9335_HPH_AUTO_CHOP WCD9335_REG(0x06, 0x0cf) +#define WCD9335_HPH_AUTO_CHOP_MASK BIT(5) +#define WCD9335_HPH_AUTO_CHOP_FORCE_ENABLE BIT(5) +#define WCD9335_HPH_AUTO_CHOP_ENABLE_BY_CMPDR_GAIN 0 +#define WCD9335_HPH_PA_CTL1 WCD9335_REG(0x06, 0x0d1) +#define WCD9335_HPH_PA_GM3_IB_SCALE_MASK GENMASK(3, 1) +#define WCD9335_HPH_PA_CTL2 WCD9335_REG(0x06, 0x0d2) +#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK BIT(2) +#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_ENABLE BIT(2) +#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_DISABLE 0 +#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK BIT(3) +#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE BIT(3) +#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE 0 +#define WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK BIT(5) +#define WCD9335_HPH_PA_CTL2_HPH_PSRR_ENABLE BIT(5) +#define WCD9335_HPH_PA_CTL2_HPH_PSRR_DISABLE 0 +#define WCD9335_HPH_L_EN WCD9335_REG(0x06, 0x0d3) +#define WCD9335_HPH_CONST_SEL_L_MASK GENMASK(7, 6) +#define WCD9335_HPH_CONST_SEL_L_BYPASS 0 +#define WCD9335_HPH_CONST_SEL_L_LP_PATH 0x40 +#define WCD9335_HPH_CONST_SEL_L_HQ_PATH 0x80 +#define WCD9335_HPH_PA_GAIN_MASK GENMASK(4, 0) +#define WCD9335_HPH_GAIN_SRC_SEL_MASK BIT(5) +#define WCD9335_HPH_GAIN_SRC_SEL_COMPANDER 0 +#define WCD9335_HPH_GAIN_SRC_SEL_REGISTER BIT(5) +#define WCD9335_HPH_L_TEST WCD9335_REG(0x06, 0x0d4) +#define WCD9335_HPH_R_EN WCD9335_REG(0x06, 0x0d6) +#define WCD9335_HPH_R_TEST WCD9335_REG(0x06, 0x0d7) +#define WCD9335_HPH_R_ATEST WCD9335_REG(0x06, 0x0d8) +#define WCD9335_HPH_RDAC_LDO_CTL WCD9335_REG(0x06, 0x0db) +#define WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_MASK GENMASK(2, 0) +#define WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_V_N1P60 0x1 +#define WCD9335_HPH_RDAC_1P65_LD_OUTCTL_MASK GENMASK(6, 4) +#define WCD9335_HPH_RDAC_1P65_LD_OUTCTL_V_N1P60 0x10 +#define WCD9335_HPH_REFBUFF_LP_CTL WCD9335_REG(0x06, 0x0de) +#define WCD9335_HPH_L_DAC_CTL WCD9335_REG(0x06, 0x0df) +#define WCD9335_HPH_DAC_LDO_POWERMODE_MASK BIT(0) +#define WCD9335_HPH_DAC_LDO_POWERMODE_LOWPOWER 0 +#define WCD9335_HPH_DAC_LDO_POWERMODE_UHQA BIT(0) +#define WCD9335_HPH_DAC_LDO_UHQA_OV_MASK BIT(1) +#define WCD9335_HPH_DAC_LDO_UHQA_OV_ENABLE BIT(1) +#define WCD9335_HPH_DAC_LDO_UHQA_OV_DISABLE 0 + +#define WCD9335_EAR_CMBUFF WCD9335_REG(0x06, 0x0e2) +#define WCD9335_DIFF_LO_LO2_COMPANDER WCD9335_REG(0x06, 0x0ea) +#define WCD9335_DIFF_LO_LO1_COMPANDER WCD9335_REG(0x06, 0x0eb) +#define WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ WCD9335_REG(0x06, 0x0f1) +#define WCD9335_DIFF_LO_COM_PA_FREQ WCD9335_REG(0x06, 0x0f2) +#define WCD9335_SE_LO_LO3_GAIN WCD9335_REG(0x06, 0x0f8) +#define WCD9335_SE_LO_LO3_CTRL WCD9335_REG(0x06, 0x0f9) +#define WCD9335_SE_LO_LO4_GAIN WCD9335_REG(0x06, 0x0fa) + +/* Page-10 Registers */ +#define WCD9335_CDC_TX0_TX_PATH_CTL WCD9335_REG(0x0a, 0x031) +#define WCD9335_CDC_TX_PATH_CTL_PCM_RATE_MASK GENMASK(3, 0) +#define WCD9335_CDC_TX_PATH_CTL(dec) WCD9335_REG(0xa, (0x31 + dec * 0x10)) +#define WCD9335_CDC_TX0_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x032) +#define WCD9335_CDC_TX_ADC_AMIC_DMIC_SEL_MASK BIT(7) +#define WCD9335_CDC_TX_ADC_DMIC_SEL BIT(7) +#define WCD9335_CDC_TX_ADC_AMIC_SEL 0 +#define WCD9335_CDC_TX0_TX_VOL_CTL WCD9335_REG(0x0a, 0x034) +#define WCD9335_CDC_TX0_TX_PATH_SEC2 WCD9335_REG(0x0a, 0x039) +#define WCD9335_CDC_TX0_TX_PATH_SEC7 WCD9335_REG(0x0a, 0x03e) +#define WCD9335_CDC_TX1_TX_PATH_CTL WCD9335_REG(0x0a, 0x041) +#define WCD9335_CDC_TX1_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x042) +#define WCD9335_CDC_TX2_TX_PATH_CTL WCD9335_REG(0x0a, 0x051) +#define WCD9335_CDC_TX2_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x052) +#define WCD9335_CDC_TX2_TX_VOL_CTL WCD9335_REG(0x0a, 0x054) +#define WCD9335_CDC_TX3_TX_PATH_CTL WCD9335_REG(0x0a, 0x061) +#define WCD9335_CDC_TX3_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x062) +#define WCD9335_CDC_TX3_TX_VOL_CTL WCD9335_REG(0x0a, 0x064) +#define WCD9335_CDC_TX4_TX_PATH_CTL WCD9335_REG(0x0a, 0x071) +#define WCD9335_CDC_TX4_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x072) +#define WCD9335_CDC_TX4_TX_VOL_CTL WCD9335_REG(0x0a, 0x074) +#define WCD9335_CDC_TX5_TX_PATH_CTL WCD9335_REG(0x0a, 0x081) +#define WCD9335_CDC_TX5_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x082) +#define WCD9335_CDC_TX5_TX_VOL_CTL WCD9335_REG(0x0a, 0x084) +#define WCD9335_CDC_TX6_TX_PATH_CTL WCD9335_REG(0x0a, 0x091) +#define WCD9335_CDC_TX6_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x092) +#define WCD9335_CDC_TX6_TX_VOL_CTL WCD9335_REG(0x0a, 0x094) +#define WCD9335_CDC_TX7_TX_PATH_CTL WCD9335_REG(0x0a, 0x0a1) +#define WCD9335_CDC_TX7_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x0a2) +#define WCD9335_CDC_TX7_TX_VOL_CTL WCD9335_REG(0x0a, 0x0a4) +#define WCD9335_CDC_TX8_TX_PATH_CTL WCD9335_REG(0x0a, 0x0b1) +#define WCD9335_CDC_TX8_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x0b2) +#define WCD9335_CDC_TX8_TX_VOL_CTL WCD9335_REG(0x0a, 0x0b4) +#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0c3) +#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0c7) +#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0cb) +#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0cf) + +/* Page-11 Registers */ +#define WCD9335_PAGE11_PAGE_REGISTER WCD9335_REG(0x0b, 0x000) +#define WCD9335_CDC_COMPANDER1_CTL0 WCD9335_REG(0x0b, 0x001) +#define WCD9335_CDC_COMPANDER1_CTL(c) WCD9335_REG(0x0b, (0x001 + c * 0x8)) +#define WCD9335_CDC_COMPANDER_CLK_EN_MASK BIT(0) +#define WCD9335_CDC_COMPANDER_CLK_ENABLE BIT(0) +#define WCD9335_CDC_COMPANDER_CLK_DISABLE 0 +#define WCD9335_CDC_COMPANDER_SOFT_RST_MASK BIT(1) +#define WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE BIT(1) +#define WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE 0 +#define WCD9335_CDC_COMPANDER_HALT_MASK BIT(2) +#define WCD9335_CDC_COMPANDER_HALT BIT(2) +#define WCD9335_CDC_COMPANDER_NOHALT 0 +#define WCD9335_CDC_COMPANDER7_CTL3 WCD9335_REG(0x0b, 0x034) +#define WCD9335_CDC_COMPANDER7_CTL7 WCD9335_REG(0x0b, 0x038) +#define WCD9335_CDC_COMPANDER8_CTL3 WCD9335_REG(0x0b, 0x03c) +#define WCD9335_CDC_COMPANDER8_CTL7 WCD9335_REG(0x0b, 0x040) +#define WCD9335_CDC_RX0_RX_PATH_CTL WCD9335_REG(0x0b, 0x041) +#define WCD9335_CDC_RX_PGA_MUTE_EN_MASK BIT(4) +#define WCD9335_CDC_RX_PGA_MUTE_ENABLE BIT(4) +#define WCD9335_CDC_RX_PGA_MUTE_DISABLE 0 +#define WCD9335_CDC_RX_CLK_EN_MASK BIT(5) +#define WCD9335_CDC_RX_CLK_ENABLE BIT(5) +#define WCD9335_CDC_RX_CLK_DISABLE 0 +#define WCD9335_CDC_RX_RESET_MASK BIT(6) +#define WCD9335_CDC_RX_RESET_ENABLE BIT(6) +#define WCD9335_CDC_RX_RESET_DISABLE 0 +#define WCD9335_CDC_RX_PATH_CTL(rx) WCD9335_REG(0x0b, (0x041 + rx * 0x14)) +#define WCD9335_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x042) +#define WCD9335_CDC_RX0_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x043) +#define WCD9335_CDC_RX0_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x044) +#define WCD9335_CDC_RX0_RX_VOL_CTL WCD9335_REG(0x0b, 0x045) +#define WCD9335_CDC_RX0_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x046) +#define WCD9335_CDC_MIX_PCM_RATE_MASK GENMASK(3, 0) +#define WCD9335_CDC_RX_PATH_MIX_CTL(rx) WCD9335_REG(0x0b, (0x46 + rx * 0x14)) +#define WCD9335_CDC_RX0_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x047) +#define WCD9335_CDC_RX0_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x048) +#define WCD9335_CDC_RX0_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x049) +#define WCD9335_CDC_RX0_RX_PATH_SEC7 WCD9335_REG(0x0b, 0x050) +#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC0 WCD9335_REG(0x0b, 0x051) +#define WCD9335_CDC_RX1_RX_PATH_CTL WCD9335_REG(0x0b, 0x055) +#define WCD9335_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x056) +#define WCD9335_CDC_RX1_RX_PATH_CFG(c) WCD9335_REG(0x0b, (0x056 + c * 0x14)) +#define WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK BIT(1) +#define WCD9335_CDC_RX_PATH_CFG_CMP_ENABLE BIT(1) +#define WCD9335_CDC_RX_PATH_CFG_CMP_DISABLE 0 +#define WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK BIT(2) +#define WCD9335_CDC_RX_PATH_CFG_HD2_ENABLE BIT(2) +#define WCD9335_CDC_RX_PATH_CFG_HD2_DISABLE 0 +#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN_MASK BIT(3) +#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN BIT(3) +#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_DISABLE 0 +#define WCD9335_CDC_RX1_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x058) +#define WCD9335_CDC_RX1_RX_VOL_CTL WCD9335_REG(0x0b, 0x059) +#define WCD9335_CDC_RX1_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x05a) +#define WCD9335_CDC_RX1_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x05b) +#define WCD9335_CDC_RX1_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x05c) +#define WCD9335_CDC_RX1_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x05d) +#define WCD9335_CDC_RX1_RX_PATH_SEC3 WCD9335_REG(0x0b, 0x060) +#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK GENMASK(1, 0) +#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_2 0x1 +#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_1 0 +#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK GENMASK(5, 2) +#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P2500 0x10 +#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000 0 +#define WCD9335_CDC_RX2_RX_PATH_CTL WCD9335_REG(0x0b, 0x069) +#define WCD9335_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x06a) +#define WCD9335_CDC_RX2_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x06c) +#define WCD9335_CDC_RX2_RX_VOL_CTL WCD9335_REG(0x0b, 0x06d) +#define WCD9335_CDC_RX2_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x06e) +#define WCD9335_CDC_RX2_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x06f) +#define WCD9335_CDC_RX2_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x070) +#define WCD9335_CDC_RX2_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x071) +#define WCD9335_CDC_RX_PATH_DEM_INP_SEL_MASK GENMASK(1, 0) +#define WCD9335_CDC_RX2_RX_PATH_SEC3 WCD9335_REG(0x0b, 0x074) +#define WCD9335_CDC_RX3_RX_PATH_CTL WCD9335_REG(0x0b, 0x07d) +#define WCD9335_CDC_RX3_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x07e) +#define WCD9335_CDC_RX3_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x080) +#define WCD9335_CDC_RX3_RX_VOL_CTL WCD9335_REG(0x0b, 0x081) +#define WCD9335_CDC_RX3_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x082) +#define WCD9335_CDC_RX3_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x083) +#define WCD9335_CDC_RX3_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x084) +#define WCD9335_CDC_RX4_RX_PATH_CTL WCD9335_REG(0x0b, 0x091) +#define WCD9335_CDC_RX4_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x092) +#define WCD9335_CDC_RX4_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x094) +#define WCD9335_CDC_RX4_RX_VOL_CTL WCD9335_REG(0x0b, 0x095) +#define WCD9335_CDC_RX4_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x096) +#define WCD9335_CDC_RX4_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x097) +#define WCD9335_CDC_RX4_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x098) +#define WCD9335_CDC_RX5_RX_PATH_CTL WCD9335_REG(0x0b, 0x0a5) +#define WCD9335_CDC_RX5_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0a6) +#define WCD9335_CDC_RX5_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0a8) +#define WCD9335_CDC_RX5_RX_VOL_CTL WCD9335_REG(0x0b, 0x0a9) +#define WCD9335_CDC_RX5_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0aa) +#define WCD9335_CDC_RX5_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0ab) +#define WCD9335_CDC_RX5_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0ac) +#define WCD9335_CDC_RX6_RX_PATH_CTL WCD9335_REG(0x0b, 0x0b9) +#define WCD9335_CDC_RX6_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0ba) +#define WCD9335_CDC_RX6_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0bc) +#define WCD9335_CDC_RX6_RX_VOL_CTL WCD9335_REG(0x0b, 0x0bd) +#define WCD9335_CDC_RX6_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0be) +#define WCD9335_CDC_RX6_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0bf) +#define WCD9335_CDC_RX6_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0c0) +#define WCD9335_CDC_RX7_RX_PATH_CTL WCD9335_REG(0x0b, 0x0cd) +#define WCD9335_CDC_RX7_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0ce) +#define WCD9335_CDC_RX7_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x0cf) +#define WCD9335_CDC_RX7_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0d0) +#define WCD9335_CDC_RX7_RX_VOL_CTL WCD9335_REG(0x0b, 0x0d1) +#define WCD9335_CDC_RX7_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0d2) +#define WCD9335_CDC_RX7_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0d3) +#define WCD9335_CDC_RX7_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0d4) +#define WCD9335_CDC_RX8_RX_PATH_CTL WCD9335_REG(0x0b, 0x0e1) +#define WCD9335_CDC_RX8_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0e2) +#define WCD9335_CDC_RX8_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x0e3) +#define WCD9335_CDC_RX8_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0e4) +#define WCD9335_CDC_RX8_RX_VOL_CTL WCD9335_REG(0x0b, 0x0e5) +#define WCD9335_CDC_RX8_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0e6) +#define WCD9335_CDC_RX8_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0e7) +#define WCD9335_CDC_RX8_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0e8) + +/* Page-12 Registers */ +#define WCD9335_PAGE12_PAGE_REGISTER WCD9335_REG(0x0c, 0x000) +#define WCD9335_CDC_CLSH_K2_MSB WCD9335_REG(0x0c, 0x00a) +#define WCD9335_CDC_CLSH_K2_LSB WCD9335_REG(0x0c, 0x00b) +#define WCD9335_CDC_BOOST0_BOOST_CTL WCD9335_REG(0x0c, 0x01a) +#define WCD9335_CDC_BOOST0_BOOST_CFG1 WCD9335_REG(0x0c, 0x01b) +#define WCD9335_CDC_BOOST0_BOOST_CFG2 WCD9335_REG(0x0c, 0x01c) +#define WCD9335_CDC_BOOST1_BOOST_CTL WCD9335_REG(0x0c, 0x022) +#define WCD9335_CDC_BOOST1_BOOST_CFG1 WCD9335_REG(0x0c, 0x023) +#define WCD9335_CDC_BOOST1_BOOST_CFG2 WCD9335_REG(0x0c, 0x024) + +/* Page-13 Registers */ +#define WCD9335_PAGE13_PAGE_REGISTER WCD9335_REG(0x0d, 0x000) +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0 WCD9335_REG(0x0d, 0x001) +#define WCD9335_CDC_RX_INP_MUX_RX_INT_CFG0(i) WCD9335_REG(0xd, (0x1 + i * 0x2)) +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1 WCD9335_REG(0xd, 0x002) +#define WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK GENMASK(3, 0) +#define WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(i) WCD9335_REG(0xd, (0x2 + i * 0x2)) + +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 WCD9335_REG(0x0d, 0x003) +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1 WCD9335_REG(0x0d, 0x004) +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0 WCD9335_REG(0x0d, 0x005) +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1 WCD9335_REG(0x0d, 0x006) +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0 WCD9335_REG(0x0d, 0x007) +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1 WCD9335_REG(0x0d, 0x008) +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0 WCD9335_REG(0x0d, 0x009) +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1 WCD9335_REG(0x0d, 0x00a) +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0 WCD9335_REG(0x0d, 0x00b) +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1 WCD9335_REG(0x0d, 0x00c) +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0 WCD9335_REG(0x0d, 0x00d) +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1 WCD9335_REG(0x0d, 0x00e) +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0 WCD9335_REG(0x0d, 0x00f) +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1 WCD9335_REG(0x0d, 0x010) +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0 WCD9335_REG(0x0d, 0x011) +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1 WCD9335_REG(0x0d, 0x012) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 WCD9335_REG(0x0d, 0x01d) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 WCD9335_REG(0x0d, 0x01e) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0 WCD9335_REG(0x0d, 0x01f) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1 WCD9335_REG(0x0d, 0x020) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0 WCD9335_REG(0x0d, 0x021) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1 WCD9335_REG(0x0d, 0x022) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0 WCD9335_REG(0x0d, 0x023) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1 WCD9335_REG(0x0d, 0x024) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 WCD9335_REG(0x0d, 0x025) +#define WCD9335_CDC_TX_INP_MUX_SEL_AMIC 0x1 +#define WCD9335_CDC_TX_INP_MUX_SEL_DMIC 0 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0 WCD9335_REG(0x0d, 0x026) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0 WCD9335_REG(0x0d, 0x027) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0 WCD9335_REG(0x0d, 0x028) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0 WCD9335_REG(0x0d, 0x029) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0 WCD9335_REG(0x0d, 0x02b) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0 WCD9335_REG(0x0d, 0x02c) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0 WCD9335_REG(0x0d, 0x02d) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0 WCD9335_REG(0x0d, 0x02e) +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0 WCD9335_REG(0x0d, 0x03a) +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1 WCD9335_REG(0x0d, 0x03b) +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2 WCD9335_REG(0x0d, 0x03c) +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3 WCD9335_REG(0x0d, 0x03d) +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0x0d, 0x041) +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0) +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_ENABLE BIT(0) +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_DISABLE 0 +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL WCD9335_REG(0x0d, 0x042) +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_EN_MASK BIT(0) +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_ENABLE BIT(0) +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_DISABLE 0 +#define WCD9335_CDC_TOP_TOP_CFG1 WCD9335_REG(0x0d, 0x082) +#define WCD9335_MAX_REGISTER WCD9335_REG(0x80, 0x0FF) + +/* SLIMBUS Slave Registers */ +#define WCD9335_SLIM_PGD_PORT_INT_EN0 WCD9335_REG(0, 0x30) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_0 WCD9335_REG(0, 0x34) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_1 WCD9335_REG(0, 0x35) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_0 WCD9335_REG(0, 0x36) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_1 WCD9335_REG(0, 0x37) +#define WCD9335_SLIM_PGD_PORT_INT_CLR_RX_0 WCD9335_REG(0, 0x38) +#define WCD9335_SLIM_PGD_PORT_INT_CLR_RX_1 WCD9335_REG(0, 0x39) +#define WCD9335_SLIM_PGD_PORT_INT_CLR_TX_0 WCD9335_REG(0, 0x3A) +#define WCD9335_SLIM_PGD_PORT_INT_CLR_TX_1 WCD9335_REG(0, 0x3B) +#define WCD9335_SLIM_PGD_PORT_INT_RX_SOURCE0 WCD9335_REG(0, 0x60) +#define WCD9335_SLIM_PGD_PORT_INT_TX_SOURCE0 WCD9335_REG(0, 0x70) +#define WCD9335_SLIM_PGD_RX_PORT_CFG(p) WCD9335_REG(0, (0x30 + p)) +#define WCD9335_SLIM_PGD_PORT_CFG(p) WCD9335_REG(0, (0x40 + p)) +#define WCD9335_SLIM_PGD_TX_PORT_CFG(p) WCD9335_REG(0, (0x50 + p)) +#define WCD9335_SLIM_PGD_PORT_INT_SRC(p) WCD9335_REG(0, (0x60 + p)) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS(p) WCD9335_REG(0, (0x80 + p)) +#define WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_0(p) WCD9335_REG(0, (0x100 + 4 * p)) +/* ports range from 10-16 */ +#define WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_1(p) WCD9335_REG(0, (0x101 + 4 * p)) +#define WCD9335_SLIM_PGD_RX_PORT_MULTI_CHNL_0(p) WCD9335_REG(0, (0x140 + 4 * p)) + +#define WCD9335_IRQ_SLIMBUS 0 +#define WCD9335_IRQ_MBHC_SW_DET 8 +#define WCD9335_IRQ_MBHC_ELECT_INS_REM_DET 9 +#define WCD9335_IRQ_MBHC_BUTTON_PRESS_DET 10 +#define WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET 11 +#define WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET 12 + +#define SLIM_MANF_ID_QCOM 0x217 +#define SLIM_PROD_CODE_WCD9335 0x1a0 + +#define WCD9335_VERSION_2_0 2 +#define WCD9335_MAX_SUPPLY 5 + +#endif /* __WCD9335_H__ */ diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 7e817e1877c2..4466e195b66d 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1214,105 +1214,105 @@ SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX8_ENA_SHIFT, 0), diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index b0789a03d699..b25877fa529d 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -1348,122 +1348,122 @@ SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 1fedf74da705..546ea735f534 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -196,7 +196,7 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); - unsigned int iface; + unsigned int iface, mode; int i; /* The set of sample rates that can be supported depends on the @@ -240,11 +240,21 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* oversampling rate */ + if (params_rate(params) > 96000) + mode = 0x40; + else if (params_rate(params) > 48000) + mode = 0x20; + else + mode = 0x00; + dev_dbg(component->dev, "wm8741_hw_params: bit size param = %d, rate param = %d", params_width(params), params_rate(params)); snd_soc_component_update_bits(component, WM8741_FORMAT_CONTROL, WM8741_IWL_MASK, iface); + snd_soc_component_update_bits(component, WM8741_MODE_CONTROL_1, WM8741_OSR_MASK, + mode); return 0; } @@ -358,6 +368,15 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } +static int wm8741_mute(struct snd_soc_dai *codec_dai, int mute) +{ + struct snd_soc_component *component = codec_dai->component; + + snd_soc_component_update_bits(component, WM8741_VOLUME_CONTROL, + WM8741_SOFT_MASK, !!mute << WM8741_SOFT_SHIFT); + return 0; +} + #define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ @@ -371,6 +390,7 @@ static const struct snd_soc_dai_ops wm8741_dai_ops = { .hw_params = wm8741_hw_params, .set_sysclk = wm8741_set_dai_sysclk, .set_fmt = wm8741_set_dai_fmt, + .digital_mute = wm8741_mute, }; static struct snd_soc_dai_driver wm8741_dai = { diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index 806245c70f8b..37467c512597 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -666,8 +666,9 @@ static int wm8770_spi_probe(struct spi_device *spi) /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) { - ret = regulator_register_notifier(wm8770->supplies[i].consumer, - &wm8770->disable_nb[i]); + ret = devm_regulator_register_notifier( + wm8770->supplies[i].consumer, + &wm8770->disable_nb[i]); if (ret) { dev_err(&spi->dev, "Failed to register regulator notifier: %d\n", @@ -687,25 +688,12 @@ static int wm8770_spi_probe(struct spi_device *spi) return ret; } -static int wm8770_spi_remove(struct spi_device *spi) -{ - struct wm8770_priv *wm8770 = spi_get_drvdata(spi); - int i; - - for (i = 0; i < ARRAY_SIZE(wm8770->supplies); ++i) - regulator_unregister_notifier(wm8770->supplies[i].consumer, - &wm8770->disable_nb[i]); - - return 0; -} - static struct spi_driver wm8770_spi_driver = { .driver = { .name = "wm8770", .of_match_table = wm8770_of_match, }, .probe = wm8770_spi_probe, - .remove = wm8770_spi_remove }; module_spi_driver(wm8770_spi_driver); diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 2a3e5fbd04e4..9e0f96e0f8ec 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1837,9 +1837,6 @@ static int wm8904_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_ON: - ret = clk_prepare_enable(wm8904->mclk); - if (ret) - return ret; break; case SND_SOC_BIAS_PREPARE: @@ -1864,6 +1861,15 @@ static int wm8904_set_bias_level(struct snd_soc_component *component, return ret; } + ret = clk_prepare_enable(wm8904->mclk); + if (ret) { + dev_err(component->dev, + "Failed to enable MCLK: %d\n", ret); + regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), + wm8904->supplies); + return ret; + } + regcache_cache_only(wm8904->regmap, false); regcache_sync(wm8904->regmap); @@ -2108,16 +2114,13 @@ static const struct regmap_config wm8904_regmap = { }; #ifdef CONFIG_OF -static enum wm8904_type wm8904_data = WM8904; -static enum wm8904_type wm8912_data = WM8912; - static const struct of_device_id wm8904_of_match[] = { { .compatible = "wlf,wm8904", - .data = &wm8904_data, + .data = (void *)WM8904, }, { .compatible = "wlf,wm8912", - .data = &wm8912_data, + .data = (void *)WM8912, }, { /* sentinel */ } @@ -2158,7 +2161,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c, match = of_match_node(wm8904_of_match, i2c->dev.of_node); if (match == NULL) return -EINVAL; - wm8904->devtype = *((enum wm8904_type *)match->data); + wm8904->devtype = (enum wm8904_type)match->data; } else { wm8904->devtype = id->driver_data; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index efd8910b1ff7..467ed78dd2df 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3424,8 +3424,9 @@ static int wm8962_probe(struct snd_soc_component *component) /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) { - ret = regulator_register_notifier(wm8962->supplies[i].consumer, - &wm8962->disable_nb[i]); + ret = devm_regulator_register_notifier( + wm8962->supplies[i].consumer, + &wm8962->disable_nb[i]); if (ret != 0) { dev_err(component->dev, "Failed to register regulator notifier: %d\n", @@ -3467,15 +3468,11 @@ static int wm8962_probe(struct snd_soc_component *component) static void wm8962_remove(struct snd_soc_component *component) { struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); - int i; cancel_delayed_work_sync(&wm8962->mic_work); wm8962_free_gpio(component); wm8962_free_beep(component); - for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) - regulator_unregister_notifier(wm8962->supplies[i].consumer, - &wm8962->disable_nb[i]); } static const struct snd_soc_component_driver soc_component_dev_wm8962 = { diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 68c99fe37097..79ee91906bb9 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1995,20 +1995,6 @@ static int wm8995_set_bias_level(struct snd_soc_component *component, return 0; } -static void wm8995_remove(struct snd_soc_component *component) -{ - struct wm8995_priv *wm8995; - int i; - - wm8995 = snd_soc_component_get_drvdata(component); - - for (i = 0; i < ARRAY_SIZE(wm8995->supplies); ++i) - regulator_unregister_notifier(wm8995->supplies[i].consumer, - &wm8995->disable_nb[i]); - - regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); -} - static int wm8995_probe(struct snd_soc_component *component) { struct wm8995_priv *wm8995; @@ -2021,8 +2007,9 @@ static int wm8995_probe(struct snd_soc_component *component) for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) wm8995->supplies[i].supply = wm8995_supply_names[i]; - ret = regulator_bulk_get(component->dev, ARRAY_SIZE(wm8995->supplies), - wm8995->supplies); + ret = devm_regulator_bulk_get(component->dev, + ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); if (ret) { dev_err(component->dev, "Failed to request supplies: %d\n", ret); return ret; @@ -2039,8 +2026,9 @@ static int wm8995_probe(struct snd_soc_component *component) /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) { - ret = regulator_register_notifier(wm8995->supplies[i].consumer, - &wm8995->disable_nb[i]); + ret = devm_regulator_register_notifier( + wm8995->supplies[i].consumer, + &wm8995->disable_nb[i]); if (ret) { dev_err(component->dev, "Failed to register regulator notifier: %d\n", @@ -2052,7 +2040,7 @@ static int wm8995_probe(struct snd_soc_component *component) wm8995->supplies); if (ret) { dev_err(component->dev, "Failed to enable supplies: %d\n", ret); - goto err_reg_get; + return ret; } ret = snd_soc_component_read32(component, WM8995_SOFTWARE_RESET); @@ -2099,8 +2087,6 @@ static int wm8995_probe(struct snd_soc_component *component) err_reg_enable: regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); -err_reg_get: - regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); return ret; } @@ -2188,7 +2174,6 @@ static struct snd_soc_dai_driver wm8995_dai[] = { static const struct snd_soc_component_driver soc_component_dev_wm8995 = { .probe = wm8995_probe, - .remove = wm8995_remove, .set_bias_level = wm8995_set_bias_level, .controls = wm8995_snd_controls, .num_controls = ARRAY_SIZE(wm8995_snd_controls), diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 91711f8958c5..ab04ea18c312 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2801,8 +2801,9 @@ static int wm8996_i2c_probe(struct i2c_client *i2c, /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) { - ret = regulator_register_notifier(wm8996->supplies[i].consumer, - &wm8996->disable_nb[i]); + ret = devm_regulator_register_notifier( + wm8996->supplies[i].consumer, + &wm8996->disable_nb[i]); if (ret != 0) { dev_err(&i2c->dev, "Failed to register regulator notifier: %d\n", @@ -3071,16 +3072,12 @@ err: static int wm8996_i2c_remove(struct i2c_client *client) { struct wm8996_priv *wm8996 = i2c_get_clientdata(client); - int i; wm8996_free_gpio(wm8996); if (wm8996->pdata.ldo_ena > 0) { gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); gpio_free(wm8996->pdata.ldo_ena); } - for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) - regulator_unregister_notifier(wm8996->supplies[i].consumer, - &wm8996->disable_nb[i]); return 0; } diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index df5b36b8fc5a..33e3dc1a1367 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -516,95 +516,95 @@ SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX8_ENA_SHIFT, 0), diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 409bed30a4e4..125fc32ad92a 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -626,96 +626,96 @@ SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2, SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, ARIZONA_SLIMRX4_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, ARIZONA_SLIMTX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d15cf6e42adc..b93fdc8d2d6f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -46,6 +46,13 @@ #define adsp_dbg(_dsp, fmt, ...) \ dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) +#define compr_err(_obj, fmt, ...) \ + adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ + ##__VA_ARGS__) +#define compr_dbg(_obj, fmt, ...) \ + adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ + ##__VA_ARGS__) + #define ADSP1_CONTROL_1 0x00 #define ADSP1_CONTROL_2 0x02 #define ADSP1_CONTROL_3 0x03 @@ -310,6 +317,12 @@ struct wm_adsp_alg_xm_struct { __be64 smoothed_power; }; +struct wm_adsp_host_buf_coeff_v1 { + __be32 host_buf_ptr; /* Host buffer pointer */ + __be32 versions; /* Version numbers */ + __be32 name[4]; /* The buffer name */ +}; + struct wm_adsp_buffer { __be32 buf1_base; /* Base addr of first buffer area */ __be32 buf1_size; /* Size of buf1 area in DSP words */ @@ -334,6 +347,7 @@ struct wm_adsp_buffer { struct wm_adsp_compr; struct wm_adsp_compr_buf { + struct list_head list; struct wm_adsp *dsp; struct wm_adsp_compr *compr; @@ -344,9 +358,13 @@ struct wm_adsp_compr_buf { u32 irq_count; int read_index; int avail; + int host_buf_mem_type; + + char *name; }; struct wm_adsp_compr { + struct list_head list; struct wm_adsp *dsp; struct wm_adsp_compr_buf *buf; @@ -357,6 +375,8 @@ struct wm_adsp_compr { unsigned int copied_total; unsigned int sample_rate; + + const char *name; }; #define WM_ADSP_DATA_WORD_SIZE 3 @@ -374,6 +394,11 @@ struct wm_adsp_compr { #define ALG_XM_FIELD(field) \ (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) +#define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 + +#define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 +#define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 + static int wm_adsp_buffer_init(struct wm_adsp *dsp); static int wm_adsp_buffer_free(struct wm_adsp *dsp); @@ -707,7 +732,7 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, mutex_lock(&dsp[e->shift_l].pwr_lock); - if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) + if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) ret = -EBUSY; else dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; @@ -2429,6 +2454,8 @@ static int wm_adsp_common_init(struct wm_adsp *dsp) INIT_LIST_HEAD(&dsp->alg_regions); INIT_LIST_HEAD(&dsp->ctl_list); + INIT_LIST_HEAD(&dsp->compr_list); + INIT_LIST_HEAD(&dsp->buffer_list); mutex_init(&dsp->pwr_lock); @@ -2971,14 +2998,19 @@ static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) { - /* - * Note this will be more complex once each DSP can support multiple - * streams - */ - if (!compr->dsp->buffer) + struct wm_adsp_compr_buf *buf = NULL, *tmp; + + list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { + if (!tmp->name || !strcmp(compr->name, tmp->name)) { + buf = tmp; + break; + } + } + + if (!buf) return -EINVAL; - compr->buf = compr->dsp->buffer; + compr->buf = buf; compr->buf->compr = compr; return 0; @@ -3001,28 +3033,33 @@ static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) { - struct wm_adsp_compr *compr; + struct wm_adsp_compr *compr, *tmp; + struct snd_soc_pcm_runtime *rtd = stream->private_data; int ret = 0; mutex_lock(&dsp->pwr_lock); if (wm_adsp_fw[dsp->fw].num_caps == 0) { - adsp_err(dsp, "Firmware does not support compressed API\n"); + adsp_err(dsp, "%s: Firmware does not support compressed API\n", + rtd->codec_dai->name); ret = -ENXIO; goto out; } if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { - adsp_err(dsp, "Firmware does not support stream direction\n"); + adsp_err(dsp, "%s: Firmware does not support stream direction\n", + rtd->codec_dai->name); ret = -EINVAL; goto out; } - if (dsp->compr) { - /* It is expect this limitation will be removed in future */ - adsp_err(dsp, "Only a single stream supported per DSP\n"); - ret = -EBUSY; - goto out; + list_for_each_entry(tmp, &dsp->compr_list, list) { + if (!strcmp(tmp->name, rtd->codec_dai->name)) { + adsp_err(dsp, "%s: Only a single stream supported per dai\n", + rtd->codec_dai->name); + ret = -EBUSY; + goto out; + } } compr = kzalloc(sizeof(*compr), GFP_KERNEL); @@ -3033,8 +3070,9 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) compr->dsp = dsp; compr->stream = stream; + compr->name = rtd->codec_dai->name; - dsp->compr = compr; + list_add_tail(&compr->list, &dsp->compr_list); stream->runtime->private_data = compr; @@ -3053,7 +3091,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream) mutex_lock(&dsp->pwr_lock); wm_adsp_compr_detach(compr); - dsp->compr = NULL; + list_del(&compr->list); kfree(compr->raw_buf); kfree(compr); @@ -3078,9 +3116,9 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { - adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", - params->buffer.fragment_size, - params->buffer.fragments); + compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", + params->buffer.fragment_size, + params->buffer.fragments); return -EINVAL; } @@ -3108,9 +3146,9 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, return 0; } - adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", - params->codec.id, params->codec.ch_in, params->codec.ch_out, - params->codec.sample_rate, params->codec.format); + compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", + params->codec.id, params->codec.ch_in, params->codec.ch_out, + params->codec.sample_rate, params->codec.format); return -EINVAL; } @@ -3132,8 +3170,8 @@ int wm_adsp_compr_set_params(struct snd_compr_stream *stream, compr->size = params->buffer; - adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", - compr->size.fragment_size, compr->size.fragments); + compr_dbg(compr, "fragment_size=%d fragments=%d\n", + compr->size.fragment_size, compr->size.fragments); size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); @@ -3219,24 +3257,106 @@ static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, unsigned int field_offset, u32 *data) { - return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, + return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type, buf->host_buf_ptr + field_offset, data); } static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, unsigned int field_offset, u32 data) { - return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, + return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type, buf->host_buf_ptr + field_offset, data); } -static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) +static void wm_adsp_remove_padding(u32 *buf, int nwords, int data_word_size) +{ + u8 *pack_in = (u8 *)buf; + u8 *pack_out = (u8 *)buf; + int i, j; + + /* Remove the padding bytes from the data read from the DSP */ + for (i = 0; i < nwords; i++) { + for (j = 0; j < data_word_size; j++) + *pack_out++ = *pack_in++; + + pack_in += sizeof(*buf) - data_word_size; + } +} + +static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) +{ + const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; + struct wm_adsp_buffer_region *region; + u32 offset = 0; + int i, ret; + + buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), + GFP_KERNEL); + if (!buf->regions) + return -ENOMEM; + + for (i = 0; i < caps->num_regions; ++i) { + region = &buf->regions[i]; + + region->offset = offset; + region->mem_type = caps->region_defs[i].mem_type; + + ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, + ®ion->base_addr); + if (ret < 0) + return ret; + + ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, + &offset); + if (ret < 0) + return ret; + + region->cumulative_size = offset; + + compr_dbg(buf, + "region=%d type=%d base=%08x off=%08x size=%08x\n", + i, region->mem_type, region->base_addr, + region->offset, region->cumulative_size); + } + + return 0; +} + +static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) +{ + buf->irq_count = 0xFFFFFFFF; + buf->read_index = -1; + buf->avail = 0; +} + +static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) +{ + struct wm_adsp_compr_buf *buf; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + buf->dsp = dsp; + + wm_adsp_buffer_clear(buf); + + list_add_tail(&buf->list, &dsp->buffer_list); + + return buf; +} + +static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) { struct wm_adsp_alg_region *alg_region; - struct wm_adsp *dsp = buf->dsp; + struct wm_adsp_compr_buf *buf; u32 xmalg, addr, magic; int i, ret; + buf = wm_adsp_buffer_alloc(dsp); + if (!buf) + return -ENOMEM; + alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); @@ -3246,7 +3366,7 @@ static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) return ret; if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) - return -EINVAL; + return -ENODEV; addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); for (i = 0; i < 5; ++i) { @@ -3264,48 +3384,30 @@ static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) if (!buf->host_buf_ptr) return -EIO; - adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); + buf->host_buf_mem_type = WMFW_ADSP2_XM; - return 0; -} - -static struct wm_coeff_ctl * -wm_adsp_find_host_buffer_ctrl(struct wm_adsp_compr_buf *buf) -{ - struct wm_adsp *dsp = buf->dsp; - struct wm_coeff_ctl *ctl; - - list_for_each_entry(ctl, &dsp->ctl_list, list) { - if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) - continue; - - if (!ctl->enabled) - continue; + ret = wm_adsp_buffer_populate(buf); + if (ret < 0) + return ret; - return ctl; - } + compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); - return NULL; + return 0; } -static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) +static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) { - struct wm_adsp *dsp = buf->dsp; - struct wm_coeff_ctl *ctl; - unsigned int reg; - u32 val; - int i, ret; - - ctl = wm_adsp_find_host_buffer_ctrl(buf); - if (!ctl) - return wm_adsp_legacy_host_buf_addr(buf); + struct wm_adsp_host_buf_coeff_v1 coeff_v1; + struct wm_adsp_compr_buf *buf; + unsigned int val, reg; + int ret, i; ret = wm_coeff_base_reg(ctl, ®); if (ret) return ret; for (i = 0; i < 5; ++i) { - ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); + ret = regmap_raw_read(ctl->dsp->regmap, reg, &val, sizeof(val)); if (ret < 0) return ret; @@ -3315,108 +3417,113 @@ static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) usleep_range(1000, 2000); } - if (!val) + if (!val) { + adsp_err(ctl->dsp, "Failed to acquire host buffer\n"); return -EIO; + } + + buf = wm_adsp_buffer_alloc(ctl->dsp); + if (!buf) + return -ENOMEM; + buf->host_buf_mem_type = ctl->alg_region.type; buf->host_buf_ptr = be32_to_cpu(val); - adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); - return 0; -} + ret = wm_adsp_buffer_populate(buf); + if (ret < 0) + return ret; -static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) -{ - const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; - struct wm_adsp_buffer_region *region; - u32 offset = 0; - int i, ret; + /* + * v0 host_buffer coefficients didn't have versioning, so if the + * control is one word, assume version 0. + */ + if (ctl->len == 4) { + compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); + return 0; + } - for (i = 0; i < caps->num_regions; ++i) { - region = &buf->regions[i]; + ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1, + sizeof(coeff_v1)); + if (ret < 0) + return ret; - region->offset = offset; - region->mem_type = caps->region_defs[i].mem_type; + coeff_v1.versions = be32_to_cpu(coeff_v1.versions); + val = coeff_v1.versions & HOST_BUF_COEFF_COMPAT_VER_MASK; + val >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; - ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, - ®ion->base_addr); - if (ret < 0) - return ret; + if (val > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { + adsp_err(ctl->dsp, + "Host buffer coeff ver %u > supported version %u\n", + val, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); + return -EINVAL; + } - ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, - &offset); - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(coeff_v1.name); i++) + coeff_v1.name[i] = be32_to_cpu(coeff_v1.name[i]); - region->cumulative_size = offset; + wm_adsp_remove_padding((u32 *)&coeff_v1.name, + ARRAY_SIZE(coeff_v1.name), + WM_ADSP_DATA_WORD_SIZE); - adsp_dbg(buf->dsp, - "region=%d type=%d base=%08x off=%08x size=%08x\n", - i, region->mem_type, region->base_addr, - region->offset, region->cumulative_size); - } + buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part, + (char *)&coeff_v1.name); - return 0; -} + compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", + buf->host_buf_ptr, val); -static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) -{ - buf->irq_count = 0xFFFFFFFF; - buf->read_index = -1; - buf->avail = 0; + return val; } static int wm_adsp_buffer_init(struct wm_adsp *dsp) { - struct wm_adsp_compr_buf *buf; + struct wm_coeff_ctl *ctl; int ret; - buf = kzalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - buf->dsp = dsp; - - wm_adsp_buffer_clear(buf); + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) + continue; - ret = wm_adsp_buffer_locate(buf); - if (ret < 0) { - adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); - goto err_buffer; - } + if (!ctl->enabled) + continue; - buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, - sizeof(*buf->regions), GFP_KERNEL); - if (!buf->regions) { - ret = -ENOMEM; - goto err_buffer; + ret = wm_adsp_buffer_parse_coeff(ctl); + if (ret < 0) { + adsp_err(dsp, "Failed to parse coeff: %d\n", ret); + goto error; + } else if (ret == 0) { + /* Only one buffer supported for version 0 */ + return 0; + } } - ret = wm_adsp_buffer_populate(buf); - if (ret < 0) { - adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); - goto err_regions; + if (list_empty(&dsp->buffer_list)) { + /* Fall back to legacy support */ + ret = wm_adsp_buffer_parse_legacy(dsp); + if (ret) { + adsp_err(dsp, "Failed to parse legacy: %d\n", ret); + goto error; + } } - dsp->buffer = buf; - return 0; -err_regions: - kfree(buf->regions); -err_buffer: - kfree(buf); +error: + wm_adsp_buffer_free(dsp); return ret; } static int wm_adsp_buffer_free(struct wm_adsp *dsp) { - if (dsp->buffer) { - wm_adsp_compr_detach(dsp->buffer->compr); + struct wm_adsp_compr_buf *buf, *tmp; - kfree(dsp->buffer->regions); - kfree(dsp->buffer); + list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { + if (buf->compr) + wm_adsp_compr_detach(buf->compr); - dsp->buffer = NULL; + kfree(buf->name); + kfree(buf->regions); + list_del(&buf->list); + kfree(buf); } return 0; @@ -3445,7 +3552,7 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) struct wm_adsp *dsp = compr->dsp; int ret = 0; - adsp_dbg(dsp, "Trigger: %d\n", cmd); + compr_dbg(compr, "Trigger: %d\n", cmd); mutex_lock(&dsp->pwr_lock); @@ -3454,8 +3561,8 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) if (!wm_adsp_compr_attached(compr)) { ret = wm_adsp_compr_attach(compr); if (ret < 0) { - adsp_err(dsp, "Failed to link buffer and stream: %d\n", - ret); + compr_err(compr, "Failed to link buffer and stream: %d\n", + ret); break; } } @@ -3471,8 +3578,8 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) HOST_BUFFER_FIELD(high_water_mark), wm_adsp_compr_frag_words(compr)); if (ret < 0) { - adsp_err(dsp, "Failed to set high water mark: %d\n", - ret); + compr_err(compr, "Failed to set high water mark: %d\n", + ret); break; } break; @@ -3513,7 +3620,7 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) read_index = sign_extend32(next_read_index, 23); if (read_index < 0) { - adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); + compr_dbg(buf, "Avail check on unstarted stream\n"); return 0; } @@ -3531,8 +3638,8 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) if (avail < 0) avail += wm_adsp_buffer_size(buf); - adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", - buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); + compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", + buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); buf->avail = avail; @@ -3547,39 +3654,40 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) mutex_lock(&dsp->pwr_lock); - buf = dsp->buffer; - compr = dsp->compr; - - if (!buf) { + if (list_empty(&dsp->buffer_list)) { ret = -ENODEV; goto out; } adsp_dbg(dsp, "Handling buffer IRQ\n"); - ret = wm_adsp_buffer_get_error(buf); - if (ret < 0) - goto out_notify; /* Wake poll to report error */ + list_for_each_entry(buf, &dsp->buffer_list, list) { + compr = buf->compr; - ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), - &buf->irq_count); - if (ret < 0) { - adsp_err(dsp, "Failed to get irq_count: %d\n", ret); - goto out; - } + ret = wm_adsp_buffer_get_error(buf); + if (ret < 0) + goto out_notify; /* Wake poll to report error */ - ret = wm_adsp_buffer_update_avail(buf); - if (ret < 0) { - adsp_err(dsp, "Error reading avail: %d\n", ret); - goto out; - } + ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), + &buf->irq_count); + if (ret < 0) { + compr_err(buf, "Failed to get irq_count: %d\n", ret); + goto out; + } - if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) - ret = WM_ADSP_COMPR_VOICE_TRIGGER; + ret = wm_adsp_buffer_update_avail(buf); + if (ret < 0) { + compr_err(buf, "Error reading avail: %d\n", ret); + goto out; + } + + if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) + ret = WM_ADSP_COMPR_VOICE_TRIGGER; out_notify: - if (compr && compr->stream) - snd_compr_fragment_elapsed(compr->stream); + if (compr && compr->stream) + snd_compr_fragment_elapsed(compr->stream); + } out: mutex_unlock(&dsp->pwr_lock); @@ -3593,8 +3701,7 @@ static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) if (buf->irq_count & 0x01) return 0; - adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", - buf->irq_count); + compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); buf->irq_count |= 0x01; @@ -3610,7 +3717,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, struct wm_adsp_compr_buf *buf; int ret = 0; - adsp_dbg(dsp, "Pointer request\n"); + compr_dbg(compr, "Pointer request\n"); mutex_lock(&dsp->pwr_lock); @@ -3625,7 +3732,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, if (buf->avail < wm_adsp_compr_frag_words(compr)) { ret = wm_adsp_buffer_update_avail(buf); if (ret < 0) { - adsp_err(dsp, "Error reading avail: %d\n", ret); + compr_err(compr, "Error reading avail: %d\n", ret); goto out; } @@ -3644,9 +3751,8 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, ret = wm_adsp_buffer_reenable_irq(buf); if (ret < 0) { - adsp_err(dsp, - "Failed to re-enable buffer IRQ: %d\n", - ret); + compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", + ret); goto out; } } @@ -3666,11 +3772,9 @@ EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) { struct wm_adsp_compr_buf *buf = compr->buf; - u8 *pack_in = (u8 *)compr->raw_buf; - u8 *pack_out = (u8 *)compr->raw_buf; unsigned int adsp_addr; int mem_type, nwords, max_read; - int i, j, ret; + int i, ret; /* Calculate read parameters */ for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) @@ -3702,13 +3806,7 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) if (ret < 0) return ret; - /* Remove the padding bytes from the data read from the DSP */ - for (i = 0; i < nwords; i++) { - for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) - *pack_out++ = *pack_in++; - - pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; - } + wm_adsp_remove_padding(compr->raw_buf, nwords, WM_ADSP_DATA_WORD_SIZE); /* update read index to account for words read */ buf->read_index += nwords; @@ -3729,11 +3827,10 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) static int wm_adsp_compr_read(struct wm_adsp_compr *compr, char __user *buf, size_t count) { - struct wm_adsp *dsp = compr->dsp; int ntotal = 0; int nwords, nbytes; - adsp_dbg(dsp, "Requested read of %zu bytes\n", count); + compr_dbg(compr, "Requested read of %zu bytes\n", count); if (!compr->buf || compr->buf->error) { snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); @@ -3745,17 +3842,18 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr, do { nwords = wm_adsp_buffer_capture_block(compr, count); if (nwords < 0) { - adsp_err(dsp, "Failed to capture block: %d\n", nwords); + compr_err(compr, "Failed to capture block: %d\n", + nwords); return nwords; } nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; - adsp_dbg(dsp, "Read %d bytes\n", nbytes); + compr_dbg(compr, "Read %d bytes\n", nbytes); if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { - adsp_err(dsp, "Failed to copy data to user: %d, %d\n", - ntotal, nbytes); + compr_err(compr, "Failed to copy data to user: %d, %d\n", + ntotal, nbytes); return -EFAULT; } diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 4b8778b0b06c..59e07ad16329 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -90,8 +90,8 @@ struct wm_adsp { struct work_struct boot_work; - struct wm_adsp_compr *compr; - struct wm_adsp_compr_buf *buffer; + struct list_head compr_list; + struct list_head buffer_list; struct mutex pwr_lock; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 2e75b5bc5f1d..7b1d9970be8b 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -57,6 +57,15 @@ config SND_SOC_FSL_ESAI This option is only useful for out-of-tree drivers since in-tree drivers select it automatically. +config SND_SOC_FSL_MICFIL + tristate "Pulse Density Modulation Microphone Interface (MICFIL) module support" + select REGMAP_MMIO + select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y if you want to add Pulse Density Modulation microphone + interface (MICFIL) support for NXP. + config SND_SOC_FSL_UTILS tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index de94fa057e24..3c0ff315b971 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -19,6 +19,7 @@ snd-soc-fsl-ssi-y := fsl_ssi.o snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-esai-objs := fsl_esai.o +snd-soc-fsl-micfil-objs := fsl_micfil.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o +obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 81f2fe2c6d23..60f87a0d99f4 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -689,6 +689,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_fail: of_node_put(asrc_np); of_node_put(codec_np); + put_device(&cpu_pdev->dev); fail: of_node_put(cpu_np); diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c new file mode 100644 index 000000000000..40c07e756481 --- /dev/null +++ b/sound/soc/fsl/fsl_micfil.c @@ -0,0 +1,826 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2018 NXP + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/kobject.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/core.h> + +#include "fsl_micfil.h" +#include "imx-pcm.h" + +#define FSL_MICFIL_RATES SNDRV_PCM_RATE_8000_48000 +#define FSL_MICFIL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +struct fsl_micfil { + struct platform_device *pdev; + struct regmap *regmap; + const struct fsl_micfil_soc_data *soc; + struct clk *mclk; + struct snd_dmaengine_dai_dma_data dma_params_rx; + unsigned int dataline; + char name[32]; + int irq[MICFIL_IRQ_LINES]; + unsigned int mclk_streams; + int quality; /*QUALITY 2-0 bits */ + bool slave_mode; + int channel_gain[8]; +}; + +struct fsl_micfil_soc_data { + unsigned int fifos; + unsigned int fifo_depth; + unsigned int dataline; + bool imx; +}; + +static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { + .imx = true, + .fifos = 8, + .fifo_depth = 8, + .dataline = 0xf, +}; + +static const struct of_device_id fsl_micfil_dt_ids[] = { + { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids); + +/* Table 5. Quality Modes + * Medium 0 0 0 + * High 0 0 1 + * Very Low 2 1 0 0 + * Very Low 1 1 0 1 + * Very Low 0 1 1 0 + * Low 1 1 1 + */ +static const char * const micfil_quality_select_texts[] = { + "Medium", "High", + "N/A", "N/A", + "VLow2", "VLow1", + "VLow0", "Low", +}; + +static const struct soc_enum fsl_micfil_quality_enum = + SOC_ENUM_SINGLE(REG_MICFIL_CTRL2, + MICFIL_CTRL2_QSEL_SHIFT, + ARRAY_SIZE(micfil_quality_select_texts), + micfil_quality_select_texts); + +static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0); + +static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { + SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, + MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0x7, gain_tlv), + SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL, + MICFIL_OUTGAIN_CHX_SHIFT(1), 0xF, 0x7, gain_tlv), + SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL, + MICFIL_OUTGAIN_CHX_SHIFT(2), 0xF, 0x7, gain_tlv), + SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL, + MICFIL_OUTGAIN_CHX_SHIFT(3), 0xF, 0x7, gain_tlv), + SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL, + MICFIL_OUTGAIN_CHX_SHIFT(4), 0xF, 0x7, gain_tlv), + SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL, + MICFIL_OUTGAIN_CHX_SHIFT(5), 0xF, 0x7, gain_tlv), + SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL, + MICFIL_OUTGAIN_CHX_SHIFT(6), 0xF, 0x7, gain_tlv), + SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL, + MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0x7, gain_tlv), + SOC_ENUM_EXT("MICFIL Quality Select", + fsl_micfil_quality_enum, + snd_soc_get_enum_double, snd_soc_put_enum_double), +}; + +static inline int get_pdm_clk(struct fsl_micfil *micfil, + unsigned int rate) +{ + u32 ctrl2_reg; + int qsel, osr; + int bclk; + + regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg); + osr = 16 - ((ctrl2_reg & MICFIL_CTRL2_CICOSR_MASK) + >> MICFIL_CTRL2_CICOSR_SHIFT); + + regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg); + qsel = ctrl2_reg & MICFIL_CTRL2_QSEL_MASK; + + switch (qsel) { + case MICFIL_HIGH_QUALITY: + bclk = rate * 8 * osr / 2; /* kfactor = 0.5 */ + break; + case MICFIL_MEDIUM_QUALITY: + case MICFIL_VLOW0_QUALITY: + bclk = rate * 4 * osr * 1; /* kfactor = 1 */ + break; + case MICFIL_LOW_QUALITY: + case MICFIL_VLOW1_QUALITY: + bclk = rate * 2 * osr * 2; /* kfactor = 2 */ + break; + case MICFIL_VLOW2_QUALITY: + bclk = rate * osr * 4; /* kfactor = 4 */ + break; + default: + dev_err(&micfil->pdev->dev, + "Please make sure you select a valid quality.\n"); + bclk = -1; + break; + } + + return bclk; +} + +static inline int get_clk_div(struct fsl_micfil *micfil, + unsigned int rate) +{ + u32 ctrl2_reg; + long mclk_rate; + int osr; + int clk_div; + + regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg); + osr = 16 - ((ctrl2_reg & MICFIL_CTRL2_CICOSR_MASK) + >> MICFIL_CTRL2_CICOSR_SHIFT); + + mclk_rate = clk_get_rate(micfil->mclk); + + clk_div = mclk_rate / (get_pdm_clk(micfil, rate) * 2); + + return clk_div; +} + +/* The SRES is a self-negated bit which provides the CPU with the + * capability to initialize the PDM Interface module through the + * slave-bus interface. This bit always reads as zero, and this + * bit is only effective when MDIS is cleared + */ +static int fsl_micfil_reset(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(micfil->regmap, + REG_MICFIL_CTRL1, + MICFIL_CTRL1_MDIS_MASK, + 0); + if (ret) { + dev_err(dev, "failed to clear MDIS bit %d\n", ret); + return ret; + } + + ret = regmap_update_bits(micfil->regmap, + REG_MICFIL_CTRL1, + MICFIL_CTRL1_SRES_MASK, + MICFIL_CTRL1_SRES); + if (ret) { + dev_err(dev, "failed to reset MICFIL: %d\n", ret); + return ret; + } + + return 0; +} + +static int fsl_micfil_set_mclk_rate(struct fsl_micfil *micfil, + unsigned int freq) +{ + struct device *dev = &micfil->pdev->dev; + int ret; + + clk_disable_unprepare(micfil->mclk); + + ret = clk_set_rate(micfil->mclk, freq * 1024); + if (ret) + dev_warn(dev, "failed to set rate (%u): %d\n", + freq * 1024, ret); + + clk_prepare_enable(micfil->mclk); + + return ret; +} + +static int fsl_micfil_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); + + if (!micfil) { + dev_err(dai->dev, + "micfil dai priv_data not set\n"); + return -EINVAL; + } + + return 0; +} + +static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); + struct device *dev = &micfil->pdev->dev; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = fsl_micfil_reset(dev); + if (ret) { + dev_err(dev, "failed to soft reset\n"); + return ret; + } + + /* DMA Interrupt Selection - DISEL bits + * 00 - DMA and IRQ disabled + * 01 - DMA req enabled + * 10 - IRQ enabled + * 11 - reserved + */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_DISEL_MASK, + (1 << MICFIL_CTRL1_DISEL_SHIFT)); + if (ret) { + dev_err(dev, "failed to update DISEL bits\n"); + return ret; + } + + /* Enable the module */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN_MASK, + MICFIL_CTRL1_PDMIEN); + if (ret) { + dev_err(dev, "failed to enable the module\n"); + return ret; + } + + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* Disable the module */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN_MASK, + 0); + if (ret) { + dev_err(dev, "failed to enable the module\n"); + return ret; + } + + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_DISEL_MASK, + (0 << MICFIL_CTRL1_DISEL_SHIFT)); + if (ret) { + dev_err(dev, "failed to update DISEL bits\n"); + return ret; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int fsl_set_clock_params(struct device *dev, unsigned int rate) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int clk_div; + int ret = 0; + + ret = fsl_micfil_set_mclk_rate(micfil, rate); + if (ret < 0) + dev_err(dev, "failed to set mclk[%lu] to rate %u\n", + clk_get_rate(micfil->mclk), rate); + + /* set CICOSR */ + ret |= regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, + MICFIL_CTRL2_CICOSR_MASK, + MICFIL_CTRL2_OSR_DEFAULT); + if (ret) + dev_err(dev, "failed to set CICOSR in reg 0x%X\n", + REG_MICFIL_CTRL2); + + /* set CLK_DIV */ + clk_div = get_clk_div(micfil, rate); + if (clk_div < 0) + ret = -EINVAL; + + ret |= regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, + MICFIL_CTRL2_CLKDIV_MASK, clk_div); + if (ret) + dev_err(dev, "failed to set CLKDIV in reg 0x%X\n", + REG_MICFIL_CTRL2); + + return ret; +} + +static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + struct device *dev = &micfil->pdev->dev; + int ret; + + /* 1. Disable the module */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN_MASK, 0); + if (ret) { + dev_err(dev, "failed to disable the module\n"); + return ret; + } + + /* enable channels */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, + 0xFF, ((1 << channels) - 1)); + if (ret) { + dev_err(dev, "failed to enable channels %d, reg 0x%X\n", ret, + REG_MICFIL_CTRL1); + return ret; + } + + ret = fsl_set_clock_params(dev, rate); + if (ret < 0) { + dev_err(dev, "Failed to set clock parameters [%d]\n", ret); + return ret; + } + + micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX; + + return 0; +} + +static int fsl_micfil_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); + struct device *dev = &micfil->pdev->dev; + + int ret; + + if (!freq) + return 0; + + ret = fsl_micfil_set_mclk_rate(micfil, freq); + if (ret < 0) + dev_err(dev, "failed to set mclk[%lu] to rate %u\n", + clk_get_rate(micfil->mclk), freq); + + return ret; +} + +static struct snd_soc_dai_ops fsl_micfil_dai_ops = { + .startup = fsl_micfil_startup, + .trigger = fsl_micfil_trigger, + .hw_params = fsl_micfil_hw_params, + .set_sysclk = fsl_micfil_set_dai_sysclk, +}; + +static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); + struct device *dev = cpu_dai->dev; + unsigned int val; + int ret; + int i; + + /* set qsel to medium */ + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, + MICFIL_CTRL2_QSEL_MASK, MICFIL_MEDIUM_QUALITY); + if (ret) { + dev_err(dev, "failed to set quality mode bits, reg 0x%X\n", + REG_MICFIL_CTRL2); + return ret; + } + + /* set default gain to max_gain */ + regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x77777777); + for (i = 0; i < 8; i++) + micfil->channel_gain[i] = 0xF; + + snd_soc_dai_init_dma_data(cpu_dai, NULL, + &micfil->dma_params_rx); + + /* FIFO Watermark Control - FIFOWMK*/ + val = MICFIL_FIFO_CTRL_FIFOWMK(micfil->soc->fifo_depth) - 1; + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL, + MICFIL_FIFO_CTRL_FIFOWMK_MASK, + val); + if (ret) { + dev_err(dev, "failed to set FIFOWMK\n"); + return ret; + } + + snd_soc_dai_set_drvdata(cpu_dai, micfil); + + return 0; +} + +static struct snd_soc_dai_driver fsl_micfil_dai = { + .probe = fsl_micfil_dai_probe, + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 8, + .rates = FSL_MICFIL_RATES, + .formats = FSL_MICFIL_FORMATS, + }, + .ops = &fsl_micfil_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_micfil_component = { + .name = "fsl-micfil-dai", + .controls = fsl_micfil_snd_controls, + .num_controls = ARRAY_SIZE(fsl_micfil_snd_controls), + +}; + +/* REGMAP */ +static const struct reg_default fsl_micfil_reg_defaults[] = { + {REG_MICFIL_CTRL1, 0x00000000}, + {REG_MICFIL_CTRL2, 0x00000000}, + {REG_MICFIL_STAT, 0x00000000}, + {REG_MICFIL_FIFO_CTRL, 0x00000007}, + {REG_MICFIL_FIFO_STAT, 0x00000000}, + {REG_MICFIL_DATACH0, 0x00000000}, + {REG_MICFIL_DATACH1, 0x00000000}, + {REG_MICFIL_DATACH2, 0x00000000}, + {REG_MICFIL_DATACH3, 0x00000000}, + {REG_MICFIL_DATACH4, 0x00000000}, + {REG_MICFIL_DATACH5, 0x00000000}, + {REG_MICFIL_DATACH6, 0x00000000}, + {REG_MICFIL_DATACH7, 0x00000000}, + {REG_MICFIL_DC_CTRL, 0x00000000}, + {REG_MICFIL_OUT_CTRL, 0x00000000}, + {REG_MICFIL_OUT_STAT, 0x00000000}, + {REG_MICFIL_VAD0_CTRL1, 0x00000000}, + {REG_MICFIL_VAD0_CTRL2, 0x000A0000}, + {REG_MICFIL_VAD0_STAT, 0x00000000}, + {REG_MICFIL_VAD0_SCONFIG, 0x00000000}, + {REG_MICFIL_VAD0_NCONFIG, 0x80000000}, + {REG_MICFIL_VAD0_NDATA, 0x00000000}, + {REG_MICFIL_VAD0_ZCD, 0x00000004}, +}; + +static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_MICFIL_CTRL1: + case REG_MICFIL_CTRL2: + case REG_MICFIL_STAT: + case REG_MICFIL_FIFO_CTRL: + case REG_MICFIL_FIFO_STAT: + case REG_MICFIL_DATACH0: + case REG_MICFIL_DATACH1: + case REG_MICFIL_DATACH2: + case REG_MICFIL_DATACH3: + case REG_MICFIL_DATACH4: + case REG_MICFIL_DATACH5: + case REG_MICFIL_DATACH6: + case REG_MICFIL_DATACH7: + case REG_MICFIL_DC_CTRL: + case REG_MICFIL_OUT_CTRL: + case REG_MICFIL_OUT_STAT: + case REG_MICFIL_VAD0_CTRL1: + case REG_MICFIL_VAD0_CTRL2: + case REG_MICFIL_VAD0_STAT: + case REG_MICFIL_VAD0_SCONFIG: + case REG_MICFIL_VAD0_NCONFIG: + case REG_MICFIL_VAD0_NDATA: + case REG_MICFIL_VAD0_ZCD: + return true; + default: + return false; + } +} + +static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_MICFIL_CTRL1: + case REG_MICFIL_CTRL2: + case REG_MICFIL_STAT: /* Write 1 to Clear */ + case REG_MICFIL_FIFO_CTRL: + case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */ + case REG_MICFIL_DC_CTRL: + case REG_MICFIL_OUT_CTRL: + case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ + case REG_MICFIL_VAD0_CTRL1: + case REG_MICFIL_VAD0_CTRL2: + case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ + case REG_MICFIL_VAD0_SCONFIG: + case REG_MICFIL_VAD0_NCONFIG: + case REG_MICFIL_VAD0_ZCD: + return true; + default: + return false; + } +} + +static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_MICFIL_STAT: + case REG_MICFIL_DATACH0: + case REG_MICFIL_DATACH1: + case REG_MICFIL_DATACH2: + case REG_MICFIL_DATACH3: + case REG_MICFIL_DATACH4: + case REG_MICFIL_DATACH5: + case REG_MICFIL_DATACH6: + case REG_MICFIL_DATACH7: + case REG_MICFIL_VAD0_STAT: + case REG_MICFIL_VAD0_NDATA: + return true; + default: + return false; + } +} + +static const struct regmap_config fsl_micfil_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = REG_MICFIL_VAD0_ZCD, + .reg_defaults = fsl_micfil_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults), + .readable_reg = fsl_micfil_readable_reg, + .volatile_reg = fsl_micfil_volatile_reg, + .writeable_reg = fsl_micfil_writeable_reg, + .cache_type = REGCACHE_RBTREE, +}; + +/* END OF REGMAP */ + +static irqreturn_t micfil_isr(int irq, void *devid) +{ + struct fsl_micfil *micfil = (struct fsl_micfil *)devid; + struct platform_device *pdev = micfil->pdev; + u32 stat_reg; + u32 fifo_stat_reg; + u32 ctrl1_reg; + bool dma_enabled; + int i; + + regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); + regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg); + regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg); + + dma_enabled = MICFIL_DMA_ENABLED(ctrl1_reg); + + /* Channel 0-7 Output Data Flags */ + for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) { + if (stat_reg & MICFIL_STAT_CHXF_MASK(i)) + dev_dbg(&pdev->dev, + "Data available in Data Channel %d\n", i); + /* if DMA is not enabled, field must be written with 1 + * to clear + */ + if (!dma_enabled) + regmap_write_bits(micfil->regmap, + REG_MICFIL_STAT, + MICFIL_STAT_CHXF_MASK(i), + 1); + } + + for (i = 0; i < MICFIL_FIFO_NUM; i++) { + if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER_MASK(i)) + dev_dbg(&pdev->dev, + "FIFO Overflow Exception flag for channel %d\n", + i); + + if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER_MASK(i)) + dev_dbg(&pdev->dev, + "FIFO Underflow Exception flag for channel %d\n", + i); + } + + return IRQ_HANDLED; +} + +static irqreturn_t micfil_err_isr(int irq, void *devid) +{ + struct fsl_micfil *micfil = (struct fsl_micfil *)devid; + struct platform_device *pdev = micfil->pdev; + u32 stat_reg; + + regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); + + if (stat_reg & MICFIL_STAT_BSY_FIL_MASK) + dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n"); + + if (stat_reg & MICFIL_STAT_FIR_RDY_MASK) + dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n"); + + if (stat_reg & MICFIL_STAT_LOWFREQF_MASK) { + dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n"); + regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, + MICFIL_STAT_LOWFREQF_MASK, 1); + } + + return IRQ_HANDLED; +} + +static int fsl_micfil_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id; + struct fsl_micfil *micfil; + struct resource *res; + void __iomem *regs; + int ret, i; + unsigned long irqflag = 0; + + micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL); + if (!micfil) + return -ENOMEM; + + micfil->pdev = pdev; + strncpy(micfil->name, np->name, sizeof(micfil->name) - 1); + + of_id = of_match_device(fsl_micfil_dt_ids, &pdev->dev); + if (!of_id || !of_id->data) + return -EINVAL; + + micfil->soc = of_id->data; + + /* ipg_clk is used to control the registers + * ipg_clk_app is used to operate the filter + */ + micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app"); + if (IS_ERR(micfil->mclk)) { + dev_err(&pdev->dev, "failed to get core clock: %ld\n", + PTR_ERR(micfil->mclk)); + return PTR_ERR(micfil->mclk); + } + + /* init regmap */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + micfil->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "ipg_clk", + regs, + &fsl_micfil_regmap_config); + if (IS_ERR(micfil->regmap)) { + dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n", + PTR_ERR(micfil->regmap)); + return PTR_ERR(micfil->regmap); + } + + /* dataline mask for RX */ + ret = of_property_read_u32_index(np, + "fsl,dataline", + 0, + &micfil->dataline); + if (ret) + micfil->dataline = 1; + + if (micfil->dataline & ~micfil->soc->dataline) { + dev_err(&pdev->dev, "dataline setting error, Mask is 0x%X\n", + micfil->soc->dataline); + return -EINVAL; + } + + /* get IRQs */ + for (i = 0; i < MICFIL_IRQ_LINES; i++) { + micfil->irq[i] = platform_get_irq(pdev, i); + dev_err(&pdev->dev, "GET IRQ: %d\n", micfil->irq[i]); + if (micfil->irq[i] < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); + return micfil->irq[i]; + } + } + + if (of_property_read_bool(np, "fsl,shared-interrupt")) + irqflag = IRQF_SHARED; + + /* Digital Microphone interface interrupt - IRQ 109 */ + ret = devm_request_irq(&pdev->dev, micfil->irq[0], + micfil_isr, irqflag, + micfil->name, micfil); + if (ret) { + dev_err(&pdev->dev, "failed to claim mic interface irq %u\n", + micfil->irq[0]); + return ret; + } + + /* Digital Microphone interface error interrupt - IRQ 110 */ + ret = devm_request_irq(&pdev->dev, micfil->irq[1], + micfil_err_isr, irqflag, + micfil->name, micfil); + if (ret) { + dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n", + micfil->irq[1]); + return ret; + } + + micfil->dma_params_rx.chan_name = "rx"; + micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; + micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; + + + platform_set_drvdata(pdev, micfil); + + pm_runtime_enable(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component, + &fsl_micfil_dai, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register component %s\n", + fsl_micfil_component.name); + return ret; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + dev_err(&pdev->dev, "failed to pcm register\n"); + + return ret; +} + +#ifdef CONFIG_PM +static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + + regcache_cache_only(micfil->regmap, true); + + clk_disable_unprepare(micfil->mclk); + + return 0; +} + +static int __maybe_unused fsl_micfil_runtime_resume(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(micfil->mclk); + if (ret < 0) + return ret; + + regcache_cache_only(micfil->regmap, false); + regcache_mark_dirty(micfil->regmap); + regcache_sync(micfil->regmap); + + return 0; +} +#endif /* CONFIG_PM*/ + +#ifdef CONFIG_PM_SLEEP +static int __maybe_unused fsl_micfil_suspend(struct device *dev) +{ + pm_runtime_force_suspend(dev); + + return 0; +} + +static int __maybe_unused fsl_micfil_resume(struct device *dev) +{ + pm_runtime_force_resume(dev); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_micfil_pm_ops = { + SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, + fsl_micfil_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(fsl_micfil_suspend, + fsl_micfil_resume) +}; + +static struct platform_driver fsl_micfil_driver = { + .probe = fsl_micfil_probe, + .driver = { + .name = "fsl-micfil-dai", + .pm = &fsl_micfil_pm_ops, + .of_match_table = fsl_micfil_dt_ids, + }, +}; +module_platform_driver(fsl_micfil_driver); + +MODULE_AUTHOR("Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>"); +MODULE_DESCRIPTION("NXP PDM Microphone Interface (MICFIL) driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h new file mode 100644 index 000000000000..bac825c3135a --- /dev/null +++ b/sound/soc/fsl/fsl_micfil.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PDM Microphone Interface for the NXP i.MX SoC + * Copyright 2018 NXP + */ + +#ifndef _FSL_MICFIL_H +#define _FSL_MICFIL_H + +/* MICFIL Register Map */ +#define REG_MICFIL_CTRL1 0x00 +#define REG_MICFIL_CTRL2 0x04 +#define REG_MICFIL_STAT 0x08 +#define REG_MICFIL_FIFO_CTRL 0x10 +#define REG_MICFIL_FIFO_STAT 0x14 +#define REG_MICFIL_DATACH0 0x24 +#define REG_MICFIL_DATACH1 0x28 +#define REG_MICFIL_DATACH2 0x2C +#define REG_MICFIL_DATACH3 0x30 +#define REG_MICFIL_DATACH4 0x34 +#define REG_MICFIL_DATACH5 0x38 +#define REG_MICFIL_DATACH6 0x3C +#define REG_MICFIL_DATACH7 0x40 +#define REG_MICFIL_DC_CTRL 0x64 +#define REG_MICFIL_OUT_CTRL 0x74 +#define REG_MICFIL_OUT_STAT 0x7C +#define REG_MICFIL_VAD0_CTRL1 0x90 +#define REG_MICFIL_VAD0_CTRL2 0x94 +#define REG_MICFIL_VAD0_STAT 0x98 +#define REG_MICFIL_VAD0_SCONFIG 0x9C +#define REG_MICFIL_VAD0_NCONFIG 0xA0 +#define REG_MICFIL_VAD0_NDATA 0xA4 +#define REG_MICFIL_VAD0_ZCD 0xA8 + +/* MICFIL Control Register 1 -- REG_MICFILL_CTRL1 0x00 */ +#define MICFIL_CTRL1_MDIS_SHIFT 31 +#define MICFIL_CTRL1_MDIS_MASK BIT(MICFIL_CTRL1_MDIS_SHIFT) +#define MICFIL_CTRL1_MDIS BIT(MICFIL_CTRL1_MDIS_SHIFT) +#define MICFIL_CTRL1_DOZEN_SHIFT 30 +#define MICFIL_CTRL1_DOZEN_MASK BIT(MICFIL_CTRL1_DOZEN_SHIFT) +#define MICFIL_CTRL1_DOZEN BIT(MICFIL_CTRL1_DOZEN_SHIFT) +#define MICFIL_CTRL1_PDMIEN_SHIFT 29 +#define MICFIL_CTRL1_PDMIEN_MASK BIT(MICFIL_CTRL1_PDMIEN_SHIFT) +#define MICFIL_CTRL1_PDMIEN BIT(MICFIL_CTRL1_PDMIEN_SHIFT) +#define MICFIL_CTRL1_DBG_SHIFT 28 +#define MICFIL_CTRL1_DBG_MASK BIT(MICFIL_CTRL1_DBG_SHIFT) +#define MICFIL_CTRL1_DBG BIT(MICFIL_CTRL1_DBG_SHIFT) +#define MICFIL_CTRL1_SRES_SHIFT 27 +#define MICFIL_CTRL1_SRES_MASK BIT(MICFIL_CTRL1_SRES_SHIFT) +#define MICFIL_CTRL1_SRES BIT(MICFIL_CTRL1_SRES_SHIFT) +#define MICFIL_CTRL1_DBGE_SHIFT 26 +#define MICFIL_CTRL1_DBGE_MASK BIT(MICFIL_CTRL1_DBGE_SHIFT) +#define MICFIL_CTRL1_DBGE BIT(MICFIL_CTRL1_DBGE_SHIFT) +#define MICFIL_CTRL1_DISEL_SHIFT 24 +#define MICFIL_CTRL1_DISEL_WIDTH 2 +#define MICFIL_CTRL1_DISEL_MASK ((BIT(MICFIL_CTRL1_DISEL_WIDTH) - 1) \ + << MICFIL_CTRL1_DISEL_SHIFT) +#define MICFIL_CTRL1_DISEL(v) (((v) << MICFIL_CTRL1_DISEL_SHIFT) \ + & MICFIL_CTRL1_DISEL_MASK) +#define MICFIL_CTRL1_ERREN_SHIFT 23 +#define MICFIL_CTRL1_ERREN_MASK BIT(MICFIL_CTRL1_ERREN_SHIFT) +#define MICFIL_CTRL1_ERREN BIT(MICFIL_CTRL1_ERREN_SHIFT) +#define MICFIL_CTRL1_CHEN_SHIFT 0 +#define MICFIL_CTRL1_CHEN_WIDTH 8 +#define MICFIL_CTRL1_CHEN_MASK(x) (BIT(x) << MICFIL_CTRL1_CHEN_SHIFT) +#define MICFIL_CTRL1_CHEN(x) (MICFIL_CTRL1_CHEN_MASK(x)) + +/* MICFIL Control Register 2 -- REG_MICFILL_CTRL2 0x04 */ +#define MICFIL_CTRL2_QSEL_SHIFT 25 +#define MICFIL_CTRL2_QSEL_WIDTH 3 +#define MICFIL_CTRL2_QSEL_MASK ((BIT(MICFIL_CTRL2_QSEL_WIDTH) - 1) \ + << MICFIL_CTRL2_QSEL_SHIFT) +#define MICFIL_HIGH_QUALITY BIT(MICFIL_CTRL2_QSEL_SHIFT) +#define MICFIL_MEDIUM_QUALITY (0 << MICFIL_CTRL2_QSEL_SHIFT) +#define MICFIL_LOW_QUALITY (7 << MICFIL_CTRL2_QSEL_SHIFT) +#define MICFIL_VLOW0_QUALITY (6 << MICFIL_CTRL2_QSEL_SHIFT) +#define MICFIL_VLOW1_QUALITY (5 << MICFIL_CTRL2_QSEL_SHIFT) +#define MICFIL_VLOW2_QUALITY (4 << MICFIL_CTRL2_QSEL_SHIFT) + +#define MICFIL_CTRL2_CICOSR_SHIFT 16 +#define MICFIL_CTRL2_CICOSR_WIDTH 4 +#define MICFIL_CTRL2_CICOSR_MASK ((BIT(MICFIL_CTRL2_CICOSR_WIDTH) - 1) \ + << MICFIL_CTRL2_CICOSR_SHIFT) +#define MICFIL_CTRL2_CICOSR(v) (((v) << MICFIL_CTRL2_CICOSR_SHIFT) \ + & MICFIL_CTRL2_CICOSR_MASK) +#define MICFIL_CTRL2_CLKDIV_SHIFT 0 +#define MICFIL_CTRL2_CLKDIV_WIDTH 8 +#define MICFIL_CTRL2_CLKDIV_MASK ((BIT(MICFIL_CTRL2_CLKDIV_WIDTH) - 1) \ + << MICFIL_CTRL2_CLKDIV_SHIFT) +#define MICFIL_CTRL2_CLKDIV(v) (((v) << MICFIL_CTRL2_CLKDIV_SHIFT) \ + & MICFIL_CTRL2_CLKDIV_MASK) + +/* MICFIL Status Register -- REG_MICFIL_STAT 0x08 */ +#define MICFIL_STAT_BSY_FIL_SHIFT 31 +#define MICFIL_STAT_BSY_FIL_MASK BIT(MICFIL_STAT_BSY_FIL_SHIFT) +#define MICFIL_STAT_BSY_FIL BIT(MICFIL_STAT_BSY_FIL_SHIFT) +#define MICFIL_STAT_FIR_RDY_SHIFT 30 +#define MICFIL_STAT_FIR_RDY_MASK BIT(MICFIL_STAT_FIR_RDY_SHIFT) +#define MICFIL_STAT_FIR_RDY BIT(MICFIL_STAT_FIR_RDY_SHIFT) +#define MICFIL_STAT_LOWFREQF_SHIFT 29 +#define MICFIL_STAT_LOWFREQF_MASK BIT(MICFIL_STAT_LOWFREQF_SHIFT) +#define MICFIL_STAT_LOWFREQF BIT(MICFIL_STAT_LOWFREQF_SHIFT) +#define MICFIL_STAT_CHXF_SHIFT(v) (v) +#define MICFIL_STAT_CHXF_MASK(v) BIT(MICFIL_STAT_CHXF_SHIFT(v)) +#define MICFIL_STAT_CHXF(v) BIT(MICFIL_STAT_CHXF_SHIFT(v)) + +/* MICFIL FIFO Control Register -- REG_MICFIL_FIFO_CTRL 0x10 */ +#define MICFIL_FIFO_CTRL_FIFOWMK_SHIFT 0 +#define MICFIL_FIFO_CTRL_FIFOWMK_WIDTH 3 +#define MICFIL_FIFO_CTRL_FIFOWMK_MASK ((BIT(MICFIL_FIFO_CTRL_FIFOWMK_WIDTH) - 1) \ + << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT) +#define MICFIL_FIFO_CTRL_FIFOWMK(v) (((v) << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT) \ + & MICFIL_FIFO_CTRL_FIFOWMK_MASK) + +/* MICFIL FIFO Status Register -- REG_MICFIL_FIFO_STAT 0x14 */ +#define MICFIL_FIFO_STAT_FIFOX_OVER_SHIFT(v) (v) +#define MICFIL_FIFO_STAT_FIFOX_OVER_MASK(v) BIT(MICFIL_FIFO_STAT_FIFOX_OVER_SHIFT(v)) +#define MICFIL_FIFO_STAT_FIFOX_UNDER_SHIFT(v) ((v) + 8) +#define MICFIL_FIFO_STAT_FIFOX_UNDER_MASK(v) BIT(MICFIL_FIFO_STAT_FIFOX_UNDER_SHIFT(v)) + +/* MICFIL HWVAD0 Control 1 Register -- REG_MICFIL_VAD0_CTRL1*/ +#define MICFIL_VAD0_CTRL1_CHSEL_SHIFT 24 +#define MICFIL_VAD0_CTRL1_CHSEL_WIDTH 3 +#define MICFIL_VAD0_CTRL1_CHSEL_MASK ((BIT(MICFIL_VAD0_CTRL1_CHSEL_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL1_CHSEL_SHIFT) +#define MICFIL_VAD0_CTRL1_CHSEL(v) (((v) << MICFIL_VAD0_CTRL1_CHSEL_SHIFT) \ + & MICFIL_VAD0_CTRL1_CHSEL_MASK) +#define MICFIL_VAD0_CTRL1_CICOSR_SHIFT 16 +#define MICFIL_VAD0_CTRL1_CICOSR_WIDTH 4 +#define MICFIL_VAD0_CTRL1_CICOSR_MASK ((BIT(MICFIL_VAD0_CTRL1_CICOSR_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL1_CICOSR_SHIFT) +#define MICFIL_VAD0_CTRL1_CICOSR(v) (((v) << MICFIL_VAD0_CTRL1_CICOSR_SHIFT) \ + & MICFIL_VAD0_CTRL1_CICOSR_MASK) +#define MICFIL_VAD0_CTRL1_INITT_SHIFT 8 +#define MICFIL_VAD0_CTRL1_INITT_WIDTH 5 +#define MICFIL_VAD0_CTRL1_INITT_MASK ((BIT(MICFIL_VAD0_CTRL1_INITT_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL1_INITT_SHIFT) +#define MICFIL_VAD0_CTRL1_INITT(v) (((v) << MICFIL_VAD0_CTRL1_INITT_SHIFT) \ + & MICFIL_VAD0_CTRL1_INITT_MASK) +#define MICFIL_VAD0_CTRL1_ST10_SHIFT 4 +#define MICFIL_VAD0_CTRL1_ST10_MASK BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT) +#define MICFIL_VAD0_CTRL1_ST10 BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT) +#define MICFIL_VAD0_CTRL1_ERIE_SHIFT 3 +#define MICFIL_VAD0_CTRL1_ERIE_MASK BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT) +#define MICFIL_VAD0_CTRL1_ERIE BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT) +#define MICFIL_VAD0_CTRL1_IE_SHIFT 2 +#define MICFIL_VAD0_CTRL1_IE_MASK BIT(MICFIL_VAD0_CTRL1_IE_SHIFT) +#define MICFIL_VAD0_CTRL1_IE BIT(MICFIL_VAD0_CTRL1_IE_SHIFT) +#define MICFIL_VAD0_CTRL1_RST_SHIFT 1 +#define MICFIL_VAD0_CTRL1_RST_MASK BIT(MICFIL_VAD0_CTRL1_RST_SHIFT) +#define MICFIL_VAD0_CTRL1_RST BIT(MICFIL_VAD0_CTRL1_RST_SHIFT) +#define MICFIL_VAD0_CTRL1_EN_SHIFT 0 +#define MICFIL_VAD0_CTRL1_EN_MASK BIT(MICFIL_VAD0_CTRL1_EN_SHIFT) +#define MICFIL_VAD0_CTRL1_EN BIT(MICFIL_VAD0_CTRL1_EN_SHIFT) + +/* MICFIL HWVAD0 Control 2 Register -- REG_MICFIL_VAD0_CTRL2*/ +#define MICFIL_VAD0_CTRL2_FRENDIS_SHIFT 31 +#define MICFIL_VAD0_CTRL2_FRENDIS_MASK BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT) +#define MICFIL_VAD0_CTRL2_FRENDIS BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT) +#define MICFIL_VAD0_CTRL2_PREFEN_SHIFT 30 +#define MICFIL_VAD0_CTRL2_PREFEN_MASK BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT) +#define MICFIL_VAD0_CTRL2_PREFEN BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT) +#define MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT 28 +#define MICFIL_VAD0_CTRL2_FOUTDIS_MASK BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT) +#define MICFIL_VAD0_CTRL2_FOUTDIS BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT) +#define MICFIL_VAD0_CTRL2_FRAMET_SHIFT 16 +#define MICFIL_VAD0_CTRL2_FRAMET_WIDTH 6 +#define MICFIL_VAD0_CTRL2_FRAMET_MASK ((BIT(MICFIL_VAD0_CTRL2_FRAMET_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL2_FRAMET_SHIFT) +#define MICFIL_VAD0_CTRL2_FRAMET(v) (((v) << MICFIL_VAD0_CTRL2_FRAMET_SHIFT) \ + & MICFIL_VAD0_CTRL2_FRAMET_MASK) +#define MICFIL_VAD0_CTRL2_INPGAIN_SHIFT 8 +#define MICFIL_VAD0_CTRL2_INPGAIN_WIDTH 4 +#define MICFIL_VAD0_CTRL2_INPGAIN_MASK ((BIT(MICFIL_VAD0_CTRL2_INPGAIN_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT) +#define MICFIL_VAD0_CTRL2_INPGAIN(v) (((v) << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT) \ + & MICFIL_VAD0_CTRL2_INPGAIN_MASK) +#define MICFIL_VAD0_CTRL2_HPF_SHIFT 0 +#define MICFIL_VAD0_CTRL2_HPF_WIDTH 2 +#define MICFIL_VAD0_CTRL2_HPF_MASK ((BIT(MICFIL_VAD0_CTRL2_HPF_WIDTH) - 1) \ + << MICFIL_VAD0_CTRL2_HPF_SHIFT) +#define MICFIL_VAD0_CTRL2_HPF(v) (((v) << MICFIL_VAD0_CTRL2_HPF_SHIFT) \ + & MICFIL_VAD0_CTRL2_HPF_MASK) + +/* MICFIL HWVAD0 Signal CONFIG Register -- REG_MICFIL_VAD0_SCONFIG */ +#define MICFIL_VAD0_SCONFIG_SFILEN_SHIFT 31 +#define MICFIL_VAD0_SCONFIG_SFILEN_MASK BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SFILEN BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT 30 +#define MICFIL_VAD0_SCONFIG_SMAXEN_MASK BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SMAXEN BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SGAIN_SHIFT 0 +#define MICFIL_VAD0_SCONFIG_SGAIN_WIDTH 4 +#define MICFIL_VAD0_SCONFIG_SGAIN_MASK ((BIT(MICFIL_VAD0_SCONFIG_SGAIN_WIDTH) - 1) \ + << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT) +#define MICFIL_VAD0_SCONFIG_SGAIN(v) (((v) << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT) \ + & MICFIL_VAD0_SCONFIG_SGAIN_MASK) + +/* MICFIL HWVAD0 Noise CONFIG Register -- REG_MICFIL_VAD0_NCONFIG */ +#define MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT 31 +#define MICFIL_VAD0_NCONFIG_NFILAUT_MASK BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT) +#define MICFIL_VAD0_NCONFIG_NFILAUT BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT) +#define MICFIL_VAD0_NCONFIG_NMINEN_SHIFT 30 +#define MICFIL_VAD0_NCONFIG_NMINEN_MASK BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NMINEN BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NDECEN_SHIFT 29 +#define MICFIL_VAD0_NCONFIG_NDECEN_MASK BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NDECEN BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NOREN_SHIFT 28 +#define MICFIL_VAD0_NCONFIG_NOREN BIT(MICFIL_VAD0_NCONFIG_NOREN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT 8 +#define MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH 5 +#define MICFIL_VAD0_NCONFIG_NFILADJ_MASK ((BIT(MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH) - 1) \ + << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT) +#define MICFIL_VAD0_NCONFIG_NFILADJ(v) (((v) << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT) \ + & MICFIL_VAD0_NCONFIG_NFILADJ_MASK) +#define MICFIL_VAD0_NCONFIG_NGAIN_SHIFT 0 +#define MICFIL_VAD0_NCONFIG_NGAIN_WIDTH 4 +#define MICFIL_VAD0_NCONFIG_NGAIN_MASK ((BIT(MICFIL_VAD0_NCONFIG_NGAIN_WIDTH) - 1) \ + << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT) +#define MICFIL_VAD0_NCONFIG_NGAIN(v) (((v) << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT) \ + & MICFIL_VAD0_NCONFIG_NGAIN_MASK) + +/* MICFIL HWVAD0 Zero-Crossing Detector - REG_MICFIL_VAD0_ZCD */ +#define MICFIL_VAD0_ZCD_ZCDTH_SHIFT 16 +#define MICFIL_VAD0_ZCD_ZCDTH_WIDTH 10 +#define MICFIL_VAD0_ZCD_ZCDTH_MASK ((BIT(MICFIL_VAD0_ZCD_ZCDTH_WIDTH) - 1) \ + << MICFIL_VAD0_ZCD_ZCDTH_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDTH(v) (((v) << MICFIL_VAD0_ZCD_ZCDTH_SHIFT)\ + & MICFIL_VAD0_ZCD_ZCDTH_MASK) +#define MICFIL_VAD0_ZCD_ZCDADJ_SHIFT 8 +#define MICFIL_VAD0_ZCD_ZCDADJ_WIDTH 4 +#define MICFIL_VAD0_ZCD_ZCDADJ_MASK ((BIT(MICFIL_VAD0_ZCD_ZCDADJ_WIDTH) - 1)\ + << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDADJ(v) (((v) << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT)\ + & MICFIL_VAD0_ZCD_ZCDADJ_MASK) +#define MICFIL_VAD0_ZCD_ZCDAND_SHIFT 4 +#define MICFIL_VAD0_ZCD_ZCDAND_MASK BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDAND BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDAUT_SHIFT 2 +#define MICFIL_VAD0_ZCD_ZCDAUT_MASK BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDAUT BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDEN_SHIFT 0 +#define MICFIL_VAD0_ZCD_ZCDEN_MASK BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDEN BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT) + +/* MICFIL HWVAD0 Status Register - REG_MICFIL_VAD0_STAT */ +#define MICFIL_VAD0_STAT_INITF_SHIFT 31 +#define MICFIL_VAD0_STAT_INITF_MASK BIT(MICFIL_VAD0_STAT_INITF_SHIFT) +#define MICFIL_VAD0_STAT_INITF BIT(MICFIL_VAD0_STAT_INITF_SHIFT) +#define MICFIL_VAD0_STAT_INSATF_SHIFT 16 +#define MICFIL_VAD0_STAT_INSATF_MASK BIT(MICFIL_VAD0_STAT_INSATF_SHIFT) +#define MICFIL_VAD0_STAT_INSATF BIT(MICFIL_VAD0_STAT_INSATF_SHIFT) +#define MICFIL_VAD0_STAT_EF_SHIFT 15 +#define MICFIL_VAD0_STAT_EF_MASK BIT(MICFIL_VAD0_STAT_EF_SHIFT) +#define MICFIL_VAD0_STAT_EF BIT(MICFIL_VAD0_STAT_EF_SHIFT) +#define MICFIL_VAD0_STAT_IF_SHIFT 0 +#define MICFIL_VAD0_STAT_IF_MASK BIT(MICFIL_VAD0_STAT_IF_SHIFT) +#define MICFIL_VAD0_STAT_IF BIT(MICFIL_VAD0_STAT_IF_SHIFT) + +/* MICFIL Output Control Register */ +#define MICFIL_OUTGAIN_CHX_SHIFT(v) (4 * (v)) + +/* Constants */ +#define MICFIL_DMA_IRQ_DISABLED(v) ((v) & MICFIL_CTRL1_DISEL_MASK) +#define MICFIL_DMA_ENABLED(v) ((0x1 << MICFIL_CTRL1_DISEL_SHIFT) \ + == ((v) & MICFIL_CTRL1_DISEL_MASK)) +#define MICFIL_IRQ_ENABLED(v) ((0x2 << MICFIL_CTRL1_DISEL_SHIFT) \ + == ((v) & MICFIL_CTRL1_DISEL_MASK)) +#define MICFIL_OUTPUT_CHANNELS 8 +#define MICFIL_FIFO_NUM 8 + +#define FIFO_PTRWID 3 +#define FIFO_LEN BIT(FIFO_PTRWID) + +#define MICFIL_IRQ_LINES 2 +#define MICFIL_MAX_RETRY 25 +#define MICFIL_SLEEP_MIN 90000 /* in us */ +#define MICFIL_SLEEP_MAX 100000 /* in us */ +#define MICFIL_DMA_MAXBURST_RX 6 +#define MICFIL_CTRL2_OSR_DEFAULT (0 << MICFIL_CTRL2_CICOSR_SHIFT) + +#endif /* _FSL_MICFIL_H */ diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 4163f2cfc06f..db9e0872f73d 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -62,10 +62,10 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) dev_dbg(dev, "isr: Start of Tx word detected\n"); if (flags & FSL_SAI_CSR_SEF) - dev_warn(dev, "isr: Tx Frame sync error detected\n"); + dev_dbg(dev, "isr: Tx Frame sync error detected\n"); if (flags & FSL_SAI_CSR_FEF) { - dev_warn(dev, "isr: Transmit underrun detected\n"); + dev_dbg(dev, "isr: Transmit underrun detected\n"); /* FIFO reset for safety */ xcsr |= FSL_SAI_CSR_FR; } @@ -96,10 +96,10 @@ irq_rx: dev_dbg(dev, "isr: Start of Rx word detected\n"); if (flags & FSL_SAI_CSR_SEF) - dev_warn(dev, "isr: Rx Frame sync error detected\n"); + dev_dbg(dev, "isr: Rx Frame sync error detected\n"); if (flags & FSL_SAI_CSR_FEF) { - dev_warn(dev, "isr: Receive overflow detected\n"); + dev_dbg(dev, "isr: Receive overflow detected\n"); /* FIFO reset for safety */ xcsr |= FSL_SAI_CSR_FR; } diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 740b90df44bb..4842e6df9a2d 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -96,7 +96,7 @@ struct fsl_spdif_priv { bool dpll_locked; u32 txrate[SPDIF_TXRATE_MAX]; u8 txclk_df[SPDIF_TXRATE_MAX]; - u8 sysclk_df[SPDIF_TXRATE_MAX]; + u16 sysclk_df[SPDIF_TXRATE_MAX]; u8 txclk_src[SPDIF_TXRATE_MAX]; u8 rxclk_src; struct clk *txclk[SPDIF_TXRATE_MAX]; @@ -376,7 +376,8 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, struct platform_device *pdev = spdif_priv->pdev; unsigned long csfs = 0; u32 stc, mask, rate; - u8 clk, txclk_df, sysclk_df; + u16 sysclk_df; + u8 clk, txclk_df; int ret; switch (sample_rate) { @@ -1109,8 +1110,9 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk); u64 rate_ideal, rate_actual, sub; - u32 sysclk_dfmin, sysclk_dfmax; - u32 txclk_df, sysclk_df, arate; + u32 arate; + u16 sysclk_dfmin, sysclk_dfmax, sysclk_df; + u8 txclk_df; /* The sysclk has an extra divisor [2, 512] */ sysclk_dfmin = is_sysclk ? 2 : 1; @@ -1320,7 +1322,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) } ret = imx_pcm_dma_init(pdev, IMX_SPDIF_DMABUF_SIZE); - if (ret) + if (ret && ret != -EPROBE_DEFER) dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret); return ret; diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index 7666dabaccfd..e6c61e07bc1a 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -152,7 +152,7 @@ enum spdif_gainsel { #define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET) #define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET) #define STC_TXCLK_DF_OFFSET 0 -#define STC_TXCLK_DF_MASK (0x7ff << STC_TXCLK_DF_OFFSET) +#define STC_TXCLK_DF_MASK (0x7f << STC_TXCLK_DF_OFFSET) #define STC_TXCLK_DF(x) ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK) #define STC_TXCLK_SRC_MAX 8 diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 0a648229e643..09b2967befd9 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1439,8 +1439,10 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi) * different name to register the device. */ if (!ssi->card_name[0] && of_get_property(np, "codec-handle", NULL)) { - sprop = of_get_property(of_find_node_by_path("/"), - "compatible", NULL); + struct device_node *root = of_find_node_by_path("/"); + + sprop = of_get_property(root, "compatible", NULL); + of_node_put(root); /* Strip "fsl," in the compatible name if applicable */ p = strrchr(sprop, ','); if (p) diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index c29200cf755a..bf8597f57dce 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -104,14 +104,16 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ssi_pdev = of_find_device_by_node(ssi_np); if (!ssi_pdev) { - dev_err(&pdev->dev, "failed to find SSI platform device\n"); + dev_dbg(&pdev->dev, "failed to find SSI platform device\n"); ret = -EPROBE_DEFER; goto fail; } + put_device(&ssi_pdev->dev); codec_dev = of_find_i2c_device_by_node(codec_np); if (!codec_dev) { - dev_err(&pdev->dev, "failed to find codec platform device\n"); - return -EPROBE_DEFER; + dev_dbg(&pdev->dev, "failed to find codec platform device\n"); + ret = -EPROBE_DEFER; + goto fail; } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); @@ -156,7 +158,9 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); goto fail; } diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index fb896b2c9ba3..797d66e43d49 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -67,10 +67,8 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) goto end; ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret) { + if (ret && ret != -EPROBE_DEFER) dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret); - goto end; - } end: of_node_put(spdif_np); diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index 92c2cf06f40a..83f1243145b0 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -8,14 +8,6 @@ config SND_SIMPLE_CARD This option enables generic simple sound card support It also support DPCM of multi CPU single Codec ststem. -config SND_SIMPLE_SCU_CARD - tristate "ASoC Simple SCU sound card support" - depends on OF - select SND_SIMPLE_CARD_UTILS - help - This option enables generic simple SCU sound card support. - It supports DPCM of multi CPU single Codec system. - config SND_AUDIO_GRAPH_CARD tristate "ASoC Audio Graph sound card support" depends on OF @@ -24,12 +16,3 @@ config SND_AUDIO_GRAPH_CARD This option enables generic simple sound card support with OF-graph DT bindings. It also support DPCM of multi CPU single Codec ststem. - -config SND_AUDIO_GRAPH_SCU_CARD - tristate "ASoC Audio Graph SCU sound card support" - depends on OF - select SND_SIMPLE_CARD_UTILS - help - This option enables generic simple SCU sound card support - with OF-graph DT bindings. - It supports DPCM of multi CPU single Codec ststem. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index 9dec293a4c4d..21c29e5e0671 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,12 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o -snd-soc-simple-scu-card-objs := simple-scu-card.o snd-soc-audio-graph-card-objs := audio-graph-card.o -snd-soc-audio-graph-scu-card-objs := audio-graph-scu-card.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o -obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o -obj-$(CONFIG_SND_AUDIO_GRAPH_SCU_CARD) += snd-soc-audio-graph-scu-card.o diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 0d6144560a1e..bb12351330e8 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -20,13 +20,13 @@ #include <linux/string.h> #include <sound/simple_card_utils.h> -struct graph_card_data { +struct graph_priv { struct snd_soc_card snd_card; struct graph_dai_props { struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ - struct snd_soc_dai_link_component platform; + struct snd_soc_dai_link_component platforms; struct asoc_simple_card_data adata; struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; @@ -39,6 +39,13 @@ struct graph_card_data { struct gpio_desc *pa_gpio; }; +struct link_info { + int dais; /* number of dai */ + int link; /* number of link */ + int conf; /* number of codec_conf */ + int cpu; /* turn for CPU / Codec */ +}; + #define graph_priv_to_card(priv) (&(priv)->snd_card) #define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) #define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) @@ -46,12 +53,12 @@ struct graph_card_data { #define PREFIX "audio-graph-card," -static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event) +static int graph_outdrv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) { struct snd_soc_dapm_context *dapm = w->dapm; - struct graph_card_data *priv = snd_soc_card_get_drvdata(dapm->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(dapm->card); switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -67,16 +74,16 @@ static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = { +static const struct snd_soc_dapm_widget graph_dapm_widgets[] = { SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM, - 0, 0, NULL, 0, asoc_graph_card_outdrv_event, + 0, 0, NULL, 0, graph_outdrv_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), }; -static int asoc_graph_card_startup(struct snd_pcm_substream *substream) +static int graph_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); int ret; @@ -91,10 +98,10 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream) return ret; } -static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) +static void graph_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); asoc_simple_card_clk_disable(dai_props->cpu_dai); @@ -102,13 +109,13 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) asoc_simple_card_clk_disable(dai_props->codec_dai); } -static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int graph_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); unsigned int mclk, mclk_fs = 0; int ret = 0; @@ -133,15 +140,15 @@ err: return ret; } -static const struct snd_soc_ops asoc_graph_card_ops = { - .startup = asoc_graph_card_startup, - .shutdown = asoc_graph_card_shutdown, - .hw_params = asoc_graph_card_hw_params, +static const struct snd_soc_ops graph_ops = { + .startup = graph_startup, + .shutdown = graph_shutdown, + .hw_params = graph_hw_params, }; -static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) +static int graph_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); int ret = 0; @@ -158,10 +165,10 @@ static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) +static int graph_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) { - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); asoc_simple_card_convert_fixup(&dai_props->adata, params); @@ -169,41 +176,64 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, - struct device_node *cpu_ep, - struct device_node *codec_ep, - struct graph_card_data *priv, - int *dai_idx, int link_idx, - int *conf_idx, int is_cpu) +static void graph_get_conversion(struct device *dev, + struct device_node *ep, + struct asoc_simple_card_data *adata) { - struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); - struct device_node *ep = is_cpu ? cpu_ep : codec_ep; + struct device_node *top = dev->of_node; struct device_node *port = of_get_parent(ep); struct device_node *ports = of_get_parent(port); struct device_node *node = of_graph_get_port_parent(ep); + + asoc_simple_card_parse_convert(dev, top, NULL, adata); + asoc_simple_card_parse_convert(dev, node, PREFIX, adata); + asoc_simple_card_parse_convert(dev, ports, NULL, adata); + asoc_simple_card_parse_convert(dev, port, NULL, adata); + asoc_simple_card_parse_convert(dev, ep, NULL, adata); +} + +static int graph_dai_link_of_dpcm(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li, + int dup_codec) +{ + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link); + struct device_node *top = dev->of_node; + struct device_node *ep = li->cpu ? cpu_ep : codec_ep; + struct device_node *port; + struct device_node *ports; + struct device_node *node; struct asoc_simple_dai *dai; struct snd_soc_dai_link_component *codecs = dai_link->codecs; int ret; - dev_dbg(dev, "link_of DPCM (for %s)\n", is_cpu ? "CPU" : "Codec"); + /* Do it all CPU endpoint, and 1st Codec endpoint */ + if (!li->cpu && dup_codec) + return 0; + + port = of_get_parent(ep); + ports = of_get_parent(port); + node = of_graph_get_port_parent(ep); + + li->link++; + + dev_dbg(dev, "link_of DPCM (%pOF)\n", ep); of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(port, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs); - asoc_simple_card_parse_convert(dev, top, NULL, &dai_props->adata); - asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); - asoc_simple_card_parse_convert(dev, ports, NULL, &dai_props->adata); - asoc_simple_card_parse_convert(dev, port, NULL, &dai_props->adata); - asoc_simple_card_parse_convert(dev, ep, NULL, &dai_props->adata); + graph_get_conversion(dev, ep, &dai_props->adata); of_node_put(ports); of_node_put(port); + of_node_put(node); - if (is_cpu) { + if (li->cpu) { /* BE is dummy */ codecs->of_node = NULL; @@ -215,7 +245,7 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, dai_link->dpcm_merged_format = 1; dai = - dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + dai_props->cpu_dai = &priv->dais[li->dais++]; ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); if (ret) @@ -244,13 +274,13 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, /* BE settings */ dai_link->no_pcm = 1; - dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup; + dai_link->be_hw_params_fixup = graph_be_hw_params_fixup; dai = - dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + dai_props->codec_dai = &priv->dais[li->dais++]; cconf = - dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + dai_props->codec_conf = &priv->codec_conf[li->conf++]; ret = asoc_simple_card_parse_graph_codec(ep, dai_link); if (ret < 0) @@ -277,14 +307,12 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, "prefix"); } + asoc_simple_card_canonicalize_platform(dai_link); + ret = asoc_simple_card_of_parse_tdm(ep, dai); if (ret) return ret; - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - return ret; - ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &dai_link->dai_fmt); if (ret < 0) @@ -292,35 +320,46 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, dai_link->dpcm_playback = 1; dai_link->dpcm_capture = 1; - dai_link->ops = &asoc_graph_card_ops; - dai_link->init = asoc_graph_card_dai_init; + dai_link->ops = &graph_ops; + dai_link->init = graph_dai_init; return 0; } -static int asoc_graph_card_dai_link_of(struct device_node *top, - struct device_node *cpu_ep, - struct device_node *codec_ep, - struct graph_card_data *priv, - int *dai_idx, int link_idx) +static int graph_dai_link_of(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li) { struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); - struct device_node *cpu_port = of_get_parent(cpu_ep); - struct device_node *codec_port = of_get_parent(codec_ep); - struct device_node *cpu_ports = of_get_parent(cpu_port); - struct device_node *codec_ports = of_get_parent(codec_port); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link); + struct device_node *top = dev->of_node; + struct device_node *cpu_port; + struct device_node *cpu_ports; + struct device_node *codec_port; + struct device_node *codec_ports; struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; int ret; - dev_dbg(dev, "link_of\n"); + /* Do it only CPU turn */ + if (!li->cpu) + return 0; + + cpu_port = of_get_parent(cpu_ep); + cpu_ports = of_get_parent(cpu_port); + codec_port = of_get_parent(codec_ep); + codec_ports = of_get_parent(codec_port); + + dev_dbg(dev, "link_of (%pOF)\n", cpu_ep); + + li->link++; cpu_dai = - dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + dai_props->cpu_dai = &priv->dais[li->dais++]; codec_dai = - dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + dai_props->codec_dai = &priv->dais[li->dais++]; /* Factor to mclk, used in hw_params() */ of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); @@ -364,10 +403,6 @@ static int asoc_graph_card_dai_link_of(struct device_node *top, if (ret < 0) return ret; - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - return ret; - ret = asoc_simple_card_set_dailink_name(dev, dai_link, "%s-%s", dai_link->cpu_dai_name, @@ -375,30 +410,89 @@ static int asoc_graph_card_dai_link_of(struct device_node *top, if (ret < 0) return ret; - dai_link->ops = &asoc_graph_card_ops; - dai_link->init = asoc_graph_card_dai_init; + dai_link->ops = &graph_ops; + dai_link->init = graph_dai_init; + asoc_simple_card_canonicalize_platform(dai_link); asoc_simple_card_canonicalize_cpu(dai_link, of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); return 0; } -static int asoc_graph_card_parse_of(struct graph_card_data *priv) +static int graph_for_each_link(struct graph_priv *priv, + struct link_info *li, + int (*func_noml)(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li), + int (*func_dpcm)(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li, int dup_codec)) { struct of_phandle_iterator it; struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_card *card = graph_priv_to_card(priv); - struct device_node *top = dev->of_node; - struct device_node *node = top; + struct device_node *node = dev->of_node; struct device_node *cpu_port; - struct device_node *cpu_ep = NULL; - struct device_node *codec_ep = NULL; - struct device_node *codec_port = NULL; - struct device_node *codec_port_old = NULL; + struct device_node *cpu_ep; + struct device_node *codec_ep; + struct device_node *codec_port; + struct device_node *codec_port_old = NULL; + struct asoc_simple_card_data adata; int rc, ret; - int link_idx, dai_idx, conf_idx; - int cpu; + + /* loop for all listed CPU port */ + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = NULL; + + /* loop for all CPU endpoint */ + while (1) { + cpu_ep = of_get_next_child(cpu_port, cpu_ep); + if (!cpu_ep) + break; + + /* get codec */ + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + codec_port = of_get_parent(codec_ep); + + of_node_put(codec_ep); + of_node_put(codec_port); + + /* get convert-xxx property */ + memset(&adata, 0, sizeof(adata)); + graph_get_conversion(dev, codec_ep, &adata); + graph_get_conversion(dev, cpu_ep, &adata); + + /* + * It is DPCM + * if Codec port has many endpoints, + * or has convert-xxx property + */ + if ((of_get_child_count(codec_port) > 1) || + adata.convert_rate || adata.convert_channels) + ret = func_dpcm(priv, cpu_ep, codec_ep, li, + (codec_port_old == codec_port)); + /* else normal sound */ + else + ret = func_noml(priv, cpu_ep, codec_ep, li); + + if (ret < 0) + return ret; + + codec_port_old = codec_port; + } + } + + return 0; +} + +static int graph_parse_of(struct graph_priv *priv) +{ + struct snd_soc_card *card = graph_priv_to_card(priv); + struct link_info li; + int ret; ret = asoc_simple_card_of_parse_widgets(card, NULL); if (ret < 0) @@ -408,11 +502,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret < 0) return ret; - link_idx = 0; - dai_idx = 0; - conf_idx = 0; - codec_port_old = NULL; - for (cpu = 1; cpu >= 0; cpu--) { + memset(&li, 0, sizeof(li)); + for (li.cpu = 1; li.cpu >= 0; li.cpu--) { /* * Detect all CPU first, and Detect all Codec 2nd. * @@ -425,66 +516,57 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) * To avoid random sub-device numbering, * detect "dummy-Codec" in last; */ - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = NULL; - while (1) { - cpu_ep = of_get_next_child(cpu_port, cpu_ep); - if (!cpu_ep) - break; - - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = of_get_parent(codec_ep); - - of_node_put(codec_ep); - of_node_put(codec_port); - - dev_dbg(dev, "%pOFf <-> %pOFf\n", cpu_ep, codec_ep); - - if (of_get_child_count(codec_port) > 1) { - /* - * for DPCM sound - */ - if (!cpu) { - if (codec_port_old == codec_port) - continue; - codec_port_old = codec_port; - } - ret = asoc_graph_card_dai_link_of_dpcm( - top, cpu_ep, codec_ep, priv, - &dai_idx, link_idx++, - &conf_idx, cpu); - } else if (cpu) { - /* - * for Normal sound - */ - ret = asoc_graph_card_dai_link_of( - top, cpu_ep, codec_ep, priv, - &dai_idx, link_idx++); - } - if (ret < 0) - return ret; - } - } + ret = graph_for_each_link(priv, &li, + graph_dai_link_of, + graph_dai_link_of_dpcm); + if (ret < 0) + return ret; } return asoc_simple_card_parse_card_name(card, NULL); } -static void asoc_graph_get_dais_count(struct device *dev, - int *link_num, - int *dais_num, - int *ccnf_num) +static int graph_count_noml(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li) { - struct of_phandle_iterator it; - struct device_node *node = dev->of_node; - struct device_node *cpu_port; - struct device_node *cpu_ep; - struct device_node *codec_ep; - struct device_node *codec_port; - struct device_node *codec_port_old; - struct device_node *codec_port_old2; - int rc; + struct device *dev = graph_priv_to_dev(priv); + + li->link += 1; /* 1xCPU-Codec */ + li->dais += 2; /* 1xCPU + 1xCodec */ + + dev_dbg(dev, "Count As Normal\n"); + + return 0; +} + +static int graph_count_dpcm(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li, + int dup_codec) +{ + struct device *dev = graph_priv_to_dev(priv); + + li->link++; /* 1xCPU-dummy */ + li->dais++; /* 1xCPU */ + + if (!dup_codec) { + li->link++; /* 1xdummy-Codec */ + li->conf++; /* 1xdummy-Codec */ + li->dais++; /* 1xCodec */ + } + + dev_dbg(dev, "Count As DPCM\n"); + + return 0; +} + +static void graph_get_dais_count(struct graph_priv *priv, + struct link_info *li) +{ + struct device *dev = graph_priv_to_dev(priv); /* * link_num : number of links. @@ -522,45 +604,26 @@ static void asoc_graph_get_dais_count(struct device *dev, * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec * => 6 DAIs = 4xCPU + 2xCodec * => 2 ccnf = 2xdummy-Codec + * + * ex4) + * CPU0 --- Codec0 (convert-rate) link : 3 + * CPU1 --- Codec1 dais : 4 + * ccnf : 1 + * + * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec + * => 4 DAIs = 2xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec */ - codec_port_old = NULL; - codec_port_old2 = NULL; - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = NULL; - while (1) { - cpu_ep = of_get_next_child(cpu_port, cpu_ep); - if (!cpu_ep) - break; - - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = of_get_parent(codec_ep); - - of_node_put(codec_ep); - of_node_put(codec_port); - - (*link_num)++; - (*dais_num)++; - - if (codec_port_old == codec_port) { - if (codec_port_old2 != codec_port_old) { - (*link_num)++; - (*ccnf_num)++; - } - - codec_port_old2 = codec_port_old; - continue; - } - - (*dais_num)++; - codec_port_old = codec_port; - } - } + graph_for_each_link(priv, li, + graph_count_noml, + graph_count_dpcm); + dev_dbg(dev, "link %d, dais %d, ccnf %d\n", + li->link, li->dais, li->conf); } -static int asoc_graph_soc_card_probe(struct snd_soc_card *card) +static int graph_card_probe(struct snd_soc_card *card) { - struct graph_card_data *priv = snd_soc_card_get_drvdata(card); + struct graph_priv *priv = snd_soc_card_get_drvdata(card); int ret; ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL); @@ -574,16 +637,16 @@ static int asoc_graph_soc_card_probe(struct snd_soc_card *card) return 0; } -static int asoc_graph_card_probe(struct platform_device *pdev) +static int graph_probe(struct platform_device *pdev) { - struct graph_card_data *priv; + struct graph_priv *priv; struct snd_soc_dai_link *dai_link; struct graph_dai_props *dai_props; struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct snd_soc_card *card; struct snd_soc_codec_conf *cconf; - int lnum = 0, dnum = 0, cnum = 0; + struct link_info li; int ret, i; /* Allocate the private data and the DAI link array */ @@ -591,14 +654,22 @@ static int asoc_graph_card_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum); - if (!lnum || !dnum) + card = graph_priv_to_card(priv); + card->owner = THIS_MODULE; + card->dev = dev; + card->dapm_widgets = graph_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets); + card->probe = graph_card_probe; + + memset(&li, 0, sizeof(li)); + graph_get_dais_count(priv, &li); + if (!li.link || !li.dais) return -EINVAL; - dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); - dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); - cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); + dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL); if (!dai_props || !dai_link || !dais) return -ENOMEM; @@ -608,10 +679,11 @@ static int asoc_graph_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < lnum; i++) { + for (i = 0; i < li.link; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; - dai_link[i].platform = &dai_props[i].platform; + dai_link[i].platforms = &dai_props[i].platforms; + dai_link[i].num_platforms = 1; } priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); @@ -621,24 +693,17 @@ static int asoc_graph_card_probe(struct platform_device *pdev) return ret; } - priv->dai_props = dai_props; - priv->dai_link = dai_link; - priv->dais = dais; - priv->codec_conf = cconf; + priv->dai_props = dai_props; + priv->dai_link = dai_link; + priv->dais = dais; + priv->codec_conf = cconf; - /* Init snd_soc_card */ - card = graph_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; card->dai_link = dai_link; - card->num_links = lnum; - card->dapm_widgets = asoc_graph_card_dapm_widgets; - card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); - card->probe = asoc_graph_soc_card_probe; + card->num_links = li.link; card->codec_conf = cconf; - card->num_configs = cnum; + card->num_configs = li.conf; - ret = asoc_graph_card_parse_of(priv); + ret = graph_parse_of(priv); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); @@ -658,30 +723,30 @@ err: return ret; } -static int asoc_graph_card_remove(struct platform_device *pdev) +static int graph_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); return asoc_simple_card_clean_reference(card); } -static const struct of_device_id asoc_graph_of_match[] = { +static const struct of_device_id graph_of_match[] = { { .compatible = "audio-graph-card", }, { .compatible = "audio-graph-scu-card", }, {}, }; -MODULE_DEVICE_TABLE(of, asoc_graph_of_match); +MODULE_DEVICE_TABLE(of, graph_of_match); -static struct platform_driver asoc_graph_card = { +static struct platform_driver graph_card = { .driver = { .name = "asoc-audio-graph-card", .pm = &snd_soc_pm_ops, - .of_match_table = asoc_graph_of_match, + .of_match_table = graph_of_match, }, - .probe = asoc_graph_card_probe, - .remove = asoc_graph_card_remove, + .probe = graph_probe, + .remove = graph_remove, }; -module_platform_driver(asoc_graph_card); +module_platform_driver(graph_card); MODULE_ALIAS("platform:asoc-audio-graph-card"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c deleted file mode 100644 index e1b192ea147b..000000000000 --- a/sound/soc/generic/audio-graph-scu-card.c +++ /dev/null @@ -1,501 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// ASoC audio graph SCU sound card support -// -// Copyright (C) 2017 Renesas Solutions Corp. -// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> -// -// based on -// ${LINUX}/sound/soc/generic/simple-scu-card.c -// ${LINUX}/sound/soc/generic/audio-graph-card.c - -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/gpio.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> -#include <linux/of_graph.h> -#include <linux/platform_device.h> -#include <linux/string.h> -#include <sound/jack.h> -#include <sound/simple_card_utils.h> - -struct graph_card_data { - struct snd_soc_card snd_card; - struct graph_dai_props { - struct asoc_simple_dai *cpu_dai; - struct asoc_simple_dai *codec_dai; - struct snd_soc_dai_link_component codecs; - struct snd_soc_dai_link_component platform; - struct asoc_simple_card_data adata; - struct snd_soc_codec_conf *codec_conf; - } *dai_props; - struct snd_soc_dai_link *dai_link; - struct asoc_simple_dai *dais; - struct asoc_simple_card_data adata; - struct snd_soc_codec_conf *codec_conf; -}; - -#define graph_priv_to_card(priv) (&(priv)->snd_card) -#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) -#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) - -#define PREFIX "audio-graph-card," - -static int asoc_graph_card_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - int ret = 0; - - ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); - if (ret) - return ret; - - ret = asoc_simple_card_clk_enable(dai_props->codec_dai); - if (ret) - asoc_simple_card_clk_disable(dai_props->cpu_dai); - - return ret; -} - -static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - - asoc_simple_card_clk_disable(dai_props->cpu_dai); - - asoc_simple_card_clk_disable(dai_props->codec_dai); -} - -static const struct snd_soc_ops asoc_graph_card_ops = { - .startup = asoc_graph_card_startup, - .shutdown = asoc_graph_card_shutdown, -}; - -static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - int ret = 0; - - ret = asoc_simple_card_init_dai(rtd->codec_dai, - dai_props->codec_dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_init_dai(rtd->cpu_dai, - dai_props->cpu_dai); - if (ret < 0) - return ret; - - return 0; -} - -static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - - asoc_simple_card_convert_fixup(&dai_props->adata, params); - - /* overwrite by top level adata if exist */ - asoc_simple_card_convert_fixup(&priv->adata, params); - - return 0; -} - -static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, - struct device_node *codec_ep, - struct graph_card_data *priv, - int *dai_idx, int link_idx, - int *conf_idx, int is_fe) -{ - struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); - struct snd_soc_card *card = graph_priv_to_card(priv); - struct device_node *ep = is_fe ? cpu_ep : codec_ep; - struct device_node *node = of_graph_get_port_parent(ep); - struct asoc_simple_dai *dai; - int ret; - - if (is_fe) { - struct snd_soc_dai_link_component *codecs; - - /* BE is dummy */ - codecs = dai_link->codecs; - codecs->of_node = NULL; - codecs->dai_name = "snd-soc-dummy-dai"; - codecs->name = "snd-soc-dummy"; - - /* FE settings */ - dai_link->dynamic = 1; - dai_link->dpcm_merged_format = 1; - - dai = - dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; - - ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); - if (ret) - return ret; - - ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "fe.%s", - dai_link->cpu_dai_name); - if (ret < 0) - return ret; - - /* card->num_links includes Codec */ - asoc_simple_card_canonicalize_cpu(dai_link, - of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); - } else { - struct snd_soc_codec_conf *cconf; - - /* FE is dummy */ - dai_link->cpu_of_node = NULL; - dai_link->cpu_dai_name = "snd-soc-dummy-dai"; - dai_link->cpu_name = "snd-soc-dummy"; - - /* BE settings */ - dai_link->no_pcm = 1; - dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup; - - dai = - dai_props->codec_dai = &priv->dais[(*dai_idx)++]; - - cconf = - dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; - - ret = asoc_simple_card_parse_graph_codec(ep, dai_link); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "be.%s", - dai_link->codecs->dai_name); - if (ret < 0) - return ret; - - /* check "prefix" from top node */ - snd_soc_of_parse_audio_prefix(card, cconf, - dai_link->codecs->of_node, - "prefix"); - /* check "prefix" from each node if top doesn't have */ - if (!cconf->of_node) - snd_soc_of_parse_node_prefix(node, cconf, - dai_link->codecs->of_node, - PREFIX "prefix"); - } - - asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); - - ret = asoc_simple_card_of_parse_tdm(ep, dai); - if (ret) - return ret; - - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, - NULL, &dai_link->dai_fmt); - if (ret < 0) - return ret; - - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; - dai_link->ops = &asoc_graph_card_ops; - dai_link->init = asoc_graph_card_dai_init; - - return 0; -} - -static int asoc_graph_card_parse_of(struct graph_card_data *priv) -{ - struct of_phandle_iterator it; - struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_card *card = graph_priv_to_card(priv); - struct device_node *node = dev->of_node; - struct device_node *cpu_port; - struct device_node *cpu_ep; - struct device_node *codec_ep; - struct device_node *codec_port; - struct device_node *codec_port_old; - int dai_idx, link_idx, conf_idx, ret; - int rc, codec; - - if (!node) - return -EINVAL; - - /* - * we need to consider "widgets", "mclk-fs" around here - * see simple-card - */ - - ret = asoc_simple_card_of_parse_routing(card, NULL); - if (ret < 0) - return ret; - - asoc_simple_card_parse_convert(dev, node, NULL, &priv->adata); - - /* - * it supports multi CPU, single CODEC only here - * see asoc_graph_get_dais_count - */ - - link_idx = 0; - dai_idx = 0; - conf_idx = 0; - codec_port_old = NULL; - for (codec = 0; codec < 2; codec++) { - /* - * To listup valid sounds continuously, - * detect all CPU-dummy first, and - * detect all dummy-Codec second - */ - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = of_get_next_child(cpu_port, NULL); - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = of_graph_get_port_parent(codec_ep); - - of_node_put(cpu_ep); - of_node_put(codec_ep); - of_node_put(cpu_port); - of_node_put(codec_port); - it.node = NULL; - - if (codec) { - if (codec_port_old == codec_port) - continue; - - codec_port_old = codec_port; - } - - ret = asoc_graph_card_dai_link_of(cpu_ep, codec_ep, - priv, &dai_idx, - link_idx++, &conf_idx, - !codec); - if (ret < 0) - goto parse_of_err; - } - } - - ret = asoc_simple_card_parse_card_name(card, NULL); - if (ret) - goto parse_of_err; - - if ((card->num_links != link_idx) || - (card->num_configs != conf_idx)) { - dev_err(dev, "dai_link or codec_config wrong (%d/%d, %d/%d)\n", - card->num_links, link_idx, card->num_configs, conf_idx); - ret = -EINVAL; - goto parse_of_err; - } - - ret = 0; - -parse_of_err: - return ret; -} - -static void asoc_graph_get_dais_count(struct device *dev, - int *link_num, - int *dais_num, - int *ccnf_num) -{ - struct of_phandle_iterator it; - struct device_node *node = dev->of_node; - struct device_node *cpu_port; - struct device_node *cpu_ep; - struct device_node *codec_ep; - struct device_node *codec_port; - struct device_node *codec_port_old; - struct device_node *codec_port_old2; - int rc; - - /* - * link_num : number of links. - * CPU-Codec / CPU-dummy / dummy-Codec - * dais_num : number of DAIs - * ccnf_num : number of codec_conf - * same number for dummy-Codec - * - * ex1) - * CPU0 --- Codec0 link : 5 - * CPU1 --- Codec1 dais : 7 - * CPU2 -/ ccnf : 1 - * CPU3 --- Codec2 - * - * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec - * => 7 DAIs = 4xCPU + 3xCodec - * => 1 ccnf = 1xdummy-Codec - * - * ex2) - * CPU0 --- Codec0 link : 5 - * CPU1 --- Codec1 dais : 6 - * CPU2 -/ ccnf : 1 - * CPU3 -/ - * - * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec - * => 6 DAIs = 4xCPU + 2xCodec - * => 1 ccnf = 1xdummy-Codec - * - * ex3) - * CPU0 --- Codec0 link : 6 - * CPU1 -/ dais : 6 - * CPU2 --- Codec1 ccnf : 2 - * CPU3 -/ - * - * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec - * => 6 DAIs = 4xCPU + 2xCodec - * => 2 ccnf = 2xdummy-Codec - */ - codec_port_old = NULL; - codec_port_old2 = NULL; - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = of_get_next_child(cpu_port, NULL); - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = of_graph_get_port_parent(codec_ep); - - of_node_put(cpu_ep); - of_node_put(codec_ep); - of_node_put(codec_port); - - (*link_num)++; - (*dais_num)++; - - if (codec_port_old == codec_port) { - if (codec_port_old2 != codec_port_old) { - (*link_num)++; - (*ccnf_num)++; - } - - codec_port_old2 = codec_port_old; - continue; - } - - (*dais_num)++; - codec_port_old = codec_port; - } -} - -static int asoc_graph_card_probe(struct platform_device *pdev) -{ - struct graph_card_data *priv; - struct snd_soc_dai_link *dai_link; - struct graph_dai_props *dai_props; - struct asoc_simple_dai *dais; - struct device *dev = &pdev->dev; - struct snd_soc_card *card; - struct snd_soc_codec_conf *cconf; - int lnum = 0, dnum = 0, cnum = 0; - int ret, i; - - /* Allocate the private data and the DAI link array */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum); - if (!lnum || !dnum) - return -EINVAL; - - dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); - dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); - cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); - if (!dai_props || !dai_link || !dais) - return -ENOMEM; - - /* - * Use snd_soc_dai_link_component instead of legacy style - * It is codec only. but cpu/platform will be supported in the future. - * see - * soc-core.c :: snd_soc_init_multicodec() - */ - for (i = 0; i < lnum; i++) { - dai_link[i].codecs = &dai_props[i].codecs; - dai_link[i].num_codecs = 1; - dai_link[i].platform = &dai_props[i].platform; - } - - priv->dai_props = dai_props; - priv->dai_link = dai_link; - priv->dais = dais; - priv->codec_conf = cconf; - - /* Init snd_soc_card */ - card = graph_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; - card->dai_link = priv->dai_link; - card->num_links = lnum; - card->codec_conf = cconf; - card->num_configs = cnum; - - ret = asoc_graph_card_parse_of(priv); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - goto err; - } - - snd_soc_card_set_drvdata(card, priv); - - ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - return 0; -err: - asoc_simple_card_clean_reference(card); - - return ret; -} - -static int asoc_graph_card_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - return asoc_simple_card_clean_reference(card); -} - -static const struct of_device_id asoc_graph_of_match[] = { - { .compatible = "audio-graph-scu-card", }, - {}, -}; -MODULE_DEVICE_TABLE(of, asoc_graph_of_match); - -static struct platform_driver asoc_graph_card = { - .driver = { - .name = "asoc-audio-graph-scu-card", - .pm = &snd_soc_pm_ops, - .of_match_table = asoc_graph_of_match, - }, - .probe = asoc_graph_card_probe, - .remove = asoc_graph_card_remove, -}; -module_platform_driver(asoc_graph_card); - -MODULE_ALIAS("platform:asoc-audio-graph-scu-card"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ASoC Audio Graph SCU Sound Card"); -MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index b807a47515eb..5c1424f03620 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -283,12 +283,20 @@ static int asoc_simple_card_get_dai_id(struct device_node *ep) /* use endpoint/port reg if exist */ ret = of_graph_parse_endpoint(ep, &info); if (ret == 0) { - if (info.id) + /* + * Because it will count port/endpoint if it doesn't have "reg". + * But, we can't judge whether it has "no reg", or "reg = <0>" + * only of_graph_parse_endpoint(). + * We need to check "reg" property + */ + if (of_get_property(ep, "reg", NULL)) return info.id; - if (info.port) + + node = of_get_parent(ep); + of_node_put(node); + if (of_get_property(node, "reg", NULL)) return info.port; } - node = of_graph_get_port_parent(ep); /* @@ -386,16 +394,13 @@ int asoc_simple_card_init_dai(struct snd_soc_dai *dai, } EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai); -int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link) +void asoc_simple_card_canonicalize_platform(struct snd_soc_dai_link *dai_link) { /* Assumes platform == cpu */ - if (!dai_link->platform->of_node) - dai_link->platform->of_node = dai_link->cpu_of_node; - - return 0; - + if (!dai_link->platforms->of_node) + dai_link->platforms->of_node = dai_link->cpu_of_node; } -EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_dailink); +EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_platform); void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, int is_single_links) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 3fe34417ec89..7147bba45a2a 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -15,13 +15,13 @@ #include <sound/soc-dai.h> #include <sound/soc.h> -struct simple_card_data { +struct simple_priv { struct snd_soc_card snd_card; struct simple_dai_props { struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ - struct snd_soc_dai_link_component platform; + struct snd_soc_dai_link_component platforms; struct asoc_simple_card_data adata; struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; @@ -33,6 +33,13 @@ struct simple_card_data { struct snd_soc_codec_conf *codec_conf; }; +struct link_info { + int dais; /* number of dai */ + int link; /* number of link */ + int conf; /* number of codec_conf */ + int cpu; /* turn for CPU / Codec */ +}; + #define simple_priv_to_card(priv) (&(priv)->snd_card) #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i)) #define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev) @@ -42,10 +49,10 @@ struct simple_card_data { #define CELL "#sound-dai-cells" #define PREFIX "simple-audio-card," -static int asoc_simple_card_startup(struct snd_pcm_substream *substream) +static int simple_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); int ret; @@ -61,10 +68,10 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) return ret; } -static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) +static void simple_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); @@ -73,8 +80,8 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) asoc_simple_card_clk_disable(dai_props->codec_dai); } -static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, - unsigned long rate) +static int simple_set_clk_rate(struct asoc_simple_dai *simple_dai, + unsigned long rate) { if (!simple_dai) return 0; @@ -88,13 +95,13 @@ static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, return clk_set_rate(simple_dai->clk, rate); } -static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int simple_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); unsigned int mclk, mclk_fs = 0; @@ -106,11 +113,11 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, if (mclk_fs) { mclk = params_rate(params) * mclk_fs; - ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk); + ret = simple_set_clk_rate(dai_props->codec_dai, mclk); if (ret < 0) return ret; - ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk); + ret = simple_set_clk_rate(dai_props->cpu_dai, mclk); if (ret < 0) return ret; @@ -129,15 +136,15 @@ err: return ret; } -static const struct snd_soc_ops asoc_simple_card_ops = { - .startup = asoc_simple_card_startup, - .shutdown = asoc_simple_card_shutdown, - .hw_params = asoc_simple_card_hw_params, +static const struct snd_soc_ops simple_ops = { + .startup = simple_startup, + .shutdown = simple_shutdown, + .hw_params = simple_hw_params, }; -static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) +static int simple_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); int ret; @@ -154,10 +161,10 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) +static int simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) { - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); asoc_simple_card_convert_fixup(&dai_props->adata, params); @@ -165,30 +172,58 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, - struct device_node *node, - struct device_node *np, - struct device_node *codec, - struct simple_card_data *priv, - int *dai_idx, int link_idx, - int *conf_idx, int is_fe, - bool is_top_level_node) +static void simple_get_conversion(struct device *dev, + struct device_node *np, + struct asoc_simple_card_data *adata) +{ + struct device_node *top = dev->of_node; + struct device_node *node = of_get_parent(np); + + asoc_simple_card_parse_convert(dev, top, PREFIX, adata); + asoc_simple_card_parse_convert(dev, node, PREFIX, adata); + asoc_simple_card_parse_convert(dev, node, NULL, adata); + asoc_simple_card_parse_convert(dev, np, NULL, adata); + + of_node_put(node); +} + +static int simple_dai_link_of_dpcm(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, + bool is_top) { struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct asoc_simple_dai *dai; struct snd_soc_dai_link_component *codecs = dai_link->codecs; - + struct device_node *top = dev->of_node; + struct device_node *node = of_get_parent(np); char prop[128]; char *prefix = ""; int ret; + /* + * |CPU |Codec : turn + * CPU |Pass |return + * Codec |return|Pass + * np + */ + if (li->cpu == (np == codec)) + return 0; + + dev_dbg(dev, "link_of DPCM (%pOF)\n", np); + + li->link++; + + of_node_put(node); + /* For single DAI link & old style of DT node */ - if (is_top_level_node) + if (is_top) prefix = PREFIX; - if (is_fe) { + if (li->cpu) { int is_single_links = 0; /* BE is dummy */ @@ -201,7 +236,7 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, dai_link->dpcm_merged_format = 1; dai = - dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + dai_props->cpu_dai = &priv->dais[li->dais++]; ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, &is_single_links); @@ -229,13 +264,13 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, /* BE settings */ dai_link->no_pcm = 1; - dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; + dai_link->be_hw_params_fixup = simple_be_hw_params_fixup; dai = - dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + dai_props->codec_dai = &priv->dais[li->dais++]; cconf = - dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + dai_props->codec_conf = &priv->codec_conf[li->conf++]; ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); if (ret < 0) @@ -260,18 +295,14 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, "prefix"); } - asoc_simple_card_parse_convert(dev, top, PREFIX, &dai_props->adata); - asoc_simple_card_parse_convert(dev, node, prefix, &dai_props->adata); - asoc_simple_card_parse_convert(dev, np, NULL, &dai_props->adata); + simple_get_conversion(dev, np, &dai_props->adata); + + asoc_simple_card_canonicalize_platform(dai_link); ret = asoc_simple_card_of_parse_tdm(np, dai); if (ret) return ret; - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - return ret; - snprintf(prop, sizeof(prop), "%smclk-fs", prefix); of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(node, prop, &dai_props->mclk_fs); @@ -284,59 +315,57 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, dai_link->dpcm_playback = 1; dai_link->dpcm_capture = 1; - dai_link->ops = &asoc_simple_card_ops; - dai_link->init = asoc_simple_card_dai_init; + dai_link->ops = &simple_ops; + dai_link->init = simple_dai_init; return 0; } -static int asoc_simple_card_dai_link_of(struct device_node *top, - struct device_node *node, - struct simple_card_data *priv, - int *dai_idx, int link_idx, - bool is_top_level_node) +static int simple_dai_link_of(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, + bool is_top) { struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; + struct device_node *top = dev->of_node; struct device_node *cpu = NULL; + struct device_node *node = NULL; struct device_node *plat = NULL; - struct device_node *codec = NULL; char prop[128]; char *prefix = ""; int ret, single_cpu; - /* For single DAI link & old style of DT node */ - if (is_top_level_node) - prefix = PREFIX; + /* + * |CPU |Codec : turn + * CPU |Pass |return + * Codec |return|return + * np + */ + if (!li->cpu || np == codec) + return 0; - snprintf(prop, sizeof(prop), "%scpu", prefix); - cpu = of_get_child_by_name(node, prop); + cpu = np; + node = of_get_parent(np); + li->link++; - if (!cpu) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } + dev_dbg(dev, "link_of (%pOF)\n", node); + + /* For single DAI link & old style of DT node */ + if (is_top) + prefix = PREFIX; snprintf(prop, sizeof(prop), "%splat", prefix); plat = of_get_child_by_name(node, prop); - snprintf(prop, sizeof(prop), "%scodec", prefix); - codec = of_get_child_by_name(node, prop); - - if (!codec) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } - cpu_dai = - dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + dai_props->cpu_dai = &priv->dais[li->dais++]; codec_dai = - dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + dai_props->codec_dai = &priv->dais[li->dais++]; ret = asoc_simple_card_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); @@ -378,10 +407,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *top, if (ret < 0) goto dai_link_of_err; - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - goto dai_link_of_err; - ret = asoc_simple_card_set_dailink_name(dev, dai_link, "%s-%s", dai_link->cpu_dai_name, @@ -389,20 +414,95 @@ static int asoc_simple_card_dai_link_of(struct device_node *top, if (ret < 0) goto dai_link_of_err; - dai_link->ops = &asoc_simple_card_ops; - dai_link->init = asoc_simple_card_dai_init; + dai_link->ops = &simple_ops; + dai_link->init = simple_dai_init; asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); + asoc_simple_card_canonicalize_platform(dai_link); dai_link_of_err: - of_node_put(cpu); - of_node_put(codec); + of_node_put(plat); + of_node_put(node); + + return ret; +} + +static int simple_for_each_link(struct simple_priv *priv, + struct link_info *li, + int (*func_noml)(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, bool is_top), + int (*func_dpcm)(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, bool is_top)) +{ + struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; + struct device_node *node; + bool is_top = 0; + int ret = 0; + + /* Check if it has dai-link */ + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = of_node_get(top); + is_top = 1; + } + /* loop for all dai-link */ + do { + struct asoc_simple_card_data adata; + struct device_node *codec; + struct device_node *np; + int num = of_get_child_count(node); + + /* get codec */ + codec = of_get_child_by_name(node, is_top ? + PREFIX "codec" : "codec"); + if (!codec) { + ret = -ENODEV; + goto error; + } + + of_node_put(codec); + + /* get convert-xxx property */ + memset(&adata, 0, sizeof(adata)); + for_each_child_of_node(node, np) + simple_get_conversion(dev, np, &adata); + + /* loop for all CPU/Codec node */ + for_each_child_of_node(node, np) { + /* + * It is DPCM + * if it has many CPUs, + * or has convert-xxx property + */ + if (num > 2 || + adata.convert_rate || adata.convert_channels) + ret = func_dpcm(priv, np, codec, li, is_top); + /* else normal sound */ + else + ret = func_noml(priv, np, codec, li, is_top); + + if (ret < 0) { + of_node_put(np); + goto error; + } + } + + node = of_get_next_child(top, node); + } while (!is_top && node); + + error: + of_node_put(node); return ret; } -static int asoc_simple_card_parse_aux_devs(struct device_node *node, - struct simple_card_data *priv) +static int simple_parse_aux_devs(struct device_node *node, + struct simple_priv *priv) { struct device *dev = simple_priv_to_dev(priv); struct device_node *aux_node; @@ -432,17 +532,13 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node, return 0; } -static int asoc_simple_card_parse_of(struct simple_card_data *priv) +static int simple_parse_of(struct simple_priv *priv) { struct device *dev = simple_priv_to_dev(priv); struct device_node *top = dev->of_node; struct snd_soc_card *card = simple_priv_to_card(priv); - struct device_node *node; - struct device_node *np; - struct device_node *codec; - bool is_fe; - int ret, loop; - int dai_idx, link_idx, conf_idx; + struct link_info li; + int ret; if (!top) return -EINVAL; @@ -456,62 +552,66 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) return ret; /* Single/Muti DAI link(s) & New style of DT node */ - loop = 1; - link_idx = 0; - dai_idx = 0; - conf_idx = 0; - node = of_get_child_by_name(top, PREFIX "dai-link"); - if (!node) { - node = of_node_get(top); - loop = 0; - } - - do { - /* DPCM */ - if (of_get_child_count(node) > 2) { - for_each_child_of_node(node, np) { - codec = of_get_child_by_name(node, - loop ? "codec" : - PREFIX "codec"); - if (!codec) - return -ENODEV; - - is_fe = (np != codec); - - ret = asoc_simple_card_dai_link_of_dpcm( - top, node, np, codec, priv, - &dai_idx, link_idx++, &conf_idx, - is_fe, !loop); - } - } else { - ret = asoc_simple_card_dai_link_of( - top, node, priv, - &dai_idx, link_idx++, !loop); - } + memset(&li, 0, sizeof(li)); + for (li.cpu = 1; li.cpu >= 0; li.cpu--) { + /* + * Detect all CPU first, and Detect all Codec 2nd. + * + * In Normal sound case, all DAIs are detected + * as "CPU-Codec". + * + * In DPCM sound case, + * all CPUs are detected as "CPU-dummy", and + * all Codecs are detected as "dummy-Codec". + * To avoid random sub-device numbering, + * detect "dummy-Codec" in last; + */ + ret = simple_for_each_link(priv, &li, + simple_dai_link_of, + simple_dai_link_of_dpcm); if (ret < 0) return ret; - - node = of_get_next_child(top, node); - } while (loop && node); + } ret = asoc_simple_card_parse_card_name(card, PREFIX); if (ret < 0) return ret; - ret = asoc_simple_card_parse_aux_devs(top, priv); + ret = simple_parse_aux_devs(top, priv); return ret; } -static void asoc_simple_card_get_dais_count(struct device *dev, - int *link_num, - int *dais_num, - int *ccnf_num) +static int simple_count_noml(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, bool is_top) { + li->dais++; /* CPU or Codec */ + if (np != codec) + li->link++; /* CPU-Codec */ + + return 0; +} + +static int simple_count_dpcm(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, bool is_top) +{ + li->dais++; /* CPU or Codec */ + li->link++; /* CPU-dummy or dummy-Codec */ + if (np == codec) + li->conf++; + + return 0; +} + +static void simple_get_dais_count(struct simple_priv *priv, + struct link_info *li) +{ + struct device *dev = simple_priv_to_dev(priv); struct device_node *top = dev->of_node; - struct device_node *node; - int loop; - int num; /* * link_num : number of links. @@ -549,37 +649,34 @@ static void asoc_simple_card_get_dais_count(struct device *dev, * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec * => 6 DAIs = 4xCPU + 2xCodec * => 2 ccnf = 2xdummy-Codec + * + * ex4) + * CPU0 --- Codec0 (convert-rate) link : 3 + * CPU1 --- Codec1 dais : 4 + * ccnf : 1 + * + * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec + * => 4 DAIs = 2xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec */ if (!top) { - (*link_num) = 1; - (*dais_num) = 2; - (*ccnf_num) = 0; + li->link = 1; + li->dais = 2; + li->conf = 0; return; } - loop = 1; - node = of_get_child_by_name(top, PREFIX "dai-link"); - if (!node) { - node = top; - loop = 0; - } + simple_for_each_link(priv, li, + simple_count_noml, + simple_count_dpcm); - do { - num = of_get_child_count(node); - (*dais_num) += num; - if (num > 2) { - (*link_num) += num; - (*ccnf_num)++; - } else { - (*link_num)++; - } - node = of_get_next_child(top, node); - } while (loop && node); + dev_dbg(dev, "link %d, dais %d, ccnf %d\n", + li->link, li->dais, li->conf); } -static int asoc_simple_soc_card_probe(struct snd_soc_card *card) +static int simple_soc_probe(struct snd_soc_card *card) { - struct simple_card_data *priv = snd_soc_card_get_drvdata(card); + struct simple_priv *priv = snd_soc_card_get_drvdata(card); int ret; ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX); @@ -593,9 +690,9 @@ static int asoc_simple_soc_card_probe(struct snd_soc_card *card) return 0; } -static int asoc_simple_card_probe(struct platform_device *pdev) +static int simple_probe(struct platform_device *pdev) { - struct simple_card_data *priv; + struct simple_priv *priv; struct snd_soc_dai_link *dai_link; struct simple_dai_props *dai_props; struct asoc_simple_dai *dais; @@ -603,7 +700,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct snd_soc_card *card; struct snd_soc_codec_conf *cconf; - int lnum = 0, dnum = 0, cnum = 0; + struct link_info li; int ret, i; /* Allocate the private data and the DAI link array */ @@ -611,14 +708,20 @@ static int asoc_simple_card_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum); - if (!lnum || !dnum) + card = simple_priv_to_card(priv); + card->owner = THIS_MODULE; + card->dev = dev; + card->probe = simple_soc_probe; + + memset(&li, 0, sizeof(li)); + simple_get_dais_count(priv, &li); + if (!li.link || !li.dais) return -EINVAL; - dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); - dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); - cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); + dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL); if (!dai_props || !dai_link || !dais) return -ENOMEM; @@ -628,30 +731,26 @@ static int asoc_simple_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < lnum; i++) { + for (i = 0; i < li.link; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; - dai_link[i].platform = &dai_props[i].platform; + dai_link[i].platforms = &dai_props[i].platforms; + dai_link[i].num_platforms = 1; } - priv->dai_props = dai_props; - priv->dai_link = dai_link; - priv->dais = dais; - priv->codec_conf = cconf; + priv->dai_props = dai_props; + priv->dai_link = dai_link; + priv->dais = dais; + priv->codec_conf = cconf; - /* Init snd_soc_card */ - card = simple_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; card->dai_link = priv->dai_link; - card->num_links = lnum; + card->num_links = li.link; card->codec_conf = cconf; - card->num_configs = cnum; - card->probe = asoc_simple_soc_card_probe; + card->num_configs = li.conf; if (np && of_device_is_available(np)) { - ret = asoc_simple_card_parse_of(priv); + ret = simple_parse_of(priv); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); @@ -686,7 +785,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) codecs->name = cinfo->codec; codecs->dai_name = cinfo->codec_dai.name; - platform = dai_link->platform; + platform = dai_link->platforms; platform->name = cinfo->platform; card->name = (cinfo->card) ? cinfo->card : cinfo->name; @@ -694,7 +793,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_link->stream_name = cinfo->name; dai_link->cpu_dai_name = cinfo->cpu_dai.name; dai_link->dai_fmt = cinfo->daifmt; - dai_link->init = asoc_simple_card_dai_init; + dai_link->init = simple_dai_init; memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai, sizeof(*priv->dai_props->cpu_dai)); memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai, @@ -714,28 +813,28 @@ err: return ret; } -static int asoc_simple_card_remove(struct platform_device *pdev) +static int simple_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); return asoc_simple_card_clean_reference(card); } -static const struct of_device_id asoc_simple_of_match[] = { +static const struct of_device_id simple_of_match[] = { { .compatible = "simple-audio-card", }, { .compatible = "simple-scu-audio-card", }, {}, }; -MODULE_DEVICE_TABLE(of, asoc_simple_of_match); +MODULE_DEVICE_TABLE(of, simple_of_match); static struct platform_driver asoc_simple_card = { .driver = { .name = "asoc-simple-card", .pm = &snd_soc_pm_ops, - .of_match_table = asoc_simple_of_match, + .of_match_table = simple_of_match, }, - .probe = asoc_simple_card_probe, - .remove = asoc_simple_card_remove, + .probe = simple_probe, + .remove = simple_remove, }; module_platform_driver(asoc_simple_card); diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c deleted file mode 100644 index 9d7299d536a8..000000000000 --- a/sound/soc/generic/simple-scu-card.c +++ /dev/null @@ -1,474 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// ASoC simple SCU sound card support -// -// Copyright (C) 2015 Renesas Solutions Corp. -// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> -// -// based on ${LINUX}/sound/soc/generic/simple-card.c - -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/string.h> -#include <sound/jack.h> -#include <sound/soc.h> -#include <sound/soc-dai.h> -#include <sound/simple_card_utils.h> - -struct simple_card_data { - struct snd_soc_card snd_card; - struct simple_dai_props { - struct asoc_simple_dai *cpu_dai; - struct asoc_simple_dai *codec_dai; - struct snd_soc_dai_link_component codecs; - struct snd_soc_dai_link_component platform; - struct asoc_simple_card_data adata; - struct snd_soc_codec_conf *codec_conf; - } *dai_props; - struct snd_soc_dai_link *dai_link; - struct asoc_simple_dai *dais; - struct asoc_simple_card_data adata; - struct snd_soc_codec_conf *codec_conf; -}; - -#define simple_priv_to_card(priv) (&(priv)->snd_card) -#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev) -#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i)) - -#define DAI "sound-dai" -#define CELL "#sound-dai-cells" -#define PREFIX "simple-audio-card," - -static int asoc_simple_card_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = - simple_priv_to_props(priv, rtd->num); - int ret; - - ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); - if (ret) - return ret; - - ret = asoc_simple_card_clk_enable(dai_props->codec_dai); - if (ret) - asoc_simple_card_clk_disable(dai_props->cpu_dai); - - return ret; -} - -static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = - simple_priv_to_props(priv, rtd->num); - - asoc_simple_card_clk_disable(dai_props->cpu_dai); - - asoc_simple_card_clk_disable(dai_props->codec_dai); -} - -static const struct snd_soc_ops asoc_simple_card_ops = { - .startup = asoc_simple_card_startup, - .shutdown = asoc_simple_card_shutdown, -}; - -static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - int ret; - - ret = asoc_simple_card_init_dai(rtd->codec_dai, - dai_props->codec_dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_init_dai(rtd->cpu_dai, - dai_props->cpu_dai); - if (ret < 0) - return ret; - - return 0; -} - -static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - - asoc_simple_card_convert_fixup(&dai_props->adata, params); - - /* overwrite by top level adata if exist */ - asoc_simple_card_convert_fixup(&priv->adata, params); - - return 0; -} - -static int asoc_simple_card_dai_link_of(struct device_node *link, - struct device_node *np, - struct device_node *codec, - struct simple_card_data *priv, - int *dai_idx, int link_idx, - int *conf_idx, int is_fe, - bool is_top_level_node) -{ - struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); - struct snd_soc_card *card = simple_priv_to_card(priv); - struct asoc_simple_dai *dai; - char *prefix = ""; - int ret; - - /* For single DAI link & old style of DT node */ - if (is_top_level_node) - prefix = PREFIX; - - if (is_fe) { - int is_single_links = 0; - struct snd_soc_dai_link_component *codecs; - - /* BE is dummy */ - codecs = dai_link->codecs; - codecs->of_node = NULL; - codecs->dai_name = "snd-soc-dummy-dai"; - codecs->name = "snd-soc-dummy"; - - /* FE settings */ - dai_link->dynamic = 1; - dai_link->dpcm_merged_format = 1; - - dai = - dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; - - ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, - &is_single_links); - if (ret) - return ret; - - ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "fe.%s", - dai_link->cpu_dai_name); - if (ret < 0) - return ret; - - asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); - } else { - struct snd_soc_codec_conf *cconf; - - /* FE is dummy */ - dai_link->cpu_of_node = NULL; - dai_link->cpu_dai_name = "snd-soc-dummy-dai"; - dai_link->cpu_name = "snd-soc-dummy"; - - /* BE settings */ - dai_link->no_pcm = 1; - dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; - - dai = - dai_props->codec_dai = &priv->dais[(*dai_idx)++]; - - cconf = - dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; - - ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "be.%s", - dai_link->codecs->dai_name); - if (ret < 0) - return ret; - - /* check "prefix" from top node */ - snd_soc_of_parse_audio_prefix(card, cconf, - dai_link->codecs->of_node, - PREFIX "prefix"); - /* check "prefix" from each node if top doesn't have */ - if (!cconf->of_node) - snd_soc_of_parse_node_prefix(np, cconf, - dai_link->codecs->of_node, - "prefix"); - } - - asoc_simple_card_parse_convert(dev, link, prefix, &dai_props->adata); - - ret = asoc_simple_card_of_parse_tdm(np, dai); - if (ret) - return ret; - - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_daifmt(dev, link, codec, - prefix, &dai_link->dai_fmt); - if (ret < 0) - return ret; - - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; - dai_link->ops = &asoc_simple_card_ops; - dai_link->init = asoc_simple_card_dai_init; - - return 0; -} - -static int asoc_simple_card_parse_of(struct simple_card_data *priv) - -{ - struct device *dev = simple_priv_to_dev(priv); - struct device_node *top = dev->of_node; - struct device_node *node; - struct device_node *np; - struct device_node *codec; - struct snd_soc_card *card = simple_priv_to_card(priv); - bool is_fe; - int ret, loop; - int dai_idx, link_idx, conf_idx; - - if (!top) - return -EINVAL; - - ret = asoc_simple_card_of_parse_widgets(card, PREFIX); - if (ret < 0) - return ret; - - ret = asoc_simple_card_of_parse_routing(card, PREFIX); - if (ret < 0) - return ret; - - asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata); - - loop = 1; - link_idx = 0; - dai_idx = 0; - conf_idx = 0; - node = of_get_child_by_name(top, PREFIX "dai-link"); - if (!node) { - node = dev->of_node; - loop = 0; - } - - do { - codec = of_get_child_by_name(node, - loop ? "codec" : PREFIX "codec"); - if (!codec) - return -ENODEV; - - for_each_child_of_node(node, np) { - is_fe = (np != codec); - - ret = asoc_simple_card_dai_link_of(node, np, codec, priv, - &dai_idx, link_idx++, - &conf_idx, - is_fe, !loop); - if (ret < 0) - return ret; - } - node = of_get_next_child(top, node); - } while (loop && node); - - ret = asoc_simple_card_parse_card_name(card, PREFIX); - if (ret < 0) - return ret; - - return 0; -} - -static void asoc_simple_card_get_dais_count(struct device *dev, - int *link_num, - int *dais_num, - int *ccnf_num) -{ - struct device_node *top = dev->of_node; - struct device_node *node; - int loop; - int num; - - /* - * link_num : number of links. - * CPU-Codec / CPU-dummy / dummy-Codec - * dais_num : number of DAIs - * ccnf_num : number of codec_conf - * same number for "dummy-Codec" - * - * ex1) - * CPU0 --- Codec0 link : 5 - * CPU1 --- Codec1 dais : 7 - * CPU2 -/ ccnf : 1 - * CPU3 --- Codec2 - * - * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec - * => 7 DAIs = 4xCPU + 3xCodec - * => 1 ccnf = 1xdummy-Codec - * - * ex2) - * CPU0 --- Codec0 link : 5 - * CPU1 --- Codec1 dais : 6 - * CPU2 -/ ccnf : 1 - * CPU3 -/ - * - * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec - * => 6 DAIs = 4xCPU + 2xCodec - * => 1 ccnf = 1xdummy-Codec - * - * ex3) - * CPU0 --- Codec0 link : 6 - * CPU1 -/ dais : 6 - * CPU2 --- Codec1 ccnf : 2 - * CPU3 -/ - * - * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec - * => 6 DAIs = 4xCPU + 2xCodec - * => 2 ccnf = 2xdummy-Codec - */ - if (!top) { - (*link_num) = 1; - (*dais_num) = 2; - (*ccnf_num) = 0; - return; - } - - loop = 1; - node = of_get_child_by_name(top, PREFIX "dai-link"); - if (!node) { - node = top; - loop = 0; - } - - do { - num = of_get_child_count(node); - (*dais_num) += num; - if (num > 2) { - (*link_num) += num; - (*ccnf_num)++; - } else { - (*link_num)++; - } - node = of_get_next_child(top, node); - } while (loop && node); -} - -static int asoc_simple_card_probe(struct platform_device *pdev) -{ - struct simple_card_data *priv; - struct snd_soc_dai_link *dai_link; - struct simple_dai_props *dai_props; - struct asoc_simple_dai *dais; - struct snd_soc_card *card; - struct snd_soc_codec_conf *cconf; - struct device *dev = &pdev->dev; - int ret, i; - int lnum = 0, dnum = 0, cnum = 0; - - /* Allocate the private data */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum); - if (!lnum || !dnum) - return -EINVAL; - - dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); - dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); - cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); - if (!dai_props || !dai_link || !dais) - return -ENOMEM; - - /* - * Use snd_soc_dai_link_component instead of legacy style - * It is codec only. but cpu/platform will be supported in the future. - * see - * soc-core.c :: snd_soc_init_multicodec() - */ - for (i = 0; i < lnum; i++) { - dai_link[i].codecs = &dai_props[i].codecs; - dai_link[i].num_codecs = 1; - dai_link[i].platform = &dai_props[i].platform; - } - - priv->dai_props = dai_props; - priv->dai_link = dai_link; - priv->dais = dais; - priv->codec_conf = cconf; - - /* Init snd_soc_card */ - card = simple_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; - card->dai_link = priv->dai_link; - card->num_links = lnum; - card->codec_conf = cconf; - card->num_configs = cnum; - - ret = asoc_simple_card_parse_of(priv); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - goto err; - } - - snd_soc_card_set_drvdata(card, priv); - - ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - return 0; -err: - asoc_simple_card_clean_reference(card); - - return ret; -} - -static int asoc_simple_card_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - return asoc_simple_card_clean_reference(card); -} - -static const struct of_device_id asoc_simple_of_match[] = { - { .compatible = "renesas,rsrc-card", }, - { .compatible = "simple-scu-audio-card", }, - {}, -}; -MODULE_DEVICE_TABLE(of, asoc_simple_of_match); - -static struct platform_driver asoc_simple_card = { - .driver = { - .name = "simple-scu-audio-card", - .pm = &snd_soc_pm_ops, - .of_match_table = asoc_simple_of_match, - }, - .probe = asoc_simple_card_probe, - .remove = asoc_simple_card_remove, -}; - -module_platform_driver(asoc_simple_card); - -MODULE_ALIAS("platform:asoc-simple-scu-card"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ASoC Simple SCU Sound Card"); -MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 3672d36b4b66..d1207ea53523 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -647,7 +647,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, set_mixer = false; } - if (set_mixer == false) + if (!set_mixer) return 0; if (SND_SOC_DAPM_EVENT_ON(event) || diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 91a2436ce952..b0873fea23ab 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -190,7 +190,7 @@ int sst_fill_stream_params(void *substream, map = ctx->pdata->pdev_strm_map; map_size = ctx->pdata->strm_map_size; - if (is_compress == true) + if (is_compress) cstream = (struct snd_compr_stream *)substream; else pstream = (struct snd_pcm_substream *)substream; diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index ac542535b9d5..ae17ce4677a5 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -255,18 +255,16 @@ static int is_byt(void) return status; } -static int is_byt_cr(struct device *dev, bool *bytcr) +static bool is_byt_cr(struct platform_device *pdev) { + struct device *dev = &pdev->dev; int status = 0; - if (IS_ENABLED(CONFIG_IOSF_MBI)) { - u32 bios_status; - - if (!is_byt() || !iosf_mbi_available()) { - /* bail silently */ - return status; - } + if (!is_byt()) + return false; + if (iosf_mbi_available()) { + u32 bios_status; status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */ MBI_REG_READ, /* 0x10 */ 0x006, /* BIOS_CONFIG */ @@ -278,15 +276,28 @@ static int is_byt_cr(struct device *dev, bool *bytcr) /* bits 26:27 mirror PMIC options */ bios_status = (bios_status >> 26) & 3; - if ((bios_status == 1) || (bios_status == 3)) - *bytcr = true; - else - dev_info(dev, "BYT-CR not detected\n"); + if (bios_status == 1 || bios_status == 3) { + dev_info(dev, "Detected Baytrail-CR platform\n"); + return true; + } + + dev_info(dev, "BYT-CR not detected\n"); } } else { - dev_info(dev, "IOSF_MBI not enabled, no BYT-CR detection\n"); + dev_info(dev, "IOSF_MBI not available, no BYT-CR detection\n"); } - return status; + + if (platform_get_resource(pdev, IORESOURCE_IRQ, 5) == NULL) { + /* + * Some devices detected as BYT-T have only a single IRQ listed, + * causing platform_get_irq with index 5 to return -ENXIO. + * The correct IRQ in this case is at index 0, as on BYT-CR. + */ + dev_info(dev, "Falling back to Baytrail-CR platform\n"); + return true; + } + + return false; } @@ -301,7 +312,6 @@ static int sst_acpi_probe(struct platform_device *pdev) struct platform_device *plat_dev; struct sst_platform_info *pdata; unsigned int dev_id; - bool bytcr = false; id = acpi_match_device(dev->driver->acpi_match_table, dev); if (!id) @@ -333,10 +343,7 @@ static int sst_acpi_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = is_byt_cr(dev, &bytcr); - if (!((ret < 0) || (bytcr == false))) { - dev_info(dev, "Detected Baytrail-CR platform\n"); - + if (is_byt_cr(pdev)) { /* override resource info */ byt_rvp_platform_data.res_info = &bytcr_res_info; } diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 5455d6e0ab53..a592df06aa58 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -146,7 +146,7 @@ static int sst_power_control(struct device *dev, bool state) int ret = 0; int usage_count = 0; - if (state == true) { + if (state) { ret = pm_runtime_get_sync(dev); usage_count = GET_USAGE_COUNT(dev); dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count); diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index b8c456753f01..321c783cf833 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -269,7 +269,7 @@ static void sst_do_memcpy(struct list_head *memcpy_list) struct sst_memcpy_list *listnode; list_for_each_entry(listnode, memcpy_list, memcpylist) { - if (listnode->is_io == true) + if (listnode->is_io) memcpy32_toio((void __iomem *)listnode->dstn, listnode->src, listnode->size); else diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c index 260447da32b8..2cd8f9668b50 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c @@ -278,7 +278,6 @@ static int sst_byt_process_notification(struct sst_byt *byt, struct sst_byt_stream *stream; u64 header; u8 msg_id, stream_id; - int handled = 1; header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); msg_id = sst_byt_header_msg_id(header); @@ -298,7 +297,7 @@ static int sst_byt_process_notification(struct sst_byt *byt, break; } - return handled; + return 1; } static irqreturn_t sst_byt_irq_thread(int irq, void *context) diff --git a/sound/soc/intel/baytrail/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c index aabb35bf6b96..498fb5346f1a 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-pcm.c +++ b/sound/soc/intel/baytrail/sst-baytrail-pcm.c @@ -188,7 +188,7 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) sst_byt_stream_start(byt, pcm_data->stream, 0); break; case SNDRV_PCM_TRIGGER_RESUME: - if (pdata->restore_stream == true) + if (pdata->restore_stream) schedule_work(&pcm_data->work); else sst_byt_stream_resume(byt, pcm_data->stream); diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 0a7e40d06395..12d6b73e9531 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -293,6 +293,7 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH depends on MFD_INTEL_LPSS && I2C && ACPI select SND_SOC_DA7219 select SND_SOC_MAX98927 + select SND_SOC_MAX98373 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI help diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index efcfd906c856..1844c88ea4e2 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -26,6 +26,7 @@ #include <sound/soc.h> #include <sound/pcm_params.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../common/sst-dsp.h" #include "../haswell/sst-haswell-ipc.h" @@ -339,6 +340,9 @@ static struct snd_soc_card bdw_rt5677_card = { static int bdw_rt5677_probe(struct platform_device *pdev) { struct bdw_rt5677_priv *bdw_rt5677; + struct snd_soc_acpi_mach *mach; + const char *platform_name = NULL; + int ret; bdw_rt5677_card.dev = &pdev->dev; @@ -350,6 +354,16 @@ static int bdw_rt5677_probe(struct platform_device *pdev) return -ENOMEM; } + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + if (mach) /* extra check since legacy does not pass parameters */ + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5677_card, + platform_name); + if (ret) + return ret; + snd_soc_card_set_drvdata(&bdw_rt5677_card, bdw_rt5677); return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5677_card); diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 99f2a0156ae8..b86c746d9b7a 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -21,6 +21,7 @@ #include <sound/soc.h> #include <sound/jack.h> #include <sound/pcm_params.h> +#include <sound/soc-acpi.h> #include "../common/sst-dsp.h" #include "../haswell/sst-haswell-ipc.h" @@ -267,7 +268,22 @@ static struct snd_soc_card broadwell_rt286 = { static int broadwell_audio_probe(struct platform_device *pdev) { + struct snd_soc_acpi_mach *mach; + const char *platform_name = NULL; + int ret; + broadwell_rt286.dev = &pdev->dev; + + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + if (mach) /* extra check since legacy does not pass parameters */ + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(&broadwell_rt286, + platform_name); + if (ret) + return ret; + return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286); } diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 6f052fc8d1e2..5cadb7f654f3 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -16,6 +16,8 @@ * GNU General Public License for more details. */ +#include <asm/cpu_device_id.h> +#include <linux/input.h> #include <linux/module.h> #include <linux/platform_device.h> #include <sound/core.h> @@ -23,6 +25,7 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../../codecs/hdac_hdmi.h" #include "../../codecs/da7219.h" #include "../../codecs/da7219-aad.h" @@ -48,6 +51,7 @@ struct bxt_card_private { enum { BXT_DPCM_AUDIO_PB = 0, BXT_DPCM_AUDIO_CP, + BXT_DPCM_AUDIO_HS_PB, BXT_DPCM_AUDIO_REF_CP, BXT_DPCM_AUDIO_DMIC_CP, BXT_DPCM_AUDIO_HDMI1_PB, @@ -102,7 +106,7 @@ static const struct snd_soc_dapm_widget broxton_widgets[] = { platform_clock_control, SND_SOC_DAPM_POST_PMD|SND_SOC_DAPM_PRE_PMU), }; -static const struct snd_soc_dapm_route broxton_map[] = { +static const struct snd_soc_dapm_route audio_map[] = { /* HP jack connectors - unknown if we have jack detection */ {"Headphone Jack", NULL, "HPL"}, {"Headphone Jack", NULL, "HPR"}, @@ -117,15 +121,6 @@ static const struct snd_soc_dapm_route broxton_map[] = { {"DMic", NULL, "SoC DMIC"}, /* CODEC BE connections */ - {"HiFi Playback", NULL, "ssp5 Tx"}, - {"ssp5 Tx", NULL, "codec0_out"}, - - {"Playback", NULL, "ssp1 Tx"}, - {"ssp1 Tx", NULL, "codec1_out"}, - - {"codec0_in", NULL, "ssp1 Rx"}, - {"ssp1 Rx", NULL, "Capture"}, - {"HDMI1", NULL, "hif5-0 Output"}, {"HDMI2", NULL, "hif6-0 Output"}, {"HDMI2", NULL, "hif7-0 Output"}, @@ -145,6 +140,28 @@ static const struct snd_soc_dapm_route broxton_map[] = { { "Headset Mic", NULL, "Platform Clock" }, }; +static const struct snd_soc_dapm_route broxton_map[] = { + {"HiFi Playback", NULL, "ssp5 Tx"}, + {"ssp5 Tx", NULL, "codec0_out"}, + + {"Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "codec1_out"}, + + {"codec0_in", NULL, "ssp1 Rx"}, + {"ssp1 Rx", NULL, "Capture"}, +}; + +static const struct snd_soc_dapm_route gemini_map[] = { + {"HiFi Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "codec0_out"}, + + {"Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "codec1_out"}, + + {"codec0_in", NULL, "ssp2 Rx"}, + {"ssp2 Rx", NULL, "Capture"}, +}; + static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -192,6 +209,12 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) return ret; } + snd_jack_set_key(broxton_headset.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(broxton_headset.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(broxton_headset.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(broxton_headset.jack, SND_JACK_BTN_3, + KEY_VOICECOMMAND); + da7219_aad_jack_det(component, &broxton_headset); snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); @@ -383,6 +406,20 @@ static struct snd_soc_dai_link broxton_dais[] = { .dpcm_capture = 1, .ops = &broxton_da7219_fe_ops, }, + [BXT_DPCM_AUDIO_HS_PB] = { + .name = "Bxt Audio Headset Playback", + .stream_name = "Headset Playback", + .cpu_dai_name = "System Pin2", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &broxton_da7219_fe_ops, + }, [BXT_DPCM_AUDIO_REF_CP] = { .name = "Bxt Audio Reference cap", @@ -531,6 +568,11 @@ static struct snd_soc_dai_link broxton_dais[] = { }, }; +static const struct x86_cpu_id glk_ids[] = { + { X86_VENDOR_INTEL, 6, 0x7A }, /* Geminilake CPU_ID */ + {} +}; + #define NAME_SIZE 32 static int bxt_card_late_probe(struct snd_soc_card *card) { @@ -540,6 +582,13 @@ static int bxt_card_late_probe(struct snd_soc_card *card) int err, i = 0; char jack_name[NAME_SIZE]; + if (x86_match_cpu(glk_ids)) + snd_soc_dapm_add_routes(&card->dapm, gemini_map, + ARRAY_SIZE(gemini_map)); + else + snd_soc_dapm_add_routes(&card->dapm, broxton_map, + ARRAY_SIZE(broxton_map)); + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), @@ -575,8 +624,8 @@ static struct snd_soc_card broxton_audio_card = { .num_controls = ARRAY_SIZE(broxton_controls), .dapm_widgets = broxton_widgets, .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), - .dapm_routes = broxton_map, - .num_dapm_routes = ARRAY_SIZE(broxton_map), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), .fully_routed = true, .late_probe = bxt_card_late_probe, }; @@ -584,6 +633,9 @@ static struct snd_soc_card broxton_audio_card = { static int broxton_audio_probe(struct platform_device *pdev) { struct bxt_card_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -593,16 +645,52 @@ static int broxton_audio_probe(struct platform_device *pdev) broxton_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&broxton_audio_card, ctx); + if (x86_match_cpu(glk_ids)) { + unsigned int i; + + broxton_audio_card.name = "glkda7219max"; + /* Fixup the SSP entries for geminilake */ + for (i = 0; i < ARRAY_SIZE(broxton_dais); i++) { + /* MAXIM_CODEC is connected to SSP1. */ + if (!strcmp(broxton_dais[i].codec_dai_name, + BXT_MAXIM_CODEC_DAI)) { + broxton_dais[i].name = "SSP1-Codec"; + broxton_dais[i].cpu_dai_name = "SSP1 Pin"; + } + /* DIALOG_CODE is connected to SSP2 */ + else if (!strcmp(broxton_dais[i].codec_dai_name, + BXT_DIALOG_CODEC_DAI)) { + broxton_dais[i].name = "SSP2-Codec"; + broxton_dais[i].cpu_dai_name = "SSP2 Pin"; + } + } + } + + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(&broxton_audio_card, + platform_name); + if (ret) + return ret; return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card); } +static const struct platform_device_id bxt_board_ids[] = { + { .name = "bxt_da7219_max98357a" }, + { .name = "glk_da7219_max98357a" }, + { } +}; + static struct platform_driver broxton_audio = { .probe = broxton_audio_probe, .driver = { .name = "bxt_da7219_max98357a", .pm = &snd_soc_pm_ops, }, + .id_table = bxt_board_ids, }; module_platform_driver(broxton_audio) @@ -612,5 +700,7 @@ MODULE_AUTHOR("Sathyanarayana Nujella <sathyanarayana.nujella@intel.com>"); MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>"); MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>"); +MODULE_AUTHOR("Naveen Manohar <naveen.m@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:bxt_da7219_max98357a"); +MODULE_ALIAS("platform:glk_da7219_max98357a"); diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 27308337ab12..e91057f83d20 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -21,6 +21,7 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include <sound/jack.h> #include <sound/pcm_params.h> #include "../../codecs/hdac_hdmi.h" @@ -576,6 +577,9 @@ static int broxton_audio_probe(struct platform_device *pdev) struct bxt_rt286_private *ctx; struct snd_soc_card *card = (struct snd_soc_card *)pdev->id_entry->driver_data; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + int ret; int i; for (i = 0; i < ARRAY_SIZE(broxton_rt298_dais); i++) { @@ -602,6 +606,15 @@ static int broxton_audio_probe(struct platform_device *pdev) card->dev = &pdev->dev; snd_soc_card_set_drvdata(card, ctx); + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, + platform_name); + if (ret) + return ret; + return devm_snd_soc_register_card(&pdev->dev, card); } diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index 2179dedb28ad..b8e884803777 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -225,6 +225,7 @@ static int bytcht_da7213_probe(struct platform_device *pdev) { struct snd_soc_card *card; struct snd_soc_acpi_mach *mach; + const char *platform_name; const char *i2c_name = NULL; int dai_index = 0; int ret_val = 0; @@ -250,6 +251,13 @@ static int bytcht_da7213_probe(struct platform_device *pdev) dailink[dai_index].codec_name = codec_name; } + /* override plaform name, if required */ + platform_name = mach->mach_params.platform; + + ret_val = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret_val) + return ret_val; + ret_val = devm_snd_soc_register_card(&pdev->dev, card); if (ret_val) { dev_err(&pdev->dev, diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index adc26dfc7d65..d2a7e6ba11ae 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -19,13 +19,20 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> #include <linux/init.h> +#include <linux/input.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/device.h> #include <linux/slab.h> +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/platform_sst_audio.h> -#include <linux/clk.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -35,27 +42,96 @@ struct byt_cht_es8316_private { struct clk *mclk; + struct snd_soc_jack jack; + struct gpio_desc *speaker_en_gpio; + bool speaker_en; }; +enum { + BYT_CHT_ES8316_INTMIC_IN1_MAP, + BYT_CHT_ES8316_INTMIC_IN2_MAP, +}; + +#define BYT_CHT_ES8316_MAP(quirk) ((quirk) & GENMASK(3, 0)) +#define BYT_CHT_ES8316_SSP0 BIT(16) +#define BYT_CHT_ES8316_MONO_SPEAKER BIT(17) + +static int quirk; + +static int quirk_override = -1; +module_param_named(quirk, quirk_override, int, 0444); +MODULE_PARM_DESC(quirk, "Board-specific quirk override"); + +static void log_quirks(struct device *dev) +{ + if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN1_MAP) + dev_info(dev, "quirk IN1_MAP enabled"); + if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN2_MAP) + dev_info(dev, "quirk IN2_MAP enabled"); + if (quirk & BYT_CHT_ES8316_SSP0) + dev_info(dev, "quirk SSP0 enabled"); + if (quirk & BYT_CHT_ES8316_MONO_SPEAKER) + dev_info(dev, "quirk MONO_SPEAKER enabled\n"); +} + +static int byt_cht_es8316_speaker_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); + + if (SND_SOC_DAPM_EVENT_ON(event)) + priv->speaker_en = true; + else + priv->speaker_en = false; + + gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en); + + return 0; +} + static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Internal Mic", NULL), - /* - * The codec supports two analog microphone inputs. I have only - * tested MIC1. A DMIC route could also potentially be added - * if such functionality is found on another platform. - */ - SND_SOC_DAPM_MIC("Microphone 1", NULL), - SND_SOC_DAPM_MIC("Microphone 2", NULL), + SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0, + byt_cht_es8316_speaker_power_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), }; static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = { - {"MIC1", NULL, "Microphone 1"}, - {"MIC2", NULL, "Microphone 2"}, - {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, + /* + * There is no separate speaker output instead the speakers are muxed to + * the HP outputs. The mux is controlled by the "Speaker Power" supply. + */ + {"Speaker", NULL, "HPOL"}, + {"Speaker", NULL, "HPOR"}, + {"Speaker", NULL, "Speaker Power"}, +}; + +static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in1_map[] = { + {"MIC1", NULL, "Internal Mic"}, + {"MIC2", NULL, "Headset Mic"}, +}; + +static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in2_map[] = { + {"MIC2", NULL, "Internal Mic"}, + {"MIC1", NULL, "Headset Mic"}, +}; + +static const struct snd_soc_dapm_route byt_cht_es8316_ssp0_map[] = { + {"Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "modem_out"}, + {"modem_in", NULL, "ssp0 Rx"}, + {"ssp0 Rx", NULL, "Capture"}, +}; + +static const struct snd_soc_dapm_route byt_cht_es8316_ssp2_map[] = { {"Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, @@ -65,19 +141,60 @@ static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = { }; static const struct snd_kcontrol_new byt_cht_es8316_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Microphone 1"), - SOC_DAPM_PIN_SWITCH("Microphone 2"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Internal Mic"), +}; + +static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, }; static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) { + struct snd_soc_component *codec = runtime->codec_dai->component; struct snd_soc_card *card = runtime->card; struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); + const struct snd_soc_dapm_route *custom_map; + int num_routes; int ret; card->dapm.idle_bias_off = true; + switch (BYT_CHT_ES8316_MAP(quirk)) { + case BYT_CHT_ES8316_INTMIC_IN1_MAP: + default: + custom_map = byt_cht_es8316_intmic_in1_map; + num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in1_map); + break; + case BYT_CHT_ES8316_INTMIC_IN2_MAP: + custom_map = byt_cht_es8316_intmic_in2_map; + num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in2_map); + break; + } + ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); + if (ret) + return ret; + + if (quirk & BYT_CHT_ES8316_SSP0) { + custom_map = byt_cht_es8316_ssp0_map; + num_routes = ARRAY_SIZE(byt_cht_es8316_ssp0_map); + } else { + custom_map = byt_cht_es8316_ssp2_map; + num_routes = ARRAY_SIZE(byt_cht_es8316_ssp2_map); + } + ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); + if (ret) + return ret; + /* * The firmware might enable the clock at boot (this information * may or may not be reflected in the enable clock register). @@ -105,6 +222,18 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) return ret; } + ret = snd_soc_card_jack_new(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &priv->jack, byt_cht_es8316_jack_pins, + ARRAY_SIZE(byt_cht_es8316_jack_pins)); + if (ret) { + dev_err(card->dev, "jack creation failed %d\n", ret); + return ret; + } + + snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_soc_component_set_jack(codec, &priv->jack, NULL); + return 0; } @@ -123,14 +252,21 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - int ret; + int ret, bits; /* The DSP will covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; channels->min = channels->max = 2; - /* set SSP2 to 24-bit */ - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + if (quirk & BYT_CHT_ES8316_SSP0) { + /* set SSP0 to 16-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + bits = 16; + } else { + /* set SSP2 to 24-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + bits = 24; + } /* * Default mode for SSP configuration is TDM 4 slot, override config @@ -147,7 +283,7 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, bits); if (ret < 0) { dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); return ret; @@ -218,6 +354,59 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = { /* SoC card */ +static char codec_name[SND_ACPI_I2C_ID_LEN]; +static char long_name[50]; /* = "bytcht-es8316-*-spk-*-mic" */ + +static int byt_cht_es8316_suspend(struct snd_soc_card *card) +{ + struct snd_soc_component *component; + + for_each_card_components(card, component) { + if (!strcmp(component->name, codec_name)) { + dev_dbg(component->dev, "disabling jack detect before suspend\n"); + snd_soc_component_set_jack(component, NULL, NULL); + break; + } + } + + return 0; +} + +static int byt_cht_es8316_resume(struct snd_soc_card *card) +{ + struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + + for_each_card_components(card, component) { + if (!strcmp(component->name, codec_name)) { + dev_dbg(component->dev, "re-enabling jack detect after resume\n"); + snd_soc_component_set_jack(component, &priv->jack, NULL); + break; + } + } + + /* + * Some Cherry Trail boards with an ES8316 codec have a bug in their + * ACPI tables where the MSSL1680 touchscreen's _PS0 and _PS3 methods + * wrongly also set the speaker-enable GPIO to 1/0. Testing has shown + * that this really is a bug and the GPIO has no influence on the + * touchscreen at all. + * + * The silead.c touchscreen driver does not support runtime suspend, so + * the GPIO can only be changed underneath us during a system suspend. + * This resume() function runs from a pm complete() callback, and thus + * is guaranteed to run after the touchscreen driver/ACPI-subsys has + * brought the touchscreen back up again (and thus changed the GPIO). + * + * So to work around this we pass GPIOD_FLAGS_BIT_NONEXCLUSIVE when + * requesting the GPIO and we set its value here to undo any changes + * done by the touchscreen's broken _PS0 ACPI method. + */ + gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en); + + return 0; +} + static struct snd_soc_card byt_cht_es8316_card = { .name = "bytcht-es8316", .owner = THIS_MODULE, @@ -230,24 +419,40 @@ static struct snd_soc_card byt_cht_es8316_card = { .controls = byt_cht_es8316_controls, .num_controls = ARRAY_SIZE(byt_cht_es8316_controls), .fully_routed = true, + .suspend_pre = byt_cht_es8316_suspend, + .resume_post = byt_cht_es8316_resume, }; -static char codec_name[SND_ACPI_I2C_ID_LEN]; +static const struct x86_cpu_id baytrail_cpu_ids[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT }, /* Valleyview */ + {} +}; + +static const struct acpi_gpio_params first_gpio = { 0, 0, false }; + +static const struct acpi_gpio_mapping byt_cht_es8316_gpios[] = { + { "speaker-enable-gpios", &first_gpio, 1 }, + { }, +}; static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) { + static const char * const mic_name[] = { "in1", "in2" }; struct byt_cht_es8316_private *priv; + struct device *dev = &pdev->dev; struct snd_soc_acpi_mach *mach; + const char *platform_name; const char *i2c_name = NULL; + struct device *codec_dev; int dai_index = 0; int i; int ret = 0; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - mach = (&pdev->dev)->platform_data; + mach = dev->platform_data; /* fix index of codec dai */ for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) { if (!strcmp(byt_cht_es8316_dais[i].codec_name, @@ -265,26 +470,94 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) byt_cht_es8316_dais[dai_index].codec_name = codec_name; } - /* register the soc card */ - byt_cht_es8316_card.dev = &pdev->dev; - snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv); + /* override plaform name, if required */ + platform_name = mach->mach_params.platform; - priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_es8316_card, + platform_name); + if (ret) + return ret; + + /* Check for BYTCR or other platform and setup quirks */ + if (x86_match_cpu(baytrail_cpu_ids) && + mach->mach_params.acpi_ipc_irq_index == 0) { + /* On BYTCR default to SSP0, internal-mic-in2-map, mono-spk */ + quirk = BYT_CHT_ES8316_SSP0 | BYT_CHT_ES8316_INTMIC_IN2_MAP | + BYT_CHT_ES8316_MONO_SPEAKER; + } else { + /* Others default to internal-mic-in1-map, mono-speaker */ + quirk = BYT_CHT_ES8316_INTMIC_IN1_MAP | + BYT_CHT_ES8316_MONO_SPEAKER; + } + if (quirk_override != -1) { + dev_info(dev, "Overriding quirk 0x%x => 0x%x\n", quirk, + quirk_override); + quirk = quirk_override; + } + log_quirks(dev); + + if (quirk & BYT_CHT_ES8316_SSP0) + byt_cht_es8316_dais[dai_index].cpu_dai_name = "ssp0-port"; + + /* get the clock */ + priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3"); if (IS_ERR(priv->mclk)) { ret = PTR_ERR(priv->mclk); - dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %d\n", - ret); + dev_err(dev, "clk_get pmc_plt_clk_3 failed: %d\n", ret); return ret; } - ret = devm_snd_soc_register_card(&pdev->dev, &byt_cht_es8316_card); + /* get speaker enable GPIO */ + codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, codec_name); + if (!codec_dev) + return -EPROBE_DEFER; + + devm_acpi_dev_add_driver_gpios(codec_dev, byt_cht_es8316_gpios); + priv->speaker_en_gpio = + gpiod_get_index(codec_dev, "speaker-enable", 0, + /* see comment in byt_cht_es8316_resume */ + GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); + put_device(codec_dev); + + if (IS_ERR(priv->speaker_en_gpio)) { + ret = PTR_ERR(priv->speaker_en_gpio); + switch (ret) { + case -ENOENT: + priv->speaker_en_gpio = NULL; + break; + default: + dev_err(dev, "get speaker GPIO failed: %d\n", ret); + /* fall through */ + case -EPROBE_DEFER: + return ret; + } + } + + /* register the soc card */ + snprintf(long_name, sizeof(long_name), "bytcht-es8316-%s-spk-%s-mic", + (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "mono" : "stereo", + mic_name[BYT_CHT_ES8316_MAP(quirk)]); + byt_cht_es8316_card.long_name = long_name; + byt_cht_es8316_card.dev = dev; + snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv); + + ret = devm_snd_soc_register_card(dev, &byt_cht_es8316_card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); + gpiod_put(priv->speaker_en_gpio); + dev_err(dev, "snd_soc_register_card failed: %d\n", ret); return ret; } platform_set_drvdata(pdev, &byt_cht_es8316_card); - return ret; + return 0; +} + +static int snd_byt_cht_es8316_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); + + gpiod_put(priv->speaker_en_gpio); + return 0; } static struct platform_driver snd_byt_cht_es8316_mc_driver = { @@ -292,6 +565,7 @@ static struct platform_driver snd_byt_cht_es8316_mc_driver = { .name = "bytcht_es8316", }, .probe = snd_byt_cht_es8316_mc_probe, + .remove = snd_byt_cht_es8316_mc_remove, }; module_platform_driver(snd_byt_cht_es8316_mc_driver); diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a22366ce33c4..940eb27158da 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -431,6 +431,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), }, .driver_data = (void *)(BYT_RT5640_IN1_MAP | @@ -1137,10 +1149,11 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { - const char * const map_name[] = { "dmic1", "dmic2", "in1", "in3" }; + static const char * const map_name[] = { "dmic1", "dmic2", "in1", "in3" }; const struct dmi_system_id *dmi_id; struct byt_rt5640_private *priv; struct snd_soc_acpi_mach *mach; + const char *platform_name; const char *i2c_name = NULL; int ret_val = 0; int dai_index = 0; @@ -1305,6 +1318,14 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) map_name[BYT_RT5640_MAP(byt_rt5640_quirk)]); byt_rt5640_card.long_name = byt_rt5640_long_name; + /* override plaform name, if required */ + platform_name = mach->mach_params.platform; + + ret_val = snd_soc_fixup_dai_links_platform_name(&byt_rt5640_card, + platform_name); + if (ret_val) + return ret_val; + ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); if (ret_val) { diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index e528995668b7..b0a4d297176e 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -91,13 +91,20 @@ enum { struct byt_rt5651_private { struct clk *mclk; struct gpio_desc *ext_amp_gpio; + struct gpio_desc *hp_detect; struct snd_soc_jack jack; }; +static const struct acpi_gpio_mapping *byt_rt5651_gpios; + /* Default: jack-detect on JD1_1, internal mic on in2, headsetmic on in3 */ static unsigned long byt_rt5651_quirk = BYT_RT5651_DEFAULT_QUIRKS | BYT_RT5651_IN2_MAP; +static unsigned int quirk_override; +module_param_named(quirk, quirk_override, uint, 0444); +MODULE_PARM_DESC(quirk, "Board-specific quirk override"); + static void log_quirks(struct device *dev) { if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC_MAP) @@ -266,7 +273,7 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = { {"DMIC L1", NULL, "Internal Mic"}, {"DMIC R1", NULL, "Internal Mic"}, - {"IN3P", NULL, "Headset Mic"}, + {"IN2P", NULL, "Headset Mic"}, }; static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = { @@ -360,6 +367,22 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, return byt_rt5651_prepare_and_enable_pll1(codec_dai, rate, bclk_ratio); } +static const struct acpi_gpio_params pov_p1006w_hp_detect = { 1, 0, false }; +static const struct acpi_gpio_params pov_p1006w_ext_amp_en = { 2, 0, true }; + +static const struct acpi_gpio_mapping byt_rt5651_pov_p1006w_gpios[] = { + { "hp-detect-gpios", &pov_p1006w_hp_detect, 1, }, + { "ext-amp-enable-gpios", &pov_p1006w_ext_amp_en, 1, }, + { }, +}; + +static int byt_rt5651_pov_p1006w_quirk_cb(const struct dmi_system_id *id) +{ + byt_rt5651_quirk = (unsigned long)id->driver_data; + byt_rt5651_gpios = byt_rt5651_pov_p1006w_gpios; + return 1; +} + static int byt_rt5651_quirk_cb(const struct dmi_system_id *id) { byt_rt5651_quirk = (unsigned long)id->driver_data; @@ -436,6 +459,23 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { BYT_RT5651_IN1_MAP), }, { + /* Point of View mobii wintab p1006w (v1.0) */ + .callback = byt_rt5651_pov_p1006w_quirk_cb, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BayTrail"), + /* Note 105b is Foxcon's USB/PCI vendor id */ + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "105B"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"), + }, + .driver_data = (void *)(BYT_RT5651_DMIC_MAP | + BYT_RT5651_OVCD_TH_2000UA | + BYT_RT5651_OVCD_SF_0P75 | + BYT_RT5651_DMIC_EN | + BYT_RT5651_MCLK_EN | + BYT_RT5651_SSP0_AIF1), + }, + { /* VIOS LTH17 */ .callback = byt_rt5651_quirk_cb, .matches = { @@ -495,6 +535,7 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); const struct snd_soc_dapm_route *custom_map; int num_routes; + int report; int ret; card->dapm.idle_bias_off = true; @@ -578,20 +619,27 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) dev_err(card->dev, "unable to set MCLK rate\n"); } - if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) { + report = 0; + if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) + report = SND_JACK_HEADSET | SND_JACK_BTN_0; + else if (priv->hp_detect) + report = SND_JACK_HEADSET; + + if (report) { ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, - &priv->jack, bytcr_jack_pins, + report, &priv->jack, bytcr_jack_pins, ARRAY_SIZE(bytcr_jack_pins)); if (ret) { dev_err(runtime->dev, "jack creation failed %d\n", ret); return ret; } - snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, - KEY_PLAYPAUSE); + if (report & SND_JACK_BTN_0) + snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, + KEY_PLAYPAUSE); - ret = snd_soc_component_set_jack(codec, &priv->jack, NULL); + ret = snd_soc_component_set_jack(codec, &priv->jack, + priv->hp_detect); if (ret) return ret; } @@ -763,7 +811,8 @@ static int byt_rt5651_resume(struct snd_soc_card *card) for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5651_codec_name)) { dev_dbg(component->dev, "re-enabling jack detect after resume\n"); - snd_soc_component_set_jack(component, &priv->jack, NULL); + snd_soc_component_set_jack(component, &priv->jack, + priv->hp_detect); break; } } @@ -795,74 +844,18 @@ static const struct x86_cpu_id cherrytrail_cpu_ids[] = { {} }; -static const struct acpi_gpio_params first_gpio = { 0, 0, false }; -static const struct acpi_gpio_params second_gpio = { 1, 0, false }; - -static const struct acpi_gpio_mapping byt_rt5651_amp_en_first[] = { - { "ext-amp-enable-gpios", &first_gpio, 1 }, - { }, -}; +static const struct acpi_gpio_params ext_amp_enable_gpios = { 0, 0, false }; -static const struct acpi_gpio_mapping byt_rt5651_amp_en_second[] = { - { "ext-amp-enable-gpios", &second_gpio, 1 }, +static const struct acpi_gpio_mapping cht_rt5651_gpios[] = { + /* + * Some boards have I2cSerialBusV2, GpioIo, GpioInt as ACPI resources, + * other boards may have I2cSerialBusV2, GpioInt, GpioIo instead. + * We want the GpioIo one for the ext-amp-enable-gpio. + */ + { "ext-amp-enable-gpios", &ext_amp_enable_gpios, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO }, { }, }; -/* - * Some boards have I2cSerialBusV2, GpioIo, GpioInt as ACPI resources, other - * boards may have I2cSerialBusV2, GpioInt, GpioIo instead. We want the - * GpioIo one for the ext-amp-enable-gpio and both count for the index in - * acpi_gpio_params index. So we have 2 different mappings and the code - * below figures out which one to use. - */ -struct byt_rt5651_acpi_resource_data { - int gpio_count; - int gpio_int_idx; -}; - -static int snd_byt_rt5651_acpi_resource(struct acpi_resource *ares, void *arg) -{ - struct byt_rt5651_acpi_resource_data *data = arg; - - if (ares->type != ACPI_RESOURCE_TYPE_GPIO) - return 0; - - if (ares->data.gpio.connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) - data->gpio_int_idx = data->gpio_count; - - data->gpio_count++; - return 0; -} - -static void snd_byt_rt5651_mc_add_amp_en_gpio_mapping(struct device *codec) -{ - struct byt_rt5651_acpi_resource_data data = { 0, -1 }; - LIST_HEAD(resources); - int ret; - - ret = acpi_dev_get_resources(ACPI_COMPANION(codec), &resources, - snd_byt_rt5651_acpi_resource, &data); - if (ret < 0) { - dev_warn(codec, "Failed to get ACPI resources, not adding external amplifier GPIO mapping\n"); - return; - } - - /* All info we need is gathered during the walk */ - acpi_dev_free_resource_list(&resources); - - switch (data.gpio_int_idx) { - case 0: - devm_acpi_dev_add_driver_gpios(codec, byt_rt5651_amp_en_second); - break; - case 1: - devm_acpi_dev_add_driver_gpios(codec, byt_rt5651_amp_en_first); - break; - default: - dev_warn(codec, "Unknown GpioInt index %d, not adding external amplifier GPIO mapping\n", - data.gpio_int_idx); - } -} - struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ u64 aif_value; /* 1: AIF1, 2: AIF2 */ u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */ @@ -870,9 +863,10 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { - const char * const mic_name[] = { "dmic", "in1", "in2", "in12" }; + static const char * const mic_name[] = { "dmic", "in1", "in2", "in12" }; struct byt_rt5651_private *priv; struct snd_soc_acpi_mach *mach; + const char *platform_name; struct device *codec_dev; const char *i2c_name = NULL; const char *hp_swapped; @@ -973,6 +967,12 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) /* check quirks before creating card */ dmi_check_system(byt_rt5651_quirk_table); + if (quirk_override) { + dev_info(&pdev->dev, "Overriding quirk 0x%x => 0x%x\n", + (unsigned int)byt_rt5651_quirk, quirk_override); + byt_rt5651_quirk = quirk_override; + } + /* Must be called before register_card, also see declaration comment. */ ret_val = byt_rt5651_add_codec_device_props(codec_dev); if (ret_val) { @@ -981,8 +981,11 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) } /* Cherry Trail devices use an external amplifier enable gpio */ - if (x86_match_cpu(cherrytrail_cpu_ids)) { - snd_byt_rt5651_mc_add_amp_en_gpio_mapping(codec_dev); + if (x86_match_cpu(cherrytrail_cpu_ids) && !byt_rt5651_gpios) + byt_rt5651_gpios = cht_rt5651_gpios; + + if (byt_rt5651_gpios) { + devm_acpi_dev_add_driver_gpios(codec_dev, byt_rt5651_gpios); priv->ext_amp_gpio = devm_fwnode_get_index_gpiod_from_child( &pdev->dev, "ext-amp-enable", 0, codec_dev->fwnode, @@ -1002,6 +1005,25 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) return ret_val; } } + priv->hp_detect = devm_fwnode_get_index_gpiod_from_child( + &pdev->dev, "hp-detect", 0, + codec_dev->fwnode, + GPIOD_IN, "hp-detect"); + if (IS_ERR(priv->hp_detect)) { + ret_val = PTR_ERR(priv->hp_detect); + switch (ret_val) { + case -ENOENT: + priv->hp_detect = NULL; + break; + default: + dev_err(&pdev->dev, "Failed to get hp-detect GPIO: %d\n", + ret_val); + /* fall through */ + case -EPROBE_DEFER: + put_device(codec_dev); + return ret_val; + } + } } put_device(codec_dev); @@ -1060,6 +1082,14 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], hp_swapped); byt_rt5651_card.long_name = byt_rt5651_long_name; + /* override plaform name, if required */ + platform_name = mach->mach_params.platform; + + ret_val = snd_soc_fixup_dai_links_platform_name(&byt_rt5651_card, + platform_name); + if (ret_val) + return ret_val; + ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card); if (ret_val) { diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 08a5152e635a..3263b0495853 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -28,6 +28,7 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include <sound/jack.h> #include "../../codecs/max98090.h" #include "../atom/sst-atom-controls.h" @@ -420,6 +421,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) int ret_val = 0; struct cht_mc_private *drv; const char *mclk_name; + struct snd_soc_acpi_mach *mach; + const char *platform_name; int quirks = 0; dmi_id = dmi_first_match(cht_max98090_quirk_table); @@ -442,6 +445,15 @@ static int snd_cht_mc_probe(struct platform_device *pdev) dev_dbg(dev, "Unable to add GPIO mapping table\n"); } + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, + platform_name); + if (ret_val) + return ret_val; + /* register the soc card */ snd_soc_card_cht.dev = &pdev->dev; snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c index 30c46977d53c..02c2fa239331 100644 --- a/sound/soc/intel/boards/cht_bsw_nau8824.c +++ b/sound/soc/intel/boards/cht_bsw_nau8824.c @@ -25,6 +25,7 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include <sound/jack.h> #include <linux/input.h> #include "../atom/sst-atom-controls.h" @@ -246,6 +247,8 @@ static struct snd_soc_card snd_soc_card_cht = { static int snd_cht_mc_probe(struct platform_device *pdev) { struct cht_mc_private *drv; + struct snd_soc_acpi_mach *mach; + const char *platform_name; int ret_val; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); @@ -253,6 +256,15 @@ static int snd_cht_mc_probe(struct platform_device *pdev) return -ENOMEM; snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, + platform_name); + if (ret_val) + return ret_val; + /* register the soc card */ snd_soc_card_cht.dev = &pdev->dev; ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 250a356a0cbf..cbc2d458483f 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -530,6 +530,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) { struct snd_soc_card *card = snd_soc_cards[0].soc_card; struct snd_soc_acpi_mach *mach; + const char *platform_name; struct cht_mc_private *drv; const char *i2c_name = NULL; bool found = false; @@ -663,6 +664,14 @@ static int snd_cht_mc_probe(struct platform_device *pdev) cht_rt5645_cpu_dai_name; } + /* override plaform name, if required */ + platform_name = mach->mach_params.platform; + + ret_val = snd_soc_fixup_dai_links_platform_name(card, + platform_name); + if (ret_val) + return ret_val; + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); if (IS_ERR(drv->mclk)) { dev_err(&pdev->dev, diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 9de64f447e7b..3d5a2b3a06f0 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -400,6 +400,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) int ret_val = 0; struct cht_mc_private *drv; struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; + const char *platform_name; const char *i2c_name; int i; @@ -410,22 +411,27 @@ static int snd_cht_mc_probe(struct platform_device *pdev) strcpy(drv->codec_name, RT5672_I2C_DEFAULT); /* fixup codec name based on HID */ - if (mach) { - i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); - if (i2c_name) { - snprintf(drv->codec_name, sizeof(drv->codec_name), - "i2c-%s", i2c_name); - for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) { - if (!strcmp(cht_dailink[i].codec_name, - RT5672_I2C_DEFAULT)) { - cht_dailink[i].codec_name = - drv->codec_name; - break; - } + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); + if (i2c_name) { + snprintf(drv->codec_name, sizeof(drv->codec_name), + "i2c-%s", i2c_name); + for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) { + if (!strcmp(cht_dailink[i].codec_name, + RT5672_I2C_DEFAULT)) { + cht_dailink[i].codec_name = drv->codec_name; + break; } } } + /* override plaform name, if required */ + platform_name = mach->mach_params.platform; + + ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, + platform_name); + if (ret_val) + return ret_val; + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); if (IS_ERR(drv->mclk)) { dev_err(&pdev->dev, diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index 8f83b182c4f9..d17126f7757c 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -16,6 +16,7 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../skylake/skl.h" #include "../../codecs/rt5682.h" #include "../../codecs/hdac_hdmi.h" @@ -126,7 +127,7 @@ static int geminilake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } @@ -571,6 +572,10 @@ static struct snd_soc_card glk_audio_card_rt5682_m98357a = { static int geminilake_audio_probe(struct platform_device *pdev) { struct glk_card_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + struct snd_soc_card *card; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -578,11 +583,19 @@ static int geminilake_audio_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ctx->hdmi_pcm_list); - glk_audio_card_rt5682_m98357a.dev = &pdev->dev; - snd_soc_card_set_drvdata(&glk_audio_card_rt5682_m98357a, ctx); + card = &glk_audio_card_rt5682_m98357a; + card->dev = &pdev->dev; + snd_soc_card_set_drvdata(card, ctx); + + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret) + return ret; - return devm_snd_soc_register_card(&pdev->dev, - &glk_audio_card_rt5682_m98357a); + return devm_snd_soc_register_card(&pdev->dev, card); } static const struct platform_device_id glk_board_ids[] = { diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index a4022983a7ce..971226d42042 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -19,6 +19,7 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include <sound/pcm_params.h> #include "../common/sst-dsp.h" @@ -189,8 +190,22 @@ static struct snd_soc_card haswell_rt5640 = { static int haswell_audio_probe(struct platform_device *pdev) { + struct snd_soc_acpi_mach *mach; + const char *platform_name = NULL; + int ret; + haswell_rt5640.dev = &pdev->dev; + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + if (mach) /* extra check since legacy does not pass parameters */ + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(&haswell_rt5640, + platform_name); + if (ret) + return ret; + return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640); } diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 723a4935ed76..2768a572d065 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -2,7 +2,7 @@ // Copyright(c) 2018 Intel Corporation. /* - * Intel Kabylake I2S Machine Driver with MAX98927 & DA7219 Codecs + * Intel Kabylake I2S Machine Driver with MAX98927, MAX98373 & DA7219 Codecs * * Modified from: * Intel Kabylake I2S Machine driver supporting MAX98927 and @@ -24,8 +24,14 @@ #define KBL_DIALOG_CODEC_DAI "da7219-hifi" #define MAX98927_CODEC_DAI "max98927-aif1" -#define MAXIM_DEV0_NAME "i2c-MX98927:00" -#define MAXIM_DEV1_NAME "i2c-MX98927:01" +#define MAX98927_DEV0_NAME "i2c-MX98927:00" +#define MAX98927_DEV1_NAME "i2c-MX98927:01" + +#define MAX98373_CODEC_DAI "max98373-aif1" +#define MAX98373_DEV0_NAME "i2c-MX98373:00" +#define MAX98373_DEV1_NAME "i2c-MX98373:01" + + #define DUAL_CHANNEL 2 #define QUAD_CHANNEL 4 #define NAME_SIZE 32 @@ -176,20 +182,38 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, for (j = 0; j < runtime->num_codecs; j++) { struct snd_soc_dai *codec_dai = runtime->codec_dais[j]; - if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { + if (!strcmp(codec_dai->component->name, MAX98927_DEV0_NAME)) { ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16); if (ret < 0) { dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret); return ret; } } - if (!strcmp(codec_dai->component->name, MAXIM_DEV1_NAME)) { + if (!strcmp(codec_dai->component->name, MAX98927_DEV1_NAME)) { ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16); if (ret < 0) { dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret); return ret; } } + if (!strcmp(codec_dai->component->name, MAX98373_DEV0_NAME)) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + 0x03, 3, 8, 24); + if (ret < 0) { + dev_err(runtime->dev, + "DEV0 TDM slot err:%d\n", ret); + return ret; + } + } + if (!strcmp(codec_dai->component->name, MAX98373_DEV1_NAME)) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + 0x0C, 3, 8, 24); + if (ret < 0) { + dev_err(runtime->dev, + "DEV0 TDM slot err:%d\n", ret); + return ret; + } + } } return 0; @@ -213,6 +237,25 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai_link *be_dai_link = dpcm->be->dai_link; /* + * Topology for kblda7219m98373 & kblmax98373 supports only S24_LE, + * where as kblda7219m98927 & kblmax98927 supports S16_LE by default. + * Skipping the port wise FE and BE configuration for kblda7219m98373 & + * kblmax98373 as the topology (FE & BE) supports S24_LE only. + */ + + if (!strcmp(rtd->card->name, "kblda7219m98373") || + !strcmp(rtd->card->name, "kblmax98373")) { + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = DUAL_CHANNEL; + + /* set SSP to 24 bit */ + snd_mask_none(fmt); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + return 0; + } + + /* * The ADSP will convert the FE rate to 48k, stereo, 24 bit */ if (!strcmp(fe_dai_link->name, "Kbl Audio Port") || @@ -221,7 +264,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = 48000; channels->min = channels->max = 2; snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); } /* @@ -229,7 +272,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, * thus changing the mask here */ if (!strcmp(be_dai_link->name, "SSP0-Codec")) - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -352,20 +395,31 @@ static struct snd_pcm_hw_constraint_list constraints_channels_quad = { static int kbl_fe_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_rt = substream->private_data; /* * On this platform for PCM device we support, * 48Khz * stereo - * 16 bit audio */ runtime->hw.channels_max = DUAL_CHANNEL; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &constraints_channels); - - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; - snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + /* + * Setup S24_LE (32 bit container and 24 bit valid data) for + * kblda7219m98373 & kblmax98373. For kblda7219m98927 & + * kblmax98927 keeping it as 16/16 due to topology FW dependency. + */ + if (!strcmp(soc_rt->card->name, "kblda7219m98373") || + !strcmp(soc_rt->card->name, "kblmax98373")) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + + } else { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + } snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); @@ -398,11 +452,23 @@ static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, static int kabylake_dmic_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_rt = substream->private_data; runtime->hw.channels_min = runtime->hw.channels_max = QUAD_CHANNEL; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &constraints_channels_quad); + /* + * Topology for kblda7219m98373 & kblmax98373 supports only S24_LE. + * The DMIC also configured for S24_LE. Forcing the DMIC format to + * S24_LE due to the topology FW dependency. + */ + if (!strcmp(soc_rt->card->name, "kblda7219m98373") || + !strcmp(soc_rt->card->name, "kblmax98373")) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + } + return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } @@ -448,29 +514,55 @@ static struct snd_soc_ops skylake_refcap_ops = { static struct snd_soc_codec_conf max98927_codec_conf[] = { { - .dev_name = MAXIM_DEV0_NAME, + .dev_name = MAX98927_DEV0_NAME, + .name_prefix = "Right", + }, + + { + .dev_name = MAX98927_DEV1_NAME, + .name_prefix = "Left", + }, +}; + +static struct snd_soc_codec_conf max98373_codec_conf[] = { + + { + .dev_name = MAX98373_DEV0_NAME, .name_prefix = "Right", }, { - .dev_name = MAXIM_DEV1_NAME, + .dev_name = MAX98373_DEV1_NAME, .name_prefix = "Left", }, }; -static struct snd_soc_dai_link_component ssp0_codec_components[] = { +static struct snd_soc_dai_link_component max98927_ssp0_codec_components[] = { { /* Left */ - .name = MAXIM_DEV0_NAME, + .name = MAX98927_DEV0_NAME, .dai_name = MAX98927_CODEC_DAI, }, { /* For Right */ - .name = MAXIM_DEV1_NAME, + .name = MAX98927_DEV1_NAME, .dai_name = MAX98927_CODEC_DAI, }, }; +static struct snd_soc_dai_link_component max98373_ssp0_codec_components[] = { + { /* Left */ + .name = MAX98373_DEV0_NAME, + .dai_name = MAX98373_CODEC_DAI, + }, + + { /* For Right */ + .name = MAX98373_DEV1_NAME, + .dai_name = MAX98373_CODEC_DAI, + }, + +}; + /* kabylake digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link kabylake_dais[] = { /* Front End DAI links */ @@ -607,8 +699,8 @@ static struct snd_soc_dai_link kabylake_dais[] = { .cpu_dai_name = "SSP0 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, - .codecs = ssp0_codec_components, - .num_codecs = ARRAY_SIZE(ssp0_codec_components), + .codecs = max98927_ssp0_codec_components, + .num_codecs = ARRAY_SIZE(max98927_ssp0_codec_components), .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, @@ -683,7 +775,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { }; /* kabylake digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link kabylake_max98927_dais[] = { +static struct snd_soc_dai_link kabylake_max98_927_373_dais[] = { /* Front End DAI links */ [KBL_DPCM_AUDIO_PB] = { .name = "Kbl Audio Port", @@ -802,8 +894,8 @@ static struct snd_soc_dai_link kabylake_max98927_dais[] = { .cpu_dai_name = "SSP0 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, - .codecs = ssp0_codec_components, - .num_codecs = ARRAY_SIZE(ssp0_codec_components), + .codecs = max98927_ssp0_codec_components, + .num_codecs = ARRAY_SIZE(max98927_ssp0_codec_components), .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, @@ -917,8 +1009,8 @@ static struct snd_soc_card kbl_audio_card_da7219_m98927 = { static struct snd_soc_card kbl_audio_card_max98927 = { .name = "kblmax98927", .owner = THIS_MODULE, - .dai_link = kabylake_max98927_dais, - .num_links = ARRAY_SIZE(kabylake_max98927_dais), + .dai_link = kabylake_max98_927_373_dais, + .num_links = ARRAY_SIZE(kabylake_max98_927_373_dais), .controls = kabylake_controls, .num_controls = ARRAY_SIZE(kabylake_controls), .dapm_widgets = kabylake_widgets, @@ -931,9 +1023,46 @@ static struct snd_soc_card kbl_audio_card_max98927 = { .late_probe = kabylake_card_late_probe, }; +static struct snd_soc_card kbl_audio_card_da7219_m98373 = { + .name = "kblda7219m98373", + .owner = THIS_MODULE, + .dai_link = kabylake_dais, + .num_links = ARRAY_SIZE(kabylake_dais), + .controls = kabylake_controls, + .num_controls = ARRAY_SIZE(kabylake_controls), + .dapm_widgets = kabylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_widgets), + .dapm_routes = kabylake_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_map), + .codec_conf = max98373_codec_conf, + .num_configs = ARRAY_SIZE(max98373_codec_conf), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +static struct snd_soc_card kbl_audio_card_max98373 = { + .name = "kblmax98373", + .owner = THIS_MODULE, + .dai_link = kabylake_max98_927_373_dais, + .num_links = ARRAY_SIZE(kabylake_max98_927_373_dais), + .controls = kabylake_controls, + .num_controls = ARRAY_SIZE(kabylake_controls), + .dapm_widgets = kabylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_widgets), + .dapm_routes = kabylake_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_map), + .codec_conf = max98373_codec_conf, + .num_configs = ARRAY_SIZE(max98373_codec_conf), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_codec_private *ctx; + struct snd_soc_dai_link *kbl_dai_link; + struct snd_soc_dai_link_component **codecs; + int i = 0; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -944,6 +1073,22 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card = (struct snd_soc_card *)pdev->id_entry->driver_data; + kbl_dai_link = kabylake_audio_card->dai_link; + + /* Update codecs for SSP0 with max98373 codec info */ + if (!strcmp(pdev->name, "kbl_da7219_max98373") || + (!strcmp(pdev->name, "kbl_max98373"))) { + for (i = 0; i < kabylake_audio_card->num_links; ++i) { + if (strcmp(kbl_dai_link[i].name, "SSP0-Codec")) + continue; + + codecs = &(kbl_dai_link[i].codecs); + *codecs = max98373_ssp0_codec_components; + kbl_dai_link[i].num_codecs = + ARRAY_SIZE(max98373_ssp0_codec_components); + break; + } + } kabylake_audio_card->dev = &pdev->dev; snd_soc_card_set_drvdata(kabylake_audio_card, ctx); @@ -961,13 +1106,23 @@ static const struct platform_device_id kbl_board_ids[] = { .driver_data = (kernel_ulong_t)&kbl_audio_card_max98927, }, + { + .name = "kbl_da7219_max98373", + .driver_data = + (kernel_ulong_t)&kbl_audio_card_da7219_m98373, + }, + { + .name = "kbl_max98373", + .driver_data = + (kernel_ulong_t)&kbl_audio_card_max98373, + }, { } }; static struct platform_driver kabylake_audio = { .probe = kabylake_audio_probe, .driver = { - .name = "kbl_da7219_max98927", + .name = "kbl_da7219_max98_927_373", .pm = &snd_soc_pm_ops, }, .id_table = kbl_board_ids, @@ -976,8 +1131,10 @@ static struct platform_driver kabylake_audio = { module_platform_driver(kabylake_audio) /* Module information */ -MODULE_DESCRIPTION("Audio KabyLake Machine driver for MAX98927 & DA7219"); +MODULE_DESCRIPTION("Audio KabyLake Machine driver for MAX98927/MAX98373 & DA7219"); MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:kbl_da7219_max98927"); MODULE_ALIAS("platform:kbl_max98927"); +MODULE_ALIAS("platform:kbl_da7219_max98373"); +MODULE_ALIAS("platform:kbl_max98373"); diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 61dedc103b19..229e39586868 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -51,9 +51,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .id = "INT343A", .drv_name = "bxt_alc298s_i2s", .fw_filename = "intel/dsp_fw_bxtn.bin", - .sof_fw_filename = "intel/sof-apl.ri", - .sof_tplg_filename = "intel/sof-apl-rt298.tplg", - .asoc_plat_name = "0000:00:0e.0", + .sof_fw_filename = "sof-apl.ri", + .sof_tplg_filename = "sof-apl-rt298.tplg", }, { .id = "DLGS7219", @@ -61,31 +60,27 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .fw_filename = "intel/dsp_fw_bxtn.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, - .sof_fw_filename = "intel/sof-apl.ri", - .sof_tplg_filename = "intel/sof-apl-da7219.tplg", - .asoc_plat_name = "0000:00:0e.0", + .sof_fw_filename = "sof-apl.ri", + .sof_tplg_filename = "sof-apl-da7219.tplg", }, { .id = "104C5122", .drv_name = "bxt-pcm512x", - .sof_fw_filename = "intel/sof-apl.ri", - .sof_tplg_filename = "intel/sof-apl-pcm512x.tplg", - .asoc_plat_name = "0000:00:0e.0", + .sof_fw_filename = "sof-apl.ri", + .sof_tplg_filename = "sof-apl-pcm512x.tplg", }, { .id = "1AEC8804", .drv_name = "bxt-wm8804", - .sof_fw_filename = "intel/sof-apl.ri", - .sof_tplg_filename = "intel/sof-apl-wm8804.tplg", - .asoc_plat_name = "0000:00:0e.0", + .sof_fw_filename = "sof-apl.ri", + .sof_tplg_filename = "sof-apl-wm8804.tplg", }, { .id = "INT34C3", .drv_name = "bxt_tdf8532", .machine_quirk = apl_quirk, - .sof_fw_filename = "intel/sof-apl.ri", - .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", - .asoc_plat_name = "0000:00:0e.0", + .sof_fw_filename = "sof-apl.ri", + .sof_tplg_filename = "sof-apl-tdf8532.tplg", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index 097dc06377ba..fe812a909db4 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -21,6 +21,7 @@ static unsigned long byt_machine_id; #define BYT_THINKPAD_10 1 +#define BYT_POV_P1006W 2 static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) { @@ -28,6 +29,11 @@ static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) return 1; } +static int byt_pov_p1006w_quirk_cb(const struct dmi_system_id *id) +{ + byt_machine_id = BYT_POV_P1006W; + return 1; +} static const struct dmi_system_id byt_table[] = { { @@ -58,6 +64,17 @@ static const struct dmi_system_id byt_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), }, }, + { + /* Point of View mobii wintab p1006w (v1.0) */ + .callback = byt_pov_p1006w_quirk_cb, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BayTrail"), + /* Note 105b is Foxcon's USB/PCI vendor id */ + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "105B"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"), + }, + }, { } }; @@ -66,9 +83,17 @@ static struct snd_soc_acpi_mach byt_thinkpad_10 = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-rt5670.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-rt5670.tplg", +}; + +static struct snd_soc_acpi_mach byt_pov_p1006w = { + .id = "10EC5640", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5651", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-rt5651.tplg", }; static struct snd_soc_acpi_mach *byt_quirk(void *arg) @@ -77,10 +102,14 @@ static struct snd_soc_acpi_mach *byt_quirk(void *arg) dmi_check_system(byt_table); - if (byt_machine_id == BYT_THINKPAD_10) + switch (byt_machine_id) { + case BYT_THINKPAD_10: return &byt_thinkpad_10; - else + case BYT_POV_P1006W: + return &byt_pov_p1006w; + default: return mach; + } } struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[] = { @@ -105,54 +134,56 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", .machine_quirk = byt_quirk, - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-rt5640.tplg", }, { .id = "10EC5642", .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-rt5640.tplg", }, { .id = "INTCCFFD", .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-rt5640.tplg", }, { .id = "10EC5651", .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-rt5651.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-rt5651.tplg", }, { .id = "DLGS7212", .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-da7213.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-da7213.tplg", }, { .id = "DLGS7213", .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-da7213.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-da7213.tplg", + }, + { + .id = "ESSX8316", + .drv_name = "bytcht_es8316", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_es8316", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-es8316.tplg", }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ { @@ -160,18 +191,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-rt5645.tplg", }, { .id = "10EC5648", .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-rt5645.tplg", }, /* use CHT driver to Baytrail Chromebooks */ { @@ -179,9 +208,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-byt.ri", - .sof_tplg_filename = "intel/sof-byt-max98090.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-byt.ri", + .sof_tplg_filename = "sof-byt-max98090.tplg", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) /* diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index 91bb99b69601..deafd87cc764 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -44,9 +44,8 @@ static struct snd_soc_acpi_mach cht_surface_mach = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5645.tplg", }; static struct snd_soc_acpi_mach *cht_quirk(void *arg) @@ -68,90 +67,80 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5670.tplg", }, { .id = "10EC5672", .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5670.tplg", }, { .id = "10EC5645", .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5645.tplg", }, { .id = "10EC5650", .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5645.tplg", }, { .id = "10EC3270", .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5645.tplg", }, { .id = "193C9890", .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-max98090.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-max98090.tplg", }, { .id = "10508824", .drv_name = "cht-bsw-nau8824", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-nau8824.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-nau8824.tplg", }, { .id = "DLGS7212", .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-da7213.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-da7213.tplg", }, { .id = "DLGS7213", .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-da7213.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-da7213.tplg", }, { .id = "ESSX8316", .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_es8316", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-es8316.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-es8316.tplg", }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ { @@ -160,18 +149,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", .machine_quirk = cht_quirk, - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5640.tplg", }, { .id = "10EC3276", .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5640.tplg", }, /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ { @@ -179,9 +166,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/sof-cht.ri", - .sof_tplg_filename = "intel/sof-cht-rt5651.tplg", - .asoc_plat_name = "sst-mfld-platform", + .sof_fw_filename = "sof-cht.ri", + .sof_tplg_filename = "sof-cht-rt5651.tplg", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) /* diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index ec8e28e7b937..a914dd238d0a 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -20,9 +20,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .drv_name = "cnl_rt274", .fw_filename = "intel/dsp_fw_cnl.bin", .pdata = &cnl_pdata, - .sof_fw_filename = "intel/sof-cnl.ri", - .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", - .asoc_plat_name = "0000:00:1f.3", + .sof_fw_filename = "sof-cnl.ri", + .sof_tplg_filename = "sof-cnl-rt274.tplg", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index 305875af71ca..3f2061475ae4 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -19,9 +19,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .id = "INT343A", .drv_name = "glk_alc298s_i2s", .fw_filename = "intel/dsp_fw_glk.bin", - .sof_fw_filename = "intel/sof-glk.ri", - .sof_tplg_filename = "intel/sof-glk-alc298.tplg", - .asoc_plat_name = "0000:00:0e.0", + .sof_fw_filename = "sof-glk.ri", + .sof_tplg_filename = "sof-glk-alc298.tplg", }, { .id = "DLGS7219", @@ -29,9 +28,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .fw_filename = "intel/dsp_fw_glk.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &glk_codecs, - .sof_fw_filename = "intel/sof-glk.ri", - .sof_tplg_filename = "intel/sof-glk-da7219.tplg", - .asoc_plat_name = "0000:00:0e.0", + .sof_fw_filename = "sof-glk.ri", + .sof_tplg_filename = "sof-glk-da7219.tplg", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-hda-match.c b/sound/soc/intel/common/soc-acpi-intel-hda-match.c index 533c1064f84b..68ae43f7b4b2 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hda-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hda-match.c @@ -23,7 +23,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_hda_machines[] = { /* .sof_fw_filename is dynamically set in sof/intel driver */ - .sof_tplg_filename = "intel/sof-hda-generic.tplg", + .sof_tplg_filename = "sof-hda-generic.tplg", /* * .machine_quirk and .quirk_data are not used here but diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index 494a0ea9b029..690b305a255b 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -23,9 +23,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST1.bin", - .sof_fw_filename = "intel/sof-hsw.ri", - .sof_tplg_filename = "intel/sof-hsw.tplg", - .asoc_plat_name = "haswell-pcm-audio", + .sof_fw_filename = "sof-hsw.ri", + .sof_tplg_filename = "sof-hsw.tplg", }, {} }; @@ -36,25 +35,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .id = "INT343A", .drv_name = "broadwell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/sof-bdw.ri", - .sof_tplg_filename = "intel/sof-bdw-rt286.tplg", - .asoc_plat_name = "haswell-pcm-audio", + .sof_fw_filename = "sof-bdw.ri", + .sof_tplg_filename = "sof-bdw-rt286.tplg", }, { .id = "RT5677CE", .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/sof-bdw.ri", - .sof_tplg_filename = "intel/sof-bdw-rt5677.tplg", - .asoc_plat_name = "haswell-pcm-audio", + .sof_fw_filename = "sof-bdw.ri", + .sof_tplg_filename = "sof-bdw-rt5677.tplg", }, { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/sof-bdw.ri", - .sof_tplg_filename = "intel/sof-bdw-rt5640.tplg", - .asoc_plat_name = "haswell-pcm-audio", + .sof_fw_filename = "sof-bdw.ri", + .sof_tplg_filename = "sof-bdw-rt5640.tplg", }, {} }; diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c index 33b441dca4d3..e5a6be5bc0ee 100644 --- a/sound/soc/intel/common/soc-acpi-intel-icl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -20,9 +20,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { .drv_name = "icl_rt274", .fw_filename = "intel/dsp_fw_icl.bin", .pdata = &icl_pdata, - .sof_fw_filename = "intel/sof-icl.ri", - .sof_tplg_filename = "intel/sof-icl-rt274.tplg", - .asoc_plat_name = "0000:00:1f.3", + .sof_fw_filename = "sof-icl.ri", + .sof_tplg_filename = "sof-icl-rt274.tplg", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c index e6fa6f470526..4b331058e807 100644 --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -37,6 +37,11 @@ static struct snd_soc_acpi_codecs kbl_7219_98927_codecs = { .codecs = {"MX98927"} }; +static struct snd_soc_acpi_codecs kbl_7219_98373_codecs = { + .num_codecs = 1, + .codecs = {"MX98373"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { { .id = "INT343A", @@ -106,6 +111,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { .drv_name = "kbl_rt5660", .fw_filename = "intel/dsp_fw_kbl.bin", }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98373", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98373_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98373", + .drv_name = "kbl_max98373", + .fw_filename = "intel/dsp_fw_kbl.bin", + .pdata = &skl_dmic_data + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index d33bdaf92c57..31fcdf12c67d 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -1216,7 +1216,7 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) return ret; } - stream->commited = 1; + stream->commited = true; trace_hsw_stream_alloc_reply(stream); return 0; diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index fe2c826e710c..2debcc2ed99a 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -544,7 +544,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, dev_err(rtd->dev, "error: invalid DAI ID %d\n", rtd->cpu_dai->id); return -EINVAL; - }; + } ret = sst_hsw_stream_format(hsw, pcm_data->stream, path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT); @@ -861,7 +861,7 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream) dev_dbg(rtd->dev, "error: free stream failed %d\n", ret); goto out; } - pcm_data->allocated = 0; + pcm_data->allocated = false; pcm_data->stream = NULL; out: diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index b0e6fb93eaf8..28c4806b196a 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -416,7 +416,7 @@ int skl_resume_dsp(struct skl *skl) snd_hdac_ext_bus_ppcap_int_enable(bus, true); /* check if DSP 1st boot is done */ - if (skl->skl_sst->is_first_boot == true) + if (skl->skl_sst->is_first_boot) return 0; /* diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 557f80c0bfe5..a4284778f117 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1423,7 +1423,7 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) if (!ops) return -EIO; - if (skl->skl_sst->is_first_boot == false) { + if (!skl->skl_sst->is_first_boot) { dev_err(component->dev, "DSP reports first boot done!!!\n"); return -EIO; } @@ -1464,6 +1464,7 @@ static const struct snd_soc_component_driver skl_component = { .ops = &skl_platform_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, + .ignore_module_refcount = 1, /* do not increase the refcount in core */ }; int skl_platform_register(struct device *dev) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index cf8848b779dc..389f1862bc43 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -3103,7 +3103,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be, ac->size = dfw_ac->max; if (ac->max) { - ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL); + ac->params = devm_kzalloc(dev, ac->max, GFP_KERNEL); if (!ac->params) return -ENOMEM; diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index e731d40afcce..b35410e4020e 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -105,3 +105,22 @@ config SND_SOC_MT8173_RT5650_RT5676 with the RT5650 and RT5676 codecs. Select Y if you have such device. If unsure select "N". + +config SND_SOC_MT8183 + tristate "ASoC support for Mediatek MT8183 chip" + depends on ARCH_MEDIATEK + select SND_SOC_MEDIATEK + help + This adds ASoC platform driver support for Mediatek MT8183 chip + that can be used with other codecs. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MTK_BTCVSD + tristate "ALSA BT SCO CVSD/MSBC Driver" + help + This is for software BTCVSD. This enable + the function for transferring/receiving + BT encoded data to/from BT firmware. + Select Y if you have such device. + If unsure select "N". diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 3bb2c47532f4..76032cae6d51 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_SND_SOC_MEDIATEK) += common/ obj-$(CONFIG_SND_SOC_MT2701) += mt2701/ obj-$(CONFIG_SND_SOC_MT6797) += mt6797/ obj-$(CONFIG_SND_SOC_MT8173) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8183) += mt8183/ diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile index cdadabc5fd16..9ab90433a8d7 100644 --- a/sound/soc/mediatek/common/Makefile +++ b/sound/soc/mediatek/common/Makefile @@ -2,3 +2,5 @@ # platform driver snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o + +obj-$(CONFIG_SND_SOC_MTK_BTCVSD) += mtk-btcvsd.o
\ No newline at end of file diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c new file mode 100644 index 000000000000..1b8bcdaf02d1 --- /dev/null +++ b/sound/soc/mediatek/common/mtk-btcvsd.c @@ -0,0 +1,1364 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Mediatek ALSA BT SCO CVSD/MSBC Driver +// +// Copyright (c) 2019 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/sched/clock.h> + +#include <sound/soc.h> + +#define BTCVSD_SND_NAME "mtk-btcvsd-snd" + +#define BT_CVSD_TX_NREADY BIT(21) +#define BT_CVSD_RX_READY BIT(22) +#define BT_CVSD_TX_UNDERFLOW BIT(23) +#define BT_CVSD_RX_OVERFLOW BIT(24) +#define BT_CVSD_INTERRUPT BIT(31) + +#define BT_CVSD_CLEAR \ + (BT_CVSD_TX_NREADY | BT_CVSD_RX_READY | BT_CVSD_TX_UNDERFLOW |\ + BT_CVSD_RX_OVERFLOW | BT_CVSD_INTERRUPT) + +/* TX */ +#define SCO_TX_ENCODE_SIZE (60) +/* 18 = 6 * 180 / SCO_TX_ENCODE_SIZE */ +#define SCO_TX_PACKER_BUF_NUM (18) + +/* RX */ +#define SCO_RX_PLC_SIZE (30) +#define SCO_RX_PACKER_BUF_NUM (64) +#define SCO_RX_PACKET_MASK (0x3F) + +#define SCO_CVSD_PACKET_VALID_SIZE 2 + +#define SCO_PACKET_120 120 +#define SCO_PACKET_180 180 + +#define BTCVSD_RX_PACKET_SIZE (SCO_RX_PLC_SIZE + SCO_CVSD_PACKET_VALID_SIZE) +#define BTCVSD_TX_PACKET_SIZE (SCO_TX_ENCODE_SIZE) + +#define BTCVSD_RX_BUF_SIZE (BTCVSD_RX_PACKET_SIZE * SCO_RX_PACKER_BUF_NUM) +#define BTCVSD_TX_BUF_SIZE (BTCVSD_TX_PACKET_SIZE * SCO_TX_PACKER_BUF_NUM) + +enum bt_sco_state { + BT_SCO_STATE_IDLE, + BT_SCO_STATE_RUNNING, + BT_SCO_STATE_ENDING, +}; + +enum bt_sco_direct { + BT_SCO_DIRECT_BT2ARM, + BT_SCO_DIRECT_ARM2BT, +}; + +enum bt_sco_packet_len { + BT_SCO_CVSD_30 = 0, + BT_SCO_CVSD_60, + BT_SCO_CVSD_90, + BT_SCO_CVSD_120, + BT_SCO_CVSD_10, + BT_SCO_CVSD_20, + BT_SCO_CVSD_MAX, +}; + +enum BT_SCO_BAND { + BT_SCO_NB, + BT_SCO_WB, +}; + +struct mtk_btcvsd_snd_hw_info { + unsigned int num_valid_addr; + unsigned long bt_sram_addr[20]; + unsigned int packet_length; + unsigned int packet_num; +}; + +struct mtk_btcvsd_snd_stream { + struct snd_pcm_substream *substream; + int stream; + + enum bt_sco_state state; + + unsigned int packet_size; + unsigned int buf_size; + u8 temp_packet_buf[SCO_PACKET_180]; + + int packet_w; + int packet_r; + snd_pcm_uframes_t prev_frame; + int prev_packet_idx; + + unsigned int xrun:1; + unsigned int timeout:1; + unsigned int mute:1; + unsigned int trigger_start:1; + unsigned int wait_flag:1; + unsigned int rw_cnt; + + unsigned long long time_stamp; + unsigned long long buf_data_equivalent_time; + + struct mtk_btcvsd_snd_hw_info buffer_info; +}; + +struct mtk_btcvsd_snd { + struct device *dev; + int irq_id; + + struct regmap *infra; + void __iomem *bt_pkv_base; + void __iomem *bt_sram_bank2_base; + + unsigned int infra_misc_offset; + unsigned int conn_bt_cvsd_mask; + unsigned int cvsd_mcu_read_offset; + unsigned int cvsd_mcu_write_offset; + unsigned int cvsd_packet_indicator; + + u32 *bt_reg_pkt_r; + u32 *bt_reg_pkt_w; + u32 *bt_reg_ctl; + + unsigned int irq_disabled:1; + + spinlock_t tx_lock; /* spinlock for bt tx stream control */ + spinlock_t rx_lock; /* spinlock for bt rx stream control */ + wait_queue_head_t tx_wait; + wait_queue_head_t rx_wait; + + struct mtk_btcvsd_snd_stream *tx; + struct mtk_btcvsd_snd_stream *rx; + u8 tx_packet_buf[BTCVSD_TX_BUF_SIZE]; + u8 rx_packet_buf[BTCVSD_RX_BUF_SIZE]; + + enum BT_SCO_BAND band; +}; + +struct mtk_btcvsd_snd_time_buffer_info { + unsigned long long data_count_equi_time; + unsigned long long time_stamp_us; +}; + +static const unsigned int btsco_packet_valid_mask[BT_SCO_CVSD_MAX][6] = { + {0x1, 0x1 << 1, 0x1 << 2, 0x1 << 3, 0x1 << 4, 0x1 << 5}, + {0x1, 0x1, 0x2, 0x2, 0x4, 0x4}, + {0x1, 0x1, 0x1, 0x2, 0x2, 0x2}, + {0x1, 0x1, 0x1, 0x1, 0x0, 0x0}, + {0x7, 0x7 << 3, 0x7 << 6, 0x7 << 9, 0x7 << 12, 0x7 << 15}, + {0x3, 0x3 << 1, 0x3 << 3, 0x3 << 4, 0x3 << 6, 0x3 << 7}, +}; + +static const unsigned int btsco_packet_info[BT_SCO_CVSD_MAX][4] = { + {30, 6, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, + SCO_PACKET_180 / SCO_RX_PLC_SIZE}, + {60, 3, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, + SCO_PACKET_180 / SCO_RX_PLC_SIZE}, + {90, 2, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, + SCO_PACKET_180 / SCO_RX_PLC_SIZE}, + {120, 1, SCO_PACKET_120 / SCO_TX_ENCODE_SIZE, + SCO_PACKET_120 / SCO_RX_PLC_SIZE}, + {10, 18, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, + SCO_PACKET_180 / SCO_RX_PLC_SIZE}, + {20, 9, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, + SCO_PACKET_180 / SCO_RX_PLC_SIZE}, +}; + +static const u8 table_msbc_silence[SCO_PACKET_180] = { + 0x01, 0x38, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, + 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, + 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, + 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, + 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00, + 0x01, 0xc8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, + 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, + 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, + 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, + 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00, + 0x01, 0xf8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, + 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, + 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, + 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, + 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00 +}; + +static void mtk_btcvsd_snd_irq_enable(struct mtk_btcvsd_snd *bt) +{ + regmap_update_bits(bt->infra, bt->infra_misc_offset, + bt->conn_bt_cvsd_mask, bt->conn_bt_cvsd_mask); +} + +static void mtk_btcvsd_snd_irq_disable(struct mtk_btcvsd_snd *bt) +{ + regmap_update_bits(bt->infra, bt->infra_misc_offset, + bt->conn_bt_cvsd_mask, 0); +} + +static void mtk_btcvsd_snd_set_state(struct mtk_btcvsd_snd *bt, + struct mtk_btcvsd_snd_stream *bt_stream, + int state) +{ + dev_dbg(bt->dev, "%s(), stream %d, state %d, tx->state %d, rx->state %d, irq_disabled %d\n", + __func__, + bt_stream->stream, state, + bt->tx->state, bt->rx->state, bt->irq_disabled); + + bt_stream->state = state; + + if (bt->tx->state == BT_SCO_STATE_IDLE && + bt->rx->state == BT_SCO_STATE_IDLE) { + if (!bt->irq_disabled) { + disable_irq(bt->irq_id); + mtk_btcvsd_snd_irq_disable(bt); + bt->irq_disabled = 1; + } + } else { + if (bt->irq_disabled) { + enable_irq(bt->irq_id); + mtk_btcvsd_snd_irq_enable(bt); + bt->irq_disabled = 0; + } + } +} + +static int mtk_btcvsd_snd_tx_init(struct mtk_btcvsd_snd *bt) +{ + memset(bt->tx, 0, sizeof(*bt->tx)); + memset(bt->tx_packet_buf, 0, sizeof(bt->tx_packet_buf)); + + bt->tx->packet_size = BTCVSD_TX_PACKET_SIZE; + bt->tx->buf_size = BTCVSD_TX_BUF_SIZE; + bt->tx->timeout = 0; + bt->tx->rw_cnt = 0; + bt->tx->stream = SNDRV_PCM_STREAM_PLAYBACK; + return 0; +} + +static int mtk_btcvsd_snd_rx_init(struct mtk_btcvsd_snd *bt) +{ + memset(bt->rx, 0, sizeof(*bt->rx)); + memset(bt->rx_packet_buf, 0, sizeof(bt->rx_packet_buf)); + + bt->rx->packet_size = BTCVSD_RX_PACKET_SIZE; + bt->rx->buf_size = BTCVSD_RX_BUF_SIZE; + bt->rx->timeout = 0; + bt->rx->rw_cnt = 0; + bt->rx->stream = SNDRV_PCM_STREAM_CAPTURE; + return 0; +} + +static void get_tx_time_stamp(struct mtk_btcvsd_snd *bt, + struct mtk_btcvsd_snd_time_buffer_info *ts) +{ + ts->time_stamp_us = bt->tx->time_stamp; + ts->data_count_equi_time = bt->tx->buf_data_equivalent_time; +} + +static void get_rx_time_stamp(struct mtk_btcvsd_snd *bt, + struct mtk_btcvsd_snd_time_buffer_info *ts) +{ + ts->time_stamp_us = bt->rx->time_stamp; + ts->data_count_equi_time = bt->rx->buf_data_equivalent_time; +} + +static int btcvsd_bytes_to_frame(struct snd_pcm_substream *substream, + int bytes) +{ + int count = bytes; + struct snd_pcm_runtime *runtime = substream->runtime; + + if (runtime->format == SNDRV_PCM_FORMAT_S32_LE || + runtime->format == SNDRV_PCM_FORMAT_U32_LE) + count = count >> 2; + else + count = count >> 1; + + count = count / runtime->channels; + return count; +} + +static void mtk_btcvsd_snd_data_transfer(enum bt_sco_direct dir, + u8 *src, u8 *dst, + unsigned int blk_size, + unsigned int blk_num) +{ + unsigned int i, j; + + if (blk_size == 60 || blk_size == 120 || blk_size == 20) { + u32 *src_32 = (u32 *)src; + u32 *dst_32 = (u32 *)dst; + + for (i = 0; i < (blk_size * blk_num / 4); i++) + *dst_32++ = *src_32++; + } else { + u16 *src_16 = (u16 *)src; + u16 *dst_16 = (u16 *)dst; + + for (j = 0; j < blk_num; j++) { + for (i = 0; i < (blk_size / 2); i++) + *dst_16++ = *src_16++; + + if (dir == BT_SCO_DIRECT_BT2ARM) + src_16++; + else + dst_16++; + } + } +} + +/* write encoded mute data to bt sram */ +static int btcvsd_tx_clean_buffer(struct mtk_btcvsd_snd *bt) +{ + unsigned int i; + unsigned int num_valid_addr; + unsigned long flags; + enum BT_SCO_BAND band = bt->band; + + /* prepare encoded mute data */ + if (band == BT_SCO_NB) + memset(bt->tx->temp_packet_buf, 170, SCO_PACKET_180); + else + memcpy(bt->tx->temp_packet_buf, + table_msbc_silence, SCO_PACKET_180); + + /* write mute data to bt tx sram buffer */ + spin_lock_irqsave(&bt->tx_lock, flags); + num_valid_addr = bt->tx->buffer_info.num_valid_addr; + + dev_info(bt->dev, "%s(), band %d, num_valid_addr %u\n", + __func__, band, num_valid_addr); + + for (i = 0; i < num_valid_addr; i++) { + void *dst; + + dev_info(bt->dev, "%s(), clean addr 0x%lx\n", __func__, + bt->tx->buffer_info.bt_sram_addr[i]); + + dst = (void *)bt->tx->buffer_info.bt_sram_addr[i]; + + mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, + bt->tx->temp_packet_buf, dst, + bt->tx->buffer_info.packet_length, + bt->tx->buffer_info.packet_num); + } + spin_unlock_irqrestore(&bt->tx_lock, flags); + + return 0; +} + +static int mtk_btcvsd_read_from_bt(struct mtk_btcvsd_snd *bt, + enum bt_sco_packet_len packet_type, + unsigned int packet_length, + unsigned int packet_num, + unsigned int blk_size, + unsigned int control) +{ + unsigned int i; + int pv; + u8 *src; + unsigned int packet_buf_ofs; + unsigned long flags; + unsigned long connsys_addr_rx, ap_addr_rx; + + connsys_addr_rx = *bt->bt_reg_pkt_r; + ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base + + (connsys_addr_rx & 0xFFFF); + + if (connsys_addr_rx == 0xdeadfeed) { + /* bt return 0xdeadfeed if read register during bt sleep */ + dev_warn(bt->dev, "%s(), connsys_addr_rx == 0xdeadfeed", + __func__); + return -EIO; + } + + src = (u8 *)ap_addr_rx; + + mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src, + bt->rx->temp_packet_buf, packet_length, + packet_num); + + spin_lock_irqsave(&bt->rx_lock, flags); + for (i = 0; i < blk_size; i++) { + packet_buf_ofs = (bt->rx->packet_w & SCO_RX_PACKET_MASK) * + bt->rx->packet_size; + memcpy(bt->rx_packet_buf + packet_buf_ofs, + bt->rx->temp_packet_buf + (SCO_RX_PLC_SIZE * i), + SCO_RX_PLC_SIZE); + if ((control & btsco_packet_valid_mask[packet_type][i]) == + btsco_packet_valid_mask[packet_type][i]) + pv = 1; + else + pv = 0; + + packet_buf_ofs += SCO_RX_PLC_SIZE; + memcpy(bt->rx_packet_buf + packet_buf_ofs, (void *)&pv, + SCO_CVSD_PACKET_VALID_SIZE); + bt->rx->packet_w++; + } + spin_unlock_irqrestore(&bt->rx_lock, flags); + return 0; +} + +int mtk_btcvsd_write_to_bt(struct mtk_btcvsd_snd *bt, + enum bt_sco_packet_len packet_type, + unsigned int packet_length, + unsigned int packet_num, + unsigned int blk_size) +{ + unsigned int i; + unsigned long flags; + u8 *dst; + unsigned long connsys_addr_tx, ap_addr_tx; + bool new_ap_addr_tx = true; + + connsys_addr_tx = *bt->bt_reg_pkt_w; + ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base + + (connsys_addr_tx & 0xFFFF); + + if (connsys_addr_tx == 0xdeadfeed) { + /* bt return 0xdeadfeed if read register during bt sleep */ + dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n", + __func__); + return -EIO; + } + + spin_lock_irqsave(&bt->tx_lock, flags); + for (i = 0; i < blk_size; i++) { + memcpy(bt->tx->temp_packet_buf + (bt->tx->packet_size * i), + (bt->tx_packet_buf + + (bt->tx->packet_r % SCO_TX_PACKER_BUF_NUM) * + bt->tx->packet_size), + bt->tx->packet_size); + + bt->tx->packet_r++; + } + spin_unlock_irqrestore(&bt->tx_lock, flags); + + dst = (u8 *)ap_addr_tx; + + if (!bt->tx->mute) { + mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, + bt->tx->temp_packet_buf, dst, + packet_length, packet_num); + } + + /* store bt tx buffer sram info */ + bt->tx->buffer_info.packet_length = packet_length; + bt->tx->buffer_info.packet_num = packet_num; + for (i = 0; i < bt->tx->buffer_info.num_valid_addr; i++) { + if (bt->tx->buffer_info.bt_sram_addr[i] == ap_addr_tx) { + new_ap_addr_tx = false; + break; + } + } + if (new_ap_addr_tx) { + unsigned int next_idx; + + spin_lock_irqsave(&bt->tx_lock, flags); + bt->tx->buffer_info.num_valid_addr++; + next_idx = bt->tx->buffer_info.num_valid_addr - 1; + bt->tx->buffer_info.bt_sram_addr[next_idx] = ap_addr_tx; + spin_unlock_irqrestore(&bt->tx_lock, flags); + dev_info(bt->dev, "%s(), new ap_addr_tx = 0x%lx, num_valid_addr %d\n", + __func__, ap_addr_tx, + bt->tx->buffer_info.num_valid_addr); + } + + if (bt->tx->mute) + btcvsd_tx_clean_buffer(bt); + + return 0; +} + +static irqreturn_t mtk_btcvsd_snd_irq_handler(int irq_id, void *dev) +{ + struct mtk_btcvsd_snd *bt = dev; + unsigned int packet_type, packet_num, packet_length; + unsigned int buf_cnt_tx, buf_cnt_rx, control; + + if (bt->rx->state != BT_SCO_STATE_RUNNING && + bt->rx->state != BT_SCO_STATE_ENDING && + bt->tx->state != BT_SCO_STATE_RUNNING && + bt->tx->state != BT_SCO_STATE_ENDING) { + dev_warn(bt->dev, "%s(), in idle state: rx->state: %d, tx->state: %d\n", + __func__, bt->rx->state, bt->tx->state); + goto irq_handler_exit; + } + + control = *bt->bt_reg_ctl; + packet_type = (control >> 18) & 0x7; + + if (((control >> 31) & 1) == 0) { + dev_warn(bt->dev, "%s(), ((control >> 31) & 1) == 0, control 0x%x\n", + __func__, control); + goto irq_handler_exit; + } + + if (packet_type >= BT_SCO_CVSD_MAX) { + dev_warn(bt->dev, "%s(), invalid packet_type %u, exit\n", + __func__, packet_type); + goto irq_handler_exit; + } + + packet_length = btsco_packet_info[packet_type][0]; + packet_num = btsco_packet_info[packet_type][1]; + buf_cnt_tx = btsco_packet_info[packet_type][2]; + buf_cnt_rx = btsco_packet_info[packet_type][3]; + + if (bt->rx->state == BT_SCO_STATE_RUNNING || + bt->rx->state == BT_SCO_STATE_ENDING) { + if (bt->rx->xrun) { + if (bt->rx->packet_w - bt->rx->packet_r <= + SCO_RX_PACKER_BUF_NUM - 2 * buf_cnt_rx) { + /* + * free space is larger then + * twice interrupt rx data size + */ + bt->rx->xrun = 0; + dev_warn(bt->dev, "%s(), rx->xrun 0!\n", + __func__); + } + } + + if (!bt->rx->xrun && + (bt->rx->packet_w - bt->rx->packet_r <= + SCO_RX_PACKER_BUF_NUM - buf_cnt_rx)) { + mtk_btcvsd_read_from_bt(bt, + packet_type, + packet_length, + packet_num, + buf_cnt_rx, + control); + bt->rx->rw_cnt++; + } else { + bt->rx->xrun = 1; + dev_warn(bt->dev, "%s(), rx->xrun 1\n", __func__); + } + } + + /* tx */ + bt->tx->timeout = 0; + if ((bt->tx->state == BT_SCO_STATE_RUNNING || + bt->tx->state == BT_SCO_STATE_ENDING) && + bt->tx->trigger_start) { + if (bt->tx->xrun) { + /* prepared data is larger then twice + * interrupt tx data size + */ + if (bt->tx->packet_w - bt->tx->packet_r >= + 2 * buf_cnt_tx) { + bt->tx->xrun = 0; + dev_warn(bt->dev, "%s(), tx->xrun 0\n", + __func__); + } + } + + if ((!bt->tx->xrun && + (bt->tx->packet_w - bt->tx->packet_r >= buf_cnt_tx)) || + bt->tx->state == BT_SCO_STATE_ENDING) { + mtk_btcvsd_write_to_bt(bt, + packet_type, + packet_length, + packet_num, + buf_cnt_tx); + bt->tx->rw_cnt++; + } else { + bt->tx->xrun = 1; + dev_warn(bt->dev, "%s(), tx->xrun 1\n", __func__); + } + } + + *bt->bt_reg_ctl &= ~BT_CVSD_CLEAR; + + if (bt->rx->state == BT_SCO_STATE_RUNNING || + bt->rx->state == BT_SCO_STATE_ENDING) { + bt->rx->wait_flag = 1; + wake_up_interruptible(&bt->rx_wait); + snd_pcm_period_elapsed(bt->rx->substream); + } + if (bt->tx->state == BT_SCO_STATE_RUNNING || + bt->tx->state == BT_SCO_STATE_ENDING) { + bt->tx->wait_flag = 1; + wake_up_interruptible(&bt->tx_wait); + snd_pcm_period_elapsed(bt->tx->substream); + } + + return IRQ_HANDLED; +irq_handler_exit: + *bt->bt_reg_ctl &= ~BT_CVSD_CLEAR; + return IRQ_HANDLED; +} + +static int wait_for_bt_irq(struct mtk_btcvsd_snd *bt, + struct mtk_btcvsd_snd_stream *bt_stream) +{ + unsigned long long t1, t2; + /* one interrupt period = 22.5ms */ + unsigned long long timeout_limit = 22500000; + int max_timeout_trial = 2; + int ret; + + bt_stream->wait_flag = 0; + + while (max_timeout_trial && !bt_stream->wait_flag) { + t1 = sched_clock(); + if (bt_stream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = wait_event_interruptible_timeout(bt->tx_wait, + bt_stream->wait_flag, + nsecs_to_jiffies(timeout_limit)); + } else { + ret = wait_event_interruptible_timeout(bt->rx_wait, + bt_stream->wait_flag, + nsecs_to_jiffies(timeout_limit)); + } + + t2 = sched_clock(); + t2 = t2 - t1; /* in ns (10^9) */ + + if (t2 > timeout_limit) { + dev_warn(bt->dev, "%s(), stream %d, timeout %llu, limit %llu, ret %d, flag %d\n", + __func__, bt_stream->stream, + t2, timeout_limit, ret, + bt_stream->wait_flag); + } + + if (ret < 0) { + /* + * error, -ERESTARTSYS if it was interrupted by + * a signal + */ + dev_warn(bt->dev, "%s(), stream %d, error, trial left %d\n", + __func__, + bt_stream->stream, max_timeout_trial); + + bt_stream->timeout = 1; + return ret; + } else if (ret == 0) { + /* conidtion is false after timeout */ + max_timeout_trial--; + dev_warn(bt->dev, "%s(), stream %d, error, timeout, condition is false, trial left %d\n", + __func__, + bt_stream->stream, max_timeout_trial); + + if (max_timeout_trial <= 0) { + bt_stream->timeout = 1; + return -ETIME; + } + } + } + + return 0; +} + +ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt, + char __user *buf, + size_t count) +{ + ssize_t read_size = 0, read_count = 0, cur_read_idx, cont; + unsigned int cur_buf_ofs = 0; + unsigned long avail; + unsigned long flags; + unsigned int packet_size = bt->rx->packet_size; + + while (count) { + spin_lock_irqsave(&bt->rx_lock, flags); + /* available data in RX packet buffer */ + avail = (bt->rx->packet_w - bt->rx->packet_r) * packet_size; + + cur_read_idx = (bt->rx->packet_r & SCO_RX_PACKET_MASK) * + packet_size; + spin_unlock_irqrestore(&bt->rx_lock, flags); + + if (!avail) { + int ret = wait_for_bt_irq(bt, bt->rx); + + if (ret) + return read_count; + + continue; + } + + /* count must be multiple of packet_size */ + if (count % packet_size != 0 || + avail % packet_size != 0) { + dev_warn(bt->dev, "%s(), count %zu or d %lu is not multiple of packet_size %dd\n", + __func__, count, avail, packet_size); + + count -= count % packet_size; + avail -= avail % packet_size; + } + + if (count > avail) + read_size = avail; + else + read_size = count; + + /* calculate continue space */ + cont = bt->rx->buf_size - cur_read_idx; + if (read_size > cont) + read_size = cont; + + if (copy_to_user(buf + cur_buf_ofs, + bt->rx_packet_buf + cur_read_idx, + read_size)) { + dev_warn(bt->dev, "%s(), copy_to_user fail\n", + __func__); + return -EFAULT; + } + + spin_lock_irqsave(&bt->rx_lock, flags); + bt->rx->packet_r += read_size / packet_size; + spin_unlock_irqrestore(&bt->rx_lock, flags); + + read_count += read_size; + cur_buf_ofs += read_size; + count -= read_size; + } + + /* + * save current timestamp & buffer time in times_tamp and + * buf_data_equivalent_time + */ + bt->rx->time_stamp = sched_clock(); + bt->rx->buf_data_equivalent_time = + (unsigned long long)(bt->rx->packet_w - bt->rx->packet_r) * + SCO_RX_PLC_SIZE * 16 * 1000 / 2 / 64; + bt->rx->buf_data_equivalent_time += read_count * SCO_RX_PLC_SIZE * + 16 * 1000 / packet_size / 2 / 64; + /* return equivalent time(us) to data count */ + bt->rx->buf_data_equivalent_time *= 1000; + + return read_count; +} + +ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt, + char __user *buf, + size_t count) +{ + int written_size = count, avail = 0, cur_write_idx, write_size, cont; + unsigned int cur_buf_ofs = 0; + unsigned long flags; + unsigned int packet_size = bt->tx->packet_size; + + /* + * save current timestamp & buffer time in time_stamp and + * buf_data_equivalent_time + */ + bt->tx->time_stamp = sched_clock(); + bt->tx->buf_data_equivalent_time = + (unsigned long long)(bt->tx->packet_w - bt->tx->packet_r) * + packet_size * 16 * 1000 / 2 / 64; + + /* return equivalent time(us) to data count */ + bt->tx->buf_data_equivalent_time *= 1000; + + while (count) { + spin_lock_irqsave(&bt->tx_lock, flags); + /* free space of TX packet buffer */ + avail = bt->tx->buf_size - + (bt->tx->packet_w - bt->tx->packet_r) * packet_size; + + cur_write_idx = (bt->tx->packet_w % SCO_TX_PACKER_BUF_NUM) * + packet_size; + spin_unlock_irqrestore(&bt->tx_lock, flags); + + if (!avail) { + int ret = wait_for_bt_irq(bt, bt->rx); + + if (ret) + return written_size; + + continue; + } + + /* count must be multiple of bt->tx->packet_size */ + if (count % packet_size != 0 || + avail % packet_size != 0) { + dev_warn(bt->dev, "%s(), count %zu or avail %d is not multiple of packet_size %d\n", + __func__, count, avail, packet_size); + count -= count % packet_size; + avail -= avail % packet_size; + } + + if (count > avail) + write_size = avail; + else + write_size = count; + + /* calculate continue space */ + cont = bt->tx->buf_size - cur_write_idx; + if (write_size > cont) + write_size = cont; + + if (copy_from_user(bt->tx_packet_buf + + cur_write_idx, + buf + cur_buf_ofs, + write_size)) { + dev_warn(bt->dev, "%s(), copy_from_user fail\n", + __func__); + return -EFAULT; + } + + spin_lock_irqsave(&bt->tx_lock, flags); + bt->tx->packet_w += write_size / packet_size; + spin_unlock_irqrestore(&bt->tx_lock, flags); + cur_buf_ofs += write_size; + count -= write_size; + } + + return written_size; +} + +static struct mtk_btcvsd_snd_stream *get_bt_stream + (struct mtk_btcvsd_snd *bt, struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return bt->tx; + else + return bt->rx; +} + +/* pcm ops */ +static const struct snd_pcm_hardware mtk_btcvsd_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .buffer_bytes_max = 24 * 1024, + .period_bytes_max = 24 * 1024, + .periods_min = 2, + .periods_max = 16, + .fifo_size = 0, +}; + +static int mtk_pcm_btcvsd_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); + int ret; + + dev_dbg(bt->dev, "%s(), stream %d, substream %p\n", + __func__, substream->stream, substream); + + snd_soc_set_runtime_hwparams(substream, &mtk_btcvsd_hardware); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = mtk_btcvsd_snd_tx_init(bt); + bt->tx->substream = substream; + } else { + ret = mtk_btcvsd_snd_rx_init(bt); + bt->rx->substream = substream; + } + + return ret; +} + +static int mtk_pcm_btcvsd_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); + struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); + + dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream); + + mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_IDLE); + bt_stream->substream = NULL; + return 0; +} + +static int mtk_pcm_btcvsd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + params_buffer_bytes(hw_params) % bt->tx->packet_size != 0) { + dev_warn(bt->dev, "%s(), error, buffer size %d not valid\n", + __func__, + params_buffer_bytes(hw_params)); + return -EINVAL; + } + + substream->runtime->dma_bytes = params_buffer_bytes(hw_params); + return 0; +} + +static int mtk_pcm_btcvsd_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + btcvsd_tx_clean_buffer(bt); + + return 0; +} + +static int mtk_pcm_btcvsd_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); + struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); + + dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream); + + mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_RUNNING); + return 0; +} + +static int mtk_pcm_btcvsd_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); + struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); + int stream = substream->stream; + int hw_packet_ptr; + + dev_dbg(bt->dev, "%s(), stream %d, cmd %d\n", + __func__, substream->stream, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + hw_packet_ptr = stream == SNDRV_PCM_STREAM_PLAYBACK ? + bt_stream->packet_r : bt_stream->packet_w; + bt_stream->prev_packet_idx = hw_packet_ptr; + bt_stream->prev_frame = 0; + bt_stream->trigger_start = 1; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + bt_stream->trigger_start = 0; + mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_ENDING); + return 0; + default: + return -EINVAL; + } +} + +static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer + (struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); + struct mtk_btcvsd_snd_stream *bt_stream; + snd_pcm_uframes_t frame = 0; + int byte = 0; + int hw_packet_ptr; + int packet_diff; + spinlock_t *lock; /* spinlock for bt stream control */ + unsigned long flags; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + lock = &bt->tx_lock; + bt_stream = bt->tx; + } else { + lock = &bt->rx_lock; + bt_stream = bt->rx; + } + + spin_lock_irqsave(lock, flags); + hw_packet_ptr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + bt->tx->packet_r : bt->rx->packet_w; + + /* get packet diff from last time */ + if (hw_packet_ptr >= bt_stream->prev_packet_idx) { + packet_diff = hw_packet_ptr - bt_stream->prev_packet_idx; + } else { + /* integer overflow */ + packet_diff = (INT_MAX - bt_stream->prev_packet_idx) + + (hw_packet_ptr - INT_MIN) + 1; + } + bt_stream->prev_packet_idx = hw_packet_ptr; + + /* increased bytes */ + byte = packet_diff * bt_stream->packet_size; + + frame = btcvsd_bytes_to_frame(substream, byte); + frame += bt_stream->prev_frame; + frame %= substream->runtime->buffer_size; + + bt_stream->prev_frame = frame; + + spin_unlock_irqrestore(lock, flags); + + return frame; +} + +static int mtk_pcm_btcvsd_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *buf, unsigned long count) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mtk_btcvsd_snd_write(bt, buf, count); + else + mtk_btcvsd_snd_read(bt, buf, count); + + return 0; +} + +static struct snd_pcm_ops mtk_btcvsd_ops = { + .open = mtk_pcm_btcvsd_open, + .close = mtk_pcm_btcvsd_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = mtk_pcm_btcvsd_hw_params, + .hw_free = mtk_pcm_btcvsd_hw_free, + .prepare = mtk_pcm_btcvsd_prepare, + .trigger = mtk_pcm_btcvsd_trigger, + .pointer = mtk_pcm_btcvsd_pointer, + .copy_user = mtk_pcm_btcvsd_copy, +}; + +/* kcontrol */ +static const char *const btsco_band_str[] = {"NB", "WB"}; + +static const struct soc_enum btcvsd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(btsco_band_str), btsco_band_str), +}; + +static int btcvsd_band_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + + ucontrol->value.integer.value[0] = bt->band; + return 0; +} + +static int btcvsd_band_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + + bt->band = ucontrol->value.integer.value[0]; + dev_dbg(bt->dev, "%s(), band %d\n", __func__, bt->band); + return 0; +} + +static int btcvsd_tx_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + + if (!bt->tx) { + ucontrol->value.integer.value[0] = 0; + return 0; + } + + ucontrol->value.integer.value[0] = bt->tx->mute; + return 0; +} + +static int btcvsd_tx_mute_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + + if (!bt->tx) + return 0; + + bt->tx->mute = ucontrol->value.integer.value[0]; + return 0; +} + +static int btcvsd_rx_irq_received_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + + if (!bt->rx) + return 0; + + ucontrol->value.integer.value[0] = bt->rx->rw_cnt ? 1 : 0; + return 0; +} + +static int btcvsd_rx_timeout_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + + if (!bt->rx) + return 0; + + ucontrol->value.integer.value[0] = bt->rx->timeout; + bt->rx->timeout = 0; + return 0; +} + +static int btcvsd_rx_timestamp_get(struct snd_kcontrol *kcontrol, + unsigned int __user *data, unsigned int size) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + int ret = 0; + struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_rx; + + if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info)) + return -EINVAL; + + get_rx_time_stamp(bt, &time_buffer_info_rx); + + dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu", + __func__, + time_buffer_info_rx.time_stamp_us, + time_buffer_info_rx.data_count_equi_time); + + if (copy_to_user(data, &time_buffer_info_rx, + sizeof(struct mtk_btcvsd_snd_time_buffer_info))) { + dev_warn(bt->dev, "%s(), copy_to_user fail", __func__); + ret = -EFAULT; + } + + return ret; +} + +static int btcvsd_tx_irq_received_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + + if (!bt->tx) + return 0; + + ucontrol->value.integer.value[0] = bt->tx->rw_cnt ? 1 : 0; + return 0; +} + +static int btcvsd_tx_timeout_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + + ucontrol->value.integer.value[0] = bt->tx->timeout; + return 0; +} + +static int btcvsd_tx_timestamp_get(struct snd_kcontrol *kcontrol, + unsigned int __user *data, unsigned int size) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); + int ret = 0; + struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_tx; + + if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info)) + return -EINVAL; + + get_tx_time_stamp(bt, &time_buffer_info_tx); + + dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu", + __func__, + time_buffer_info_tx.time_stamp_us, + time_buffer_info_tx.data_count_equi_time); + + if (copy_to_user(data, &time_buffer_info_tx, + sizeof(struct mtk_btcvsd_snd_time_buffer_info))) { + dev_warn(bt->dev, "%s(), copy_to_user fail", __func__); + ret = -EFAULT; + } + + return ret; +} + +static const struct snd_kcontrol_new mtk_btcvsd_snd_controls[] = { + SOC_ENUM_EXT("BTCVSD Band", btcvsd_enum[0], + btcvsd_band_get, btcvsd_band_set), + SOC_SINGLE_BOOL_EXT("BTCVSD Tx Mute Switch", 0, + btcvsd_tx_mute_get, btcvsd_tx_mute_set), + SOC_SINGLE_BOOL_EXT("BTCVSD Tx Irq Received Switch", 0, + btcvsd_tx_irq_received_get, NULL), + SOC_SINGLE_BOOL_EXT("BTCVSD Tx Timeout Switch", 0, + btcvsd_tx_timeout_get, NULL), + SOC_SINGLE_BOOL_EXT("BTCVSD Rx Irq Received Switch", 0, + btcvsd_rx_irq_received_get, NULL), + SOC_SINGLE_BOOL_EXT("BTCVSD Rx Timeout Switch", 0, + btcvsd_rx_timeout_get, NULL), + SND_SOC_BYTES_TLV("BTCVSD Rx Timestamp", + sizeof(struct mtk_btcvsd_snd_time_buffer_info), + btcvsd_rx_timestamp_get, NULL), + SND_SOC_BYTES_TLV("BTCVSD Tx Timestamp", + sizeof(struct mtk_btcvsd_snd_time_buffer_info), + btcvsd_tx_timestamp_get, NULL), +}; + +static int mtk_btcvsd_snd_component_probe(struct snd_soc_component *component) +{ + return snd_soc_add_component_controls(component, + mtk_btcvsd_snd_controls, + ARRAY_SIZE(mtk_btcvsd_snd_controls)); +} + +static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = { + .name = BTCVSD_SND_NAME, + .ops = &mtk_btcvsd_ops, + .probe = mtk_btcvsd_snd_component_probe, +}; + +static int mtk_btcvsd_snd_probe(struct platform_device *pdev) +{ + int ret = 0; + int irq_id; + u32 offset[5] = {0, 0, 0, 0, 0}; + struct mtk_btcvsd_snd *btcvsd; + struct device *dev = &pdev->dev; + + /* init btcvsd private data */ + btcvsd = devm_kzalloc(dev, sizeof(*btcvsd), GFP_KERNEL); + if (!btcvsd) + return -ENOMEM; + platform_set_drvdata(pdev, btcvsd); + btcvsd->dev = dev; + + /* init tx/rx */ + btcvsd->rx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->rx), GFP_KERNEL); + if (!btcvsd->rx) + return -ENOMEM; + + btcvsd->tx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->tx), GFP_KERNEL); + if (!btcvsd->tx) + return -ENOMEM; + + spin_lock_init(&btcvsd->tx_lock); + spin_lock_init(&btcvsd->rx_lock); + + init_waitqueue_head(&btcvsd->tx_wait); + init_waitqueue_head(&btcvsd->rx_wait); + + mtk_btcvsd_snd_tx_init(btcvsd); + mtk_btcvsd_snd_rx_init(btcvsd); + + /* irq */ + irq_id = platform_get_irq(pdev, 0); + if (irq_id <= 0) { + dev_err(dev, "%pOFn no irq found\n", dev->of_node); + return irq_id < 0 ? irq_id : -ENXIO; + } + + ret = devm_request_irq(dev, irq_id, mtk_btcvsd_snd_irq_handler, + IRQF_TRIGGER_LOW, "BTCVSD_ISR_Handle", + (void *)btcvsd); + if (ret) { + dev_err(dev, "could not request_irq for BTCVSD_ISR_Handle\n"); + return ret; + } + + btcvsd->irq_id = irq_id; + + /* iomap */ + btcvsd->bt_pkv_base = of_iomap(dev->of_node, 0); + if (!btcvsd->bt_pkv_base) { + dev_err(dev, "iomap bt_pkv_base fail\n"); + return -EIO; + } + + btcvsd->bt_sram_bank2_base = of_iomap(dev->of_node, 1); + if (!btcvsd->bt_sram_bank2_base) { + dev_err(dev, "iomap bt_sram_bank2_base fail\n"); + return -EIO; + } + + btcvsd->infra = syscon_regmap_lookup_by_phandle(dev->of_node, + "mediatek,infracfg"); + if (IS_ERR(btcvsd->infra)) { + dev_err(dev, "cannot find infra controller: %ld\n", + PTR_ERR(btcvsd->infra)); + return PTR_ERR(btcvsd->infra); + } + + /* get offset */ + ret = of_property_read_u32_array(dev->of_node, "mediatek,offset", + offset, + ARRAY_SIZE(offset)); + if (ret) { + dev_warn(dev, "%s(), get offset fail, ret %d\n", __func__, ret); + return ret; + } + btcvsd->infra_misc_offset = offset[0]; + btcvsd->conn_bt_cvsd_mask = offset[1]; + btcvsd->cvsd_mcu_read_offset = offset[2]; + btcvsd->cvsd_mcu_write_offset = offset[3]; + btcvsd->cvsd_packet_indicator = offset[4]; + + btcvsd->bt_reg_pkt_r = btcvsd->bt_pkv_base + + btcvsd->cvsd_mcu_read_offset; + btcvsd->bt_reg_pkt_w = btcvsd->bt_pkv_base + + btcvsd->cvsd_mcu_write_offset; + btcvsd->bt_reg_ctl = btcvsd->bt_pkv_base + + btcvsd->cvsd_packet_indicator; + + /* init state */ + mtk_btcvsd_snd_set_state(btcvsd, btcvsd->tx, BT_SCO_STATE_IDLE); + mtk_btcvsd_snd_set_state(btcvsd, btcvsd->rx, BT_SCO_STATE_IDLE); + + return devm_snd_soc_register_component(dev, &mtk_btcvsd_snd_platform, + NULL, 0); +} + +static int mtk_btcvsd_snd_remove(struct platform_device *pdev) +{ + struct mtk_btcvsd_snd *btcvsd = dev_get_drvdata(&pdev->dev); + + iounmap(btcvsd->bt_pkv_base); + iounmap(btcvsd->bt_sram_bank2_base); + return 0; +} + +static const struct of_device_id mtk_btcvsd_snd_dt_match[] = { + { .compatible = "mediatek,mtk-btcvsd-snd", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_btcvsd_snd_dt_match); + +static struct platform_driver mtk_btcvsd_snd_driver = { + .driver = { + .name = "mtk-btcvsd-snd", + .of_match_table = mtk_btcvsd_snd_dt_match, + }, + .probe = mtk_btcvsd_snd_probe, + .remove = mtk_btcvsd_snd_remove, +}; + +module_platform_driver(mtk_btcvsd_snd_driver); + +MODULE_DESCRIPTION("Mediatek ALSA BT SCO CVSD/MSBC Driver"); +MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/mediatek/mt8183/Makefile b/sound/soc/mediatek/mt8183/Makefile new file mode 100644 index 000000000000..f3ee6ac98fe8 --- /dev/null +++ b/sound/soc/mediatek/mt8183/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +# platform driver +snd-soc-mt8183-afe-objs := \ + mt8183-afe-pcm.o \ + mt8183-afe-clk.o \ + mt8183-dai-i2s.o \ + mt8183-dai-tdm.o \ + mt8183-dai-pcm.o \ + mt8183-dai-hostless.o \ + mt8183-dai-adda.o + +obj-$(CONFIG_SND_SOC_MT8183) += snd-soc-mt8183-afe.o diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-clk.c b/sound/soc/mediatek/mt8183/mt8183-afe-clk.c new file mode 100644 index 000000000000..f523ad103acc --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-afe-clk.c @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt8183-afe-clk.c -- Mediatek 8183 afe clock ctrl +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/clk.h> + +#include "mt8183-afe-common.h" +#include "mt8183-afe-clk.h" +#include "mt8183-reg.h" + +enum { + CLK_AFE = 0, + CLK_TML, + CLK_APLL22M, + CLK_APLL24M, + CLK_APLL1_TUNER, + CLK_APLL2_TUNER, + CLK_I2S1_BCLK_SW, + CLK_I2S2_BCLK_SW, + CLK_I2S3_BCLK_SW, + CLK_I2S4_BCLK_SW, + CLK_INFRA_SYS_AUDIO, + CLK_MUX_AUDIO, + CLK_MUX_AUDIOINTBUS, + CLK_TOP_SYSPLL_D2_D4, + /* apll related mux */ + CLK_TOP_MUX_AUD_1, + CLK_TOP_APLL1_CK, + CLK_TOP_MUX_AUD_2, + CLK_TOP_APLL2_CK, + CLK_TOP_MUX_AUD_ENG1, + CLK_TOP_APLL1_D8, + CLK_TOP_MUX_AUD_ENG2, + CLK_TOP_APLL2_D8, + CLK_TOP_I2S0_M_SEL, + CLK_TOP_I2S1_M_SEL, + CLK_TOP_I2S2_M_SEL, + CLK_TOP_I2S3_M_SEL, + CLK_TOP_I2S4_M_SEL, + CLK_TOP_I2S5_M_SEL, + CLK_TOP_APLL12_DIV0, + CLK_TOP_APLL12_DIV1, + CLK_TOP_APLL12_DIV2, + CLK_TOP_APLL12_DIV3, + CLK_TOP_APLL12_DIV4, + CLK_TOP_APLL12_DIVB, + CLK_CLK26M, + CLK_NUM +}; + +static const char *aud_clks[CLK_NUM] = { + [CLK_AFE] = "aud_afe_clk", + [CLK_TML] = "aud_tml_clk", + [CLK_APLL22M] = "aud_apll22m_clk", + [CLK_APLL24M] = "aud_apll24m_clk", + [CLK_APLL1_TUNER] = "aud_apll1_tuner_clk", + [CLK_APLL2_TUNER] = "aud_apll2_tuner_clk", + [CLK_I2S1_BCLK_SW] = "aud_i2s1_bclk_sw", + [CLK_I2S2_BCLK_SW] = "aud_i2s2_bclk_sw", + [CLK_I2S3_BCLK_SW] = "aud_i2s3_bclk_sw", + [CLK_I2S4_BCLK_SW] = "aud_i2s4_bclk_sw", + [CLK_INFRA_SYS_AUDIO] = "aud_infra_clk", + [CLK_MUX_AUDIO] = "top_mux_audio", + [CLK_MUX_AUDIOINTBUS] = "top_mux_aud_intbus", + [CLK_TOP_SYSPLL_D2_D4] = "top_syspll_d2_d4", + [CLK_TOP_MUX_AUD_1] = "top_mux_aud_1", + [CLK_TOP_APLL1_CK] = "top_apll1_ck", + [CLK_TOP_MUX_AUD_2] = "top_mux_aud_2", + [CLK_TOP_APLL2_CK] = "top_apll2_ck", + [CLK_TOP_MUX_AUD_ENG1] = "top_mux_aud_eng1", + [CLK_TOP_APLL1_D8] = "top_apll1_d8", + [CLK_TOP_MUX_AUD_ENG2] = "top_mux_aud_eng2", + [CLK_TOP_APLL2_D8] = "top_apll2_d8", + [CLK_TOP_I2S0_M_SEL] = "top_i2s0_m_sel", + [CLK_TOP_I2S1_M_SEL] = "top_i2s1_m_sel", + [CLK_TOP_I2S2_M_SEL] = "top_i2s2_m_sel", + [CLK_TOP_I2S3_M_SEL] = "top_i2s3_m_sel", + [CLK_TOP_I2S4_M_SEL] = "top_i2s4_m_sel", + [CLK_TOP_I2S5_M_SEL] = "top_i2s5_m_sel", + [CLK_TOP_APLL12_DIV0] = "top_apll12_div0", + [CLK_TOP_APLL12_DIV1] = "top_apll12_div1", + [CLK_TOP_APLL12_DIV2] = "top_apll12_div2", + [CLK_TOP_APLL12_DIV3] = "top_apll12_div3", + [CLK_TOP_APLL12_DIV4] = "top_apll12_div4", + [CLK_TOP_APLL12_DIVB] = "top_apll12_divb", + [CLK_CLK26M] = "top_clk26m_clk", +}; + +int mt8183_init_clock(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int i; + + afe_priv->clk = devm_kcalloc(afe->dev, CLK_NUM, sizeof(*afe_priv->clk), + GFP_KERNEL); + if (!afe_priv->clk) + return -ENOMEM; + + for (i = 0; i < CLK_NUM; i++) { + afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]); + if (IS_ERR(afe_priv->clk[i])) { + dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n", + __func__, aud_clks[i], + PTR_ERR(afe_priv->clk[i])); + return PTR_ERR(afe_priv->clk[i]); + } + } + + return 0; +} + +int mt8183_afe_enable_clock(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUDIO]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_INFRA_SYS_AUDIO], ret); + goto CLK_INFRA_SYS_AUDIO_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIO]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_MUX_AUDIO], ret); + goto CLK_MUX_AUDIO_ERR; + } + + ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIO], + afe_priv->clk[CLK_CLK26M]); + if (ret) { + dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_MUX_AUDIO], + aud_clks[CLK_CLK26M], ret); + goto CLK_MUX_AUDIO_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret); + goto CLK_MUX_AUDIO_INTBUS_ERR; + } + + ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIOINTBUS], + afe_priv->clk[CLK_TOP_SYSPLL_D2_D4]); + if (ret) { + dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_MUX_AUDIOINTBUS], + aud_clks[CLK_TOP_SYSPLL_D2_D4], ret); + goto CLK_MUX_AUDIO_INTBUS_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_AFE]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_AFE], ret); + goto CLK_AFE_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_I2S1_BCLK_SW]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_I2S1_BCLK_SW], ret); + goto CLK_I2S1_BCLK_SW_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_I2S2_BCLK_SW]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_I2S2_BCLK_SW], ret); + goto CLK_I2S2_BCLK_SW_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_I2S3_BCLK_SW]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_I2S3_BCLK_SW], ret); + goto CLK_I2S3_BCLK_SW_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_I2S4_BCLK_SW]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_I2S4_BCLK_SW], ret); + goto CLK_I2S4_BCLK_SW_ERR; + } + + return 0; + +CLK_I2S4_BCLK_SW_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_I2S3_BCLK_SW]); +CLK_I2S3_BCLK_SW_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_I2S2_BCLK_SW]); +CLK_I2S2_BCLK_SW_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_I2S1_BCLK_SW]); +CLK_I2S1_BCLK_SW_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_AFE]); +CLK_AFE_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); +CLK_MUX_AUDIO_INTBUS_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]); +CLK_MUX_AUDIO_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]); +CLK_INFRA_SYS_AUDIO_ERR: + return ret; +} + +int mt8183_afe_disable_clock(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + + clk_disable_unprepare(afe_priv->clk[CLK_I2S4_BCLK_SW]); + clk_disable_unprepare(afe_priv->clk[CLK_I2S3_BCLK_SW]); + clk_disable_unprepare(afe_priv->clk[CLK_I2S2_BCLK_SW]); + clk_disable_unprepare(afe_priv->clk[CLK_I2S1_BCLK_SW]); + clk_disable_unprepare(afe_priv->clk[CLK_AFE]); + clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); + clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]); + clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]); + + return 0; +} + +/* apll */ +static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int ret; + + if (enable) { + ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_1]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_1], ret); + goto ERR_ENABLE_CLK_TOP_MUX_AUD_1; + } + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1], + afe_priv->clk[CLK_TOP_APLL1_CK]); + if (ret) { + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_1], + aud_clks[CLK_TOP_APLL1_CK], ret); + goto ERR_SELECT_CLK_TOP_MUX_AUD_1; + } + + /* 180.6336 / 8 = 22.5792MHz */ + ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], ret); + goto ERR_ENABLE_CLK_TOP_MUX_AUD_ENG1; + } + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1], + afe_priv->clk[CLK_TOP_APLL1_D8]); + if (ret) { + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], + aud_clks[CLK_TOP_APLL1_D8], ret); + goto ERR_SELECT_CLK_TOP_MUX_AUD_ENG1; + } + } else { + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1], + afe_priv->clk[CLK_CLK26M]); + if (ret) { + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], + aud_clks[CLK_CLK26M], ret); + goto EXIT; + } + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]); + + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1], + afe_priv->clk[CLK_CLK26M]); + if (ret) { + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_1], + aud_clks[CLK_CLK26M], ret); + goto EXIT; + } + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]); + } + + return 0; + +ERR_SELECT_CLK_TOP_MUX_AUD_ENG1: + clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1], + afe_priv->clk[CLK_CLK26M]); + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]); +ERR_ENABLE_CLK_TOP_MUX_AUD_ENG1: +ERR_SELECT_CLK_TOP_MUX_AUD_1: + clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1], + afe_priv->clk[CLK_CLK26M]); + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]); +ERR_ENABLE_CLK_TOP_MUX_AUD_1: +EXIT: + return ret; +} + +static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int ret; + + if (enable) { + ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_2]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_2], ret); + goto ERR_ENABLE_CLK_TOP_MUX_AUD_2; + } + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2], + afe_priv->clk[CLK_TOP_APLL2_CK]); + if (ret) { + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_2], + aud_clks[CLK_TOP_APLL2_CK], ret); + goto ERR_SELECT_CLK_TOP_MUX_AUD_2; + } + + /* 196.608 / 8 = 24.576MHz */ + ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], ret); + goto ERR_ENABLE_CLK_TOP_MUX_AUD_ENG2; + } + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2], + afe_priv->clk[CLK_TOP_APLL2_D8]); + if (ret) { + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], + aud_clks[CLK_TOP_APLL2_D8], ret); + goto ERR_SELECT_CLK_TOP_MUX_AUD_ENG2; + } + } else { + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2], + afe_priv->clk[CLK_CLK26M]); + if (ret) { + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], + aud_clks[CLK_CLK26M], ret); + goto EXIT; + } + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]); + + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2], + afe_priv->clk[CLK_CLK26M]); + if (ret) { + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_2], + aud_clks[CLK_CLK26M], ret); + goto EXIT; + } + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]); + } + + return 0; + +ERR_SELECT_CLK_TOP_MUX_AUD_ENG2: + clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2], + afe_priv->clk[CLK_CLK26M]); + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]); +ERR_ENABLE_CLK_TOP_MUX_AUD_ENG2: +ERR_SELECT_CLK_TOP_MUX_AUD_2: + clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2], + afe_priv->clk[CLK_CLK26M]); + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]); +ERR_ENABLE_CLK_TOP_MUX_AUD_2: +EXIT: + return ret; +} + +int mt8183_apll1_enable(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int ret; + + /* setting for APLL */ + apll1_mux_setting(afe, true); + + ret = clk_prepare_enable(afe_priv->clk[CLK_APLL22M]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_APLL22M], ret); + goto ERR_CLK_APLL22M; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_APLL1_TUNER]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_APLL1_TUNER], ret); + goto ERR_CLK_APLL1_TUNER; + } + + regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, + 0x0000FFF7, 0x00000832); + regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x1); + + regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE, + AFE_22M_ON_MASK_SFT, + 0x1 << AFE_22M_ON_SFT); + + return 0; + +ERR_CLK_APLL1_TUNER: + clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]); +ERR_CLK_APLL22M: + return ret; +} + +void mt8183_apll1_disable(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + + regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE, + AFE_22M_ON_MASK_SFT, + 0x0 << AFE_22M_ON_SFT); + + regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x0); + + clk_disable_unprepare(afe_priv->clk[CLK_APLL1_TUNER]); + clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]); + + apll1_mux_setting(afe, false); +} + +int mt8183_apll2_enable(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int ret; + + /* setting for APLL */ + apll2_mux_setting(afe, true); + + ret = clk_prepare_enable(afe_priv->clk[CLK_APLL24M]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_APLL24M], ret); + goto ERR_CLK_APLL24M; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_APLL2_TUNER]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_APLL2_TUNER], ret); + goto ERR_CLK_APLL2_TUNER; + } + + regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, + 0x0000FFF7, 0x00000634); + regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x1); + + regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE, + AFE_24M_ON_MASK_SFT, + 0x1 << AFE_24M_ON_SFT); + + return 0; + +ERR_CLK_APLL2_TUNER: + clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]); +ERR_CLK_APLL24M: + return ret; +} + +void mt8183_apll2_disable(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + + regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE, + AFE_24M_ON_MASK_SFT, + 0x0 << AFE_24M_ON_SFT); + + regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x0); + + clk_disable_unprepare(afe_priv->clk[CLK_APLL2_TUNER]); + clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]); + + apll2_mux_setting(afe, false); +} + +int mt8183_get_apll_rate(struct mtk_base_afe *afe, int apll) +{ + return (apll == MT8183_APLL1) ? 180633600 : 196608000; +} + +int mt8183_get_apll_by_rate(struct mtk_base_afe *afe, int rate) +{ + return ((rate % 8000) == 0) ? MT8183_APLL2 : MT8183_APLL1; +} + +int mt8183_get_apll_by_name(struct mtk_base_afe *afe, const char *name) +{ + if (strcmp(name, APLL1_W_NAME) == 0) + return MT8183_APLL1; + else + return MT8183_APLL2; +} + +/* mck */ +struct mt8183_mck_div { + int m_sel_id; + int div_clk_id; +}; + +static const struct mt8183_mck_div mck_div[MT8183_MCK_NUM] = { + [MT8183_I2S0_MCK] = { + .m_sel_id = CLK_TOP_I2S0_M_SEL, + .div_clk_id = CLK_TOP_APLL12_DIV0, + }, + [MT8183_I2S1_MCK] = { + .m_sel_id = CLK_TOP_I2S1_M_SEL, + .div_clk_id = CLK_TOP_APLL12_DIV1, + }, + [MT8183_I2S2_MCK] = { + .m_sel_id = CLK_TOP_I2S2_M_SEL, + .div_clk_id = CLK_TOP_APLL12_DIV2, + }, + [MT8183_I2S3_MCK] = { + .m_sel_id = CLK_TOP_I2S3_M_SEL, + .div_clk_id = CLK_TOP_APLL12_DIV3, + }, + [MT8183_I2S4_MCK] = { + .m_sel_id = CLK_TOP_I2S4_M_SEL, + .div_clk_id = CLK_TOP_APLL12_DIV4, + }, + [MT8183_I2S4_BCK] = { + .m_sel_id = -1, + .div_clk_id = CLK_TOP_APLL12_DIVB, + }, + [MT8183_I2S5_MCK] = { + .m_sel_id = -1, + .div_clk_id = -1, + }, +}; + +int mt8183_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int apll = mt8183_get_apll_by_rate(afe, rate); + int apll_clk_id = apll == MT8183_APLL1 ? + CLK_TOP_MUX_AUD_1 : CLK_TOP_MUX_AUD_2; + int m_sel_id = mck_div[mck_id].m_sel_id; + int div_clk_id = mck_div[mck_id].div_clk_id; + int ret; + + /* i2s5 mck not support */ + if (mck_id == MT8183_I2S5_MCK) + return 0; + + /* select apll */ + if (m_sel_id >= 0) { + ret = clk_prepare_enable(afe_priv->clk[m_sel_id]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[m_sel_id], ret); + goto ERR_ENABLE_MCLK; + } + ret = clk_set_parent(afe_priv->clk[m_sel_id], + afe_priv->clk[apll_clk_id]); + if (ret) { + dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[m_sel_id], + aud_clks[apll_clk_id], ret); + goto ERR_SELECT_MCLK; + } + } + + /* enable div, set rate */ + ret = clk_prepare_enable(afe_priv->clk[div_clk_id]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[div_clk_id], ret); + goto ERR_ENABLE_MCLK_DIV; + } + ret = clk_set_rate(afe_priv->clk[div_clk_id], rate); + if (ret) { + dev_err(afe->dev, "%s(), clk_set_rate %s, rate %d, fail %d\n", + __func__, aud_clks[div_clk_id], + rate, ret); + goto ERR_SET_MCLK_RATE; + return ret; + } + + return 0; + +ERR_SET_MCLK_RATE: + clk_disable_unprepare(afe_priv->clk[div_clk_id]); +ERR_ENABLE_MCLK_DIV: +ERR_SELECT_MCLK: + if (m_sel_id >= 0) + clk_disable_unprepare(afe_priv->clk[m_sel_id]); +ERR_ENABLE_MCLK: + return ret; +} + +void mt8183_mck_disable(struct mtk_base_afe *afe, int mck_id) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int m_sel_id = mck_div[mck_id].m_sel_id; + int div_clk_id = mck_div[mck_id].div_clk_id; + + clk_disable_unprepare(afe_priv->clk[div_clk_id]); + if (m_sel_id >= 0) + clk_disable_unprepare(afe_priv->clk[m_sel_id]); +} diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-clk.h b/sound/soc/mediatek/mt8183/mt8183-afe-clk.h new file mode 100644 index 000000000000..2c510aa80fc7 --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-afe-clk.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8183-afe-clk.h -- Mediatek 8183 afe clock ctrl definition + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef _MT8183_AFE_CLK_H_ +#define _MT8183_AFE_CLK_H_ + +/* APLL */ +#define APLL1_W_NAME "APLL1" +#define APLL2_W_NAME "APLL2" +enum { + MT8183_APLL1 = 0, + MT8183_APLL2, +}; + +struct mtk_base_afe; + +int mt8183_init_clock(struct mtk_base_afe *afe); +int mt8183_afe_enable_clock(struct mtk_base_afe *afe); +int mt8183_afe_disable_clock(struct mtk_base_afe *afe); + +int mt8183_apll1_enable(struct mtk_base_afe *afe); +void mt8183_apll1_disable(struct mtk_base_afe *afe); + +int mt8183_apll2_enable(struct mtk_base_afe *afe); +void mt8183_apll2_disable(struct mtk_base_afe *afe); + +int mt8183_get_apll_rate(struct mtk_base_afe *afe, int apll); +int mt8183_get_apll_by_rate(struct mtk_base_afe *afe, int rate); +int mt8183_get_apll_by_name(struct mtk_base_afe *afe, const char *name); + +int mt8183_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate); +void mt8183_mck_disable(struct mtk_base_afe *afe, int mck_id); +#endif diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-common.h b/sound/soc/mediatek/mt8183/mt8183-afe-common.h new file mode 100644 index 000000000000..b220e7a7db7e --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-afe-common.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8183-afe-common.h -- Mediatek 8183 audio driver definitions + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef _MT_8183_AFE_COMMON_H_ +#define _MT_8183_AFE_COMMON_H_ + +#include <sound/soc.h> +#include <linux/list.h> +#include <linux/regmap.h> +#include "../common/mtk-base-afe.h" + +enum { + MT8183_MEMIF_DL1, + MT8183_MEMIF_DL2, + MT8183_MEMIF_DL3, + MT8183_MEMIF_VUL12, + MT8183_MEMIF_VUL2, + MT8183_MEMIF_AWB, + MT8183_MEMIF_AWB2, + MT8183_MEMIF_MOD_DAI, + MT8183_MEMIF_HDMI, + MT8183_MEMIF_NUM, + MT8183_DAI_ADDA = MT8183_MEMIF_NUM, + MT8183_DAI_PCM_1, + MT8183_DAI_PCM_2, + MT8183_DAI_I2S_0, + MT8183_DAI_I2S_1, + MT8183_DAI_I2S_2, + MT8183_DAI_I2S_3, + MT8183_DAI_I2S_5, + MT8183_DAI_TDM, + MT8183_DAI_HOSTLESS_LPBK, + MT8183_DAI_HOSTLESS_SPEECH, + MT8183_DAI_NUM, +}; + +enum { + MT8183_IRQ_0, + MT8183_IRQ_1, + MT8183_IRQ_2, + MT8183_IRQ_3, + MT8183_IRQ_4, + MT8183_IRQ_5, + MT8183_IRQ_6, + MT8183_IRQ_7, + MT8183_IRQ_8, /* hw bundle to TDM */ + MT8183_IRQ_11, + MT8183_IRQ_12, + MT8183_IRQ_NUM, +}; + +enum { + MT8183_MTKAIF_PROTOCOL_1 = 0, + MT8183_MTKAIF_PROTOCOL_2, + MT8183_MTKAIF_PROTOCOL_2_CLK_P2, +}; + +/* MCLK */ +enum { + MT8183_I2S0_MCK = 0, + MT8183_I2S1_MCK, + MT8183_I2S2_MCK, + MT8183_I2S3_MCK, + MT8183_I2S4_MCK, + MT8183_I2S4_BCK, + MT8183_I2S5_MCK, + MT8183_MCK_NUM, +}; + +struct clk; + +struct mt8183_afe_private { + struct clk **clk; + + int pm_runtime_bypass_reg_ctl; + + /* dai */ + void *dai_priv[MT8183_DAI_NUM]; + + /* adda */ + int mtkaif_protocol; + int mtkaif_calibration_ok; + int mtkaif_chosen_phase[4]; + int mtkaif_phase_cycle[4]; + int mtkaif_calibration_num_phase; + int mtkaif_dmic; + + /* mck */ + int mck_rate[MT8183_MCK_NUM]; +}; + +unsigned int mt8183_general_rate_transform(struct device *dev, + unsigned int rate); +unsigned int mt8183_rate_transform(struct device *dev, + unsigned int rate, int aud_blk); + +/* dai register */ +int mt8183_dai_adda_register(struct mtk_base_afe *afe); +int mt8183_dai_pcm_register(struct mtk_base_afe *afe); +int mt8183_dai_i2s_register(struct mtk_base_afe *afe); +int mt8183_dai_tdm_register(struct mtk_base_afe *afe); +int mt8183_dai_hostless_register(struct mtk_base_afe *afe); +#endif diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c new file mode 100644 index 000000000000..4e045dd305a7 --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -0,0 +1,1237 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Mediatek ALSA SoC AFE platform driver for 8183 +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> + +#include "mt8183-afe-common.h" +#include "mt8183-afe-clk.h" +#include "mt8183-interconnection.h" +#include "mt8183-reg.h" +#include "../common/mtk-afe-platform-driver.h" +#include "../common/mtk-afe-fe-dai.h" + +enum { + MTK_AFE_RATE_8K = 0, + MTK_AFE_RATE_11K = 1, + MTK_AFE_RATE_12K = 2, + MTK_AFE_RATE_384K = 3, + MTK_AFE_RATE_16K = 4, + MTK_AFE_RATE_22K = 5, + MTK_AFE_RATE_24K = 6, + MTK_AFE_RATE_130K = 7, + MTK_AFE_RATE_32K = 8, + MTK_AFE_RATE_44K = 9, + MTK_AFE_RATE_48K = 10, + MTK_AFE_RATE_88K = 11, + MTK_AFE_RATE_96K = 12, + MTK_AFE_RATE_176K = 13, + MTK_AFE_RATE_192K = 14, + MTK_AFE_RATE_260K = 15, +}; + +enum { + MTK_AFE_DAI_MEMIF_RATE_8K = 0, + MTK_AFE_DAI_MEMIF_RATE_16K = 1, + MTK_AFE_DAI_MEMIF_RATE_32K = 2, + MTK_AFE_DAI_MEMIF_RATE_48K = 3, +}; + +enum { + MTK_AFE_PCM_RATE_8K = 0, + MTK_AFE_PCM_RATE_16K = 1, + MTK_AFE_PCM_RATE_32K = 2, + MTK_AFE_PCM_RATE_48K = 3, +}; + +unsigned int mt8183_general_rate_transform(struct device *dev, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_RATE_8K; + case 11025: + return MTK_AFE_RATE_11K; + case 12000: + return MTK_AFE_RATE_12K; + case 16000: + return MTK_AFE_RATE_16K; + case 22050: + return MTK_AFE_RATE_22K; + case 24000: + return MTK_AFE_RATE_24K; + case 32000: + return MTK_AFE_RATE_32K; + case 44100: + return MTK_AFE_RATE_44K; + case 48000: + return MTK_AFE_RATE_48K; + case 88200: + return MTK_AFE_RATE_88K; + case 96000: + return MTK_AFE_RATE_96K; + case 130000: + return MTK_AFE_RATE_130K; + case 176400: + return MTK_AFE_RATE_176K; + case 192000: + return MTK_AFE_RATE_192K; + case 260000: + return MTK_AFE_RATE_260K; + default: + dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n", + __func__, rate, MTK_AFE_RATE_48K); + return MTK_AFE_RATE_48K; + } +} + +static unsigned int dai_memif_rate_transform(struct device *dev, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_DAI_MEMIF_RATE_8K; + case 16000: + return MTK_AFE_DAI_MEMIF_RATE_16K; + case 32000: + return MTK_AFE_DAI_MEMIF_RATE_32K; + case 48000: + return MTK_AFE_DAI_MEMIF_RATE_48K; + default: + dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n", + __func__, rate, MTK_AFE_DAI_MEMIF_RATE_16K); + return MTK_AFE_DAI_MEMIF_RATE_16K; + } +} + +unsigned int mt8183_rate_transform(struct device *dev, + unsigned int rate, int aud_blk) +{ + switch (aud_blk) { + case MT8183_MEMIF_MOD_DAI: + return dai_memif_rate_transform(dev, rate); + default: + return mt8183_general_rate_transform(dev, rate); + } +} + +static const struct snd_pcm_hardware mt8183_afe_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 256, + .period_bytes_max = 4 * 48 * 1024, + .periods_min = 2, + .periods_max = 256, + .buffer_bytes_max = 8 * 48 * 1024, + .fifo_size = 0, +}; + +static int mt8183_memif_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + int id = rtd->cpu_dai->id; + + return mt8183_rate_transform(afe->dev, rate, id); +} + +static int mt8183_irq_fs(struct snd_pcm_substream *substream, unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + return mt8183_general_rate_transform(afe->dev, rate); +} + +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_PCM_DAI_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000) + +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mt8183_memif_dai_driver[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", + .id = MT8183_MEMIF_DL1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "DL2", + .id = MT8183_MEMIF_DL2, + .playback = { + .stream_name = "DL2", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "DL3", + .id = MT8183_MEMIF_DL3, + .playback = { + .stream_name = "DL3", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL1", + .id = MT8183_MEMIF_VUL12, + .capture = { + .stream_name = "UL1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL2", + .id = MT8183_MEMIF_AWB, + .capture = { + .stream_name = "UL2", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL3", + .id = MT8183_MEMIF_VUL2, + .capture = { + .stream_name = "UL3", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL4", + .id = MT8183_MEMIF_AWB2, + .capture = { + .stream_name = "UL4", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL_MONO_1", + .id = MT8183_MEMIF_MOD_DAI, + .capture = { + .stream_name = "UL_MONO_1", + .channels_min = 1, + .channels_max = 1, + .rates = MTK_PCM_DAI_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "HDMI", + .id = MT8183_MEMIF_HDMI, + .playback = { + .stream_name = "HDMI", + .channels_min = 2, + .channels_max = 8, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, +}; + +/* dma widget & routes*/ +static const struct snd_kcontrol_new memif_ul1_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN21, + I_ADDA_UL_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul1_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN22, + I_ADDA_UL_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul2_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN5, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN5, + I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN5, + I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN5, + I_DL3_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul2_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN6, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN6, + I_DL1_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN6, + I_DL2_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN6, + I_DL3_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul3_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN32, + I_ADDA_UL_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul3_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN33, + I_ADDA_UL_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul4_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN38, + I_ADDA_UL_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul4_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN39, + I_ADDA_UL_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul_mono_1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN12, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN12, + I_ADDA_UL_CH2, 1, 0), +}; + +static const struct snd_soc_dapm_widget mt8183_memif_widgets[] = { + /* memif */ + SND_SOC_DAPM_MIXER("UL1_CH1", SND_SOC_NOPM, 0, 0, + memif_ul1_ch1_mix, ARRAY_SIZE(memif_ul1_ch1_mix)), + SND_SOC_DAPM_MIXER("UL1_CH2", SND_SOC_NOPM, 0, 0, + memif_ul1_ch2_mix, ARRAY_SIZE(memif_ul1_ch2_mix)), + + SND_SOC_DAPM_MIXER("UL2_CH1", SND_SOC_NOPM, 0, 0, + memif_ul2_ch1_mix, ARRAY_SIZE(memif_ul2_ch1_mix)), + SND_SOC_DAPM_MIXER("UL2_CH2", SND_SOC_NOPM, 0, 0, + memif_ul2_ch2_mix, ARRAY_SIZE(memif_ul2_ch2_mix)), + + SND_SOC_DAPM_MIXER("UL3_CH1", SND_SOC_NOPM, 0, 0, + memif_ul3_ch1_mix, ARRAY_SIZE(memif_ul3_ch1_mix)), + SND_SOC_DAPM_MIXER("UL3_CH2", SND_SOC_NOPM, 0, 0, + memif_ul3_ch2_mix, ARRAY_SIZE(memif_ul3_ch2_mix)), + + SND_SOC_DAPM_MIXER("UL4_CH1", SND_SOC_NOPM, 0, 0, + memif_ul4_ch1_mix, ARRAY_SIZE(memif_ul4_ch1_mix)), + SND_SOC_DAPM_MIXER("UL4_CH2", SND_SOC_NOPM, 0, 0, + memif_ul4_ch2_mix, ARRAY_SIZE(memif_ul4_ch2_mix)), + + SND_SOC_DAPM_MIXER("UL_MONO_1_CH1", SND_SOC_NOPM, 0, 0, + memif_ul_mono_1_mix, + ARRAY_SIZE(memif_ul_mono_1_mix)), +}; + +static const struct snd_soc_dapm_route mt8183_memif_routes[] = { + /* capture */ + {"UL1", NULL, "UL1_CH1"}, + {"UL1", NULL, "UL1_CH2"}, + {"UL1_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL1_CH2", "ADDA_UL_CH2", "ADDA Capture"}, + + {"UL2", NULL, "UL2_CH1"}, + {"UL2", NULL, "UL2_CH2"}, + {"UL2_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL2_CH2", "ADDA_UL_CH2", "ADDA Capture"}, + + {"UL3", NULL, "UL3_CH1"}, + {"UL3", NULL, "UL3_CH2"}, + {"UL3_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL3_CH2", "ADDA_UL_CH2", "ADDA Capture"}, + + {"UL4", NULL, "UL4_CH1"}, + {"UL4", NULL, "UL4_CH2"}, + {"UL4_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL4_CH2", "ADDA_UL_CH2", "ADDA Capture"}, + + {"UL_MONO_1", NULL, "UL_MONO_1_CH1"}, + {"UL_MONO_1_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL_MONO_1_CH1", "ADDA_UL_CH2", "ADDA Capture"}, +}; + +static const struct snd_soc_component_driver mt8183_afe_pcm_dai_component = { + .name = "mt8183-afe-pcm-dai", +}; + +static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = { + [MT8183_MEMIF_DL1] = { + .name = "DL1", + .id = MT8183_MEMIF_DL1, + .reg_ofs_base = AFE_DL1_BASE, + .reg_ofs_cur = AFE_DL1_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = DL1_MODE_SFT, + .fs_maskbit = DL1_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = DL1_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = DL1_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = DL1_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT8183_MEMIF_DL2] = { + .name = "DL2", + .id = MT8183_MEMIF_DL2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = DL2_MODE_SFT, + .fs_maskbit = DL2_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = DL2_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = DL2_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = DL2_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT8183_MEMIF_DL3] = { + .name = "DL3", + .id = MT8183_MEMIF_DL3, + .reg_ofs_base = AFE_DL3_BASE, + .reg_ofs_cur = AFE_DL3_CUR, + .fs_reg = AFE_DAC_CON2, + .fs_shift = DL3_MODE_SFT, + .fs_maskbit = DL3_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = DL3_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = DL3_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = DL3_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT8183_MEMIF_VUL2] = { + .name = "VUL2", + .id = MT8183_MEMIF_VUL2, + .reg_ofs_base = AFE_VUL2_BASE, + .reg_ofs_cur = AFE_VUL2_CUR, + .fs_reg = AFE_DAC_CON2, + .fs_shift = VUL2_MODE_SFT, + .fs_maskbit = VUL2_MODE_MASK, + .mono_reg = AFE_DAC_CON2, + .mono_shift = VUL2_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = VUL2_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = VUL2_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT8183_MEMIF_AWB] = { + .name = "AWB", + .id = MT8183_MEMIF_AWB, + .reg_ofs_base = AFE_AWB_BASE, + .reg_ofs_cur = AFE_AWB_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = AWB_MODE_SFT, + .fs_maskbit = AWB_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = AWB_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = AWB_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = AWB_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT8183_MEMIF_AWB2] = { + .name = "AWB2", + .id = MT8183_MEMIF_AWB2, + .reg_ofs_base = AFE_AWB2_BASE, + .reg_ofs_cur = AFE_AWB2_CUR, + .fs_reg = AFE_DAC_CON2, + .fs_shift = AWB2_MODE_SFT, + .fs_maskbit = AWB2_MODE_MASK, + .mono_reg = AFE_DAC_CON2, + .mono_shift = AWB2_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = AWB2_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = AWB2_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT8183_MEMIF_VUL12] = { + .name = "VUL12", + .id = MT8183_MEMIF_VUL12, + .reg_ofs_base = AFE_VUL_D2_BASE, + .reg_ofs_cur = AFE_VUL_D2_CUR, + .fs_reg = AFE_DAC_CON0, + .fs_shift = VUL12_MODE_SFT, + .fs_maskbit = VUL12_MODE_MASK, + .mono_reg = AFE_DAC_CON0, + .mono_shift = VUL12_MONO_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = VUL12_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = VUL12_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT8183_MEMIF_MOD_DAI] = { + .name = "MOD_DAI", + .id = MT8183_MEMIF_MOD_DAI, + .reg_ofs_base = AFE_MOD_DAI_BASE, + .reg_ofs_cur = AFE_MOD_DAI_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = MOD_DAI_MODE_SFT, + .fs_maskbit = MOD_DAI_MODE_MASK, + .mono_reg = -1, + .mono_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = MOD_DAI_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = MOD_DAI_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT8183_MEMIF_HDMI] = { + .name = "HDMI", + .id = MT8183_MEMIF_HDMI, + .reg_ofs_base = AFE_HDMI_OUT_BASE, + .reg_ofs_cur = AFE_HDMI_OUT_CUR, + .fs_reg = -1, + .fs_shift = -1, + .fs_maskbit = -1, + .mono_reg = -1, + .mono_shift = -1, + .enable_reg = -1, /* control in tdm for sync start */ + .enable_shift = -1, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = HDMI_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, +}; + +static const struct mtk_base_irq_data irq_data[MT8183_IRQ_NUM] = { + [MT8183_IRQ_0] = { + .id = MT8183_IRQ_0, + .irq_cnt_reg = AFE_IRQ_MCU_CNT0, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON1, + .irq_fs_shift = IRQ0_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ0_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ0_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ0_MCU_CLR_SFT, + }, + [MT8183_IRQ_1] = { + .id = MT8183_IRQ_1, + .irq_cnt_reg = AFE_IRQ_MCU_CNT1, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON1, + .irq_fs_shift = IRQ1_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ1_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ1_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ1_MCU_CLR_SFT, + }, + [MT8183_IRQ_2] = { + .id = MT8183_IRQ_2, + .irq_cnt_reg = AFE_IRQ_MCU_CNT2, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON1, + .irq_fs_shift = IRQ2_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ2_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ2_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ2_MCU_CLR_SFT, + }, + [MT8183_IRQ_3] = { + .id = MT8183_IRQ_3, + .irq_cnt_reg = AFE_IRQ_MCU_CNT3, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON1, + .irq_fs_shift = IRQ3_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ3_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ3_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ3_MCU_CLR_SFT, + }, + [MT8183_IRQ_4] = { + .id = MT8183_IRQ_4, + .irq_cnt_reg = AFE_IRQ_MCU_CNT4, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON1, + .irq_fs_shift = IRQ4_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ4_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ4_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ4_MCU_CLR_SFT, + }, + [MT8183_IRQ_5] = { + .id = MT8183_IRQ_5, + .irq_cnt_reg = AFE_IRQ_MCU_CNT5, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON1, + .irq_fs_shift = IRQ5_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ5_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ5_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ5_MCU_CLR_SFT, + }, + [MT8183_IRQ_6] = { + .id = MT8183_IRQ_6, + .irq_cnt_reg = AFE_IRQ_MCU_CNT6, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON1, + .irq_fs_shift = IRQ6_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ6_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ6_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ6_MCU_CLR_SFT, + }, + [MT8183_IRQ_7] = { + .id = MT8183_IRQ_7, + .irq_cnt_reg = AFE_IRQ_MCU_CNT7, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON1, + .irq_fs_shift = IRQ7_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ7_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ7_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ7_MCU_CLR_SFT, + }, + [MT8183_IRQ_8] = { + .id = MT8183_IRQ_8, + .irq_cnt_reg = AFE_IRQ_MCU_CNT8, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = -1, + .irq_fs_shift = -1, + .irq_fs_maskbit = -1, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ8_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ8_MCU_CLR_SFT, + }, + [MT8183_IRQ_11] = { + .id = MT8183_IRQ_11, + .irq_cnt_reg = AFE_IRQ_MCU_CNT11, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON2, + .irq_fs_shift = IRQ11_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ11_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ11_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ11_MCU_CLR_SFT, + }, + [MT8183_IRQ_12] = { + .id = MT8183_IRQ_12, + .irq_cnt_reg = AFE_IRQ_MCU_CNT12, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON2, + .irq_fs_shift = IRQ12_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ12_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON0, + .irq_en_shift = IRQ12_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ12_MCU_CLR_SFT, + }, +}; + +static bool mt8183_is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* these auto-gen reg has read-only bit, so put it as volatile */ + /* volatile reg cannot be cached, so cannot be set when power off */ + switch (reg) { + case AUDIO_TOP_CON0: /* reg bit controlled by CCF */ + case AUDIO_TOP_CON1: /* reg bit controlled by CCF */ + case AUDIO_TOP_CON3: + case AFE_DL1_CUR: + case AFE_DL1_END: + case AFE_DL2_CUR: + case AFE_DL2_END: + case AFE_AWB_END: + case AFE_AWB_CUR: + case AFE_VUL_END: + case AFE_VUL_CUR: + case AFE_MEMIF_MON0: + case AFE_MEMIF_MON1: + case AFE_MEMIF_MON2: + case AFE_MEMIF_MON3: + case AFE_MEMIF_MON4: + case AFE_MEMIF_MON5: + case AFE_MEMIF_MON6: + case AFE_MEMIF_MON7: + case AFE_MEMIF_MON8: + case AFE_MEMIF_MON9: + case AFE_ADDA_SRC_DEBUG_MON0: + case AFE_ADDA_SRC_DEBUG_MON1: + case AFE_ADDA_UL_SRC_MON0: + case AFE_ADDA_UL_SRC_MON1: + case AFE_SIDETONE_MON: + case AFE_SIDETONE_CON0: + case AFE_SIDETONE_COEFF: + case AFE_BUS_MON0: + case AFE_MRGIF_MON0: + case AFE_MRGIF_MON1: + case AFE_MRGIF_MON2: + case AFE_I2S_MON: + case AFE_DAC_MON: + case AFE_VUL2_END: + case AFE_VUL2_CUR: + case AFE_IRQ0_MCU_CNT_MON: + case AFE_IRQ6_MCU_CNT_MON: + case AFE_MOD_DAI_END: + case AFE_MOD_DAI_CUR: + case AFE_VUL_D2_END: + case AFE_VUL_D2_CUR: + case AFE_DL3_CUR: + case AFE_DL3_END: + case AFE_HDMI_OUT_CON0: + case AFE_HDMI_OUT_CUR: + case AFE_HDMI_OUT_END: + case AFE_IRQ3_MCU_CNT_MON: + case AFE_IRQ4_MCU_CNT_MON: + case AFE_IRQ_MCU_STATUS: + case AFE_IRQ_MCU_CLR: + case AFE_IRQ_MCU_MON2: + case AFE_IRQ1_MCU_CNT_MON: + case AFE_IRQ2_MCU_CNT_MON: + case AFE_IRQ1_MCU_EN_CNT_MON: + case AFE_IRQ5_MCU_CNT_MON: + case AFE_IRQ7_MCU_CNT_MON: + case AFE_GAIN1_CUR: + case AFE_GAIN2_CUR: + case AFE_SRAM_DELSEL_CON0: + case AFE_SRAM_DELSEL_CON2: + case AFE_SRAM_DELSEL_CON3: + case AFE_ASRC_2CH_CON12: + case AFE_ASRC_2CH_CON13: + case PCM_INTF_CON2: + case FPGA_CFG0: + case FPGA_CFG1: + case FPGA_CFG2: + case FPGA_CFG3: + case AUDIO_TOP_DBG_MON0: + case AUDIO_TOP_DBG_MON1: + case AFE_IRQ8_MCU_CNT_MON: + case AFE_IRQ11_MCU_CNT_MON: + case AFE_IRQ12_MCU_CNT_MON: + case AFE_CBIP_MON0: + case AFE_CBIP_SLV_MUX_MON0: + case AFE_CBIP_SLV_DECODER_MON0: + case AFE_ADDA6_SRC_DEBUG_MON0: + case AFE_ADD6A_UL_SRC_MON0: + case AFE_ADDA6_UL_SRC_MON1: + case AFE_DL1_CUR_MSB: + case AFE_DL2_CUR_MSB: + case AFE_AWB_CUR_MSB: + case AFE_VUL_CUR_MSB: + case AFE_VUL2_CUR_MSB: + case AFE_MOD_DAI_CUR_MSB: + case AFE_VUL_D2_CUR_MSB: + case AFE_DL3_CUR_MSB: + case AFE_HDMI_OUT_CUR_MSB: + case AFE_AWB2_END: + case AFE_AWB2_CUR: + case AFE_AWB2_CUR_MSB: + case AFE_ADDA_DL_SDM_FIFO_MON: + case AFE_ADDA_DL_SRC_LCH_MON: + case AFE_ADDA_DL_SRC_RCH_MON: + case AFE_ADDA_DL_SDM_OUT_MON: + case AFE_CONNSYS_I2S_MON: + case AFE_ASRC_2CH_CON0: + case AFE_ASRC_2CH_CON2: + case AFE_ASRC_2CH_CON3: + case AFE_ASRC_2CH_CON4: + case AFE_ASRC_2CH_CON5: + case AFE_ASRC_2CH_CON7: + case AFE_ASRC_2CH_CON8: + case AFE_MEMIF_MON12: + case AFE_MEMIF_MON13: + case AFE_MEMIF_MON14: + case AFE_MEMIF_MON15: + case AFE_MEMIF_MON16: + case AFE_MEMIF_MON17: + case AFE_MEMIF_MON18: + case AFE_MEMIF_MON19: + case AFE_MEMIF_MON20: + case AFE_MEMIF_MON21: + case AFE_MEMIF_MON22: + case AFE_MEMIF_MON23: + case AFE_MEMIF_MON24: + case AFE_ADDA_MTKAIF_MON0: + case AFE_ADDA_MTKAIF_MON1: + case AFE_AUD_PAD_TOP: + case AFE_GENERAL1_ASRC_2CH_CON0: + case AFE_GENERAL1_ASRC_2CH_CON2: + case AFE_GENERAL1_ASRC_2CH_CON3: + case AFE_GENERAL1_ASRC_2CH_CON4: + case AFE_GENERAL1_ASRC_2CH_CON5: + case AFE_GENERAL1_ASRC_2CH_CON7: + case AFE_GENERAL1_ASRC_2CH_CON8: + case AFE_GENERAL1_ASRC_2CH_CON12: + case AFE_GENERAL1_ASRC_2CH_CON13: + case AFE_GENERAL2_ASRC_2CH_CON0: + case AFE_GENERAL2_ASRC_2CH_CON2: + case AFE_GENERAL2_ASRC_2CH_CON3: + case AFE_GENERAL2_ASRC_2CH_CON4: + case AFE_GENERAL2_ASRC_2CH_CON5: + case AFE_GENERAL2_ASRC_2CH_CON7: + case AFE_GENERAL2_ASRC_2CH_CON8: + case AFE_GENERAL2_ASRC_2CH_CON12: + case AFE_GENERAL2_ASRC_2CH_CON13: + return true; + default: + return false; + }; +} + +static const struct regmap_config mt8183_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .volatile_reg = mt8183_is_volatile_reg, + + .max_register = AFE_MAX_REGISTER, + .num_reg_defaults_raw = AFE_MAX_REGISTER, + + .cache_type = REGCACHE_FLAT, +}; + +static irqreturn_t mt8183_afe_irq_handler(int irq_id, void *dev) +{ + struct mtk_base_afe *afe = dev; + struct mtk_base_afe_irq *irq; + unsigned int status; + unsigned int status_mcu; + unsigned int mcu_en; + int ret; + int i; + irqreturn_t irq_ret = IRQ_HANDLED; + + /* get irq that is sent to MCU */ + regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en); + + ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status); + /* only care IRQ which is sent to MCU */ + status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS; + + if (ret || status_mcu == 0) { + dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n", + __func__, ret, status, mcu_en); + + irq_ret = IRQ_NONE; + goto err_irq; + } + + for (i = 0; i < MT8183_MEMIF_NUM; i++) { + struct mtk_base_afe_memif *memif = &afe->memif[i]; + + if (!memif->substream) + continue; + + if (memif->irq_usage < 0) + continue; + + irq = &afe->irqs[memif->irq_usage]; + + if (status_mcu & (1 << irq->irq_data->irq_en_shift)) + snd_pcm_period_elapsed(memif->substream); + } + +err_irq: + /* clear irq */ + regmap_write(afe->regmap, + AFE_IRQ_MCU_CLR, + status_mcu); + + return irq_ret; +} + +static int mt8183_afe_runtime_suspend(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + unsigned int value; + int ret; + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + /* disable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, AFE_ON_MASK_SFT, 0x0); + + ret = regmap_read_poll_timeout(afe->regmap, + AFE_DAC_MON, + value, + (value & AFE_ON_RETM_MASK_SFT) == 0, + 20, + 1 * 1000 * 1000); + if (ret) + dev_warn(afe->dev, "%s(), ret %d\n", __func__, ret); + + /* make sure all irq status are cleared, twice intended */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff); + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff); + + /* cache only */ + regcache_cache_only(afe->regmap, true); + regcache_mark_dirty(afe->regmap); + +skip_regmap: + return mt8183_afe_disable_clock(afe); +} + +static int mt8183_afe_runtime_resume(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = mt8183_afe_enable_clock(afe); + if (ret) + return ret; + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + regcache_cache_only(afe->regmap, false); + regcache_sync(afe->regmap); + + /* enable audio sys DCM for power saving */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, 0x1 << 29, 0x1 << 29); + + /* force cpu use 8_24 format when writing 32bit data */ + regmap_update_bits(afe->regmap, AFE_MEMIF_MSB, + CPU_HD_ALIGN_MASK_SFT, 0 << CPU_HD_ALIGN_SFT); + + /* set all output port to 24bit */ + regmap_write(afe->regmap, AFE_CONN_24BIT, 0xffffffff); + regmap_write(afe->regmap, AFE_CONN_24BIT_1, 0xffffffff); + + /* enable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); + +skip_regmap: + return 0; +} + +static int mt8183_afe_component_probe(struct snd_soc_component *component) +{ + return mtk_afe_add_sub_dai_control(component); +} + +static const struct snd_soc_component_driver mt8183_afe_component = { + .name = AFE_PCM_NAME, + .ops = &mtk_afe_pcm_ops, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mtk_afe_pcm_free, + .probe = mt8183_afe_component_probe, +}; + +static int mt8183_dai_memif_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mt8183_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt8183_memif_dai_driver); + + dai->dapm_widgets = mt8183_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt8183_memif_widgets); + dai->dapm_routes = mt8183_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt8183_memif_routes); + return 0; +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt8183_dai_adda_register, + mt8183_dai_i2s_register, + mt8183_dai_pcm_register, + mt8183_dai_tdm_register, + mt8183_dai_hostless_register, + mt8183_dai_memif_register, +}; + +static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) +{ + struct mtk_base_afe *afe; + struct mt8183_afe_private *afe_priv; + struct device *dev; + int i, irq_id, ret; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + platform_set_drvdata(pdev, afe); + + afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), + GFP_KERNEL); + if (!afe->platform_priv) + return -ENOMEM; + + afe_priv = afe->platform_priv; + afe->dev = &pdev->dev; + dev = afe->dev; + + /* initial audio related clock */ + ret = mt8183_init_clock(afe); + if (ret) { + dev_err(dev, "init clock error\n"); + return ret; + } + + pm_runtime_enable(dev); + + /* regmap init */ + afe->regmap = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(afe->regmap)) { + dev_err(dev, "could not get regmap from parent\n"); + return PTR_ERR(afe->regmap); + } + ret = regmap_attach_dev(dev, afe->regmap, &mt8183_afe_regmap_config); + if (ret) { + dev_warn(dev, "regmap_attach_dev fail, ret %d\n", ret); + return ret; + } + + /* enable clock for regcache get default value from hw */ + afe_priv->pm_runtime_bypass_reg_ctl = true; + pm_runtime_get_sync(&pdev->dev); + + ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); + if (ret) { + dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret); + return ret; + } + + pm_runtime_put_sync(&pdev->dev); + afe_priv->pm_runtime_bypass_reg_ctl = false; + + regcache_cache_only(afe->regmap, true); + regcache_mark_dirty(afe->regmap); + + pm_runtime_get_sync(&pdev->dev); + + /* init memif */ + afe->memif_size = MT8183_MEMIF_NUM; + afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), + GFP_KERNEL); + if (!afe->memif) + return -ENOMEM; + + for (i = 0; i < afe->memif_size; i++) { + afe->memif[i].data = &memif_data[i]; + afe->memif[i].irq_usage = -1; + } + + afe->memif[MT8183_MEMIF_HDMI].irq_usage = MT8183_IRQ_8; + afe->memif[MT8183_MEMIF_HDMI].const_irq = 1; + + mutex_init(&afe->irq_alloc_lock); + + /* init memif */ + /* irq initialize */ + afe->irqs_size = MT8183_IRQ_NUM; + afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), + GFP_KERNEL); + if (!afe->irqs) + return -ENOMEM; + + for (i = 0; i < afe->irqs_size; i++) + afe->irqs[i].irq_data = &irq_data[i]; + + /* request irq */ + irq_id = platform_get_irq(pdev, 0); + if (!irq_id) { + dev_err(dev, "%pOFn no irq found\n", dev->of_node); + return -ENXIO; + } + ret = devm_request_irq(dev, irq_id, mt8183_afe_irq_handler, + IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); + if (ret) { + dev_err(dev, "could not request_irq for asys-isr\n"); + return ret; + } + + /* init sub_dais */ + INIT_LIST_HEAD(&afe->sub_dais); + + for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { + ret = dai_register_cbs[i](afe); + if (ret) { + dev_warn(afe->dev, "dai register i %d fail, ret %d\n", + i, ret); + return ret; + } + } + + /* init dai_driver and component_driver */ + ret = mtk_afe_combine_sub_dai(afe); + if (ret) { + dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n", + ret); + return ret; + } + + afe->mtk_afe_hardware = &mt8183_afe_hardware; + afe->memif_fs = mt8183_memif_fs; + afe->irq_fs = mt8183_irq_fs; + + afe->runtime_resume = mt8183_afe_runtime_resume; + afe->runtime_suspend = mt8183_afe_runtime_suspend; + + /* register component */ + ret = devm_snd_soc_register_component(&pdev->dev, + &mt8183_afe_component, + NULL, 0); + if (ret) { + dev_warn(dev, "err_platform\n"); + return ret; + } + + ret = devm_snd_soc_register_component(afe->dev, + &mt8183_afe_pcm_dai_component, + afe->dai_drivers, + afe->num_dai_drivers); + if (ret) { + dev_warn(dev, "err_dai_component\n"); + return ret; + } + + return ret; +} + +static int mt8183_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_put_sync(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + mt8183_afe_runtime_suspend(&pdev->dev); + return 0; +} + +static const struct of_device_id mt8183_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt8183-audio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt8183_afe_pcm_dt_match); + +static const struct dev_pm_ops mt8183_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mt8183_afe_runtime_suspend, + mt8183_afe_runtime_resume, NULL) +}; + +static struct platform_driver mt8183_afe_pcm_driver = { + .driver = { + .name = "mt8183-audio", + .of_match_table = mt8183_afe_pcm_dt_match, +#ifdef CONFIG_PM + .pm = &mt8183_afe_pm_ops, +#endif + }, + .probe = mt8183_afe_pcm_dev_probe, + .remove = mt8183_afe_pcm_dev_remove, +}; + +module_platform_driver(mt8183_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 8183"); +MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-adda.c b/sound/soc/mediatek/mt8183/mt8183-dai-adda.c new file mode 100644 index 000000000000..017d7d1d9148 --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-dai-adda.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI ADDA Control +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/regmap.h> +#include <linux/delay.h> +#include "mt8183-afe-common.h" +#include "mt8183-interconnection.h" +#include "mt8183-reg.h" + +enum { + AUDIO_SDM_LEVEL_MUTE = 0, + AUDIO_SDM_LEVEL_NORMAL = 0x1d, + /* if you change level normal */ + /* you need to change formula of hp impedance and dc trim too */ +}; + +enum { + DELAY_DATA_MISO1 = 0, + DELAY_DATA_MISO2, +}; + +enum { + MTK_AFE_ADDA_DL_RATE_8K = 0, + MTK_AFE_ADDA_DL_RATE_11K = 1, + MTK_AFE_ADDA_DL_RATE_12K = 2, + MTK_AFE_ADDA_DL_RATE_16K = 3, + MTK_AFE_ADDA_DL_RATE_22K = 4, + MTK_AFE_ADDA_DL_RATE_24K = 5, + MTK_AFE_ADDA_DL_RATE_32K = 6, + MTK_AFE_ADDA_DL_RATE_44K = 7, + MTK_AFE_ADDA_DL_RATE_48K = 8, + MTK_AFE_ADDA_DL_RATE_96K = 9, + MTK_AFE_ADDA_DL_RATE_192K = 10, +}; + +enum { + MTK_AFE_ADDA_UL_RATE_8K = 0, + MTK_AFE_ADDA_UL_RATE_16K = 1, + MTK_AFE_ADDA_UL_RATE_32K = 2, + MTK_AFE_ADDA_UL_RATE_48K = 3, + MTK_AFE_ADDA_UL_RATE_96K = 4, + MTK_AFE_ADDA_UL_RATE_192K = 5, + MTK_AFE_ADDA_UL_RATE_48K_HD = 6, +}; + +static unsigned int adda_dl_rate_transform(struct mtk_base_afe *afe, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_ADDA_DL_RATE_8K; + case 11025: + return MTK_AFE_ADDA_DL_RATE_11K; + case 12000: + return MTK_AFE_ADDA_DL_RATE_12K; + case 16000: + return MTK_AFE_ADDA_DL_RATE_16K; + case 22050: + return MTK_AFE_ADDA_DL_RATE_22K; + case 24000: + return MTK_AFE_ADDA_DL_RATE_24K; + case 32000: + return MTK_AFE_ADDA_DL_RATE_32K; + case 44100: + return MTK_AFE_ADDA_DL_RATE_44K; + case 48000: + return MTK_AFE_ADDA_DL_RATE_48K; + case 96000: + return MTK_AFE_ADDA_DL_RATE_96K; + case 192000: + return MTK_AFE_ADDA_DL_RATE_192K; + default: + dev_warn(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n", + __func__, rate); + return MTK_AFE_ADDA_DL_RATE_48K; + } +} + +static unsigned int adda_ul_rate_transform(struct mtk_base_afe *afe, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_ADDA_UL_RATE_8K; + case 16000: + return MTK_AFE_ADDA_UL_RATE_16K; + case 32000: + return MTK_AFE_ADDA_UL_RATE_32K; + case 48000: + return MTK_AFE_ADDA_UL_RATE_48K; + case 96000: + return MTK_AFE_ADDA_UL_RATE_96K; + case 192000: + return MTK_AFE_ADDA_UL_RATE_192K; + default: + dev_warn(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n", + __func__, rate); + return MTK_AFE_ADDA_UL_RATE_48K; + } +} + +/* dai component */ +static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN3, I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN3, I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN3, I_DL3_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN3, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN3, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN3, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN3, + I_PCM_2_CAP_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN4, I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN4, I_DL1_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN4, I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN4, I_DL2_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN4, I_DL3_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN4, I_DL3_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN4, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN4, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN4, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN4, + I_PCM_2_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN4, + I_PCM_1_CAP_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN4, + I_PCM_2_CAP_CH2, 1, 0), +}; + +static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + + dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* update setting to dmic */ + if (afe_priv->mtkaif_dmic) { + /* mtkaif_rxif_data_mode = 1, dmic */ + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0, + 0x1, 0x1); + + /* dmic mode, 3.25M*/ + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0, + 0x0, 0xf << 20); + regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, + 0x0, 0x1 << 5); + regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, + 0x0, 0x3 << 14); + + /* turn on dmic, ch1, ch2 */ + regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, + 0x1 << 1, 0x1 << 1); + regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, + 0x3 << 21, 0x3 << 21); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */ + usleep_range(125, 135); + + /* reset dmic */ + afe_priv->mtkaif_dmic = 0; + break; + default: + break; + } + + return 0; +} + +/* mtkaif dmic */ +static const char * const mt8183_adda_off_on_str[] = { + "Off", "On" +}; + +static const struct soc_enum mt8183_adda_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8183_adda_off_on_str), + mt8183_adda_off_on_str), +}; + +static int mt8183_adda_dmic_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + + ucontrol->value.integer.value[0] = afe_priv->mtkaif_dmic; + + return 0; +} + +static int mt8183_adda_dmic_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + + afe_priv->mtkaif_dmic = ucontrol->value.integer.value[0]; + + dev_info(afe->dev, "%s(), kcontrol name %s, mtkaif_dmic %d\n", + __func__, kcontrol->id.name, afe_priv->mtkaif_dmic); + + return 0; +} + +static const struct snd_kcontrol_new mtk_adda_controls[] = { + SOC_ENUM_EXT("MTKAIF_DMIC", mt8183_adda_enum[0], + mt8183_adda_dmic_get, mt8183_adda_dmic_set), +}; + +enum { + SUPPLY_SEQ_ADDA_AFE_ON, + SUPPLY_SEQ_ADDA_DL_ON, + SUPPLY_SEQ_ADDA_UL_ON, +}; + +static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = { + /* adda */ + SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0, + mtk_adda_dl_ch1_mix, + ARRAY_SIZE(mtk_adda_dl_ch1_mix)), + SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0, + mtk_adda_dl_ch2_mix, + ARRAY_SIZE(mtk_adda_dl_ch2_mix)), + + SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON, + AFE_ADDA_UL_DL_CON0, ADDA_AFE_ON_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON, + AFE_ADDA_DL_SRC2_CON0, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON, + AFE_ADDA_UL_SRC_CON0, + UL_SRC_ON_TMP_CTL_SFT, 0, + mtk_adda_ul_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_clk"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_predis_clk"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_clk"), + SND_SOC_DAPM_CLOCK_SUPPLY("mtkaif_26m_clk"), +}; + +static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { + /* playback */ + {"ADDA_DL_CH1", "DL1_CH1", "DL1"}, + {"ADDA_DL_CH2", "DL1_CH1", "DL1"}, + {"ADDA_DL_CH2", "DL1_CH2", "DL1"}, + + {"ADDA_DL_CH1", "DL2_CH1", "DL2"}, + {"ADDA_DL_CH2", "DL2_CH1", "DL2"}, + {"ADDA_DL_CH2", "DL2_CH2", "DL2"}, + + {"ADDA_DL_CH1", "DL3_CH1", "DL3"}, + {"ADDA_DL_CH2", "DL3_CH1", "DL3"}, + {"ADDA_DL_CH2", "DL3_CH2", "DL3"}, + + {"ADDA Playback", NULL, "ADDA_DL_CH1"}, + {"ADDA Playback", NULL, "ADDA_DL_CH2"}, + + /* adda enable */ + {"ADDA Playback", NULL, "ADDA Enable"}, + {"ADDA Playback", NULL, "ADDA Playback Enable"}, + {"ADDA Capture", NULL, "ADDA Enable"}, + {"ADDA Capture", NULL, "ADDA Capture Enable"}, + + /* clk */ + {"ADDA Playback", NULL, "mtkaif_26m_clk"}, + {"ADDA Playback", NULL, "aud_dac_clk"}, + {"ADDA Playback", NULL, "aud_dac_predis_clk"}, + + {"ADDA Capture", NULL, "mtkaif_26m_clk"}, + {"ADDA Capture", NULL, "aud_adc_clk"}, +}; + +static int set_mtkaif_rx(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int delay_data; + int delay_cycle; + + switch (afe_priv->mtkaif_protocol) { + case MT8183_MTKAIF_PROTOCOL_2_CLK_P2: + regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x38); + regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x39); + /* mtkaif_rxif_clkinv_adc inverse for calibration */ + regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, + 0x80010000); + + if (afe_priv->mtkaif_phase_cycle[0] >= + afe_priv->mtkaif_phase_cycle[1]) { + delay_data = DELAY_DATA_MISO1; + delay_cycle = afe_priv->mtkaif_phase_cycle[0] - + afe_priv->mtkaif_phase_cycle[1]; + } else { + delay_data = DELAY_DATA_MISO2; + delay_cycle = afe_priv->mtkaif_phase_cycle[1] - + afe_priv->mtkaif_phase_cycle[0]; + } + + regmap_update_bits(afe->regmap, + AFE_ADDA_MTKAIF_RX_CFG2, + MTKAIF_RXIF_DELAY_DATA_MASK_SFT, + delay_data << MTKAIF_RXIF_DELAY_DATA_SFT); + + regmap_update_bits(afe->regmap, + AFE_ADDA_MTKAIF_RX_CFG2, + MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT, + delay_cycle << MTKAIF_RXIF_DELAY_CYCLE_SFT); + break; + case MT8183_MTKAIF_PROTOCOL_2: + regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31); + regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, + 0x00010000); + break; + case MT8183_MTKAIF_PROTOCOL_1: + regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31); + regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, 0x0); + default: + break; + } + + return 0; +} + +/* dai ops */ +static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + + dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n", + __func__, dai->id, substream->stream, rate); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + unsigned int dl_src2_con0 = 0; + unsigned int dl_src2_con1 = 0; + + /* clean predistortion */ + regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0); + regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0); + + /* set sampling rate */ + dl_src2_con0 = adda_dl_rate_transform(afe, rate) << 28; + + /* set output mode */ + switch (rate) { + case 192000: + dl_src2_con0 |= (0x1 << 24); /* UP_SAMPLING_RATE_X2 */ + dl_src2_con0 |= 1 << 14; + break; + case 96000: + dl_src2_con0 |= (0x2 << 24); /* UP_SAMPLING_RATE_X4 */ + dl_src2_con0 |= 1 << 14; + break; + default: + dl_src2_con0 |= (0x3 << 24); /* UP_SAMPLING_RATE_X8 */ + break; + } + + /* turn off mute function */ + dl_src2_con0 |= (0x03 << 11); + + /* set voice input data if input sample rate is 8k or 16k */ + if (rate == 8000 || rate == 16000) + dl_src2_con0 |= 0x01 << 5; + + /* SA suggest apply -0.3db to audio/speech path */ + dl_src2_con1 = 0xf74f0000; + + /* turn on down-link gain */ + dl_src2_con0 = dl_src2_con0 | (0x01 << 1); + + regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON0, dl_src2_con0); + regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON1, dl_src2_con1); + + /* set sdm gain */ + regmap_update_bits(afe->regmap, + AFE_ADDA_DL_SDM_DCCOMP_CON, + ATTGAIN_CTL_MASK_SFT, + AUDIO_SDM_LEVEL_NORMAL << ATTGAIN_CTL_SFT); + } else { + unsigned int voice_mode = 0; + unsigned int ul_src_con0 = 0; /* default value */ + + /* set mtkaif protocol */ + set_mtkaif_rx(afe); + + /* Using Internal ADC */ + regmap_update_bits(afe->regmap, + AFE_ADDA_TOP_CON0, + 0x1 << 0, + 0x0 << 0); + + voice_mode = adda_ul_rate_transform(afe, rate); + + ul_src_con0 |= (voice_mode << 17) & (0x7 << 17); + + regmap_write(afe->regmap, AFE_ADDA_UL_SRC_CON0, ul_src_con0); + + /* mtkaif_rxif_data_mode = 0, amic */ + regmap_update_bits(afe->regmap, + AFE_ADDA_MTKAIF_RX_CFG0, + 0x1 << 0, + 0x0 << 0); + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_adda_ops = { + .hw_params = mtk_dai_adda_hw_params, +}; + +/* dai driver */ +#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000) + +#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { + { + .name = "ADDA", + .id = MT8183_DAI_ADDA, + .playback = { + .stream_name = "ADDA Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ADDA_PLAYBACK_RATES, + .formats = MTK_ADDA_FORMATS, + }, + .capture = { + .stream_name = "ADDA Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ADDA_CAPTURE_RATES, + .formats = MTK_ADDA_FORMATS, + }, + .ops = &mtk_dai_adda_ops, + }, +}; + +int mt8183_dai_adda_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_adda_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver); + + dai->controls = mtk_adda_controls; + dai->num_controls = ARRAY_SIZE(mtk_adda_controls); + dai->dapm_widgets = mtk_dai_adda_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets); + dai->dapm_routes = mtk_dai_adda_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes); + return 0; +} diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-hostless.c b/sound/soc/mediatek/mt8183/mt8183-dai-hostless.c new file mode 100644 index 000000000000..1667ad352d34 --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-dai-hostless.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI Hostless Control +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include "mt8183-afe-common.h" + +/* dai component */ +static const struct snd_soc_dapm_route mtk_dai_hostless_routes[] = { + /* Hostless ADDA Loopback */ + {"ADDA_DL_CH1", "ADDA_UL_CH1", "Hostless LPBK DL"}, + {"ADDA_DL_CH1", "ADDA_UL_CH2", "Hostless LPBK DL"}, + {"ADDA_DL_CH2", "ADDA_UL_CH1", "Hostless LPBK DL"}, + {"ADDA_DL_CH2", "ADDA_UL_CH2", "Hostless LPBK DL"}, + {"Hostless LPBK UL", NULL, "ADDA Capture"}, + + /* Hostless Speech */ + {"ADDA_DL_CH1", "PCM_1_CAP_CH1", "Hostless Speech DL"}, + {"ADDA_DL_CH2", "PCM_1_CAP_CH1", "Hostless Speech DL"}, + {"ADDA_DL_CH2", "PCM_1_CAP_CH2", "Hostless Speech DL"}, + {"ADDA_DL_CH1", "PCM_2_CAP_CH1", "Hostless Speech DL"}, + {"ADDA_DL_CH2", "PCM_2_CAP_CH1", "Hostless Speech DL"}, + {"ADDA_DL_CH2", "PCM_2_CAP_CH2", "Hostless Speech DL"}, + {"PCM_1_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"}, + {"PCM_1_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"}, + {"PCM_2_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"}, + {"PCM_2_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"}, + + {"Hostless Speech UL", NULL, "PCM 1 Capture"}, + {"Hostless Speech UL", NULL, "PCM 2 Capture"}, + {"Hostless Speech UL", NULL, "ADDA Capture"}, +}; + +/* dai ops */ +static int mtk_dai_hostless_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + return snd_soc_set_runtime_hwparams(substream, afe->mtk_afe_hardware); +} + +static const struct snd_soc_dai_ops mtk_dai_hostless_ops = { + .startup = mtk_dai_hostless_startup, +}; + +/* dai driver */ +#define MTK_HOSTLESS_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_HOSTLESS_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_hostless_driver[] = { + { + .name = "Hostless LPBK DAI", + .id = MT8183_DAI_HOSTLESS_LPBK, + .playback = { + .stream_name = "Hostless LPBK DL", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_HOSTLESS_RATES, + .formats = MTK_HOSTLESS_FORMATS, + }, + .capture = { + .stream_name = "Hostless LPBK UL", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_HOSTLESS_RATES, + .formats = MTK_HOSTLESS_FORMATS, + }, + .ops = &mtk_dai_hostless_ops, + }, + { + .name = "Hostless Speech DAI", + .id = MT8183_DAI_HOSTLESS_SPEECH, + .playback = { + .stream_name = "Hostless Speech DL", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_HOSTLESS_RATES, + .formats = MTK_HOSTLESS_FORMATS, + }, + .capture = { + .stream_name = "Hostless Speech UL", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_HOSTLESS_RATES, + .formats = MTK_HOSTLESS_FORMATS, + }, + .ops = &mtk_dai_hostless_ops, + }, +}; + +int mt8183_dai_hostless_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_hostless_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver); + + dai->dapm_routes = mtk_dai_hostless_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes); + + return 0; +} diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-i2s.c b/sound/soc/mediatek/mt8183/mt8183-dai-i2s.c new file mode 100644 index 000000000000..777e93d70bea --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-dai-i2s.c @@ -0,0 +1,1040 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI I2S Control +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/bitops.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt8183-afe-clk.h" +#include "mt8183-afe-common.h" +#include "mt8183-interconnection.h" +#include "mt8183-reg.h" + +enum { + I2S_FMT_EIAJ = 0, + I2S_FMT_I2S = 1, +}; + +enum { + I2S_WLEN_16_BIT = 0, + I2S_WLEN_32_BIT = 1, +}; + +enum { + I2S_HD_NORMAL = 0, + I2S_HD_LOW_JITTER = 1, +}; + +enum { + I2S1_SEL_O28_O29 = 0, + I2S1_SEL_O03_O04 = 1, +}; + +enum { + I2S_IN_PAD_CONNSYS = 0, + I2S_IN_PAD_IO_MUX = 1, +}; + +struct mtk_afe_i2s_priv { + int id; + int rate; /* for determine which apll to use */ + int low_jitter_en; + + const char *share_property_name; + int share_i2s_id; + + int mclk_id; + int mclk_rate; + int mclk_apll; +}; + +static unsigned int get_i2s_wlen(snd_pcm_format_t format) +{ + return snd_pcm_format_physical_width(format) <= 16 ? + I2S_WLEN_16_BIT : I2S_WLEN_32_BIT; +} + +#define MTK_AFE_I2S0_KCONTROL_NAME "I2S0_HD_Mux" +#define MTK_AFE_I2S1_KCONTROL_NAME "I2S1_HD_Mux" +#define MTK_AFE_I2S2_KCONTROL_NAME "I2S2_HD_Mux" +#define MTK_AFE_I2S3_KCONTROL_NAME "I2S3_HD_Mux" +#define MTK_AFE_I2S5_KCONTROL_NAME "I2S5_HD_Mux" + +#define I2S0_HD_EN_W_NAME "I2S0_HD_EN" +#define I2S1_HD_EN_W_NAME "I2S1_HD_EN" +#define I2S2_HD_EN_W_NAME "I2S2_HD_EN" +#define I2S3_HD_EN_W_NAME "I2S3_HD_EN" +#define I2S5_HD_EN_W_NAME "I2S5_HD_EN" + +#define I2S0_MCLK_EN_W_NAME "I2S0_MCLK_EN" +#define I2S1_MCLK_EN_W_NAME "I2S1_MCLK_EN" +#define I2S2_MCLK_EN_W_NAME "I2S2_MCLK_EN" +#define I2S3_MCLK_EN_W_NAME "I2S3_MCLK_EN" +#define I2S5_MCLK_EN_W_NAME "I2S5_MCLK_EN" + +static int get_i2s_id_by_name(struct mtk_base_afe *afe, + const char *name) +{ + if (strncmp(name, "I2S0", 4) == 0) + return MT8183_DAI_I2S_0; + else if (strncmp(name, "I2S1", 4) == 0) + return MT8183_DAI_I2S_1; + else if (strncmp(name, "I2S2", 4) == 0) + return MT8183_DAI_I2S_2; + else if (strncmp(name, "I2S3", 4) == 0) + return MT8183_DAI_I2S_3; + else if (strncmp(name, "I2S5", 4) == 0) + return MT8183_DAI_I2S_5; + else + return -EINVAL; +} + +static struct mtk_afe_i2s_priv *get_i2s_priv_by_name(struct mtk_base_afe *afe, + const char *name) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int dai_id = get_i2s_id_by_name(afe, name); + + if (dai_id < 0) + return NULL; + + return afe_priv->dai_priv[dai_id]; +} + +/* low jitter control */ +static const char * const mt8183_i2s_hd_str[] = { + "Normal", "Low_Jitter" +}; + +static const struct soc_enum mt8183_i2s_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8183_i2s_hd_str), + mt8183_i2s_hd_str), +}; + +static int mt8183_i2s_hd_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_afe_i2s_priv *i2s_priv; + + i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name); + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = i2s_priv->low_jitter_en; + + return 0; +} + +static int mt8183_i2s_hd_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_afe_i2s_priv *i2s_priv; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int hd_en; + + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + + hd_en = ucontrol->value.integer.value[0]; + + dev_info(afe->dev, "%s(), kcontrol name %s, hd_en %d\n", + __func__, kcontrol->id.name, hd_en); + + i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name); + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return -EINVAL; + } + + i2s_priv->low_jitter_en = hd_en; + + return 0; +} + +static const struct snd_kcontrol_new mtk_dai_i2s_controls[] = { + SOC_ENUM_EXT(MTK_AFE_I2S0_KCONTROL_NAME, mt8183_i2s_enum[0], + mt8183_i2s_hd_get, mt8183_i2s_hd_set), + SOC_ENUM_EXT(MTK_AFE_I2S1_KCONTROL_NAME, mt8183_i2s_enum[0], + mt8183_i2s_hd_get, mt8183_i2s_hd_set), + SOC_ENUM_EXT(MTK_AFE_I2S2_KCONTROL_NAME, mt8183_i2s_enum[0], + mt8183_i2s_hd_get, mt8183_i2s_hd_set), + SOC_ENUM_EXT(MTK_AFE_I2S3_KCONTROL_NAME, mt8183_i2s_enum[0], + mt8183_i2s_hd_get, mt8183_i2s_hd_set), + SOC_ENUM_EXT(MTK_AFE_I2S5_KCONTROL_NAME, mt8183_i2s_enum[0], + mt8183_i2s_hd_get, mt8183_i2s_hd_set), +}; + +/* dai component */ +/* interconnection */ +static const struct snd_kcontrol_new mtk_i2s3_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN0, I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN0, I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN0, I_DL3_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN0, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN0, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN0, + I_PCM_2_CAP_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_i2s3_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN1, I_DL1_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN1, I_DL2_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN1, I_DL3_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN1, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN1, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN1, + I_PCM_2_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN1, + I_PCM_1_CAP_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN1, + I_PCM_2_CAP_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_i2s1_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN28, I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN28, I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN28, I_DL3_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN28, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN28, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN28, + I_PCM_2_CAP_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_i2s1_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN29, I_DL1_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN29, I_DL2_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN29, I_DL3_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN29, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN29, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN29, + I_PCM_2_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN29, + I_PCM_1_CAP_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN29, + I_PCM_2_CAP_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_i2s5_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN30, I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN30, I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN30, I_DL3_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN30, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN30, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN30, + I_PCM_2_CAP_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_i2s5_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN31, I_DL1_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN31, I_DL2_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN31, I_DL3_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN31, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN31, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN31, + I_PCM_2_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN31, + I_PCM_1_CAP_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN31, + I_PCM_2_CAP_CH2, 1, 0), +}; + +enum { + SUPPLY_SEQ_APLL, + SUPPLY_SEQ_I2S_MCLK_EN, + SUPPLY_SEQ_I2S_HD_EN, + SUPPLY_SEQ_I2S_EN, +}; + +static int mtk_apll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + + dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strcmp(w->name, APLL1_W_NAME) == 0) + mt8183_apll1_enable(afe); + else + mt8183_apll2_enable(afe); + break; + case SND_SOC_DAPM_POST_PMD: + if (strcmp(w->name, APLL1_W_NAME) == 0) + mt8183_apll1_disable(afe); + else + mt8183_apll2_disable(afe); + break; + default: + break; + } + + return 0; +} + +static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_afe_i2s_priv *i2s_priv; + + dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + i2s_priv = get_i2s_priv_by_name(afe, w->name); + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mt8183_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate); + break; + case SND_SOC_DAPM_POST_PMD: + i2s_priv->mclk_rate = 0; + mt8183_mck_disable(afe, i2s_priv->mclk_id); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = { + SND_SOC_DAPM_MIXER("I2S1_CH1", SND_SOC_NOPM, 0, 0, + mtk_i2s1_ch1_mix, + ARRAY_SIZE(mtk_i2s1_ch1_mix)), + SND_SOC_DAPM_MIXER("I2S1_CH2", SND_SOC_NOPM, 0, 0, + mtk_i2s1_ch2_mix, + ARRAY_SIZE(mtk_i2s1_ch2_mix)), + + SND_SOC_DAPM_MIXER("I2S3_CH1", SND_SOC_NOPM, 0, 0, + mtk_i2s3_ch1_mix, + ARRAY_SIZE(mtk_i2s3_ch1_mix)), + SND_SOC_DAPM_MIXER("I2S3_CH2", SND_SOC_NOPM, 0, 0, + mtk_i2s3_ch2_mix, + ARRAY_SIZE(mtk_i2s3_ch2_mix)), + + SND_SOC_DAPM_MIXER("I2S5_CH1", SND_SOC_NOPM, 0, 0, + mtk_i2s5_ch1_mix, + ARRAY_SIZE(mtk_i2s5_ch1_mix)), + SND_SOC_DAPM_MIXER("I2S5_CH2", SND_SOC_NOPM, 0, 0, + mtk_i2s5_ch2_mix, + ARRAY_SIZE(mtk_i2s5_ch2_mix)), + + /* i2s en*/ + SND_SOC_DAPM_SUPPLY_S("I2S0_EN", SUPPLY_SEQ_I2S_EN, + AFE_I2S_CON, I2S_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S1_EN", SUPPLY_SEQ_I2S_EN, + AFE_I2S_CON1, I2S_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2_EN", SUPPLY_SEQ_I2S_EN, + AFE_I2S_CON2, I2S_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S3_EN", SUPPLY_SEQ_I2S_EN, + AFE_I2S_CON3, I2S_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S5_EN", SUPPLY_SEQ_I2S_EN, + AFE_I2S_CON4, I2S5_EN_SFT, 0, + NULL, 0), + /* i2s hd en */ + SND_SOC_DAPM_SUPPLY_S(I2S0_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN, + AFE_I2S_CON, I2S1_HD_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S(I2S1_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN, + AFE_I2S_CON1, I2S2_HD_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S(I2S2_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN, + AFE_I2S_CON2, I2S3_HD_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S(I2S3_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN, + AFE_I2S_CON3, I2S4_HD_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S(I2S5_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN, + AFE_I2S_CON4, I2S5_HD_EN_SFT, 0, + NULL, 0), + + /* i2s mclk en */ + SND_SOC_DAPM_SUPPLY_S(I2S0_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN, + SND_SOC_NOPM, 0, 0, + mtk_mclk_en_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S(I2S1_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN, + SND_SOC_NOPM, 0, 0, + mtk_mclk_en_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S(I2S2_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN, + SND_SOC_NOPM, 0, 0, + mtk_mclk_en_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S(I2S3_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN, + SND_SOC_NOPM, 0, 0, + mtk_mclk_en_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S(I2S5_MCLK_EN_W_NAME, SUPPLY_SEQ_I2S_MCLK_EN, + SND_SOC_NOPM, 0, 0, + mtk_mclk_en_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* apll */ + SND_SOC_DAPM_SUPPLY_S(APLL1_W_NAME, SUPPLY_SEQ_APLL, + SND_SOC_NOPM, 0, 0, + mtk_apll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S(APLL2_W_NAME, SUPPLY_SEQ_APLL, + SND_SOC_NOPM, 0, 0, + mtk_apll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static int mtk_afe_i2s_share_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_afe_i2s_priv *i2s_priv; + + i2s_priv = get_i2s_priv_by_name(afe, sink->name); + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return 0; + } + + if (i2s_priv->share_i2s_id < 0) + return 0; + + return i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name); +} + +static int mtk_afe_i2s_hd_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_afe_i2s_priv *i2s_priv; + + i2s_priv = get_i2s_priv_by_name(afe, sink->name); + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return 0; + } + + if (get_i2s_id_by_name(afe, sink->name) == + get_i2s_id_by_name(afe, source->name)) + return i2s_priv->low_jitter_en; + + /* check if share i2s need hd en */ + if (i2s_priv->share_i2s_id < 0) + return 0; + + if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name)) + return i2s_priv->low_jitter_en; + + return 0; +} + +static int mtk_afe_i2s_apll_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_afe_i2s_priv *i2s_priv; + int cur_apll; + int i2s_need_apll; + + i2s_priv = get_i2s_priv_by_name(afe, w->name); + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return 0; + } + + /* which apll */ + cur_apll = mt8183_get_apll_by_name(afe, source->name); + + /* choose APLL from i2s rate */ + i2s_need_apll = mt8183_get_apll_by_rate(afe, i2s_priv->rate); + + return (i2s_need_apll == cur_apll) ? 1 : 0; +} + +static int mtk_afe_i2s_mclk_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_afe_i2s_priv *i2s_priv; + + i2s_priv = get_i2s_priv_by_name(afe, sink->name); + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return 0; + } + + if (get_i2s_id_by_name(afe, sink->name) == + get_i2s_id_by_name(afe, source->name)) + return (i2s_priv->mclk_rate > 0) ? 1 : 0; + + /* check if share i2s need mclk */ + if (i2s_priv->share_i2s_id < 0) + return 0; + + if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name)) + return (i2s_priv->mclk_rate > 0) ? 1 : 0; + + return 0; +} + +static int mtk_afe_mclk_apll_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mtk_afe_i2s_priv *i2s_priv; + int cur_apll; + + i2s_priv = get_i2s_priv_by_name(afe, w->name); + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return 0; + } + + /* which apll */ + cur_apll = mt8183_get_apll_by_name(afe, source->name); + + return (i2s_priv->mclk_apll == cur_apll) ? 1 : 0; +} + +static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = { + /* i2s0 */ + {"I2S0", NULL, "I2S0_EN"}, + {"I2S0", NULL, "I2S1_EN", mtk_afe_i2s_share_connect}, + {"I2S0", NULL, "I2S2_EN", mtk_afe_i2s_share_connect}, + {"I2S0", NULL, "I2S3_EN", mtk_afe_i2s_share_connect}, + {"I2S0", NULL, "I2S5_EN", mtk_afe_i2s_share_connect}, + + {"I2S0", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S0", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S0", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S0", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S0", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {I2S0_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect}, + {I2S0_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect}, + + {"I2S0", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S0", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S0", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S0", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S0", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {I2S0_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {I2S0_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + /* i2s1 */ + {"I2S1_CH1", "DL1_CH1", "DL1"}, + {"I2S1_CH2", "DL1_CH2", "DL1"}, + + {"I2S1_CH1", "DL2_CH1", "DL2"}, + {"I2S1_CH2", "DL2_CH2", "DL2"}, + + {"I2S1_CH1", "DL3_CH1", "DL3"}, + {"I2S1_CH2", "DL3_CH2", "DL3"}, + + {"I2S1", NULL, "I2S1_CH1"}, + {"I2S1", NULL, "I2S1_CH2"}, + + {"I2S1", NULL, "I2S0_EN", mtk_afe_i2s_share_connect}, + {"I2S1", NULL, "I2S1_EN"}, + {"I2S1", NULL, "I2S2_EN", mtk_afe_i2s_share_connect}, + {"I2S1", NULL, "I2S3_EN", mtk_afe_i2s_share_connect}, + {"I2S1", NULL, "I2S5_EN", mtk_afe_i2s_share_connect}, + + {"I2S1", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S1", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S1", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S1", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S1", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {I2S1_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect}, + {I2S1_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect}, + + {"I2S1", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S1", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S1", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S1", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S1", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {I2S1_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {I2S1_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + /* i2s2 */ + {"I2S2", NULL, "I2S0_EN", mtk_afe_i2s_share_connect}, + {"I2S2", NULL, "I2S1_EN", mtk_afe_i2s_share_connect}, + {"I2S2", NULL, "I2S2_EN"}, + {"I2S2", NULL, "I2S3_EN", mtk_afe_i2s_share_connect}, + {"I2S2", NULL, "I2S5_EN", mtk_afe_i2s_share_connect}, + + {"I2S2", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S2", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S2", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S2", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S2", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {I2S2_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect}, + {I2S2_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect}, + + {"I2S2", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S2", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S2", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S2", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S2", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {I2S2_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {I2S2_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + /* i2s3 */ + {"I2S3_CH1", "DL1_CH1", "DL1"}, + {"I2S3_CH2", "DL1_CH2", "DL1"}, + + {"I2S3_CH1", "DL2_CH1", "DL2"}, + {"I2S3_CH2", "DL2_CH2", "DL2"}, + + {"I2S3_CH1", "DL3_CH1", "DL3"}, + {"I2S3_CH2", "DL3_CH2", "DL3"}, + + {"I2S3", NULL, "I2S3_CH1"}, + {"I2S3", NULL, "I2S3_CH2"}, + + {"I2S3", NULL, "I2S0_EN", mtk_afe_i2s_share_connect}, + {"I2S3", NULL, "I2S1_EN", mtk_afe_i2s_share_connect}, + {"I2S3", NULL, "I2S2_EN", mtk_afe_i2s_share_connect}, + {"I2S3", NULL, "I2S3_EN"}, + {"I2S3", NULL, "I2S5_EN", mtk_afe_i2s_share_connect}, + + {"I2S3", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S3", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S3", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S3", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S3", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {I2S3_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect}, + {I2S3_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect}, + + {"I2S3", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S3", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S3", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S3", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S3", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {I2S3_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {I2S3_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, + + /* i2s5 */ + {"I2S5_CH1", "DL1_CH1", "DL1"}, + {"I2S5_CH2", "DL1_CH2", "DL1"}, + + {"I2S5_CH1", "DL2_CH1", "DL2"}, + {"I2S5_CH2", "DL2_CH2", "DL2"}, + + {"I2S5_CH1", "DL3_CH1", "DL3"}, + {"I2S5_CH2", "DL3_CH2", "DL3"}, + + {"I2S5", NULL, "I2S5_CH1"}, + {"I2S5", NULL, "I2S5_CH2"}, + + {"I2S5", NULL, "I2S0_EN", mtk_afe_i2s_share_connect}, + {"I2S5", NULL, "I2S1_EN", mtk_afe_i2s_share_connect}, + {"I2S5", NULL, "I2S2_EN", mtk_afe_i2s_share_connect}, + {"I2S5", NULL, "I2S3_EN", mtk_afe_i2s_share_connect}, + {"I2S5", NULL, "I2S5_EN"}, + + {"I2S5", NULL, I2S0_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S5", NULL, I2S1_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S5", NULL, I2S2_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S5", NULL, I2S3_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {"I2S5", NULL, I2S5_HD_EN_W_NAME, mtk_afe_i2s_hd_connect}, + {I2S5_HD_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_i2s_apll_connect}, + {I2S5_HD_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_i2s_apll_connect}, + + {"I2S5", NULL, I2S0_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S5", NULL, I2S1_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S5", NULL, I2S2_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S5", NULL, I2S3_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {"I2S5", NULL, I2S5_MCLK_EN_W_NAME, mtk_afe_i2s_mclk_connect}, + {I2S5_MCLK_EN_W_NAME, NULL, APLL1_W_NAME, mtk_afe_mclk_apll_connect}, + {I2S5_MCLK_EN_W_NAME, NULL, APLL2_W_NAME, mtk_afe_mclk_apll_connect}, +}; + +/* dai ops */ +static int mtk_dai_i2s_config(struct mtk_base_afe *afe, + struct snd_pcm_hw_params *params, + int i2s_id) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct mtk_afe_i2s_priv *i2s_priv = afe_priv->dai_priv[i2s_id]; + + unsigned int rate = params_rate(params); + unsigned int rate_reg = mt8183_rate_transform(afe->dev, + rate, i2s_id); + snd_pcm_format_t format = params_format(params); + unsigned int i2s_con = 0; + int ret = 0; + + dev_info(afe->dev, "%s(), id %d, rate %d, format %d\n", + __func__, + i2s_id, + rate, format); + + if (i2s_priv) + i2s_priv->rate = rate; + else + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + + switch (i2s_id) { + case MT8183_DAI_I2S_0: + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + I2S_MODE_MASK_SFT, rate_reg << I2S_MODE_SFT); + i2s_con = I2S_IN_PAD_IO_MUX << I2SIN_PAD_SEL_SFT; + i2s_con |= I2S_FMT_I2S << I2S_FMT_SFT; + i2s_con |= get_i2s_wlen(format) << I2S_WLEN_SFT; + regmap_update_bits(afe->regmap, AFE_I2S_CON, + 0xffffeffe, i2s_con); + break; + case MT8183_DAI_I2S_1: + i2s_con = I2S1_SEL_O28_O29 << I2S2_SEL_O03_O04_SFT; + i2s_con |= rate_reg << I2S2_OUT_MODE_SFT; + i2s_con |= I2S_FMT_I2S << I2S2_FMT_SFT; + i2s_con |= get_i2s_wlen(format) << I2S2_WLEN_SFT; + regmap_update_bits(afe->regmap, AFE_I2S_CON1, + 0xffffeffe, i2s_con); + break; + case MT8183_DAI_I2S_2: + i2s_con = 8 << I2S3_UPDATE_WORD_SFT; + i2s_con |= rate_reg << I2S3_OUT_MODE_SFT; + i2s_con |= I2S_FMT_I2S << I2S3_FMT_SFT; + i2s_con |= get_i2s_wlen(format) << I2S3_WLEN_SFT; + regmap_update_bits(afe->regmap, AFE_I2S_CON2, + 0xffffeffe, i2s_con); + break; + case MT8183_DAI_I2S_3: + i2s_con = rate_reg << I2S4_OUT_MODE_SFT; + i2s_con |= I2S_FMT_I2S << I2S4_FMT_SFT; + i2s_con |= get_i2s_wlen(format) << I2S4_WLEN_SFT; + regmap_update_bits(afe->regmap, AFE_I2S_CON3, + 0xffffeffe, i2s_con); + break; + case MT8183_DAI_I2S_5: + i2s_con = rate_reg << I2S5_OUT_MODE_SFT; + i2s_con |= I2S_FMT_I2S << I2S5_FMT_SFT; + i2s_con |= get_i2s_wlen(format) << I2S5_WLEN_SFT; + regmap_update_bits(afe->regmap, AFE_I2S_CON4, + 0xffffeffe, i2s_con); + break; + default: + dev_warn(afe->dev, "%s(), id %d not support\n", + __func__, i2s_id); + return -EINVAL; + } + + /* set share i2s */ + if (i2s_priv && i2s_priv->share_i2s_id >= 0) + ret = mtk_dai_i2s_config(afe, params, i2s_priv->share_i2s_id); + + return ret; +} + +static int mtk_dai_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + return mtk_dai_i2s_config(afe, params, dai->id); +} + +static int mtk_dai_i2s_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dai->dev); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct mtk_afe_i2s_priv *i2s_priv = afe_priv->dai_priv[dai->id]; + int apll; + int apll_rate; + + if (!i2s_priv) { + dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__); + return -EINVAL; + } + + if (dir != SND_SOC_CLOCK_OUT) { + dev_warn(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__); + return -EINVAL; + } + + dev_info(afe->dev, "%s(), freq %d\n", __func__, freq); + + apll = mt8183_get_apll_by_rate(afe, freq); + apll_rate = mt8183_get_apll_rate(afe, apll); + + if (freq > apll_rate) { + dev_warn(afe->dev, "%s(), freq > apll rate", __func__); + return -EINVAL; + } + + if (apll_rate % freq != 0) { + dev_warn(afe->dev, "%s(), APLL cannot generate freq Hz", + __func__); + return -EINVAL; + } + + i2s_priv->mclk_rate = freq; + i2s_priv->mclk_apll = apll; + + if (i2s_priv->share_i2s_id > 0) { + struct mtk_afe_i2s_priv *share_i2s_priv; + + share_i2s_priv = afe_priv->dai_priv[i2s_priv->share_i2s_id]; + if (!share_i2s_priv) { + dev_warn(afe->dev, "%s(), share_i2s_priv == NULL", + __func__); + return -EINVAL; + } + + share_i2s_priv->mclk_rate = i2s_priv->mclk_rate; + share_i2s_priv->mclk_apll = i2s_priv->mclk_apll; + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_i2s_ops = { + .hw_params = mtk_dai_i2s_hw_params, + .set_sysclk = mtk_dai_i2s_set_sysclk, +}; + +/* dai driver */ +#define MTK_I2S_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = { + { + .name = "I2S0", + .id = MT8183_DAI_I2S_0, + .capture = { + .stream_name = "I2S0", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_I2S_RATES, + .formats = MTK_I2S_FORMATS, + }, + .ops = &mtk_dai_i2s_ops, + }, + { + .name = "I2S1", + .id = MT8183_DAI_I2S_1, + .playback = { + .stream_name = "I2S1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_I2S_RATES, + .formats = MTK_I2S_FORMATS, + }, + .ops = &mtk_dai_i2s_ops, + }, + { + .name = "I2S2", + .id = MT8183_DAI_I2S_2, + .capture = { + .stream_name = "I2S2", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_I2S_RATES, + .formats = MTK_I2S_FORMATS, + }, + .ops = &mtk_dai_i2s_ops, + }, + { + .name = "I2S3", + .id = MT8183_DAI_I2S_3, + .playback = { + .stream_name = "I2S3", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_I2S_RATES, + .formats = MTK_I2S_FORMATS, + }, + .ops = &mtk_dai_i2s_ops, + }, + { + .name = "I2S5", + .id = MT8183_DAI_I2S_5, + .playback = { + .stream_name = "I2S5", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_I2S_RATES, + .formats = MTK_I2S_FORMATS, + }, + .ops = &mtk_dai_i2s_ops, + }, +}; + +/* this enum is merely for mtk_afe_i2s_priv declare */ +enum { + DAI_I2S0 = 0, + DAI_I2S1, + DAI_I2S2, + DAI_I2S3, + DAI_I2S5, + DAI_I2S_NUM, +}; + +static const struct mtk_afe_i2s_priv mt8183_i2s_priv[DAI_I2S_NUM] = { + [DAI_I2S0] = { + .id = MT8183_DAI_I2S_0, + .mclk_id = MT8183_I2S0_MCK, + .share_property_name = "i2s0-share", + .share_i2s_id = -1, + }, + [DAI_I2S1] = { + .id = MT8183_DAI_I2S_1, + .mclk_id = MT8183_I2S1_MCK, + .share_property_name = "i2s1-share", + .share_i2s_id = -1, + }, + [DAI_I2S2] = { + .id = MT8183_DAI_I2S_2, + .mclk_id = MT8183_I2S2_MCK, + .share_property_name = "i2s2-share", + .share_i2s_id = -1, + }, + [DAI_I2S3] = { + .id = MT8183_DAI_I2S_3, + .mclk_id = MT8183_I2S3_MCK, + .share_property_name = "i2s3-share", + .share_i2s_id = -1, + }, + [DAI_I2S5] = { + .id = MT8183_DAI_I2S_5, + .mclk_id = MT8183_I2S5_MCK, + .share_property_name = "i2s5-share", + .share_i2s_id = -1, + }, +}; + +static int mt8183_dai_i2s_get_share(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + const struct device_node *of_node = afe->dev->of_node; + const char *of_str; + const char *property_name; + struct mtk_afe_i2s_priv *i2s_priv; + int i; + + for (i = 0; i < DAI_I2S_NUM; i++) { + i2s_priv = afe_priv->dai_priv[mt8183_i2s_priv[i].id]; + property_name = mt8183_i2s_priv[i].share_property_name; + if (of_property_read_string(of_node, property_name, &of_str)) + continue; + i2s_priv->share_i2s_id = get_i2s_id_by_name(afe, of_str); + } + + return 0; +} + +static int mt8183_dai_i2s_set_priv(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct mtk_afe_i2s_priv *i2s_priv; + int i; + + for (i = 0; i < DAI_I2S_NUM; i++) { + i2s_priv = devm_kzalloc(afe->dev, + sizeof(struct mtk_afe_i2s_priv), + GFP_KERNEL); + if (!i2s_priv) + return -ENOMEM; + + memcpy(i2s_priv, &mt8183_i2s_priv[i], + sizeof(struct mtk_afe_i2s_priv)); + + afe_priv->dai_priv[mt8183_i2s_priv[i].id] = i2s_priv; + } + + return 0; +} + +int mt8183_dai_i2s_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + int ret; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_i2s_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver); + + dai->controls = mtk_dai_i2s_controls; + dai->num_controls = ARRAY_SIZE(mtk_dai_i2s_controls); + dai->dapm_widgets = mtk_dai_i2s_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets); + dai->dapm_routes = mtk_dai_i2s_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes); + + /* set all dai i2s private data */ + ret = mt8183_dai_i2s_set_priv(afe); + if (ret) + return ret; + + /* parse share i2s */ + ret = mt8183_dai_i2s_get_share(afe); + if (ret) + return ret; + + return 0; +} diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c new file mode 100644 index 000000000000..bc3ba3228f08 --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI I2S Control +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt8183-afe-common.h" +#include "mt8183-interconnection.h" +#include "mt8183-reg.h" + +enum AUD_TX_LCH_RPT { + AUD_TX_LCH_RPT_NO_REPEAT = 0, + AUD_TX_LCH_RPT_REPEAT = 1 +}; + +enum AUD_VBT_16K_MODE { + AUD_VBT_16K_MODE_DISABLE = 0, + AUD_VBT_16K_MODE_ENABLE = 1 +}; + +enum AUD_EXT_MODEM { + AUD_EXT_MODEM_SELECT_INTERNAL = 0, + AUD_EXT_MODEM_SELECT_EXTERNAL = 1 +}; + +enum AUD_PCM_SYNC_TYPE { + /* bck sync length = 1 */ + AUD_PCM_ONE_BCK_CYCLE_SYNC = 0, + /* bck sync length = PCM_INTF_CON1[9:13] */ + AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1 +}; + +enum AUD_BT_MODE { + AUD_BT_MODE_DUAL_MIC_ON_TX = 0, + AUD_BT_MODE_SINGLE_MIC_ON_TX = 1 +}; + +enum AUD_PCM_AFIFO_SRC { + /* slave mode & external modem uses different crystal */ + AUD_PCM_AFIFO_ASRC = 0, + /* slave mode & external modem uses the same crystal */ + AUD_PCM_AFIFO_AFIFO = 1 +}; + +enum AUD_PCM_CLOCK_SOURCE { + AUD_PCM_CLOCK_MASTER_MODE = 0, + AUD_PCM_CLOCK_SLAVE_MODE = 1 +}; + +enum AUD_PCM_WLEN { + AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0, + AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1 +}; + +enum AUD_PCM_MODE { + AUD_PCM_MODE_PCM_MODE_8K = 0, + AUD_PCM_MODE_PCM_MODE_16K = 1, + AUD_PCM_MODE_PCM_MODE_32K = 2, + AUD_PCM_MODE_PCM_MODE_48K = 3, +}; + +enum AUD_PCM_FMT { + AUD_PCM_FMT_I2S = 0, + AUD_PCM_FMT_EIAJ = 1, + AUD_PCM_FMT_PCM_MODE_A = 2, + AUD_PCM_FMT_PCM_MODE_B = 3 +}; + +enum AUD_BCLK_OUT_INV { + AUD_BCLK_OUT_INV_NO_INVERSE = 0, + AUD_BCLK_OUT_INV_INVERSE = 1 +}; + +enum AUD_PCM_EN { + AUD_PCM_EN_DISABLE = 0, + AUD_PCM_EN_ENABLE = 1 +}; + +/* dai component */ +static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN7, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN7, + I_DL2_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN8, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN8, + I_DL2_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_1_playback_ch4_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN27, + I_DL1_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_2_playback_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN17, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN17, + I_DL2_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_2_playback_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN18, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN18, + I_DL2_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_2_playback_ch4_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN24, + I_DL1_CH1, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { + /* inter-connections */ + SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0, + mtk_pcm_1_playback_ch1_mix, + ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)), + SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0, + mtk_pcm_1_playback_ch2_mix, + ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)), + SND_SOC_DAPM_MIXER("PCM_1_PB_CH4", SND_SOC_NOPM, 0, 0, + mtk_pcm_1_playback_ch4_mix, + ARRAY_SIZE(mtk_pcm_1_playback_ch4_mix)), + SND_SOC_DAPM_MIXER("PCM_2_PB_CH1", SND_SOC_NOPM, 0, 0, + mtk_pcm_2_playback_ch1_mix, + ARRAY_SIZE(mtk_pcm_2_playback_ch1_mix)), + SND_SOC_DAPM_MIXER("PCM_2_PB_CH2", SND_SOC_NOPM, 0, 0, + mtk_pcm_2_playback_ch2_mix, + ARRAY_SIZE(mtk_pcm_2_playback_ch2_mix)), + SND_SOC_DAPM_MIXER("PCM_2_PB_CH4", SND_SOC_NOPM, 0, 0, + mtk_pcm_2_playback_ch4_mix, + ARRAY_SIZE(mtk_pcm_2_playback_ch4_mix)), + + SND_SOC_DAPM_SUPPLY("PCM_1_EN", PCM_INTF_CON1, PCM_EN_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY("PCM_2_EN", PCM2_INTF_CON, PCM2_EN_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_INPUT("MD1_TO_AFE"), + SND_SOC_DAPM_INPUT("MD2_TO_AFE"), + SND_SOC_DAPM_OUTPUT("AFE_TO_MD1"), + SND_SOC_DAPM_OUTPUT("AFE_TO_MD2"), +}; + +static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { + {"PCM 1 Playback", NULL, "PCM_1_PB_CH1"}, + {"PCM 1 Playback", NULL, "PCM_1_PB_CH2"}, + {"PCM 1 Playback", NULL, "PCM_1_PB_CH4"}, + {"PCM 2 Playback", NULL, "PCM_2_PB_CH1"}, + {"PCM 2 Playback", NULL, "PCM_2_PB_CH2"}, + {"PCM 2 Playback", NULL, "PCM_2_PB_CH4"}, + + {"PCM 1 Playback", NULL, "PCM_1_EN"}, + {"PCM 2 Playback", NULL, "PCM_2_EN"}, + {"PCM 1 Capture", NULL, "PCM_1_EN"}, + {"PCM 2 Capture", NULL, "PCM_2_EN"}, + + {"AFE_TO_MD1", NULL, "PCM 2 Playback"}, + {"AFE_TO_MD2", NULL, "PCM 1 Playback"}, + {"PCM 2 Capture", NULL, "MD1_TO_AFE"}, + {"PCM 1 Capture", NULL, "MD2_TO_AFE"}, + + {"PCM_1_PB_CH1", "DL2_CH1", "DL2"}, + {"PCM_1_PB_CH2", "DL2_CH2", "DL2"}, + {"PCM_1_PB_CH4", "DL1_CH1", "DL1"}, + {"PCM_2_PB_CH1", "DL2_CH1", "DL2"}, + {"PCM_2_PB_CH2", "DL2_CH2", "DL2"}, + {"PCM_2_PB_CH4", "DL1_CH1", "DL1"}, +}; + +/* dai ops */ +static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int rate_reg = mt8183_rate_transform(afe->dev, rate, dai->id); + unsigned int pcm_con = 0; + + dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d, rate_reg %d, widget active p %d, c %d\n", + __func__, + dai->id, + substream->stream, + rate, + rate_reg, + dai->playback_widget->active, + dai->capture_widget->active); + + if (dai->playback_widget->active || dai->capture_widget->active) + return 0; + + switch (dai->id) { + case MT8183_DAI_PCM_1: + pcm_con |= AUD_BCLK_OUT_INV_NO_INVERSE << PCM_BCLK_OUT_INV_SFT; + pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT; + pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT; + pcm_con |= AUD_EXT_MODEM_SELECT_INTERNAL << PCM_EXT_MODEM_SFT; + pcm_con |= 0 << PCM_SYNC_LENGTH_SFT; + pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT; + pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT; + pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT; + pcm_con |= AUD_PCM_CLOCK_SLAVE_MODE << PCM_SLAVE_SFT; + pcm_con |= rate_reg << PCM_MODE_SFT; + pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM_FMT_SFT; + + regmap_update_bits(afe->regmap, PCM_INTF_CON1, + 0xfffffffe, pcm_con); + break; + case MT8183_DAI_PCM_2: + pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM2_TX_LCH_RPT_SFT; + pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM2_VBT_16K_MODE_SFT; + pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM2_BT_MODE_SFT; + pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM2_AFIFO_SFT; + pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM2_WLEN_SFT; + pcm_con |= rate_reg << PCM2_MODE_SFT; + pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM2_FMT_SFT; + + regmap_update_bits(afe->regmap, PCM2_INTF_CON, + 0xfffffffe, pcm_con); + break; + default: + dev_warn(afe->dev, "%s(), id %d not support\n", + __func__, dai->id); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_pcm_ops = { + .hw_params = mtk_dai_pcm_hw_params, +}; + +/* dai driver */ +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000) + +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = { + { + .name = "PCM 1", + .id = MT8183_DAI_PCM_1, + .playback = { + .stream_name = "PCM 1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .capture = { + .stream_name = "PCM 1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_dai_pcm_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "PCM 2", + .id = MT8183_DAI_PCM_2, + .playback = { + .stream_name = "PCM 2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .capture = { + .stream_name = "PCM 2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_dai_pcm_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, +}; + +int mt8183_dai_pcm_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_pcm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); + + dai->dapm_widgets = mtk_dai_pcm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); + dai->dapm_routes = mtk_dai_pcm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); + + return 0; +} diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-tdm.c b/sound/soc/mediatek/mt8183/mt8183-dai-tdm.c new file mode 100644 index 000000000000..8983d54a9b67 --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-dai-tdm.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI TDM Control +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt8183-afe-clk.h" +#include "mt8183-afe-common.h" +#include "mt8183-interconnection.h" +#include "mt8183-reg.h" + +struct mtk_afe_tdm_priv { + int bck_id; + int bck_rate; + + int mclk_id; + int mclk_multiple; /* according to sample rate */ + int mclk_rate; + int mclk_apll; +}; + +enum { + TDM_WLEN_16_BIT = 1, + TDM_WLEN_32_BIT = 2, +}; + +enum { + TDM_CHANNEL_BCK_16 = 0, + TDM_CHANNEL_BCK_24 = 1, + TDM_CHANNEL_BCK_32 = 2, +}; + +enum { + TDM_CHANNEL_NUM_2 = 0, + TDM_CHANNEL_NUM_4 = 1, + TDM_CHANNEL_NUM_8 = 2, +}; + +enum { + TDM_CH_START_O30_O31 = 0, + TDM_CH_START_O32_O33, + TDM_CH_START_O34_O35, + TDM_CH_START_O36_O37, + TDM_CH_ZERO, +}; + +enum { + HDMI_BIT_WIDTH_16_BIT = 0, + HDMI_BIT_WIDTH_32_BIT = 1, +}; + +static unsigned int get_hdmi_wlen(snd_pcm_format_t format) +{ + return snd_pcm_format_physical_width(format) <= 16 ? + HDMI_BIT_WIDTH_16_BIT : HDMI_BIT_WIDTH_32_BIT; +} + +static unsigned int get_tdm_wlen(snd_pcm_format_t format) +{ + return snd_pcm_format_physical_width(format) <= 16 ? + TDM_WLEN_16_BIT : TDM_WLEN_32_BIT; +} + +static unsigned int get_tdm_channel_bck(snd_pcm_format_t format) +{ + return snd_pcm_format_physical_width(format) <= 16 ? + TDM_CHANNEL_BCK_16 : TDM_CHANNEL_BCK_32; +} + +static unsigned int get_tdm_lrck_width(snd_pcm_format_t format) +{ + return snd_pcm_format_physical_width(format) - 1; +} + +static unsigned int get_tdm_ch(unsigned int ch) +{ + switch (ch) { + case 1: + case 2: + return TDM_CHANNEL_NUM_2; + case 3: + case 4: + return TDM_CHANNEL_NUM_4; + case 5: + case 6: + case 7: + case 8: + default: + return TDM_CHANNEL_NUM_8; + } +} + +/* interconnection */ +enum { + HDMI_CONN_CH0 = 0, + HDMI_CONN_CH1, + HDMI_CONN_CH2, + HDMI_CONN_CH3, + HDMI_CONN_CH4, + HDMI_CONN_CH5, + HDMI_CONN_CH6, + HDMI_CONN_CH7, +}; + +static const char *const hdmi_conn_mux_map[] = { + "CH0", "CH1", "CH2", "CH3", + "CH4", "CH5", "CH6", "CH7", +}; + +static int hdmi_conn_mux_map_value[] = { + HDMI_CONN_CH0, + HDMI_CONN_CH1, + HDMI_CONN_CH2, + HDMI_CONN_CH3, + HDMI_CONN_CH4, + HDMI_CONN_CH5, + HDMI_CONN_CH6, + HDMI_CONN_CH7, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum, + AFE_HDMI_CONN0, + HDMI_O_0_SFT, + HDMI_O_0_MASK, + hdmi_conn_mux_map, + hdmi_conn_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch0_mux_control = + SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum, + AFE_HDMI_CONN0, + HDMI_O_1_SFT, + HDMI_O_1_MASK, + hdmi_conn_mux_map, + hdmi_conn_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch1_mux_control = + SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum, + AFE_HDMI_CONN0, + HDMI_O_2_SFT, + HDMI_O_2_MASK, + hdmi_conn_mux_map, + hdmi_conn_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch2_mux_control = + SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum, + AFE_HDMI_CONN0, + HDMI_O_3_SFT, + HDMI_O_3_MASK, + hdmi_conn_mux_map, + hdmi_conn_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch3_mux_control = + SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum, + AFE_HDMI_CONN0, + HDMI_O_4_SFT, + HDMI_O_4_MASK, + hdmi_conn_mux_map, + hdmi_conn_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch4_mux_control = + SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum, + AFE_HDMI_CONN0, + HDMI_O_5_SFT, + HDMI_O_5_MASK, + hdmi_conn_mux_map, + hdmi_conn_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch5_mux_control = + SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum, + AFE_HDMI_CONN0, + HDMI_O_6_SFT, + HDMI_O_6_MASK, + hdmi_conn_mux_map, + hdmi_conn_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch6_mux_control = + SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum, + AFE_HDMI_CONN0, + HDMI_O_7_SFT, + HDMI_O_7_MASK, + hdmi_conn_mux_map, + hdmi_conn_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch7_mux_control = + SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum); + +enum { + SUPPLY_SEQ_APLL, + SUPPLY_SEQ_TDM_MCK_EN, + SUPPLY_SEQ_TDM_BCK_EN, +}; + +static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM]; + + dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mt8183_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate); + break; + case SND_SOC_DAPM_POST_PMD: + mt8183_mck_disable(afe, tdm_priv->bck_id); + break; + default: + break; + } + + return 0; +} + +static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM]; + + dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mt8183_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate); + break; + case SND_SOC_DAPM_POST_PMD: + tdm_priv->mclk_rate = 0; + mt8183_mck_disable(afe, tdm_priv->mclk_id); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = { + SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch0_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch1_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch2_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch3_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch4_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch5_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch6_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch7_mux_control), + + SND_SOC_DAPM_CLOCK_SUPPLY("aud_tdm_clk"), + + SND_SOC_DAPM_SUPPLY_S("TDM_BCK", SUPPLY_SEQ_TDM_BCK_EN, + SND_SOC_NOPM, 0, 0, + mtk_tdm_bck_en_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("TDM_MCK", SUPPLY_SEQ_TDM_MCK_EN, + SND_SOC_NOPM, 0, 0, + mtk_tdm_mck_en_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static int mtk_afe_tdm_apll_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM]; + int cur_apll; + + /* which apll */ + cur_apll = mt8183_get_apll_by_name(afe, source->name); + + return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0; +} + +static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = { + {"HDMI_CH0_MUX", "CH0", "HDMI"}, + {"HDMI_CH0_MUX", "CH1", "HDMI"}, + {"HDMI_CH0_MUX", "CH2", "HDMI"}, + {"HDMI_CH0_MUX", "CH3", "HDMI"}, + {"HDMI_CH0_MUX", "CH4", "HDMI"}, + {"HDMI_CH0_MUX", "CH5", "HDMI"}, + {"HDMI_CH0_MUX", "CH6", "HDMI"}, + {"HDMI_CH0_MUX", "CH7", "HDMI"}, + + {"HDMI_CH1_MUX", "CH0", "HDMI"}, + {"HDMI_CH1_MUX", "CH1", "HDMI"}, + {"HDMI_CH1_MUX", "CH2", "HDMI"}, + {"HDMI_CH1_MUX", "CH3", "HDMI"}, + {"HDMI_CH1_MUX", "CH4", "HDMI"}, + {"HDMI_CH1_MUX", "CH5", "HDMI"}, + {"HDMI_CH1_MUX", "CH6", "HDMI"}, + {"HDMI_CH1_MUX", "CH7", "HDMI"}, + + {"HDMI_CH2_MUX", "CH0", "HDMI"}, + {"HDMI_CH2_MUX", "CH1", "HDMI"}, + {"HDMI_CH2_MUX", "CH2", "HDMI"}, + {"HDMI_CH2_MUX", "CH3", "HDMI"}, + {"HDMI_CH2_MUX", "CH4", "HDMI"}, + {"HDMI_CH2_MUX", "CH5", "HDMI"}, + {"HDMI_CH2_MUX", "CH6", "HDMI"}, + {"HDMI_CH2_MUX", "CH7", "HDMI"}, + + {"HDMI_CH3_MUX", "CH0", "HDMI"}, + {"HDMI_CH3_MUX", "CH1", "HDMI"}, + {"HDMI_CH3_MUX", "CH2", "HDMI"}, + {"HDMI_CH3_MUX", "CH3", "HDMI"}, + {"HDMI_CH3_MUX", "CH4", "HDMI"}, + {"HDMI_CH3_MUX", "CH5", "HDMI"}, + {"HDMI_CH3_MUX", "CH6", "HDMI"}, + {"HDMI_CH3_MUX", "CH7", "HDMI"}, + + {"HDMI_CH4_MUX", "CH0", "HDMI"}, + {"HDMI_CH4_MUX", "CH1", "HDMI"}, + {"HDMI_CH4_MUX", "CH2", "HDMI"}, + {"HDMI_CH4_MUX", "CH3", "HDMI"}, + {"HDMI_CH4_MUX", "CH4", "HDMI"}, + {"HDMI_CH4_MUX", "CH5", "HDMI"}, + {"HDMI_CH4_MUX", "CH6", "HDMI"}, + {"HDMI_CH4_MUX", "CH7", "HDMI"}, + + {"HDMI_CH5_MUX", "CH0", "HDMI"}, + {"HDMI_CH5_MUX", "CH1", "HDMI"}, + {"HDMI_CH5_MUX", "CH2", "HDMI"}, + {"HDMI_CH5_MUX", "CH3", "HDMI"}, + {"HDMI_CH5_MUX", "CH4", "HDMI"}, + {"HDMI_CH5_MUX", "CH5", "HDMI"}, + {"HDMI_CH5_MUX", "CH6", "HDMI"}, + {"HDMI_CH5_MUX", "CH7", "HDMI"}, + + {"HDMI_CH6_MUX", "CH0", "HDMI"}, + {"HDMI_CH6_MUX", "CH1", "HDMI"}, + {"HDMI_CH6_MUX", "CH2", "HDMI"}, + {"HDMI_CH6_MUX", "CH3", "HDMI"}, + {"HDMI_CH6_MUX", "CH4", "HDMI"}, + {"HDMI_CH6_MUX", "CH5", "HDMI"}, + {"HDMI_CH6_MUX", "CH6", "HDMI"}, + {"HDMI_CH6_MUX", "CH7", "HDMI"}, + + {"HDMI_CH7_MUX", "CH0", "HDMI"}, + {"HDMI_CH7_MUX", "CH1", "HDMI"}, + {"HDMI_CH7_MUX", "CH2", "HDMI"}, + {"HDMI_CH7_MUX", "CH3", "HDMI"}, + {"HDMI_CH7_MUX", "CH4", "HDMI"}, + {"HDMI_CH7_MUX", "CH5", "HDMI"}, + {"HDMI_CH7_MUX", "CH6", "HDMI"}, + {"HDMI_CH7_MUX", "CH7", "HDMI"}, + + {"TDM", NULL, "HDMI_CH0_MUX"}, + {"TDM", NULL, "HDMI_CH1_MUX"}, + {"TDM", NULL, "HDMI_CH2_MUX"}, + {"TDM", NULL, "HDMI_CH3_MUX"}, + {"TDM", NULL, "HDMI_CH4_MUX"}, + {"TDM", NULL, "HDMI_CH5_MUX"}, + {"TDM", NULL, "HDMI_CH6_MUX"}, + {"TDM", NULL, "HDMI_CH7_MUX"}, + + {"TDM", NULL, "aud_tdm_clk"}, + {"TDM", NULL, "TDM_BCK"}, + {"TDM_BCK", NULL, "TDM_MCK"}, + {"TDM_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect}, + {"TDM_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect}, +}; + +/* dai ops */ +static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe, + struct mtk_afe_tdm_priv *tdm_priv, + int freq) +{ + int apll; + int apll_rate; + + apll = mt8183_get_apll_by_rate(afe, freq); + apll_rate = mt8183_get_apll_rate(afe, apll); + + if (!freq || freq > apll_rate) { + dev_warn(afe->dev, + "%s(), freq(%d Hz) invalid\n", __func__, freq); + return -EINVAL; + } + + if (apll_rate % freq != 0) { + dev_warn(afe->dev, + "%s(), APLL cannot generate %d Hz", __func__, freq); + return -EINVAL; + } + + tdm_priv->mclk_rate = freq; + tdm_priv->mclk_apll = apll; + + return 0; +} + +static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + int tdm_id = dai->id; + struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[tdm_id]; + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + snd_pcm_format_t format = params_format(params); + unsigned int tdm_con = 0; + + /* calculate mclk_rate, if not set explicitly */ + if (!tdm_priv->mclk_rate) { + tdm_priv->mclk_rate = rate * tdm_priv->mclk_multiple; + mtk_dai_tdm_cal_mclk(afe, + tdm_priv, + tdm_priv->mclk_rate); + } + + /* calculate bck */ + tdm_priv->bck_rate = rate * + channels * + snd_pcm_format_physical_width(format); + + if (tdm_priv->bck_rate > tdm_priv->mclk_rate) + dev_warn(afe->dev, "%s(), bck_rate > mclk_rate rate", __func__); + + if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0) + dev_warn(afe->dev, "%s(), bck cannot generate", __func__); + + dev_info(afe->dev, "%s(), id %d, rate %d, channels %d, format %d, mclk_rate %d, bck_rate %d\n", + __func__, + tdm_id, rate, channels, format, + tdm_priv->mclk_rate, tdm_priv->bck_rate); + + /* set tdm */ + tdm_con = 1 << BCK_INVERSE_SFT; + tdm_con |= 1 << LRCK_INVERSE_SFT; + tdm_con |= 1 << DELAY_DATA_SFT; + tdm_con |= 1 << LEFT_ALIGN_SFT; + tdm_con |= get_tdm_wlen(format) << WLEN_SFT; + tdm_con |= get_tdm_ch(channels) << CHANNEL_NUM_SFT; + tdm_con |= get_tdm_channel_bck(format) << CHANNEL_BCK_CYCLES_SFT; + tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT; + regmap_write(afe->regmap, AFE_TDM_CON1, tdm_con); + + switch (channels) { + case 1: + case 2: + tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT; + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT; + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT; + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT; + break; + case 3: + case 4: + tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT; + tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT; + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT; + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT; + break; + case 5: + case 6: + tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT; + tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT; + tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT; + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT; + break; + case 7: + case 8: + tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT; + tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT; + tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT; + tdm_con |= TDM_CH_START_O36_O37 << ST_CH_PAIR_SOUT3_SFT; + break; + default: + tdm_con = 0; + } + regmap_write(afe->regmap, AFE_TDM_CON2, tdm_con); + + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, + AFE_HDMI_OUT_CH_NUM_MASK_SFT, + channels << AFE_HDMI_OUT_CH_NUM_SFT); + + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, + AFE_HDMI_OUT_BIT_WIDTH_MASK_SFT, + get_hdmi_wlen(format) << AFE_HDMI_OUT_BIT_WIDTH_SFT); + return 0; +} + +static int mtk_dai_tdm_trigger(struct snd_pcm_substream *substream, + int cmd, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + /* enable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, + AFE_HDMI_OUT_ON_MASK_SFT, + 0x1 << AFE_HDMI_OUT_ON_SFT); + /* enable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, + TDM_EN_MASK_SFT, 0x1 << TDM_EN_SFT); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + /* disable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, + TDM_EN_MASK_SFT, 0); + /* disable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, + AFE_HDMI_OUT_ON_MASK_SFT, + 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dai->dev); + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id]; + + if (!tdm_priv) { + dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__); + return -EINVAL; + } + + if (dir != SND_SOC_CLOCK_OUT) { + dev_warn(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__); + return -EINVAL; + } + + dev_info(afe->dev, "%s(), freq %d\n", __func__, freq); + + return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq); +} + +static const struct snd_soc_dai_ops mtk_dai_tdm_ops = { + .hw_params = mtk_dai_tdm_hw_params, + .trigger = mtk_dai_tdm_trigger, + .set_sysclk = mtk_dai_tdm_set_sysclk, +}; + +/* dai driver */ +#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = { + { + .name = "TDM", + .id = MT8183_DAI_TDM, + .playback = { + .stream_name = "TDM", + .channels_min = 2, + .channels_max = 8, + .rates = MTK_TDM_RATES, + .formats = MTK_TDM_FORMATS, + }, + .ops = &mtk_dai_tdm_ops, + }, +}; + +int mt8183_dai_tdm_register(struct mtk_base_afe *afe) +{ + struct mt8183_afe_private *afe_priv = afe->platform_priv; + struct mtk_afe_tdm_priv *tdm_priv; + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_tdm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver); + + dai->dapm_widgets = mtk_dai_tdm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets); + dai->dapm_routes = mtk_dai_tdm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes); + + tdm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_tdm_priv), + GFP_KERNEL); + if (!tdm_priv) + return -ENOMEM; + + tdm_priv->mclk_multiple = 128; + tdm_priv->bck_id = MT8183_I2S4_BCK; + tdm_priv->mclk_id = MT8183_I2S4_MCK; + + afe_priv->dai_priv[MT8183_DAI_TDM] = tdm_priv; + return 0; +} diff --git a/sound/soc/mediatek/mt8183/mt8183-interconnection.h b/sound/soc/mediatek/mt8183/mt8183-interconnection.h new file mode 100644 index 000000000000..6332f5f3e987 --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-interconnection.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Mediatek MT8183 audio driver interconnection definition + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef _MT8183_INTERCONNECTION_H_ +#define _MT8183_INTERCONNECTION_H_ + +#define I_I2S0_CH1 0 +#define I_I2S0_CH2 1 +#define I_ADDA_UL_CH1 3 +#define I_ADDA_UL_CH2 4 +#define I_DL1_CH1 5 +#define I_DL1_CH2 6 +#define I_DL2_CH1 7 +#define I_DL2_CH2 8 +#define I_PCM_1_CAP_CH1 9 +#define I_GAIN1_OUT_CH1 10 +#define I_GAIN1_OUT_CH2 11 +#define I_GAIN2_OUT_CH1 12 +#define I_GAIN2_OUT_CH2 13 +#define I_PCM_2_CAP_CH1 14 +#define I_PCM_2_CAP_CH2 21 +#define I_PCM_1_CAP_CH2 22 +#define I_DL3_CH1 23 +#define I_DL3_CH2 24 +#define I_I2S2_CH1 25 +#define I_I2S2_CH2 26 + +#endif diff --git a/sound/soc/mediatek/mt8183/mt8183-reg.h b/sound/soc/mediatek/mt8183/mt8183-reg.h new file mode 100644 index 000000000000..e0482f2826da --- /dev/null +++ b/sound/soc/mediatek/mt8183/mt8183-reg.h @@ -0,0 +1,1666 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8183-reg.h -- Mediatek 8183 audio driver reg definition + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef _MT8183_REG_H_ +#define _MT8183_REG_H_ + +#define AUDIO_TOP_CON0 0x0000 +#define AUDIO_TOP_CON1 0x0004 +#define AUDIO_TOP_CON3 0x000c +#define AFE_DAC_CON0 0x0010 +#define AFE_DAC_CON1 0x0014 +#define AFE_I2S_CON 0x0018 +#define AFE_DAIBT_CON0 0x001c +#define AFE_CONN0 0x0020 +#define AFE_CONN1 0x0024 +#define AFE_CONN2 0x0028 +#define AFE_CONN3 0x002c +#define AFE_CONN4 0x0030 +#define AFE_I2S_CON1 0x0034 +#define AFE_I2S_CON2 0x0038 +#define AFE_MRGIF_CON 0x003c +#define AFE_DL1_BASE 0x0040 +#define AFE_DL1_CUR 0x0044 +#define AFE_DL1_END 0x0048 +#define AFE_I2S_CON3 0x004c +#define AFE_DL2_BASE 0x0050 +#define AFE_DL2_CUR 0x0054 +#define AFE_DL2_END 0x0058 +#define AFE_CONN5 0x005c +#define AFE_CONN_24BIT 0x006c +#define AFE_AWB_BASE 0x0070 +#define AFE_AWB_END 0x0078 +#define AFE_AWB_CUR 0x007c +#define AFE_VUL_BASE 0x0080 +#define AFE_VUL_END 0x0088 +#define AFE_VUL_CUR 0x008c +#define AFE_CONN6 0x00bc +#define AFE_MEMIF_MSB 0x00cc +#define AFE_MEMIF_MON0 0x00d0 +#define AFE_MEMIF_MON1 0x00d4 +#define AFE_MEMIF_MON2 0x00d8 +#define AFE_MEMIF_MON3 0x00dc +#define AFE_MEMIF_MON4 0x00e0 +#define AFE_MEMIF_MON5 0x00e4 +#define AFE_MEMIF_MON6 0x00e8 +#define AFE_MEMIF_MON7 0x00ec +#define AFE_MEMIF_MON8 0x00f0 +#define AFE_MEMIF_MON9 0x00f4 +#define AFE_ADDA_DL_SRC2_CON0 0x0108 +#define AFE_ADDA_DL_SRC2_CON1 0x010c +#define AFE_ADDA_UL_SRC_CON0 0x0114 +#define AFE_ADDA_UL_SRC_CON1 0x0118 +#define AFE_ADDA_TOP_CON0 0x0120 +#define AFE_ADDA_UL_DL_CON0 0x0124 +#define AFE_ADDA_SRC_DEBUG 0x012c +#define AFE_ADDA_SRC_DEBUG_MON0 0x0130 +#define AFE_ADDA_SRC_DEBUG_MON1 0x0134 +#define AFE_ADDA_UL_SRC_MON0 0x0148 +#define AFE_ADDA_UL_SRC_MON1 0x014c +#define AFE_SIDETONE_DEBUG 0x01d0 +#define AFE_SIDETONE_MON 0x01d4 +#define AFE_SINEGEN_CON2 0x01dc +#define AFE_SIDETONE_CON0 0x01e0 +#define AFE_SIDETONE_COEFF 0x01e4 +#define AFE_SIDETONE_CON1 0x01e8 +#define AFE_SIDETONE_GAIN 0x01ec +#define AFE_SINEGEN_CON0 0x01f0 +#define AFE_TOP_CON0 0x0200 +#define AFE_BUS_CFG 0x0240 +#define AFE_BUS_MON0 0x0244 +#define AFE_ADDA_PREDIS_CON0 0x0260 +#define AFE_ADDA_PREDIS_CON1 0x0264 +#define AFE_MRGIF_MON0 0x0270 +#define AFE_MRGIF_MON1 0x0274 +#define AFE_MRGIF_MON2 0x0278 +#define AFE_I2S_MON 0x027c +#define AFE_ADDA_IIR_COEF_02_01 0x0290 +#define AFE_ADDA_IIR_COEF_04_03 0x0294 +#define AFE_ADDA_IIR_COEF_06_05 0x0298 +#define AFE_ADDA_IIR_COEF_08_07 0x029c +#define AFE_ADDA_IIR_COEF_10_09 0x02a0 +#define AFE_DAC_CON2 0x02e0 +#define AFE_IRQ_MCU_CON1 0x02e4 +#define AFE_IRQ_MCU_CON2 0x02e8 +#define AFE_DAC_MON 0x02ec +#define AFE_VUL2_BASE 0x02f0 +#define AFE_VUL2_END 0x02f8 +#define AFE_VUL2_CUR 0x02fc +#define AFE_IRQ_MCU_CNT0 0x0300 +#define AFE_IRQ_MCU_CNT6 0x0304 +#define AFE_IRQ_MCU_CNT8 0x0308 +#define AFE_IRQ_MCU_EN1 0x030c +#define AFE_IRQ0_MCU_CNT_MON 0x0310 +#define AFE_IRQ6_MCU_CNT_MON 0x0314 +#define AFE_MOD_DAI_BASE 0x0330 +#define AFE_MOD_DAI_END 0x0338 +#define AFE_MOD_DAI_CUR 0x033c +#define AFE_VUL_D2_BASE 0x0350 +#define AFE_VUL_D2_END 0x0358 +#define AFE_VUL_D2_CUR 0x035c +#define AFE_DL3_BASE 0x0360 +#define AFE_DL3_CUR 0x0364 +#define AFE_DL3_END 0x0368 +#define AFE_HDMI_OUT_CON0 0x0370 +#define AFE_HDMI_OUT_BASE 0x0374 +#define AFE_HDMI_OUT_CUR 0x0378 +#define AFE_HDMI_OUT_END 0x037c +#define AFE_HDMI_CONN0 0x0390 +#define AFE_IRQ3_MCU_CNT_MON 0x0398 +#define AFE_IRQ4_MCU_CNT_MON 0x039c +#define AFE_IRQ_MCU_CON0 0x03a0 +#define AFE_IRQ_MCU_STATUS 0x03a4 +#define AFE_IRQ_MCU_CLR 0x03a8 +#define AFE_IRQ_MCU_CNT1 0x03ac +#define AFE_IRQ_MCU_CNT2 0x03b0 +#define AFE_IRQ_MCU_EN 0x03b4 +#define AFE_IRQ_MCU_MON2 0x03b8 +#define AFE_IRQ_MCU_CNT5 0x03bc +#define AFE_IRQ1_MCU_CNT_MON 0x03c0 +#define AFE_IRQ2_MCU_CNT_MON 0x03c4 +#define AFE_IRQ1_MCU_EN_CNT_MON 0x03c8 +#define AFE_IRQ5_MCU_CNT_MON 0x03cc +#define AFE_MEMIF_MINLEN 0x03d0 +#define AFE_MEMIF_MAXLEN 0x03d4 +#define AFE_MEMIF_PBUF_SIZE 0x03d8 +#define AFE_IRQ_MCU_CNT7 0x03dc +#define AFE_IRQ7_MCU_CNT_MON 0x03e0 +#define AFE_IRQ_MCU_CNT3 0x03e4 +#define AFE_IRQ_MCU_CNT4 0x03e8 +#define AFE_IRQ_MCU_CNT11 0x03ec +#define AFE_APLL1_TUNER_CFG 0x03f0 +#define AFE_APLL2_TUNER_CFG 0x03f4 +#define AFE_MEMIF_HD_MODE 0x03f8 +#define AFE_MEMIF_HDALIGN 0x03fc +#define AFE_CONN33 0x0408 +#define AFE_IRQ_MCU_CNT12 0x040c +#define AFE_GAIN1_CON0 0x0410 +#define AFE_GAIN1_CON1 0x0414 +#define AFE_GAIN1_CON2 0x0418 +#define AFE_GAIN1_CON3 0x041c +#define AFE_CONN7 0x0420 +#define AFE_GAIN1_CUR 0x0424 +#define AFE_GAIN2_CON0 0x0428 +#define AFE_GAIN2_CON1 0x042c +#define AFE_GAIN2_CON2 0x0430 +#define AFE_GAIN2_CON3 0x0434 +#define AFE_CONN8 0x0438 +#define AFE_GAIN2_CUR 0x043c +#define AFE_CONN9 0x0440 +#define AFE_CONN10 0x0444 +#define AFE_CONN11 0x0448 +#define AFE_CONN12 0x044c +#define AFE_CONN13 0x0450 +#define AFE_CONN14 0x0454 +#define AFE_CONN15 0x0458 +#define AFE_CONN16 0x045c +#define AFE_CONN17 0x0460 +#define AFE_CONN18 0x0464 +#define AFE_CONN19 0x0468 +#define AFE_CONN20 0x046c +#define AFE_CONN21 0x0470 +#define AFE_CONN22 0x0474 +#define AFE_CONN23 0x0478 +#define AFE_CONN24 0x047c +#define AFE_CONN_RS 0x0494 +#define AFE_CONN_DI 0x0498 +#define AFE_CONN25 0x04b0 +#define AFE_CONN26 0x04b4 +#define AFE_CONN27 0x04b8 +#define AFE_CONN28 0x04bc +#define AFE_CONN29 0x04c0 +#define AFE_CONN30 0x04c4 +#define AFE_CONN31 0x04c8 +#define AFE_CONN32 0x04cc +#define AFE_SRAM_DELSEL_CON0 0x04f0 +#define AFE_SRAM_DELSEL_CON2 0x04f8 +#define AFE_SRAM_DELSEL_CON3 0x04fc +#define AFE_ASRC_2CH_CON12 0x0528 +#define AFE_ASRC_2CH_CON13 0x052c +#define PCM_INTF_CON1 0x0530 +#define PCM_INTF_CON2 0x0538 +#define PCM2_INTF_CON 0x053c +#define AFE_TDM_CON1 0x0548 +#define AFE_TDM_CON2 0x054c +#define AFE_CONN34 0x0580 +#define FPGA_CFG0 0x05b0 +#define FPGA_CFG1 0x05b4 +#define FPGA_CFG2 0x05c0 +#define FPGA_CFG3 0x05c4 +#define AUDIO_TOP_DBG_CON 0x05c8 +#define AUDIO_TOP_DBG_MON0 0x05cc +#define AUDIO_TOP_DBG_MON1 0x05d0 +#define AFE_IRQ8_MCU_CNT_MON 0x05e4 +#define AFE_IRQ11_MCU_CNT_MON 0x05e8 +#define AFE_IRQ12_MCU_CNT_MON 0x05ec +#define AFE_GENERAL_REG0 0x0800 +#define AFE_GENERAL_REG1 0x0804 +#define AFE_GENERAL_REG2 0x0808 +#define AFE_GENERAL_REG3 0x080c +#define AFE_GENERAL_REG4 0x0810 +#define AFE_GENERAL_REG5 0x0814 +#define AFE_GENERAL_REG6 0x0818 +#define AFE_GENERAL_REG7 0x081c +#define AFE_GENERAL_REG8 0x0820 +#define AFE_GENERAL_REG9 0x0824 +#define AFE_GENERAL_REG10 0x0828 +#define AFE_GENERAL_REG11 0x082c +#define AFE_GENERAL_REG12 0x0830 +#define AFE_GENERAL_REG13 0x0834 +#define AFE_GENERAL_REG14 0x0838 +#define AFE_GENERAL_REG15 0x083c +#define AFE_CBIP_CFG0 0x0840 +#define AFE_CBIP_MON0 0x0844 +#define AFE_CBIP_SLV_MUX_MON0 0x0848 +#define AFE_CBIP_SLV_DECODER_MON0 0x084c +#define AFE_CONN0_1 0x0900 +#define AFE_CONN1_1 0x0904 +#define AFE_CONN2_1 0x0908 +#define AFE_CONN3_1 0x090c +#define AFE_CONN4_1 0x0910 +#define AFE_CONN5_1 0x0914 +#define AFE_CONN6_1 0x0918 +#define AFE_CONN7_1 0x091c +#define AFE_CONN8_1 0x0920 +#define AFE_CONN9_1 0x0924 +#define AFE_CONN10_1 0x0928 +#define AFE_CONN11_1 0x092c +#define AFE_CONN12_1 0x0930 +#define AFE_CONN13_1 0x0934 +#define AFE_CONN14_1 0x0938 +#define AFE_CONN15_1 0x093c +#define AFE_CONN16_1 0x0940 +#define AFE_CONN17_1 0x0944 +#define AFE_CONN18_1 0x0948 +#define AFE_CONN19_1 0x094c +#define AFE_CONN20_1 0x0950 +#define AFE_CONN21_1 0x0954 +#define AFE_CONN22_1 0x0958 +#define AFE_CONN23_1 0x095c +#define AFE_CONN24_1 0x0960 +#define AFE_CONN25_1 0x0964 +#define AFE_CONN26_1 0x0968 +#define AFE_CONN27_1 0x096c +#define AFE_CONN28_1 0x0970 +#define AFE_CONN29_1 0x0974 +#define AFE_CONN30_1 0x0978 +#define AFE_CONN31_1 0x097c +#define AFE_CONN32_1 0x0980 +#define AFE_CONN33_1 0x0984 +#define AFE_CONN34_1 0x0988 +#define AFE_CONN_RS_1 0x098c +#define AFE_CONN_DI_1 0x0990 +#define AFE_CONN_24BIT_1 0x0994 +#define AFE_CONN_REG 0x0998 +#define AFE_CONN35 0x09a0 +#define AFE_CONN36 0x09a4 +#define AFE_CONN37 0x09a8 +#define AFE_CONN38 0x09ac +#define AFE_CONN35_1 0x09b0 +#define AFE_CONN36_1 0x09b4 +#define AFE_CONN37_1 0x09b8 +#define AFE_CONN38_1 0x09bc +#define AFE_CONN39 0x09c0 +#define AFE_CONN40 0x09c4 +#define AFE_CONN41 0x09c8 +#define AFE_CONN42 0x09cc +#define AFE_CONN39_1 0x09e0 +#define AFE_CONN40_1 0x09e4 +#define AFE_CONN41_1 0x09e8 +#define AFE_CONN42_1 0x09ec +#define AFE_I2S_CON4 0x09f8 +#define AFE_ADDA6_TOP_CON0 0x0a80 +#define AFE_ADDA6_UL_SRC_CON0 0x0a84 +#define AFE_ADD6_UL_SRC_CON1 0x0a88 +#define AFE_ADDA6_SRC_DEBUG 0x0a8c +#define AFE_ADDA6_SRC_DEBUG_MON0 0x0a90 +#define AFE_ADDA6_ULCF_CFG_02_01 0x0aa0 +#define AFE_ADDA6_ULCF_CFG_04_03 0x0aa4 +#define AFE_ADDA6_ULCF_CFG_06_05 0x0aa8 +#define AFE_ADDA6_ULCF_CFG_08_07 0x0aac +#define AFE_ADDA6_ULCF_CFG_10_09 0x0ab0 +#define AFE_ADDA6_ULCF_CFG_12_11 0x0ab4 +#define AFE_ADDA6_ULCF_CFG_14_13 0x0ab8 +#define AFE_ADDA6_ULCF_CFG_16_15 0x0abc +#define AFE_ADDA6_ULCF_CFG_18_17 0x0ac0 +#define AFE_ADDA6_ULCF_CFG_20_19 0x0ac4 +#define AFE_ADDA6_ULCF_CFG_22_21 0x0ac8 +#define AFE_ADDA6_ULCF_CFG_24_23 0x0acc +#define AFE_ADDA6_ULCF_CFG_26_25 0x0ad0 +#define AFE_ADDA6_ULCF_CFG_28_27 0x0ad4 +#define AFE_ADDA6_ULCF_CFG_30_29 0x0ad8 +#define AFE_ADD6A_UL_SRC_MON0 0x0ae4 +#define AFE_ADDA6_UL_SRC_MON1 0x0ae8 +#define AFE_CONN43 0x0af8 +#define AFE_CONN43_1 0x0afc +#define AFE_DL1_BASE_MSB 0x0b00 +#define AFE_DL1_CUR_MSB 0x0b04 +#define AFE_DL1_END_MSB 0x0b08 +#define AFE_DL2_BASE_MSB 0x0b10 +#define AFE_DL2_CUR_MSB 0x0b14 +#define AFE_DL2_END_MSB 0x0b18 +#define AFE_AWB_BASE_MSB 0x0b20 +#define AFE_AWB_END_MSB 0x0b28 +#define AFE_AWB_CUR_MSB 0x0b2c +#define AFE_VUL_BASE_MSB 0x0b30 +#define AFE_VUL_END_MSB 0x0b38 +#define AFE_VUL_CUR_MSB 0x0b3c +#define AFE_VUL2_BASE_MSB 0x0b50 +#define AFE_VUL2_END_MSB 0x0b58 +#define AFE_VUL2_CUR_MSB 0x0b5c +#define AFE_MOD_DAI_BASE_MSB 0x0b60 +#define AFE_MOD_DAI_END_MSB 0x0b68 +#define AFE_MOD_DAI_CUR_MSB 0x0b6c +#define AFE_VUL_D2_BASE_MSB 0x0b80 +#define AFE_VUL_D2_END_MSB 0x0b88 +#define AFE_VUL_D2_CUR_MSB 0x0b8c +#define AFE_DL3_BASE_MSB 0x0b90 +#define AFE_DL3_CUR_MSB 0x0b94 +#define AFE_DL3_END_MSB 0x0b98 +#define AFE_HDMI_OUT_BASE_MSB 0x0ba4 +#define AFE_HDMI_OUT_CUR_MSB 0x0ba8 +#define AFE_HDMI_OUT_END_MSB 0x0bac +#define AFE_AWB2_BASE 0x0bd0 +#define AFE_AWB2_END 0x0bd8 +#define AFE_AWB2_CUR 0x0bdc +#define AFE_AWB2_BASE_MSB 0x0be0 +#define AFE_AWB2_END_MSB 0x0be8 +#define AFE_AWB2_CUR_MSB 0x0bec +#define AFE_ADDA_DL_SDM_DCCOMP_CON 0x0c50 +#define AFE_ADDA_DL_SDM_TEST 0x0c54 +#define AFE_ADDA_DL_DC_COMP_CFG0 0x0c58 +#define AFE_ADDA_DL_DC_COMP_CFG1 0x0c5c +#define AFE_ADDA_DL_SDM_FIFO_MON 0x0c60 +#define AFE_ADDA_DL_SRC_LCH_MON 0x0c64 +#define AFE_ADDA_DL_SRC_RCH_MON 0x0c68 +#define AFE_ADDA_DL_SDM_OUT_MON 0x0c6c +#define AFE_CONNSYS_I2S_CON 0x0c78 +#define AFE_CONNSYS_I2S_MON 0x0c7c +#define AFE_ASRC_2CH_CON0 0x0c80 +#define AFE_ASRC_2CH_CON1 0x0c84 +#define AFE_ASRC_2CH_CON2 0x0c88 +#define AFE_ASRC_2CH_CON3 0x0c8c +#define AFE_ASRC_2CH_CON4 0x0c90 +#define AFE_ASRC_2CH_CON5 0x0c94 +#define AFE_ASRC_2CH_CON6 0x0c98 +#define AFE_ASRC_2CH_CON7 0x0c9c +#define AFE_ASRC_2CH_CON8 0x0ca0 +#define AFE_ASRC_2CH_CON9 0x0ca4 +#define AFE_ASRC_2CH_CON10 0x0ca8 +#define AFE_ADDA6_IIR_COEF_02_01 0x0ce0 +#define AFE_ADDA6_IIR_COEF_04_03 0x0ce4 +#define AFE_ADDA6_IIR_COEF_06_05 0x0ce8 +#define AFE_ADDA6_IIR_COEF_08_07 0x0cec +#define AFE_ADDA6_IIR_COEF_10_09 0x0cf0 +#define AFE_ADDA_PREDIS_CON2 0x0d40 +#define AFE_ADDA_PREDIS_CON3 0x0d44 +#define AFE_MEMIF_MON12 0x0d70 +#define AFE_MEMIF_MON13 0x0d74 +#define AFE_MEMIF_MON14 0x0d78 +#define AFE_MEMIF_MON15 0x0d7c +#define AFE_MEMIF_MON16 0x0d80 +#define AFE_MEMIF_MON17 0x0d84 +#define AFE_MEMIF_MON18 0x0d88 +#define AFE_MEMIF_MON19 0x0d8c +#define AFE_MEMIF_MON20 0x0d90 +#define AFE_MEMIF_MON21 0x0d94 +#define AFE_MEMIF_MON22 0x0d98 +#define AFE_MEMIF_MON23 0x0d9c +#define AFE_MEMIF_MON24 0x0da0 +#define AFE_HD_ENGEN_ENABLE 0x0dd0 +#define AFE_ADDA_MTKAIF_CFG0 0x0e00 +#define AFE_ADDA_MTKAIF_TX_CFG1 0x0e14 +#define AFE_ADDA_MTKAIF_RX_CFG0 0x0e20 +#define AFE_ADDA_MTKAIF_RX_CFG1 0x0e24 +#define AFE_ADDA_MTKAIF_RX_CFG2 0x0e28 +#define AFE_ADDA_MTKAIF_MON0 0x0e34 +#define AFE_ADDA_MTKAIF_MON1 0x0e38 +#define AFE_AUD_PAD_TOP 0x0e40 +#define AFE_GENERAL1_ASRC_2CH_CON0 0x0e80 +#define AFE_GENERAL1_ASRC_2CH_CON1 0x0e84 +#define AFE_GENERAL1_ASRC_2CH_CON2 0x0e88 +#define AFE_GENERAL1_ASRC_2CH_CON3 0x0e8c +#define AFE_GENERAL1_ASRC_2CH_CON4 0x0e90 +#define AFE_GENERAL1_ASRC_2CH_CON5 0x0e94 +#define AFE_GENERAL1_ASRC_2CH_CON6 0x0e98 +#define AFE_GENERAL1_ASRC_2CH_CON7 0x0e9c +#define AFE_GENERAL1_ASRC_2CH_CON8 0x0ea0 +#define AFE_GENERAL1_ASRC_2CH_CON9 0x0ea4 +#define AFE_GENERAL1_ASRC_2CH_CON10 0x0ea8 +#define AFE_GENERAL1_ASRC_2CH_CON12 0x0eb0 +#define AFE_GENERAL1_ASRC_2CH_CON13 0x0eb4 +#define GENERAL_ASRC_MODE 0x0eb8 +#define GENERAL_ASRC_EN_ON 0x0ebc +#define AFE_GENERAL2_ASRC_2CH_CON0 0x0f00 +#define AFE_GENERAL2_ASRC_2CH_CON1 0x0f04 +#define AFE_GENERAL2_ASRC_2CH_CON2 0x0f08 +#define AFE_GENERAL2_ASRC_2CH_CON3 0x0f0c +#define AFE_GENERAL2_ASRC_2CH_CON4 0x0f10 +#define AFE_GENERAL2_ASRC_2CH_CON5 0x0f14 +#define AFE_GENERAL2_ASRC_2CH_CON6 0x0f18 +#define AFE_GENERAL2_ASRC_2CH_CON7 0x0f1c +#define AFE_GENERAL2_ASRC_2CH_CON8 0x0f20 +#define AFE_GENERAL2_ASRC_2CH_CON9 0x0f24 +#define AFE_GENERAL2_ASRC_2CH_CON10 0x0f28 +#define AFE_GENERAL2_ASRC_2CH_CON12 0x0f30 +#define AFE_GENERAL2_ASRC_2CH_CON13 0x0f34 + +#define AFE_MAX_REGISTER AFE_GENERAL2_ASRC_2CH_CON13 +#define AFE_IRQ_STATUS_BITS 0x1fff + +/* AFE_DAC_CON0 */ +#define AWB2_ON_SFT 29 +#define AWB2_ON_MASK 0x1 +#define AWB2_ON_MASK_SFT (0x1 << 29) +#define VUL2_ON_SFT 27 +#define VUL2_ON_MASK 0x1 +#define VUL2_ON_MASK_SFT (0x1 << 27) +#define MOD_DAI_DUP_WR_SFT 26 +#define MOD_DAI_DUP_WR_MASK 0x1 +#define MOD_DAI_DUP_WR_MASK_SFT (0x1 << 26) +#define VUL12_MODE_SFT 20 +#define VUL12_MODE_MASK 0xf +#define VUL12_MODE_MASK_SFT (0xf << 20) +#define VUL12_R_MONO_SFT 11 +#define VUL12_R_MONO_MASK 0x1 +#define VUL12_R_MONO_MASK_SFT (0x1 << 11) +#define VUL12_MONO_SFT 10 +#define VUL12_MONO_MASK 0x1 +#define VUL12_MONO_MASK_SFT (0x1 << 10) +#define VUL12_ON_SFT 9 +#define VUL12_ON_MASK 0x1 +#define VUL12_ON_MASK_SFT (0x1 << 9) +#define MOD_DAI_ON_SFT 7 +#define MOD_DAI_ON_MASK 0x1 +#define MOD_DAI_ON_MASK_SFT (0x1 << 7) +#define AWB_ON_SFT 6 +#define AWB_ON_MASK 0x1 +#define AWB_ON_MASK_SFT (0x1 << 6) +#define DL3_ON_SFT 5 +#define DL3_ON_MASK 0x1 +#define DL3_ON_MASK_SFT (0x1 << 5) +#define VUL_ON_SFT 3 +#define VUL_ON_MASK 0x1 +#define VUL_ON_MASK_SFT (0x1 << 3) +#define DL2_ON_SFT 2 +#define DL2_ON_MASK 0x1 +#define DL2_ON_MASK_SFT (0x1 << 2) +#define DL1_ON_SFT 1 +#define DL1_ON_MASK 0x1 +#define DL1_ON_MASK_SFT (0x1 << 1) +#define AFE_ON_SFT 0 +#define AFE_ON_MASK 0x1 +#define AFE_ON_MASK_SFT (0x1 << 0) + +/* AFE_DAC_CON1 */ +#define MOD_DAI_MODE_SFT 30 +#define MOD_DAI_MODE_MASK 0x3 +#define MOD_DAI_MODE_MASK_SFT (0x3 << 30) +#define VUL_R_MONO_SFT 28 +#define VUL_R_MONO_MASK 0x1 +#define VUL_R_MONO_MASK_SFT (0x1 << 28) +#define VUL_DATA_SFT 27 +#define VUL_DATA_MASK 0x1 +#define VUL_DATA_MASK_SFT (0x1 << 27) +#define AWB_R_MONO_SFT 25 +#define AWB_R_MONO_MASK 0x1 +#define AWB_R_MONO_MASK_SFT (0x1 << 25) +#define AWB_DATA_SFT 24 +#define AWB_DATA_MASK 0x1 +#define AWB_DATA_MASK_SFT (0x1 << 24) +#define DL3_DATA_SFT 23 +#define DL3_DATA_MASK 0x1 +#define DL3_DATA_MASK_SFT (0x1 << 23) +#define DL2_DATA_SFT 22 +#define DL2_DATA_MASK 0x1 +#define DL2_DATA_MASK_SFT (0x1 << 22) +#define DL1_DATA_SFT 21 +#define DL1_DATA_MASK 0x1 +#define DL1_DATA_MASK_SFT (0x1 << 21) +#define VUL_MODE_SFT 16 +#define VUL_MODE_MASK 0xf +#define VUL_MODE_MASK_SFT (0xf << 16) +#define AWB_MODE_SFT 12 +#define AWB_MODE_MASK 0xf +#define AWB_MODE_MASK_SFT (0xf << 12) +#define I2S_MODE_SFT 8 +#define I2S_MODE_MASK 0xf +#define I2S_MODE_MASK_SFT (0xf << 8) +#define DL2_MODE_SFT 4 +#define DL2_MODE_MASK 0xf +#define DL2_MODE_MASK_SFT (0xf << 4) +#define DL1_MODE_SFT 0 +#define DL1_MODE_MASK 0xf +#define DL1_MODE_MASK_SFT (0xf << 0) + +/* AFE_DAC_CON2 */ +#define AWB2_R_MONO_SFT 21 +#define AWB2_R_MONO_MASK 0x1 +#define AWB2_R_MONO_MASK_SFT (0x1 << 21) +#define AWB2_DATA_SFT 20 +#define AWB2_DATA_MASK 0x1 +#define AWB2_DATA_MASK_SFT (0x1 << 20) +#define AWB2_MODE_SFT 16 +#define AWB2_MODE_MASK 0xf +#define AWB2_MODE_MASK_SFT (0xf << 16) +#define DL3_MODE_SFT 8 +#define DL3_MODE_MASK 0xf +#define DL3_MODE_MASK_SFT (0xf << 8) +#define VUL2_MODE_SFT 4 +#define VUL2_MODE_MASK 0xf +#define VUL2_MODE_MASK_SFT (0xf << 4) +#define VUL2_R_MONO_SFT 1 +#define VUL2_R_MONO_MASK 0x1 +#define VUL2_R_MONO_MASK_SFT (0x1 << 1) +#define VUL2_DATA_SFT 0 +#define VUL2_DATA_MASK 0x1 +#define VUL2_DATA_MASK_SFT (0x1 << 0) + +/* AFE_DAC_MON */ +#define AFE_ON_RETM_SFT 0 +#define AFE_ON_RETM_MASK 0x1 +#define AFE_ON_RETM_MASK_SFT (0x1 << 0) + +/* AFE_I2S_CON */ +#define BCK_NEG_EG_LATCH_SFT 30 +#define BCK_NEG_EG_LATCH_MASK 0x1 +#define BCK_NEG_EG_LATCH_MASK_SFT (0x1 << 30) +#define BCK_INV_SFT 29 +#define BCK_INV_MASK 0x1 +#define BCK_INV_MASK_SFT (0x1 << 29) +#define I2SIN_PAD_SEL_SFT 28 +#define I2SIN_PAD_SEL_MASK 0x1 +#define I2SIN_PAD_SEL_MASK_SFT (0x1 << 28) +#define I2S_LOOPBACK_SFT 20 +#define I2S_LOOPBACK_MASK 0x1 +#define I2S_LOOPBACK_MASK_SFT (0x1 << 20) +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17) +#define I2S1_HD_EN_SFT 12 +#define I2S1_HD_EN_MASK 0x1 +#define I2S1_HD_EN_MASK_SFT (0x1 << 12) +#define INV_PAD_CTRL_SFT 7 +#define INV_PAD_CTRL_MASK 0x1 +#define INV_PAD_CTRL_MASK_SFT (0x1 << 7) +#define I2S_BYPSRC_SFT 6 +#define I2S_BYPSRC_MASK 0x1 +#define I2S_BYPSRC_MASK_SFT (0x1 << 6) +#define INV_LRCK_SFT 5 +#define INV_LRCK_MASK 0x1 +#define INV_LRCK_MASK_SFT (0x1 << 5) +#define I2S_FMT_SFT 3 +#define I2S_FMT_MASK 0x1 +#define I2S_FMT_MASK_SFT (0x1 << 3) +#define I2S_SRC_SFT 2 +#define I2S_SRC_MASK 0x1 +#define I2S_SRC_MASK_SFT (0x1 << 2) +#define I2S_WLEN_SFT 1 +#define I2S_WLEN_MASK 0x1 +#define I2S_WLEN_MASK_SFT (0x1 << 1) +#define I2S_EN_SFT 0 +#define I2S_EN_MASK 0x1 +#define I2S_EN_MASK_SFT (0x1 << 0) + +/* AFE_I2S_CON1 */ +#define I2S2_LR_SWAP_SFT 31 +#define I2S2_LR_SWAP_MASK 0x1 +#define I2S2_LR_SWAP_MASK_SFT (0x1 << 31) +#define I2S2_SEL_O19_O20_SFT 18 +#define I2S2_SEL_O19_O20_MASK 0x1 +#define I2S2_SEL_O19_O20_MASK_SFT (0x1 << 18) +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17) +#define I2S2_SEL_O03_O04_SFT 16 +#define I2S2_SEL_O03_O04_MASK 0x1 +#define I2S2_SEL_O03_O04_MASK_SFT (0x1 << 16) +#define I2S2_32BIT_EN_SFT 13 +#define I2S2_32BIT_EN_MASK 0x1 +#define I2S2_32BIT_EN_MASK_SFT (0x1 << 13) +#define I2S2_HD_EN_SFT 12 +#define I2S2_HD_EN_MASK 0x1 +#define I2S2_HD_EN_MASK_SFT (0x1 << 12) +#define I2S2_OUT_MODE_SFT 8 +#define I2S2_OUT_MODE_MASK 0xf +#define I2S2_OUT_MODE_MASK_SFT (0xf << 8) +#define INV_LRCK_SFT 5 +#define INV_LRCK_MASK 0x1 +#define INV_LRCK_MASK_SFT (0x1 << 5) +#define I2S2_FMT_SFT 3 +#define I2S2_FMT_MASK 0x1 +#define I2S2_FMT_MASK_SFT (0x1 << 3) +#define I2S2_WLEN_SFT 1 +#define I2S2_WLEN_MASK 0x1 +#define I2S2_WLEN_MASK_SFT (0x1 << 1) +#define I2S2_EN_SFT 0 +#define I2S2_EN_MASK 0x1 +#define I2S2_EN_MASK_SFT (0x1 << 0) + +/* AFE_I2S_CON2 */ +#define I2S3_LR_SWAP_SFT 31 +#define I2S3_LR_SWAP_MASK 0x1 +#define I2S3_LR_SWAP_MASK_SFT (0x1 << 31) +#define I2S3_UPDATE_WORD_SFT 24 +#define I2S3_UPDATE_WORD_MASK 0x1f +#define I2S3_UPDATE_WORD_MASK_SFT (0x1f << 24) +#define I2S3_BCK_INV_SFT 23 +#define I2S3_BCK_INV_MASK 0x1 +#define I2S3_BCK_INV_MASK_SFT (0x1 << 23) +#define I2S3_FPGA_BIT_TEST_SFT 22 +#define I2S3_FPGA_BIT_TEST_MASK 0x1 +#define I2S3_FPGA_BIT_TEST_MASK_SFT (0x1 << 22) +#define I2S3_FPGA_BIT_SFT 21 +#define I2S3_FPGA_BIT_MASK 0x1 +#define I2S3_FPGA_BIT_MASK_SFT (0x1 << 21) +#define I2S3_LOOPBACK_SFT 20 +#define I2S3_LOOPBACK_MASK 0x1 +#define I2S3_LOOPBACK_MASK_SFT (0x1 << 20) +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17) +#define I2S3_HD_EN_SFT 12 +#define I2S3_HD_EN_MASK 0x1 +#define I2S3_HD_EN_MASK_SFT (0x1 << 12) +#define I2S3_OUT_MODE_SFT 8 +#define I2S3_OUT_MODE_MASK 0xf +#define I2S3_OUT_MODE_MASK_SFT (0xf << 8) +#define I2S3_FMT_SFT 3 +#define I2S3_FMT_MASK 0x1 +#define I2S3_FMT_MASK_SFT (0x1 << 3) +#define I2S3_WLEN_SFT 1 +#define I2S3_WLEN_MASK 0x1 +#define I2S3_WLEN_MASK_SFT (0x1 << 1) +#define I2S3_EN_SFT 0 +#define I2S3_EN_MASK 0x1 +#define I2S3_EN_MASK_SFT (0x1 << 0) + +/* AFE_I2S_CON3 */ +#define I2S4_LR_SWAP_SFT 31 +#define I2S4_LR_SWAP_MASK 0x1 +#define I2S4_LR_SWAP_MASK_SFT (0x1 << 31) +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17) +#define I2S4_32BIT_EN_SFT 13 +#define I2S4_32BIT_EN_MASK 0x1 +#define I2S4_32BIT_EN_MASK_SFT (0x1 << 13) +#define I2S4_HD_EN_SFT 12 +#define I2S4_HD_EN_MASK 0x1 +#define I2S4_HD_EN_MASK_SFT (0x1 << 12) +#define I2S4_OUT_MODE_SFT 8 +#define I2S4_OUT_MODE_MASK 0xf +#define I2S4_OUT_MODE_MASK_SFT (0xf << 8) +#define INV_LRCK_SFT 5 +#define INV_LRCK_MASK 0x1 +#define INV_LRCK_MASK_SFT (0x1 << 5) +#define I2S4_FMT_SFT 3 +#define I2S4_FMT_MASK 0x1 +#define I2S4_FMT_MASK_SFT (0x1 << 3) +#define I2S4_WLEN_SFT 1 +#define I2S4_WLEN_MASK 0x1 +#define I2S4_WLEN_MASK_SFT (0x1 << 1) +#define I2S4_EN_SFT 0 +#define I2S4_EN_MASK 0x1 +#define I2S4_EN_MASK_SFT (0x1 << 0) + +/* AFE_I2S_CON4 */ +#define I2S5_LR_SWAP_SFT 31 +#define I2S5_LR_SWAP_MASK 0x1 +#define I2S5_LR_SWAP_MASK_SFT (0x1 << 31) +#define I2S_LOOPBACK_SFT 20 +#define I2S_LOOPBACK_MASK 0x1 +#define I2S_LOOPBACK_MASK_SFT (0x1 << 20) +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_SFT 17 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK 0x1 +#define I2S_ONOFF_NOT_RESET_CK_ENABLE_MASK_SFT (0x1 << 17) +#define I2S5_32BIT_EN_SFT 13 +#define I2S5_32BIT_EN_MASK 0x1 +#define I2S5_32BIT_EN_MASK_SFT (0x1 << 13) +#define I2S5_HD_EN_SFT 12 +#define I2S5_HD_EN_MASK 0x1 +#define I2S5_HD_EN_MASK_SFT (0x1 << 12) +#define I2S5_OUT_MODE_SFT 8 +#define I2S5_OUT_MODE_MASK 0xf +#define I2S5_OUT_MODE_MASK_SFT (0xf << 8) +#define INV_LRCK_SFT 5 +#define INV_LRCK_MASK 0x1 +#define INV_LRCK_MASK_SFT (0x1 << 5) +#define I2S5_FMT_SFT 3 +#define I2S5_FMT_MASK 0x1 +#define I2S5_FMT_MASK_SFT (0x1 << 3) +#define I2S5_WLEN_SFT 1 +#define I2S5_WLEN_MASK 0x1 +#define I2S5_WLEN_MASK_SFT (0x1 << 1) +#define I2S5_EN_SFT 0 +#define I2S5_EN_MASK 0x1 +#define I2S5_EN_MASK_SFT (0x1 << 0) + +/* AFE_GAIN1_CON0 */ +#define GAIN1_SAMPLE_PER_STEP_SFT 8 +#define GAIN1_SAMPLE_PER_STEP_MASK 0xff +#define GAIN1_SAMPLE_PER_STEP_MASK_SFT (0xff << 8) +#define GAIN1_MODE_SFT 4 +#define GAIN1_MODE_MASK 0xf +#define GAIN1_MODE_MASK_SFT (0xf << 4) +#define GAIN1_ON_SFT 0 +#define GAIN1_ON_MASK 0x1 +#define GAIN1_ON_MASK_SFT (0x1 << 0) + +/* AFE_GAIN1_CON1 */ +#define GAIN1_TARGET_SFT 0 +#define GAIN1_TARGET_MASK 0xfffff +#define GAIN1_TARGET_MASK_SFT (0xfffff << 0) + +/* AFE_GAIN2_CON0 */ +#define GAIN2_SAMPLE_PER_STEP_SFT 8 +#define GAIN2_SAMPLE_PER_STEP_MASK 0xff +#define GAIN2_SAMPLE_PER_STEP_MASK_SFT (0xff << 8) +#define GAIN2_MODE_SFT 4 +#define GAIN2_MODE_MASK 0xf +#define GAIN2_MODE_MASK_SFT (0xf << 4) +#define GAIN2_ON_SFT 0 +#define GAIN2_ON_MASK 0x1 +#define GAIN2_ON_MASK_SFT (0x1 << 0) + +/* AFE_GAIN2_CON1 */ +#define GAIN2_TARGET_SFT 0 +#define GAIN2_TARGET_MASK 0xfffff +#define GAIN2_TARGET_MASK_SFT (0xfffff << 0) + +/* AFE_GAIN1_CUR */ +#define AFE_GAIN1_CUR_SFT 0 +#define AFE_GAIN1_CUR_MASK 0xfffff +#define AFE_GAIN1_CUR_MASK_SFT (0xfffff << 0) + +/* AFE_GAIN2_CUR */ +#define AFE_GAIN2_CUR_SFT 0 +#define AFE_GAIN2_CUR_MASK 0xfffff +#define AFE_GAIN2_CUR_MASK_SFT (0xfffff << 0) + +/* AFE_MEMIF_HD_MODE */ +#define AWB2_HD_SFT 28 +#define AWB2_HD_MASK 0x3 +#define AWB2_HD_MASK_SFT (0x3 << 28) +#define HDMI_HD_SFT 20 +#define HDMI_HD_MASK 0x3 +#define HDMI_HD_MASK_SFT (0x3 << 20) +#define MOD_DAI_HD_SFT 18 +#define MOD_DAI_HD_MASK 0x3 +#define MOD_DAI_HD_MASK_SFT (0x3 << 18) +#define DAI_HD_SFT 16 +#define DAI_HD_MASK 0x3 +#define DAI_HD_MASK_SFT (0x3 << 16) +#define VUL2_HD_SFT 14 +#define VUL2_HD_MASK 0x3 +#define VUL2_HD_MASK_SFT (0x3 << 14) +#define VUL12_HD_SFT 12 +#define VUL12_HD_MASK 0x3 +#define VUL12_HD_MASK_SFT (0x3 << 12) +#define VUL_HD_SFT 10 +#define VUL_HD_MASK 0x3 +#define VUL_HD_MASK_SFT (0x3 << 10) +#define AWB_HD_SFT 8 +#define AWB_HD_MASK 0x3 +#define AWB_HD_MASK_SFT (0x3 << 8) +#define DL3_HD_SFT 6 +#define DL3_HD_MASK 0x3 +#define DL3_HD_MASK_SFT (0x3 << 6) +#define DL2_HD_SFT 4 +#define DL2_HD_MASK 0x3 +#define DL2_HD_MASK_SFT (0x3 << 4) +#define DL1_HD_SFT 0 +#define DL1_HD_MASK 0x3 +#define DL1_HD_MASK_SFT (0x3 << 0) + +/* AFE_MEMIF_HDALIGN */ +#define AWB2_NORMAL_MODE_SFT 30 +#define AWB2_NORMAL_MODE_MASK 0x1 +#define AWB2_NORMAL_MODE_MASK_SFT (0x1 << 30) +#define HDMI_NORMAL_MODE_SFT 26 +#define HDMI_NORMAL_MODE_MASK 0x1 +#define HDMI_NORMAL_MODE_MASK_SFT (0x1 << 26) +#define MOD_DAI_NORMAL_MODE_SFT 25 +#define MOD_DAI_NORMAL_MODE_MASK 0x1 +#define MOD_DAI_NORMAL_MODE_MASK_SFT (0x1 << 25) +#define DAI_NORMAL_MODE_SFT 24 +#define DAI_NORMAL_MODE_MASK 0x1 +#define DAI_NORMAL_MODE_MASK_SFT (0x1 << 24) +#define VUL2_NORMAL_MODE_SFT 23 +#define VUL2_NORMAL_MODE_MASK 0x1 +#define VUL2_NORMAL_MODE_MASK_SFT (0x1 << 23) +#define VUL12_NORMAL_MODE_SFT 22 +#define VUL12_NORMAL_MODE_MASK 0x1 +#define VUL12_NORMAL_MODE_MASK_SFT (0x1 << 22) +#define VUL_NORMAL_MODE_SFT 21 +#define VUL_NORMAL_MODE_MASK 0x1 +#define VUL_NORMAL_MODE_MASK_SFT (0x1 << 21) +#define AWB_NORMAL_MODE_SFT 20 +#define AWB_NORMAL_MODE_MASK 0x1 +#define AWB_NORMAL_MODE_MASK_SFT (0x1 << 20) +#define DL3_NORMAL_MODE_SFT 19 +#define DL3_NORMAL_MODE_MASK 0x1 +#define DL3_NORMAL_MODE_MASK_SFT (0x1 << 19) +#define DL2_NORMAL_MODE_SFT 18 +#define DL2_NORMAL_MODE_MASK 0x1 +#define DL2_NORMAL_MODE_MASK_SFT (0x1 << 18) +#define DL1_NORMAL_MODE_SFT 16 +#define DL1_NORMAL_MODE_MASK 0x1 +#define DL1_NORMAL_MODE_MASK_SFT (0x1 << 16) +#define RESERVED1_SFT 15 +#define RESERVED1_MASK 0x1 +#define RESERVED1_MASK_SFT (0x1 << 15) +#define AWB2_ALIGN_SFT 14 +#define AWB2_ALIGN_MASK 0x1 +#define AWB2_ALIGN_MASK_SFT (0x1 << 14) +#define HDMI_HD_ALIGN_SFT 10 +#define HDMI_HD_ALIGN_MASK 0x1 +#define HDMI_HD_ALIGN_MASK_SFT (0x1 << 10) +#define MOD_DAI_HD_ALIGN_SFT 9 +#define MOD_DAI_HD_ALIGN_MASK 0x1 +#define MOD_DAI_HD_ALIGN_MASK_SFT (0x1 << 9) +#define VUL2_HD_ALIGN_SFT 7 +#define VUL2_HD_ALIGN_MASK 0x1 +#define VUL2_HD_ALIGN_MASK_SFT (0x1 << 7) +#define VUL12_HD_ALIGN_SFT 6 +#define VUL12_HD_ALIGN_MASK 0x1 +#define VUL12_HD_ALIGN_MASK_SFT (0x1 << 6) +#define VUL_HD_ALIGN_SFT 5 +#define VUL_HD_ALIGN_MASK 0x1 +#define VUL_HD_ALIGN_MASK_SFT (0x1 << 5) +#define AWB_HD_ALIGN_SFT 4 +#define AWB_HD_ALIGN_MASK 0x1 +#define AWB_HD_ALIGN_MASK_SFT (0x1 << 4) +#define DL3_HD_ALIGN_SFT 3 +#define DL3_HD_ALIGN_MASK 0x1 +#define DL3_HD_ALIGN_MASK_SFT (0x1 << 3) +#define DL2_HD_ALIGN_SFT 2 +#define DL2_HD_ALIGN_MASK 0x1 +#define DL2_HD_ALIGN_MASK_SFT (0x1 << 2) +#define DL1_HD_ALIGN_SFT 0 +#define DL1_HD_ALIGN_MASK 0x1 +#define DL1_HD_ALIGN_MASK_SFT (0x1 << 0) + +/* PCM_INTF_CON1 */ +#define PCM_FIX_VALUE_SEL_SFT 31 +#define PCM_FIX_VALUE_SEL_MASK 0x1 +#define PCM_FIX_VALUE_SEL_MASK_SFT (0x1 << 31) +#define PCM_BUFFER_LOOPBACK_SFT 30 +#define PCM_BUFFER_LOOPBACK_MASK 0x1 +#define PCM_BUFFER_LOOPBACK_MASK_SFT (0x1 << 30) +#define PCM_PARALLEL_LOOPBACK_SFT 29 +#define PCM_PARALLEL_LOOPBACK_MASK 0x1 +#define PCM_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 29) +#define PCM_SERIAL_LOOPBACK_SFT 28 +#define PCM_SERIAL_LOOPBACK_MASK 0x1 +#define PCM_SERIAL_LOOPBACK_MASK_SFT (0x1 << 28) +#define PCM_DAI_PCM_LOOPBACK_SFT 27 +#define PCM_DAI_PCM_LOOPBACK_MASK 0x1 +#define PCM_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 27) +#define PCM_I2S_PCM_LOOPBACK_SFT 26 +#define PCM_I2S_PCM_LOOPBACK_MASK 0x1 +#define PCM_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 26) +#define PCM_SYNC_DELSEL_SFT 25 +#define PCM_SYNC_DELSEL_MASK 0x1 +#define PCM_SYNC_DELSEL_MASK_SFT (0x1 << 25) +#define PCM_TX_LR_SWAP_SFT 24 +#define PCM_TX_LR_SWAP_MASK 0x1 +#define PCM_TX_LR_SWAP_MASK_SFT (0x1 << 24) +#define PCM_SYNC_OUT_INV_SFT 23 +#define PCM_SYNC_OUT_INV_MASK 0x1 +#define PCM_SYNC_OUT_INV_MASK_SFT (0x1 << 23) +#define PCM_BCLK_OUT_INV_SFT 22 +#define PCM_BCLK_OUT_INV_MASK 0x1 +#define PCM_BCLK_OUT_INV_MASK_SFT (0x1 << 22) +#define PCM_SYNC_IN_INV_SFT 21 +#define PCM_SYNC_IN_INV_MASK 0x1 +#define PCM_SYNC_IN_INV_MASK_SFT (0x1 << 21) +#define PCM_BCLK_IN_INV_SFT 20 +#define PCM_BCLK_IN_INV_MASK 0x1 +#define PCM_BCLK_IN_INV_MASK_SFT (0x1 << 20) +#define PCM_TX_LCH_RPT_SFT 19 +#define PCM_TX_LCH_RPT_MASK 0x1 +#define PCM_TX_LCH_RPT_MASK_SFT (0x1 << 19) +#define PCM_VBT_16K_MODE_SFT 18 +#define PCM_VBT_16K_MODE_MASK 0x1 +#define PCM_VBT_16K_MODE_MASK_SFT (0x1 << 18) +#define PCM_EXT_MODEM_SFT 17 +#define PCM_EXT_MODEM_MASK 0x1 +#define PCM_EXT_MODEM_MASK_SFT (0x1 << 17) +#define PCM_24BIT_SFT 16 +#define PCM_24BIT_MASK 0x1 +#define PCM_24BIT_MASK_SFT (0x1 << 16) +#define PCM_WLEN_SFT 14 +#define PCM_WLEN_MASK 0x3 +#define PCM_WLEN_MASK_SFT (0x3 << 14) +#define PCM_SYNC_LENGTH_SFT 9 +#define PCM_SYNC_LENGTH_MASK 0x1f +#define PCM_SYNC_LENGTH_MASK_SFT (0x1f << 9) +#define PCM_SYNC_TYPE_SFT 8 +#define PCM_SYNC_TYPE_MASK 0x1 +#define PCM_SYNC_TYPE_MASK_SFT (0x1 << 8) +#define PCM_BT_MODE_SFT 7 +#define PCM_BT_MODE_MASK 0x1 +#define PCM_BT_MODE_MASK_SFT (0x1 << 7) +#define PCM_BYP_ASRC_SFT 6 +#define PCM_BYP_ASRC_MASK 0x1 +#define PCM_BYP_ASRC_MASK_SFT (0x1 << 6) +#define PCM_SLAVE_SFT 5 +#define PCM_SLAVE_MASK 0x1 +#define PCM_SLAVE_MASK_SFT (0x1 << 5) +#define PCM_MODE_SFT 3 +#define PCM_MODE_MASK 0x3 +#define PCM_MODE_MASK_SFT (0x3 << 3) +#define PCM_FMT_SFT 1 +#define PCM_FMT_MASK 0x3 +#define PCM_FMT_MASK_SFT (0x3 << 1) +#define PCM_EN_SFT 0 +#define PCM_EN_MASK 0x1 +#define PCM_EN_MASK_SFT (0x1 << 0) + +/* PCM_INTF_CON2 */ +#define PCM1_TX_FIFO_OV_SFT 31 +#define PCM1_TX_FIFO_OV_MASK 0x1 +#define PCM1_TX_FIFO_OV_MASK_SFT (0x1 << 31) +#define PCM1_RX_FIFO_OV_SFT 30 +#define PCM1_RX_FIFO_OV_MASK 0x1 +#define PCM1_RX_FIFO_OV_MASK_SFT (0x1 << 30) +#define PCM2_TX_FIFO_OV_SFT 29 +#define PCM2_TX_FIFO_OV_MASK 0x1 +#define PCM2_TX_FIFO_OV_MASK_SFT (0x1 << 29) +#define PCM2_RX_FIFO_OV_SFT 28 +#define PCM2_RX_FIFO_OV_MASK 0x1 +#define PCM2_RX_FIFO_OV_MASK_SFT (0x1 << 28) +#define PCM1_SYNC_GLITCH_SFT 27 +#define PCM1_SYNC_GLITCH_MASK 0x1 +#define PCM1_SYNC_GLITCH_MASK_SFT (0x1 << 27) +#define PCM2_SYNC_GLITCH_SFT 26 +#define PCM2_SYNC_GLITCH_MASK 0x1 +#define PCM2_SYNC_GLITCH_MASK_SFT (0x1 << 26) +#define TX3_RCH_DBG_MODE_SFT 17 +#define TX3_RCH_DBG_MODE_MASK 0x1 +#define TX3_RCH_DBG_MODE_MASK_SFT (0x1 << 17) +#define PCM1_PCM2_LOOPBACK_SFT 16 +#define PCM1_PCM2_LOOPBACK_MASK 0x1 +#define PCM1_PCM2_LOOPBACK_MASK_SFT (0x1 << 16) +#define DAI_PCM_LOOPBACK_CH_SFT 14 +#define DAI_PCM_LOOPBACK_CH_MASK 0x3 +#define DAI_PCM_LOOPBACK_CH_MASK_SFT (0x3 << 14) +#define I2S_PCM_LOOPBACK_CH_SFT 12 +#define I2S_PCM_LOOPBACK_CH_MASK 0x3 +#define I2S_PCM_LOOPBACK_CH_MASK_SFT (0x3 << 12) +#define TX_FIX_VALUE_SFT 0 +#define TX_FIX_VALUE_MASK 0xff +#define TX_FIX_VALUE_MASK_SFT (0xff << 0) + +/* PCM2_INTF_CON */ +#define PCM2_TX_FIX_VALUE_SFT 24 +#define PCM2_TX_FIX_VALUE_MASK 0xff +#define PCM2_TX_FIX_VALUE_MASK_SFT (0xff << 24) +#define PCM2_FIX_VALUE_SEL_SFT 23 +#define PCM2_FIX_VALUE_SEL_MASK 0x1 +#define PCM2_FIX_VALUE_SEL_MASK_SFT (0x1 << 23) +#define PCM2_BUFFER_LOOPBACK_SFT 22 +#define PCM2_BUFFER_LOOPBACK_MASK 0x1 +#define PCM2_BUFFER_LOOPBACK_MASK_SFT (0x1 << 22) +#define PCM2_PARALLEL_LOOPBACK_SFT 21 +#define PCM2_PARALLEL_LOOPBACK_MASK 0x1 +#define PCM2_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 21) +#define PCM2_SERIAL_LOOPBACK_SFT 20 +#define PCM2_SERIAL_LOOPBACK_MASK 0x1 +#define PCM2_SERIAL_LOOPBACK_MASK_SFT (0x1 << 20) +#define PCM2_DAI_PCM_LOOPBACK_SFT 19 +#define PCM2_DAI_PCM_LOOPBACK_MASK 0x1 +#define PCM2_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 19) +#define PCM2_I2S_PCM_LOOPBACK_SFT 18 +#define PCM2_I2S_PCM_LOOPBACK_MASK 0x1 +#define PCM2_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 18) +#define PCM2_SYNC_DELSEL_SFT 17 +#define PCM2_SYNC_DELSEL_MASK 0x1 +#define PCM2_SYNC_DELSEL_MASK_SFT (0x1 << 17) +#define PCM2_TX_LR_SWAP_SFT 16 +#define PCM2_TX_LR_SWAP_MASK 0x1 +#define PCM2_TX_LR_SWAP_MASK_SFT (0x1 << 16) +#define PCM2_SYNC_IN_INV_SFT 15 +#define PCM2_SYNC_IN_INV_MASK 0x1 +#define PCM2_SYNC_IN_INV_MASK_SFT (0x1 << 15) +#define PCM2_BCLK_IN_INV_SFT 14 +#define PCM2_BCLK_IN_INV_MASK 0x1 +#define PCM2_BCLK_IN_INV_MASK_SFT (0x1 << 14) +#define PCM2_TX_LCH_RPT_SFT 13 +#define PCM2_TX_LCH_RPT_MASK 0x1 +#define PCM2_TX_LCH_RPT_MASK_SFT (0x1 << 13) +#define PCM2_VBT_16K_MODE_SFT 12 +#define PCM2_VBT_16K_MODE_MASK 0x1 +#define PCM2_VBT_16K_MODE_MASK_SFT (0x1 << 12) +#define PCM2_LOOPBACK_CH_SEL_SFT 10 +#define PCM2_LOOPBACK_CH_SEL_MASK 0x3 +#define PCM2_LOOPBACK_CH_SEL_MASK_SFT (0x3 << 10) +#define PCM2_TX2_BT_MODE_SFT 8 +#define PCM2_TX2_BT_MODE_MASK 0x1 +#define PCM2_TX2_BT_MODE_MASK_SFT (0x1 << 8) +#define PCM2_BT_MODE_SFT 7 +#define PCM2_BT_MODE_MASK 0x1 +#define PCM2_BT_MODE_MASK_SFT (0x1 << 7) +#define PCM2_AFIFO_SFT 6 +#define PCM2_AFIFO_MASK 0x1 +#define PCM2_AFIFO_MASK_SFT (0x1 << 6) +#define PCM2_WLEN_SFT 5 +#define PCM2_WLEN_MASK 0x1 +#define PCM2_WLEN_MASK_SFT (0x1 << 5) +#define PCM2_MODE_SFT 3 +#define PCM2_MODE_MASK 0x3 +#define PCM2_MODE_MASK_SFT (0x3 << 3) +#define PCM2_FMT_SFT 1 +#define PCM2_FMT_MASK 0x3 +#define PCM2_FMT_MASK_SFT (0x3 << 1) +#define PCM2_EN_SFT 0 +#define PCM2_EN_MASK 0x1 +#define PCM2_EN_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_CFG0 */ +#define MTKAIF_RXIF_CLKINV_ADC_SFT 31 +#define MTKAIF_RXIF_CLKINV_ADC_MASK 0x1 +#define MTKAIF_RXIF_CLKINV_ADC_MASK_SFT (0x1 << 31) +#define MTKAIF_RXIF_BYPASS_SRC_SFT 17 +#define MTKAIF_RXIF_BYPASS_SRC_MASK 0x1 +#define MTKAIF_RXIF_BYPASS_SRC_MASK_SFT (0x1 << 17) +#define MTKAIF_RXIF_PROTOCOL2_SFT 16 +#define MTKAIF_RXIF_PROTOCOL2_MASK 0x1 +#define MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 16) +#define MTKAIF_TXIF_BYPASS_SRC_SFT 5 +#define MTKAIF_TXIF_BYPASS_SRC_MASK 0x1 +#define MTKAIF_TXIF_BYPASS_SRC_MASK_SFT (0x1 << 5) +#define MTKAIF_TXIF_PROTOCOL2_SFT 4 +#define MTKAIF_TXIF_PROTOCOL2_MASK 0x1 +#define MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4) +#define MTKAIF_TXIF_8TO5_SFT 2 +#define MTKAIF_TXIF_8TO5_MASK 0x1 +#define MTKAIF_TXIF_8TO5_MASK_SFT (0x1 << 2) +#define MTKAIF_RXIF_8TO5_SFT 1 +#define MTKAIF_RXIF_8TO5_MASK 0x1 +#define MTKAIF_RXIF_8TO5_MASK_SFT (0x1 << 1) +#define MTKAIF_IF_LOOPBACK1_SFT 0 +#define MTKAIF_IF_LOOPBACK1_MASK 0x1 +#define MTKAIF_IF_LOOPBACK1_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG2 */ +#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 16 +#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1 +#define MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 16) +#define MTKAIF_RXIF_DELAY_CYCLE_SFT 12 +#define MTKAIF_RXIF_DELAY_CYCLE_MASK 0xf +#define MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT (0xf << 12) +#define MTKAIF_RXIF_DELAY_DATA_SFT 8 +#define MTKAIF_RXIF_DELAY_DATA_MASK 0x1 +#define MTKAIF_RXIF_DELAY_DATA_MASK_SFT (0x1 << 8) +#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4 +#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7 +#define MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4) + +/* AFE_ADDA_DL_SRC2_CON0 */ +#define DL_2_INPUT_MODE_CTL_SFT 28 +#define DL_2_INPUT_MODE_CTL_MASK 0xf +#define DL_2_INPUT_MODE_CTL_MASK_SFT (0xf << 28) +#define DL_2_CH1_SATURATION_EN_CTL_SFT 27 +#define DL_2_CH1_SATURATION_EN_CTL_MASK 0x1 +#define DL_2_CH1_SATURATION_EN_CTL_MASK_SFT (0x1 << 27) +#define DL_2_CH2_SATURATION_EN_CTL_SFT 26 +#define DL_2_CH2_SATURATION_EN_CTL_MASK 0x1 +#define DL_2_CH2_SATURATION_EN_CTL_MASK_SFT (0x1 << 26) +#define DL_2_OUTPUT_SEL_CTL_SFT 24 +#define DL_2_OUTPUT_SEL_CTL_MASK 0x3 +#define DL_2_OUTPUT_SEL_CTL_MASK_SFT (0x3 << 24) +#define DL_2_FADEIN_0START_EN_SFT 16 +#define DL_2_FADEIN_0START_EN_MASK 0x3 +#define DL_2_FADEIN_0START_EN_MASK_SFT (0x3 << 16) +#define DL_DISABLE_HW_CG_CTL_SFT 15 +#define DL_DISABLE_HW_CG_CTL_MASK 0x1 +#define DL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 15) +#define C_DATA_EN_SEL_CTL_PRE_SFT 14 +#define C_DATA_EN_SEL_CTL_PRE_MASK 0x1 +#define C_DATA_EN_SEL_CTL_PRE_MASK_SFT (0x1 << 14) +#define DL_2_SIDE_TONE_ON_CTL_PRE_SFT 13 +#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK 0x1 +#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK_SFT (0x1 << 13) +#define DL_2_MUTE_CH1_OFF_CTL_PRE_SFT 12 +#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK 0x1 +#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK_SFT (0x1 << 12) +#define DL_2_MUTE_CH2_OFF_CTL_PRE_SFT 11 +#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK 0x1 +#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK_SFT (0x1 << 11) +#define DL2_ARAMPSP_CTL_PRE_SFT 9 +#define DL2_ARAMPSP_CTL_PRE_MASK 0x3 +#define DL2_ARAMPSP_CTL_PRE_MASK_SFT (0x3 << 9) +#define DL_2_IIRMODE_CTL_PRE_SFT 6 +#define DL_2_IIRMODE_CTL_PRE_MASK 0x7 +#define DL_2_IIRMODE_CTL_PRE_MASK_SFT (0x7 << 6) +#define DL_2_VOICE_MODE_CTL_PRE_SFT 5 +#define DL_2_VOICE_MODE_CTL_PRE_MASK 0x1 +#define DL_2_VOICE_MODE_CTL_PRE_MASK_SFT (0x1 << 5) +#define D2_2_MUTE_CH1_ON_CTL_PRE_SFT 4 +#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK 0x1 +#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK_SFT (0x1 << 4) +#define D2_2_MUTE_CH2_ON_CTL_PRE_SFT 3 +#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK 0x1 +#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK_SFT (0x1 << 3) +#define DL_2_IIR_ON_CTL_PRE_SFT 2 +#define DL_2_IIR_ON_CTL_PRE_MASK 0x1 +#define DL_2_IIR_ON_CTL_PRE_MASK_SFT (0x1 << 2) +#define DL_2_GAIN_ON_CTL_PRE_SFT 1 +#define DL_2_GAIN_ON_CTL_PRE_MASK 0x1 +#define DL_2_GAIN_ON_CTL_PRE_MASK_SFT (0x1 << 1) +#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_DL_SRC2_CON1 */ +#define DL_2_GAIN_CTL_PRE_SFT 16 +#define DL_2_GAIN_CTL_PRE_MASK 0xffff +#define DL_2_GAIN_CTL_PRE_MASK_SFT (0xffff << 16) +#define DL_2_GAIN_MODE_CTL_SFT 0 +#define DL_2_GAIN_MODE_CTL_MASK 0x1 +#define DL_2_GAIN_MODE_CTL_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_UL_SRC_CON0 */ +#define ULCF_CFG_EN_CTL_SFT 31 +#define ULCF_CFG_EN_CTL_MASK 0x1 +#define ULCF_CFG_EN_CTL_MASK_SFT (0x1 << 31) +#define UL_MODE_3P25M_CH2_CTL_SFT 22 +#define UL_MODE_3P25M_CH2_CTL_MASK 0x1 +#define UL_MODE_3P25M_CH2_CTL_MASK_SFT (0x1 << 22) +#define UL_MODE_3P25M_CH1_CTL_SFT 21 +#define UL_MODE_3P25M_CH1_CTL_MASK 0x1 +#define UL_MODE_3P25M_CH1_CTL_MASK_SFT (0x1 << 21) +#define UL_VOICE_MODE_CH1_CH2_CTL_SFT 17 +#define UL_VOICE_MODE_CH1_CH2_CTL_MASK 0x7 +#define UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT (0x7 << 17) +#define DMIC_LOW_POWER_MODE_CTL_SFT 14 +#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3 +#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14) +#define UL_DISABLE_HW_CG_CTL_SFT 12 +#define UL_DISABLE_HW_CG_CTL_MASK 0x1 +#define UL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 12) +#define UL_IIR_ON_TMP_CTL_SFT 10 +#define UL_IIR_ON_TMP_CTL_MASK 0x1 +#define UL_IIR_ON_TMP_CTL_MASK_SFT (0x1 << 10) +#define UL_IIRMODE_CTL_SFT 7 +#define UL_IIRMODE_CTL_MASK 0x7 +#define UL_IIRMODE_CTL_MASK_SFT (0x7 << 7) +#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5) +#define UL_LOOP_BACK_MODE_CTL_SFT 2 +#define UL_LOOP_BACK_MODE_CTL_MASK 0x1 +#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2) +#define UL_SDM_3_LEVEL_CTL_SFT 1 +#define UL_SDM_3_LEVEL_CTL_MASK 0x1 +#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1) +#define UL_SRC_ON_TMP_CTL_SFT 0 +#define UL_SRC_ON_TMP_CTL_MASK 0x1 +#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_UL_SRC_CON1 */ +#define C_DAC_EN_CTL_SFT 27 +#define C_DAC_EN_CTL_MASK 0x1 +#define C_DAC_EN_CTL_MASK_SFT (0x1 << 27) +#define C_MUTE_SW_CTL_SFT 26 +#define C_MUTE_SW_CTL_MASK 0x1 +#define C_MUTE_SW_CTL_MASK_SFT (0x1 << 26) +#define ASDM_SRC_SEL_CTL_SFT 25 +#define ASDM_SRC_SEL_CTL_MASK 0x1 +#define ASDM_SRC_SEL_CTL_MASK_SFT (0x1 << 25) +#define C_AMP_DIV_CH2_CTL_SFT 21 +#define C_AMP_DIV_CH2_CTL_MASK 0x7 +#define C_AMP_DIV_CH2_CTL_MASK_SFT (0x7 << 21) +#define C_FREQ_DIV_CH2_CTL_SFT 16 +#define C_FREQ_DIV_CH2_CTL_MASK 0x1f +#define C_FREQ_DIV_CH2_CTL_MASK_SFT (0x1f << 16) +#define C_SINE_MODE_CH2_CTL_SFT 12 +#define C_SINE_MODE_CH2_CTL_MASK 0xf +#define C_SINE_MODE_CH2_CTL_MASK_SFT (0xf << 12) +#define C_AMP_DIV_CH1_CTL_SFT 9 +#define C_AMP_DIV_CH1_CTL_MASK 0x7 +#define C_AMP_DIV_CH1_CTL_MASK_SFT (0x7 << 9) +#define C_FREQ_DIV_CH1_CTL_SFT 4 +#define C_FREQ_DIV_CH1_CTL_MASK 0x1f +#define C_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 4) +#define C_SINE_MODE_CH1_CTL_SFT 0 +#define C_SINE_MODE_CH1_CTL_MASK 0xf +#define C_SINE_MODE_CH1_CTL_MASK_SFT (0xf << 0) + +/* AFE_ADDA_TOP_CON0 */ +#define C_LOOP_BACK_MODE_CTL_SFT 12 +#define C_LOOP_BACK_MODE_CTL_MASK 0xf +#define C_LOOP_BACK_MODE_CTL_MASK_SFT (0xf << 12) +#define C_EXT_ADC_CTL_SFT 0 +#define C_EXT_ADC_CTL_MASK 0x1 +#define C_EXT_ADC_CTL_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_UL_DL_CON0 */ +#define AFE_ADDA6_UL_LR_SWAP_SFT 15 +#define AFE_ADDA6_UL_LR_SWAP_MASK 0x1 +#define AFE_ADDA6_UL_LR_SWAP_MASK_SFT (0x1 << 15) +#define AFE_ADDA6_CKDIV_RST_SFT 14 +#define AFE_ADDA6_CKDIV_RST_MASK 0x1 +#define AFE_ADDA6_CKDIV_RST_MASK_SFT (0x1 << 14) +#define AFE_ADDA6_FIFO_AUTO_RST_SFT 13 +#define AFE_ADDA6_FIFO_AUTO_RST_MASK 0x1 +#define AFE_ADDA6_FIFO_AUTO_RST_MASK_SFT (0x1 << 13) +#define UL_FIFO_DIGMIC_TESTIN_SFT 5 +#define UL_FIFO_DIGMIC_TESTIN_MASK 0x3 +#define UL_FIFO_DIGMIC_TESTIN_MASK_SFT (0x3 << 5) +#define UL_FIFO_DIGMIC_WDATA_TESTEN_SFT 4 +#define UL_FIFO_DIGMIC_WDATA_TESTEN_MASK 0x1 +#define UL_FIFO_DIGMIC_WDATA_TESTEN_MASK_SFT (0x1 << 4) +#define ADDA_AFE_ON_SFT 0 +#define ADDA_AFE_ON_MASK 0x1 +#define ADDA_AFE_ON_MASK_SFT (0x1 << 0) + +/* AFE_SIDETONE_CON0 */ +#define R_RDY_SFT 30 +#define R_RDY_MASK 0x1 +#define R_RDY_MASK_SFT (0x1 << 30) +#define W_RDY_SFT 29 +#define W_RDY_MASK 0x1 +#define W_RDY_MASK_SFT (0x1 << 29) +#define R_W_EN_SFT 25 +#define R_W_EN_MASK 0x1 +#define R_W_EN_MASK_SFT (0x1 << 25) +#define R_W_SEL_SFT 24 +#define R_W_SEL_MASK 0x1 +#define R_W_SEL_MASK_SFT (0x1 << 24) +#define SEL_CH2_SFT 23 +#define SEL_CH2_MASK 0x1 +#define SEL_CH2_MASK_SFT (0x1 << 23) +#define SIDE_TONE_COEFFICIENT_ADDR_SFT 16 +#define SIDE_TONE_COEFFICIENT_ADDR_MASK 0x1f +#define SIDE_TONE_COEFFICIENT_ADDR_MASK_SFT (0x1f << 16) +#define SIDE_TONE_COEFFICIENT_SFT 0 +#define SIDE_TONE_COEFFICIENT_MASK 0xffff +#define SIDE_TONE_COEFFICIENT_MASK_SFT (0xffff << 0) + +/* AFE_SIDETONE_COEFF */ +#define SIDE_TONE_COEFF_SFT 0 +#define SIDE_TONE_COEFF_MASK 0xffff +#define SIDE_TONE_COEFF_MASK_SFT (0xffff << 0) + +/* AFE_SIDETONE_CON1 */ +#define STF_BYPASS_MODE_SFT 31 +#define STF_BYPASS_MODE_MASK 0x1 +#define STF_BYPASS_MODE_MASK_SFT (0x1 << 31) +#define STF_BYPASS_MODE_O28_O29_SFT 30 +#define STF_BYPASS_MODE_O28_O29_MASK 0x1 +#define STF_BYPASS_MODE_O28_O29_MASK_SFT (0x1 << 30) +#define STF_BYPASS_MODE_I2S4_SFT 29 +#define STF_BYPASS_MODE_I2S4_MASK 0x1 +#define STF_BYPASS_MODE_I2S4_MASK_SFT (0x1 << 29) +#define STF_BYPASS_MODE_I2S5_SFT 28 +#define STF_BYPASS_MODE_I2S5_MASK 0x1 +#define STF_BYPASS_MODE_I2S5_MASK_SFT (0x1 << 28) +#define STF_INPUT_EN_SEL_SFT 13 +#define STF_INPUT_EN_SEL_MASK 0x1 +#define STF_INPUT_EN_SEL_MASK_SFT (0x1 << 13) +#define STF_SOURCE_FROM_O19O20_SFT 12 +#define STF_SOURCE_FROM_O19O20_MASK 0x1 +#define STF_SOURCE_FROM_O19O20_MASK_SFT (0x1 << 12) +#define SIDE_TONE_ON_SFT 8 +#define SIDE_TONE_ON_MASK 0x1 +#define SIDE_TONE_ON_MASK_SFT (0x1 << 8) +#define SIDE_TONE_HALF_TAP_NUM_SFT 0 +#define SIDE_TONE_HALF_TAP_NUM_MASK 0x3f +#define SIDE_TONE_HALF_TAP_NUM_MASK_SFT (0x3f << 0) + +/* AFE_SIDETONE_GAIN */ +#define POSITIVE_GAIN_SFT 16 +#define POSITIVE_GAIN_MASK 0x7 +#define POSITIVE_GAIN_MASK_SFT (0x7 << 16) +#define SIDE_TONE_GAIN_SFT 0 +#define SIDE_TONE_GAIN_MASK 0xffff +#define SIDE_TONE_GAIN_MASK_SFT (0xffff << 0) + +/* AFE_ADDA_DL_SDM_DCCOMP_CON */ +#define AUD_DC_COMP_EN_SFT 8 +#define AUD_DC_COMP_EN_MASK 0x1 +#define AUD_DC_COMP_EN_MASK_SFT (0x1 << 8) +#define ATTGAIN_CTL_SFT 0 +#define ATTGAIN_CTL_MASK 0x3f +#define ATTGAIN_CTL_MASK_SFT (0x3f << 0) + +/* AFE_SINEGEN_CON0 */ +#define DAC_EN_SFT 26 +#define DAC_EN_MASK 0x1 +#define DAC_EN_MASK_SFT (0x1 << 26) +#define MUTE_SW_CH2_SFT 25 +#define MUTE_SW_CH2_MASK 0x1 +#define MUTE_SW_CH2_MASK_SFT (0x1 << 25) +#define MUTE_SW_CH1_SFT 24 +#define MUTE_SW_CH1_MASK 0x1 +#define MUTE_SW_CH1_MASK_SFT (0x1 << 24) +#define SINE_MODE_CH2_SFT 20 +#define SINE_MODE_CH2_MASK 0xf +#define SINE_MODE_CH2_MASK_SFT (0xf << 20) +#define AMP_DIV_CH2_SFT 17 +#define AMP_DIV_CH2_MASK 0x7 +#define AMP_DIV_CH2_MASK_SFT (0x7 << 17) +#define FREQ_DIV_CH2_SFT 12 +#define FREQ_DIV_CH2_MASK 0x1f +#define FREQ_DIV_CH2_MASK_SFT (0x1f << 12) +#define SINE_MODE_CH1_SFT 8 +#define SINE_MODE_CH1_MASK 0xf +#define SINE_MODE_CH1_MASK_SFT (0xf << 8) +#define AMP_DIV_CH1_SFT 5 +#define AMP_DIV_CH1_MASK 0x7 +#define AMP_DIV_CH1_MASK_SFT (0x7 << 5) +#define FREQ_DIV_CH1_SFT 0 +#define FREQ_DIV_CH1_MASK 0x1f +#define FREQ_DIV_CH1_MASK_SFT (0x1f << 0) + +/* AFE_SINEGEN_CON2 */ +#define INNER_LOOP_BACK_MODE_SFT 0 +#define INNER_LOOP_BACK_MODE_MASK 0x3f +#define INNER_LOOP_BACK_MODE_MASK_SFT (0x3f << 0) + +/* AFE_MEMIF_MINLEN */ +#define HDMI_MINLEN_SFT 24 +#define HDMI_MINLEN_MASK 0xf +#define HDMI_MINLEN_MASK_SFT (0xf << 24) +#define DL3_MINLEN_SFT 12 +#define DL3_MINLEN_MASK 0xf +#define DL3_MINLEN_MASK_SFT (0xf << 12) +#define DL2_MINLEN_SFT 8 +#define DL2_MINLEN_MASK 0xf +#define DL2_MINLEN_MASK_SFT (0xf << 8) +#define DL1_DATA2_MINLEN_SFT 4 +#define DL1_DATA2_MINLEN_MASK 0xf +#define DL1_DATA2_MINLEN_MASK_SFT (0xf << 4) +#define DL1_MINLEN_SFT 0 +#define DL1_MINLEN_MASK 0xf +#define DL1_MINLEN_MASK_SFT (0xf << 0) + +/* AFE_MEMIF_MAXLEN */ +#define HDMI_MAXLEN_SFT 24 +#define HDMI_MAXLEN_MASK 0xf +#define HDMI_MAXLEN_MASK_SFT (0xf << 24) +#define DL3_MAXLEN_SFT 8 +#define DL3_MAXLEN_MASK 0xf +#define DL3_MAXLEN_MASK_SFT (0xf << 8) +#define DL2_MAXLEN_SFT 4 +#define DL2_MAXLEN_MASK 0xf +#define DL2_MAXLEN_MASK_SFT (0xf << 4) +#define DL1_MAXLEN_SFT 0 +#define DL1_MAXLEN_MASK 0x3 +#define DL1_MAXLEN_MASK_SFT (0x3 << 0) + +/* AFE_MEMIF_PBUF_SIZE */ +#define VUL12_4CH_SFT 17 +#define VUL12_4CH_MASK 0x1 +#define VUL12_4CH_MASK_SFT (0x1 << 17) +#define DL3_PBUF_SIZE_SFT 10 +#define DL3_PBUF_SIZE_MASK 0x3 +#define DL3_PBUF_SIZE_MASK_SFT (0x3 << 10) +#define HDMI_PBUF_SIZE_SFT 4 +#define HDMI_PBUF_SIZE_MASK 0x3 +#define HDMI_PBUF_SIZE_MASK_SFT (0x3 << 4) +#define DL2_PBUF_SIZE_SFT 2 +#define DL2_PBUF_SIZE_MASK 0x3 +#define DL2_PBUF_SIZE_MASK_SFT (0x3 << 2) +#define DL1_PBUF_SIZE_SFT 0 +#define DL1_PBUF_SIZE_MASK 0x3 +#define DL1_PBUF_SIZE_MASK_SFT (0x3 << 0) + +/* AFE_HD_ENGEN_ENABLE */ +#define AFE_24M_ON_SFT 1 +#define AFE_24M_ON_MASK 0x1 +#define AFE_24M_ON_MASK_SFT (0x1 << 1) +#define AFE_22M_ON_SFT 0 +#define AFE_22M_ON_MASK 0x1 +#define AFE_22M_ON_MASK_SFT (0x1 << 0) + +/* AFE_IRQ_MCU_CON0 */ +#define IRQ12_MCU_ON_SFT 12 +#define IRQ12_MCU_ON_MASK 0x1 +#define IRQ12_MCU_ON_MASK_SFT (0x1 << 12) +#define IRQ11_MCU_ON_SFT 11 +#define IRQ11_MCU_ON_MASK 0x1 +#define IRQ11_MCU_ON_MASK_SFT (0x1 << 11) +#define IRQ10_MCU_ON_SFT 10 +#define IRQ10_MCU_ON_MASK 0x1 +#define IRQ10_MCU_ON_MASK_SFT (0x1 << 10) +#define IRQ9_MCU_ON_SFT 9 +#define IRQ9_MCU_ON_MASK 0x1 +#define IRQ9_MCU_ON_MASK_SFT (0x1 << 9) +#define IRQ8_MCU_ON_SFT 8 +#define IRQ8_MCU_ON_MASK 0x1 +#define IRQ8_MCU_ON_MASK_SFT (0x1 << 8) +#define IRQ7_MCU_ON_SFT 7 +#define IRQ7_MCU_ON_MASK 0x1 +#define IRQ7_MCU_ON_MASK_SFT (0x1 << 7) +#define IRQ6_MCU_ON_SFT 6 +#define IRQ6_MCU_ON_MASK 0x1 +#define IRQ6_MCU_ON_MASK_SFT (0x1 << 6) +#define IRQ5_MCU_ON_SFT 5 +#define IRQ5_MCU_ON_MASK 0x1 +#define IRQ5_MCU_ON_MASK_SFT (0x1 << 5) +#define IRQ4_MCU_ON_SFT 4 +#define IRQ4_MCU_ON_MASK 0x1 +#define IRQ4_MCU_ON_MASK_SFT (0x1 << 4) +#define IRQ3_MCU_ON_SFT 3 +#define IRQ3_MCU_ON_MASK 0x1 +#define IRQ3_MCU_ON_MASK_SFT (0x1 << 3) +#define IRQ2_MCU_ON_SFT 2 +#define IRQ2_MCU_ON_MASK 0x1 +#define IRQ2_MCU_ON_MASK_SFT (0x1 << 2) +#define IRQ1_MCU_ON_SFT 1 +#define IRQ1_MCU_ON_MASK 0x1 +#define IRQ1_MCU_ON_MASK_SFT (0x1 << 1) +#define IRQ0_MCU_ON_SFT 0 +#define IRQ0_MCU_ON_MASK 0x1 +#define IRQ0_MCU_ON_MASK_SFT (0x1 << 0) + +/* AFE_IRQ_MCU_CON1 */ +#define IRQ7_MCU_MODE_SFT 28 +#define IRQ7_MCU_MODE_MASK 0xf +#define IRQ7_MCU_MODE_MASK_SFT (0xf << 28) +#define IRQ6_MCU_MODE_SFT 24 +#define IRQ6_MCU_MODE_MASK 0xf +#define IRQ6_MCU_MODE_MASK_SFT (0xf << 24) +#define IRQ5_MCU_MODE_SFT 20 +#define IRQ5_MCU_MODE_MASK 0xf +#define IRQ5_MCU_MODE_MASK_SFT (0xf << 20) +#define IRQ4_MCU_MODE_SFT 16 +#define IRQ4_MCU_MODE_MASK 0xf +#define IRQ4_MCU_MODE_MASK_SFT (0xf << 16) +#define IRQ3_MCU_MODE_SFT 12 +#define IRQ3_MCU_MODE_MASK 0xf +#define IRQ3_MCU_MODE_MASK_SFT (0xf << 12) +#define IRQ2_MCU_MODE_SFT 8 +#define IRQ2_MCU_MODE_MASK 0xf +#define IRQ2_MCU_MODE_MASK_SFT (0xf << 8) +#define IRQ1_MCU_MODE_SFT 4 +#define IRQ1_MCU_MODE_MASK 0xf +#define IRQ1_MCU_MODE_MASK_SFT (0xf << 4) +#define IRQ0_MCU_MODE_SFT 0 +#define IRQ0_MCU_MODE_MASK 0xf +#define IRQ0_MCU_MODE_MASK_SFT (0xf << 0) + +/* AFE_IRQ_MCU_CON2 */ +#define IRQ12_MCU_MODE_SFT 4 +#define IRQ12_MCU_MODE_MASK 0xf +#define IRQ12_MCU_MODE_MASK_SFT (0xf << 4) +#define IRQ11_MCU_MODE_SFT 0 +#define IRQ11_MCU_MODE_MASK 0xf +#define IRQ11_MCU_MODE_MASK_SFT (0xf << 0) + +/* AFE_IRQ_MCU_CLR */ +#define IRQ12_MCU_MISS_CNT_CLR_SFT 28 +#define IRQ12_MCU_MISS_CNT_CLR_MASK 0x1 +#define IRQ12_MCU_MISS_CNT_CLR_MASK_SFT (0x1 << 28) +#define IRQ11_MCU_MISS_CNT_CLR_SFT 27 +#define IRQ11_MCU_MISS_CNT_CLR_MASK 0x1 +#define IRQ11_MCU_MISS_CNT_CLR_MASK_SFT (0x1 << 27) +#define IRQ10_MCU_MISS_CLR_SFT 26 +#define IRQ10_MCU_MISS_CLR_MASK 0x1 +#define IRQ10_MCU_MISS_CLR_MASK_SFT (0x1 << 26) +#define IRQ9_MCU_MISS_CLR_SFT 25 +#define IRQ9_MCU_MISS_CLR_MASK 0x1 +#define IRQ9_MCU_MISS_CLR_MASK_SFT (0x1 << 25) +#define IRQ8_MCU_MISS_CLR_SFT 24 +#define IRQ8_MCU_MISS_CLR_MASK 0x1 +#define IRQ8_MCU_MISS_CLR_MASK_SFT (0x1 << 24) +#define IRQ7_MCU_MISS_CLR_SFT 23 +#define IRQ7_MCU_MISS_CLR_MASK 0x1 +#define IRQ7_MCU_MISS_CLR_MASK_SFT (0x1 << 23) +#define IRQ6_MCU_MISS_CLR_SFT 22 +#define IRQ6_MCU_MISS_CLR_MASK 0x1 +#define IRQ6_MCU_MISS_CLR_MASK_SFT (0x1 << 22) +#define IRQ5_MCU_MISS_CLR_SFT 21 +#define IRQ5_MCU_MISS_CLR_MASK 0x1 +#define IRQ5_MCU_MISS_CLR_MASK_SFT (0x1 << 21) +#define IRQ4_MCU_MISS_CLR_SFT 20 +#define IRQ4_MCU_MISS_CLR_MASK 0x1 +#define IRQ4_MCU_MISS_CLR_MASK_SFT (0x1 << 20) +#define IRQ3_MCU_MISS_CLR_SFT 19 +#define IRQ3_MCU_MISS_CLR_MASK 0x1 +#define IRQ3_MCU_MISS_CLR_MASK_SFT (0x1 << 19) +#define IRQ2_MCU_MISS_CLR_SFT 18 +#define IRQ2_MCU_MISS_CLR_MASK 0x1 +#define IRQ2_MCU_MISS_CLR_MASK_SFT (0x1 << 18) +#define IRQ1_MCU_MISS_CLR_SFT 17 +#define IRQ1_MCU_MISS_CLR_MASK 0x1 +#define IRQ1_MCU_MISS_CLR_MASK_SFT (0x1 << 17) +#define IRQ0_MCU_MISS_CLR_SFT 16 +#define IRQ0_MCU_MISS_CLR_MASK 0x1 +#define IRQ0_MCU_MISS_CLR_MASK_SFT (0x1 << 16) +#define IRQ12_MCU_CLR_SFT 12 +#define IRQ12_MCU_CLR_MASK 0x1 +#define IRQ12_MCU_CLR_MASK_SFT (0x1 << 12) +#define IRQ11_MCU_CLR_SFT 11 +#define IRQ11_MCU_CLR_MASK 0x1 +#define IRQ11_MCU_CLR_MASK_SFT (0x1 << 11) +#define IRQ10_MCU_CLR_SFT 10 +#define IRQ10_MCU_CLR_MASK 0x1 +#define IRQ10_MCU_CLR_MASK_SFT (0x1 << 10) +#define IRQ9_MCU_CLR_SFT 9 +#define IRQ9_MCU_CLR_MASK 0x1 +#define IRQ9_MCU_CLR_MASK_SFT (0x1 << 9) +#define IRQ8_MCU_CLR_SFT 8 +#define IRQ8_MCU_CLR_MASK 0x1 +#define IRQ8_MCU_CLR_MASK_SFT (0x1 << 8) +#define IRQ7_MCU_CLR_SFT 7 +#define IRQ7_MCU_CLR_MASK 0x1 +#define IRQ7_MCU_CLR_MASK_SFT (0x1 << 7) +#define IRQ6_MCU_CLR_SFT 6 +#define IRQ6_MCU_CLR_MASK 0x1 +#define IRQ6_MCU_CLR_MASK_SFT (0x1 << 6) +#define IRQ5_MCU_CLR_SFT 5 +#define IRQ5_MCU_CLR_MASK 0x1 +#define IRQ5_MCU_CLR_MASK_SFT (0x1 << 5) +#define IRQ4_MCU_CLR_SFT 4 +#define IRQ4_MCU_CLR_MASK 0x1 +#define IRQ4_MCU_CLR_MASK_SFT (0x1 << 4) +#define IRQ3_MCU_CLR_SFT 3 +#define IRQ3_MCU_CLR_MASK 0x1 +#define IRQ3_MCU_CLR_MASK_SFT (0x1 << 3) +#define IRQ2_MCU_CLR_SFT 2 +#define IRQ2_MCU_CLR_MASK 0x1 +#define IRQ2_MCU_CLR_MASK_SFT (0x1 << 2) +#define IRQ1_MCU_CLR_SFT 1 +#define IRQ1_MCU_CLR_MASK 0x1 +#define IRQ1_MCU_CLR_MASK_SFT (0x1 << 1) +#define IRQ0_MCU_CLR_SFT 0 +#define IRQ0_MCU_CLR_MASK 0x1 +#define IRQ0_MCU_CLR_MASK_SFT (0x1 << 0) + +/* AFE_MEMIF_MSB */ +#define CPU_COMPACT_MODE_SFT 29 +#define CPU_COMPACT_MODE_MASK 0x1 +#define CPU_COMPACT_MODE_MASK_SFT (0x1 << 29) +#define CPU_HD_ALIGN_SFT 28 +#define CPU_HD_ALIGN_MASK 0x1 +#define CPU_HD_ALIGN_MASK_SFT (0x1 << 28) +#define AWB2_AXI_WR_SIGN_SFT 24 +#define AWB2_AXI_WR_SIGN_MASK 0x1 +#define AWB2_AXI_WR_SIGN_MASK_SFT (0x1 << 24) +#define VUL2_AXI_WR_SIGN_SFT 22 +#define VUL2_AXI_WR_SIGN_MASK 0x1 +#define VUL2_AXI_WR_SIGN_MASK_SFT (0x1 << 22) +#define VUL12_AXI_WR_SIGN_SFT 21 +#define VUL12_AXI_WR_SIGN_MASK 0x1 +#define VUL12_AXI_WR_SIGN_MASK_SFT (0x1 << 21) +#define VUL_AXI_WR_SIGN_SFT 20 +#define VUL_AXI_WR_SIGN_MASK 0x1 +#define VUL_AXI_WR_SIGN_MASK_SFT (0x1 << 20) +#define MOD_DAI_AXI_WR_SIGN_SFT 18 +#define MOD_DAI_AXI_WR_SIGN_MASK 0x1 +#define MOD_DAI_AXI_WR_SIGN_MASK_SFT (0x1 << 18) +#define AWB_MSTR_SIGN_SFT 17 +#define AWB_MSTR_SIGN_MASK 0x1 +#define AWB_MSTR_SIGN_MASK_SFT (0x1 << 17) +#define SYSRAM_SIGN_SFT 16 +#define SYSRAM_SIGN_MASK 0x1 +#define SYSRAM_SIGN_MASK_SFT (0x1 << 16) + +/* AFE_HDMI_CONN0 */ +#define HDMI_O_7_SFT 21 +#define HDMI_O_7_MASK 0x7 +#define HDMI_O_7_MASK_SFT (0x7 << 21) +#define HDMI_O_6_SFT 18 +#define HDMI_O_6_MASK 0x7 +#define HDMI_O_6_MASK_SFT (0x7 << 18) +#define HDMI_O_5_SFT 15 +#define HDMI_O_5_MASK 0x7 +#define HDMI_O_5_MASK_SFT (0x7 << 15) +#define HDMI_O_4_SFT 12 +#define HDMI_O_4_MASK 0x7 +#define HDMI_O_4_MASK_SFT (0x7 << 12) +#define HDMI_O_3_SFT 9 +#define HDMI_O_3_MASK 0x7 +#define HDMI_O_3_MASK_SFT (0x7 << 9) +#define HDMI_O_2_SFT 6 +#define HDMI_O_2_MASK 0x7 +#define HDMI_O_2_MASK_SFT (0x7 << 6) +#define HDMI_O_1_SFT 3 +#define HDMI_O_1_MASK 0x7 +#define HDMI_O_1_MASK_SFT (0x7 << 3) +#define HDMI_O_0_SFT 0 +#define HDMI_O_0_MASK 0x7 +#define HDMI_O_0_MASK_SFT (0x7 << 0) + +/* AFE_TDM_CON1 */ +#define TDM_EN_SFT 0 +#define TDM_EN_MASK 0x1 +#define TDM_EN_MASK_SFT (0x1 << 0) +#define BCK_INVERSE_SFT 1 +#define BCK_INVERSE_MASK 0x1 +#define BCK_INVERSE_MASK_SFT (0x1 << 1) +#define LRCK_INVERSE_SFT 2 +#define LRCK_INVERSE_MASK 0x1 +#define LRCK_INVERSE_MASK_SFT (0x1 << 2) +#define DELAY_DATA_SFT 3 +#define DELAY_DATA_MASK 0x1 +#define DELAY_DATA_MASK_SFT (0x1 << 3) +#define LEFT_ALIGN_SFT 4 +#define LEFT_ALIGN_MASK 0x1 +#define LEFT_ALIGN_MASK_SFT (0x1 << 4) +#define WLEN_SFT 8 +#define WLEN_MASK 0x3 +#define WLEN_MASK_SFT (0x3 << 8) +#define CHANNEL_NUM_SFT 10 +#define CHANNEL_NUM_MASK 0x3 +#define CHANNEL_NUM_MASK_SFT (0x3 << 10) +#define CHANNEL_BCK_CYCLES_SFT 12 +#define CHANNEL_BCK_CYCLES_MASK 0x3 +#define CHANNEL_BCK_CYCLES_MASK_SFT (0x3 << 12) +#define DAC_BIT_NUM_SFT 16 +#define DAC_BIT_NUM_MASK 0x1f +#define DAC_BIT_NUM_MASK_SFT (0x1f << 16) +#define LRCK_TDM_WIDTH_SFT 24 +#define LRCK_TDM_WIDTH_MASK 0xff +#define LRCK_TDM_WIDTH_MASK_SFT (0xff << 24) + +/* AFE_TDM_CON2 */ +#define ST_CH_PAIR_SOUT0_SFT 0 +#define ST_CH_PAIR_SOUT0_MASK 0x7 +#define ST_CH_PAIR_SOUT0_MASK_SFT (0x7 << 0) +#define ST_CH_PAIR_SOUT1_SFT 4 +#define ST_CH_PAIR_SOUT1_MASK 0x7 +#define ST_CH_PAIR_SOUT1_MASK_SFT (0x7 << 4) +#define ST_CH_PAIR_SOUT2_SFT 8 +#define ST_CH_PAIR_SOUT2_MASK 0x7 +#define ST_CH_PAIR_SOUT2_MASK_SFT (0x7 << 8) +#define ST_CH_PAIR_SOUT3_SFT 12 +#define ST_CH_PAIR_SOUT3_MASK 0x7 +#define ST_CH_PAIR_SOUT3_MASK_SFT (0x7 << 12) +#define TDM_FIX_VALUE_SEL_SFT 16 +#define TDM_FIX_VALUE_SEL_MASK 0x1 +#define TDM_FIX_VALUE_SEL_MASK_SFT (0x1 << 16) +#define TDM_I2S_LOOPBACK_SFT 20 +#define TDM_I2S_LOOPBACK_MASK 0x1 +#define TDM_I2S_LOOPBACK_MASK_SFT (0x1 << 20) +#define TDM_I2S_LOOPBACK_CH_SFT 21 +#define TDM_I2S_LOOPBACK_CH_MASK 0x3 +#define TDM_I2S_LOOPBACK_CH_MASK_SFT (0x3 << 21) +#define TDM_FIX_VALUE_SFT 24 +#define TDM_FIX_VALUE_MASK 0xff +#define TDM_FIX_VALUE_MASK_SFT (0xff << 24) + +/* AFE_HDMI_OUT_CON0 */ +#define AFE_HDMI_OUT_ON_RETM_SFT 8 +#define AFE_HDMI_OUT_ON_RETM_MASK 0x1 +#define AFE_HDMI_OUT_ON_RETM_MASK_SFT (0x1 << 8) +#define AFE_HDMI_OUT_CH_NUM_SFT 4 +#define AFE_HDMI_OUT_CH_NUM_MASK 0xf +#define AFE_HDMI_OUT_CH_NUM_MASK_SFT (0xf << 4) +#define AFE_HDMI_OUT_BIT_WIDTH_SFT 1 +#define AFE_HDMI_OUT_BIT_WIDTH_MASK 0x1 +#define AFE_HDMI_OUT_BIT_WIDTH_MASK_SFT (0x1 << 1) +#define AFE_HDMI_OUT_ON_SFT 0 +#define AFE_HDMI_OUT_ON_MASK 0x1 +#define AFE_HDMI_OUT_ON_MASK_SFT (0x1 << 0) +#endif diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 0ab2a9dcb720..ea4929d73318 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -30,7 +30,6 @@ snd-soc-magician-objs := magician.o snd-soc-mioa701-objs := mioa701_wm9713.o snd-soc-z2-objs := z2.o snd-soc-imote2-objs := imote2.o -snd-soc-raumfeld-objs := raumfeld.o snd-soc-brownstone-objs := brownstone.o snd-soc-ttc-dkb-objs := ttc-dkb.o diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 804ae0d93058..75ceb04d8bf0 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -98,10 +98,12 @@ config SND_SOC_MSM8996 config SND_SOC_SDM845 tristate "SoC Machine driver for SDM845 boards" - depends on QCOM_APR + depends on QCOM_APR && MFD_CROS_EC select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON select SND_SOC_RT5663 + select SND_SOC_MAX98927 + select SND_SOC_CROS_EC_CODEC help To add support for audio on Qualcomm Technologies Inc. SDM845 SoC-based systems. diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index 1dd23bba1bed..4b559932adc3 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -164,41 +164,52 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) if (!cpu || !codec) { dev_err(dev, "Can't find cpu/codec DT node\n"); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto error; } link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); if (!link->cpu_of_node) { dev_err(card->dev, "error getting cpu phandle\n"); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto error; } ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); if (ret) { dev_err(card->dev, "error getting cpu dai name\n"); - return ERR_PTR(ret); + goto error; } ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { dev_err(card->dev, "error getting codec dai name\n"); - return ERR_PTR(ret); + goto error; } link->platform_of_node = link->cpu_of_node; ret = of_property_read_string(np, "link-name", &link->name); if (ret) { dev_err(card->dev, "error getting codec dai_link name\n"); - return ERR_PTR(ret); + goto error; } link->stream_name = link->name; link->init = apq8016_sbc_dai_init; link++; + + of_node_put(cpu); + of_node_put(codec); } return data; + + error: + of_node_put(np); + of_node_put(cpu); + of_node_put(codec); + return ERR_PTR(ret); } static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index fb45f396ab4a..94363fd6846a 100644 --- a/sound/soc/qcom/apq8096.c +++ b/sound/soc/qcom/apq8096.c @@ -9,6 +9,10 @@ #include <sound/pcm.h> #include "common.h" +#define SLIM_MAX_TX_PORTS 16 +#define SLIM_MAX_RX_PORTS 16 +#define WCD9335_DEFAULT_MCLK_RATE 9600000 + static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -23,14 +27,79 @@ static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret = 0; + + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret != 0 && ret != -ENOTSUPP) { + pr_err("failed to get codec chan map, err:%d\n", ret); + goto end; + } else if (ret == -ENOTSUPP) { + return 0; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + rx_ch_cnt, rx_ch); + else + ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, tx_ch, + 0, NULL); + if (ret != 0 && ret != -ENOTSUPP) + pr_err("Failed to set cpu chan map, err:%d\n", ret); + else if (ret == -ENOTSUPP) + ret = 0; +end: + return ret; +} + +static struct snd_soc_ops apq8096_ops = { + .hw_params = msm_snd_hw_params, +}; + +static int apq8096_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + /* + * Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + snd_soc_dai_set_sysclk(codec_dai, 0, WCD9335_DEFAULT_MCLK_RATE, + SNDRV_PCM_STREAM_PLAYBACK); + + return 0; +} + static void apq8096_add_be_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; int i; for_each_card_prelinks(card, i, link) { - if (link->no_pcm == 1) + if (link->no_pcm == 1) { link->be_hw_params_fixup = apq8096_be_hw_params_fixup; + link->init = apq8096_init; + link->ops = &apq8096_ops; + } } } diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 4715527054e5..5661025e8cec 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -42,6 +42,9 @@ int qcom_snd_parse_of(struct snd_soc_card *card) link = card->dai_link; for_each_child_of_node(dev->of_node, np) { cpu = of_get_child_by_name(np, "cpu"); + platform = of_get_child_by_name(np, "platform"); + codec = of_get_child_by_name(np, "codec"); + if (!cpu) { dev_err(dev, "Can't find cpu DT node\n"); ret = -EINVAL; @@ -63,8 +66,6 @@ int qcom_snd_parse_of(struct snd_soc_card *card) goto err; } - platform = of_get_child_by_name(np, "platform"); - codec = of_get_child_by_name(np, "codec"); if (codec && platform) { link->platform_of_node = of_parse_phandle(platform, "sound-dai", @@ -100,10 +101,15 @@ int qcom_snd_parse_of(struct snd_soc_card *card) link->dpcm_capture = 1; link->stream_name = link->name; link++; + + of_node_put(cpu); + of_node_put(codec); + of_node_put(platform); } return 0; err: + of_node_put(np); of_node_put(cpu); of_node_put(codec); of_node_put(platform); diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 6f66a58e23ca..882f52ed8231 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -248,12 +248,14 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) break; case SECONDARY_MI2S_TX: + codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; if (++(data->sec_mi2s_clk_count) == 1) { snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); } snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); break; case QUATERNARY_TDM_RX_0: diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 7ae580d677c8..0ae15d01a3f6 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -17,5 +17,6 @@ * otherwise actual DMA channel names must be passed to this function. */ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter, - const char *tx, const char *rx); + const char *tx, const char *rx, + struct device *dma_dev); #endif /* _SAMSUNG_DMA_H */ diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c index 9104c98deeb7..302871974cb3 100644 --- a/sound/soc/samsung/dmaengine.c +++ b/sound/soc/samsung/dmaengine.c @@ -25,9 +25,9 @@ #include "dma.h" int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter, - const char *tx, const char *rx) + const char *tx, const char *rx, + struct device *dma_dev) { - unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT; struct snd_dmaengine_pcm_config *pcm_conf; pcm_conf = devm_kzalloc(dev, sizeof(*pcm_conf), GFP_KERNEL); @@ -36,15 +36,13 @@ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter, pcm_conf->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config; pcm_conf->compat_filter_fn = filter; + pcm_conf->dma_dev = dma_dev; - if (dev->of_node) { - pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx; - pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx; - } else { - flags |= SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME; - } + pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx; + pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx; - return devm_snd_dmaengine_pcm_register(dev, pcm_conf, flags); + return devm_snd_dmaengine_pcm_register(dev, pcm_conf, + SND_DMAENGINE_PCM_FLAG_COMPAT); } EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index d4bde4834ce5..4231001226f4 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1,14 +1,9 @@ -/* sound/soc/samsung/i2s.c - * - * ALSA SoC Audio Layer - Samsung I2S Controller driver - * - * Copyright (c) 2010 Samsung Electronics Co. Ltd. - * Jaswinder Singh <jassisinghbrar@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Audio Layer - Samsung I2S Controller driver +// +// Copyright (c) 2010 Samsung Electronics Co. Ltd. +// Jaswinder Singh <jassisinghbrar@gmail.com> #include <dt-bindings/sound/samsung-i2s.h> #include <linux/delay.h> @@ -34,6 +29,9 @@ #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) +#define SAMSUNG_I2S_ID_PRIMARY 1 +#define SAMSUNG_I2S_ID_SECONDARY 2 + struct samsung_i2s_variant_regs { unsigned int bfs_off; unsigned int rfs_off; @@ -57,64 +55,88 @@ struct samsung_i2s_dai_data { struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; - /* Memory mapped SFR region */ - void __iomem *addr; - /* Rate of RCLK source clock */ - unsigned long rclk_srcrate; - /* Frame Clock */ + + /* Frame clock */ unsigned frmclk; /* - * Specifically requested RCLK,BCLK by MACHINE Driver. + * Specifically requested RCLK, BCLK by machine driver. * 0 indicates CPU driver is free to choose any value. */ unsigned rfs, bfs; - /* I2S Controller's core clock */ - struct clk *clk; - /* Clock for generating I2S signals */ - struct clk *op_clk; /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */ struct i2s_dai *pri_dai; /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */ struct i2s_dai *sec_dai; -#define DAI_OPENED (1 << 0) /* Dai is opened */ -#define DAI_MANAGER (1 << 1) /* Dai is the manager */ + +#define DAI_OPENED (1 << 0) /* DAI is opened */ +#define DAI_MANAGER (1 << 1) /* DAI is the manager */ unsigned mode; + /* Driver for this DAI */ - struct snd_soc_dai_driver i2s_dai_drv; + struct snd_soc_dai_driver *drv; + /* DMA parameters */ struct snd_dmaengine_dai_dma_data dma_playback; struct snd_dmaengine_dai_dma_data dma_capture; struct snd_dmaengine_dai_dma_data idma_playback; dma_filter_fn filter; - u32 quirks; - u32 suspend_i2smod; - u32 suspend_i2scon; - u32 suspend_i2spsr; - const struct samsung_i2s_variant_regs *variant_regs; + + struct samsung_i2s_priv *priv; +}; + +struct samsung_i2s_priv { + struct platform_device *pdev; + struct platform_device *pdev_sec; + + /* Memory mapped SFR region */ + void __iomem *addr; /* Spinlock protecting access to the device's registers */ - spinlock_t spinlock; - spinlock_t *lock; + spinlock_t lock; + + /* Lock for cross interface checks */ + spinlock_t pcm_lock; + + /* CPU DAIs and their corresponding drivers */ + struct i2s_dai *dai; + struct snd_soc_dai_driver *dai_drv; + int num_dais; + + /* The I2S controller's core clock */ + struct clk *clk; + + /* Clock for generating I2S signals */ + struct clk *op_clk; + + /* Rate of RCLK source clock */ + unsigned long rclk_srcrate; + + /* Cache of selected I2S registers for system suspend */ + u32 suspend_i2smod; + u32 suspend_i2scon; + u32 suspend_i2spsr; + + const struct samsung_i2s_variant_regs *variant_regs; + u32 quirks; - /* Below fields are only valid if this is the primary FIFO */ + /* The clock provider's data */ struct clk *clk_table[3]; struct clk_onecell_data clk_data; }; -/* Lock for cross i/f checks */ -static DEFINE_SPINLOCK(lock); - -/* If this is the 'overlay' stereo DAI */ +/* Returns true if this is the 'overlay' stereo DAI */ static inline bool is_secondary(struct i2s_dai *i2s) { - return i2s->pri_dai ? true : false; + return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY; } /* If operating in SoC-Slave mode */ static inline bool is_slave(struct i2s_dai *i2s) { - u32 mod = readl(i2s->addr + I2SMOD); - return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false; + struct samsung_i2s_priv *priv = i2s->priv; + + u32 mod = readl(priv->addr + I2SMOD); + return (mod & (1 << priv->variant_regs->mss_off)) ? true : false; } /* If this interface of the controller is transmitting data */ @@ -125,7 +147,7 @@ static inline bool tx_active(struct i2s_dai *i2s) if (!i2s) return false; - active = readl(i2s->addr + I2SCON); + active = readl(i2s->priv->addr + I2SCON); if (is_secondary(i2s)) active &= CON_TXSDMA_ACTIVE; @@ -163,7 +185,7 @@ static inline bool rx_active(struct i2s_dai *i2s) if (!i2s) return false; - active = readl(i2s->addr + I2SCON) & CON_RXDMA_ACTIVE; + active = readl(i2s->priv->addr + I2SCON) & CON_RXDMA_ACTIVE; return active ? true : false; } @@ -202,7 +224,9 @@ static inline bool any_active(struct i2s_dai *i2s) static inline struct i2s_dai *to_info(struct snd_soc_dai *dai) { - return snd_soc_dai_get_drvdata(dai); + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); + + return &priv->dai[dai->id - 1]; } static inline bool is_opened(struct i2s_dai *i2s) @@ -224,9 +248,11 @@ static inline bool is_manager(struct i2s_dai *i2s) /* Read RCLK of I2S (in multiples of LRCLK) */ static inline unsigned get_rfs(struct i2s_dai *i2s) { + struct samsung_i2s_priv *priv = i2s->priv; u32 rfs; - rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off; - rfs &= i2s->variant_regs->rfs_mask; + + rfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->rfs_off; + rfs &= priv->variant_regs->rfs_mask; switch (rfs) { case 7: return 192; @@ -243,10 +269,11 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) /* Write RCLK of I2S (in multiples of LRCLK) */ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { - u32 mod = readl(i2s->addr + I2SMOD); - int rfs_shift = i2s->variant_regs->rfs_off; + struct samsung_i2s_priv *priv = i2s->priv; + u32 mod = readl(priv->addr + I2SMOD); + int rfs_shift = priv->variant_regs->rfs_off; - mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift); + mod &= ~(priv->variant_regs->rfs_mask << rfs_shift); switch (rfs) { case 192: @@ -275,15 +302,17 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) break; } - writel(mod, i2s->addr + I2SMOD); + writel(mod, priv->addr + I2SMOD); } -/* Read Bit-Clock of I2S (in multiples of LRCLK) */ +/* Read bit-clock of I2S (in multiples of LRCLK) */ static inline unsigned get_bfs(struct i2s_dai *i2s) { + struct samsung_i2s_priv *priv = i2s->priv; u32 bfs; - bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off; - bfs &= i2s->variant_regs->bfs_mask; + + bfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->bfs_off; + bfs &= priv->variant_regs->bfs_mask; switch (bfs) { case 8: return 256; @@ -298,12 +327,13 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) } } -/* Write Bit-Clock of I2S (in multiples of LRCLK) */ +/* Write bit-clock of I2S (in multiples of LRCLK) */ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { - u32 mod = readl(i2s->addr + I2SMOD); - int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; - int bfs_shift = i2s->variant_regs->bfs_off; + struct samsung_i2s_priv *priv = i2s->priv; + u32 mod = readl(priv->addr + I2SMOD); + int tdm = priv->quirks & QUIRK_SUPPORTS_TDM; + int bfs_shift = priv->variant_regs->bfs_off; /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ if (!tdm && bfs > 48) { @@ -311,7 +341,7 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) return; } - mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift); + mod &= ~(priv->variant_regs->bfs_mask << bfs_shift); switch (bfs) { case 48: @@ -346,13 +376,13 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) return; } - writel(mod, i2s->addr + I2SMOD); + writel(mod, priv->addr + I2SMOD); } -/* Sample-Size */ +/* Sample size */ static inline int get_blc(struct i2s_dai *i2s) { - int blc = readl(i2s->addr + I2SMOD); + int blc = readl(i2s->priv->addr + I2SMOD); blc = (blc >> 13) & 0x3; @@ -363,11 +393,12 @@ static inline int get_blc(struct i2s_dai *i2s) } } -/* TX Channel Control */ +/* TX channel control */ static void i2s_txctrl(struct i2s_dai *i2s, int on) { - void __iomem *addr = i2s->addr; - int txr_off = i2s->variant_regs->txr_off; + struct samsung_i2s_priv *priv = i2s->priv; + void __iomem *addr = priv->addr; + int txr_off = priv->variant_regs->txr_off; u32 con = readl(addr + I2SCON); u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); @@ -416,8 +447,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) /* RX Channel Control */ static void i2s_rxctrl(struct i2s_dai *i2s, int on) { - void __iomem *addr = i2s->addr; - int txr_off = i2s->variant_regs->txr_off; + struct samsung_i2s_priv *priv = i2s->priv; + void __iomem *addr = priv->addr; + int txr_off = priv->variant_regs->txr_off; u32 con = readl(addr + I2SCON); u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); @@ -453,9 +485,9 @@ static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush) return; if (is_secondary(i2s)) - fic = i2s->addr + I2SFICS; + fic = i2s->priv->addr + I2SFICS; else - fic = i2s->addr + I2SFIC; + fic = i2s->priv->addr + I2SFIC; /* Flush the FIFO */ writel(readl(fic) | flush, fic); @@ -468,12 +500,13 @@ static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush) writel(readl(fic) & ~flush, fic); } -static int i2s_set_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int rfs, int dir) +static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, + int dir) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = get_other_dai(i2s); - const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; + const struct samsung_i2s_variant_regs *i2s_regs = priv->variant_regs; unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; u32 mod, mask, val = 0; @@ -482,9 +515,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, pm_runtime_get_sync(dai->dev); - spin_lock_irqsave(i2s->lock, flags); - mod = readl(i2s->addr + I2SMOD); - spin_unlock_irqrestore(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); + mod = readl(priv->addr + I2SMOD); + spin_unlock_irqrestore(&priv->lock, flags); switch (clk_id) { case SAMSUNG_I2S_OPCLK: @@ -519,51 +552,46 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ mask = 1 << i2s_regs->rclksrc_off; - if ((i2s->quirks & QUIRK_NO_MUXPSR) + if ((priv->quirks & QUIRK_NO_MUXPSR) || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) clk_id = 0; else clk_id = 1; if (!any_active(i2s)) { - if (i2s->op_clk && !IS_ERR(i2s->op_clk)) { + if (priv->op_clk && !IS_ERR(priv->op_clk)) { if ((clk_id && !(mod & rsrc_mask)) || (!clk_id && (mod & rsrc_mask))) { - clk_disable_unprepare(i2s->op_clk); - clk_put(i2s->op_clk); + clk_disable_unprepare(priv->op_clk); + clk_put(priv->op_clk); } else { - i2s->rclk_srcrate = - clk_get_rate(i2s->op_clk); + priv->rclk_srcrate = + clk_get_rate(priv->op_clk); goto done; } } if (clk_id) - i2s->op_clk = clk_get(&i2s->pdev->dev, + priv->op_clk = clk_get(&i2s->pdev->dev, "i2s_opclk1"); else - i2s->op_clk = clk_get(&i2s->pdev->dev, + priv->op_clk = clk_get(&i2s->pdev->dev, "i2s_opclk0"); - if (WARN_ON(IS_ERR(i2s->op_clk))) { - ret = PTR_ERR(i2s->op_clk); - i2s->op_clk = NULL; + if (WARN_ON(IS_ERR(priv->op_clk))) { + ret = PTR_ERR(priv->op_clk); + priv->op_clk = NULL; goto err; } - ret = clk_prepare_enable(i2s->op_clk); + ret = clk_prepare_enable(priv->op_clk); if (ret) { - clk_put(i2s->op_clk); - i2s->op_clk = NULL; + clk_put(priv->op_clk); + priv->op_clk = NULL; goto err; } - i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); + priv->rclk_srcrate = clk_get_rate(priv->op_clk); - /* Over-ride the other's */ - if (other) { - other->op_clk = i2s->op_clk; - other->rclk_srcrate = i2s->rclk_srcrate; - } } else if ((!clk_id && (mod & rsrc_mask)) || (clk_id && !(mod & rsrc_mask))) { dev_err(&i2s->pdev->dev, @@ -572,8 +600,6 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, goto err; } else { /* Call can't be on the active DAI */ - i2s->op_clk = other->op_clk; - i2s->rclk_srcrate = other->rclk_srcrate; goto done; } @@ -586,11 +612,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, goto err; } - spin_lock_irqsave(i2s->lock, flags); - mod = readl(i2s->addr + I2SMOD); + spin_lock_irqsave(&priv->lock, flags); + mod = readl(priv->addr + I2SMOD); mod = (mod & ~mask) | val; - writel(mod, i2s->addr + I2SMOD); - spin_unlock_irqrestore(i2s->lock, flags); + writel(mod, priv->addr + I2SMOD); + spin_unlock_irqrestore(&priv->lock, flags); done: pm_runtime_put(dai->dev); @@ -600,18 +626,17 @@ err: return ret; } -static int i2s_set_fmt(struct snd_soc_dai *dai, - unsigned int fmt) +static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = get_other_dai(i2s); int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; u32 mod, tmp = 0; unsigned long flags; - lrp_shift = i2s->variant_regs->lrp_off; - sdf_shift = i2s->variant_regs->sdf_off; - mod_slave = 1 << i2s->variant_regs->mss_off; + lrp_shift = priv->variant_regs->lrp_off; + sdf_shift = priv->variant_regs->sdf_off; + mod_slave = 1 << priv->variant_regs->mss_off; sdf_mask = MOD_SDF_MASK << sdf_shift; lrp_rlow = MOD_LR_RLOW << lrp_shift; @@ -662,8 +687,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any * clock configuration assigned in DT is not overwritten. */ - if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL && - other->clk_data.clks == NULL) + if (priv->rclk_srcrate == 0 && priv->clk_data.clks == NULL) i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0, 0, SND_SOC_CLOCK_IN); break; @@ -673,15 +697,15 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, } pm_runtime_get_sync(dai->dev); - spin_lock_irqsave(i2s->lock, flags); - mod = readl(i2s->addr + I2SMOD); + spin_lock_irqsave(&priv->lock, flags); + mod = readl(priv->addr + I2SMOD); /* * Don't change the I2S mode if any controller is active on this * channel. */ if (any_active(i2s) && ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); pm_runtime_put(dai->dev); dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); @@ -690,8 +714,8 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, mod &= ~(sdf_mask | lrp_rlow | mod_slave); mod |= tmp; - writel(mod, i2s->addr + I2SMOD); - spin_unlock_irqrestore(i2s->lock, flags); + writel(mod, priv->addr + I2SMOD); + spin_unlock_irqrestore(&priv->lock, flags); pm_runtime_put(dai->dev); return 0; @@ -700,8 +724,8 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, static int i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = get_other_dai(i2s); u32 mod, mask = 0, val = 0; struct clk *rclksrc; unsigned long flags; @@ -714,7 +738,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, switch (params_channels(params)) { case 6: val |= MOD_DC2_EN; - /* fall through */ + /* Fall through */ case 4: val |= MOD_DC1_EN; break; @@ -776,37 +800,35 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - spin_lock_irqsave(i2s->lock, flags); - mod = readl(i2s->addr + I2SMOD); + spin_lock_irqsave(&priv->lock, flags); + mod = readl(priv->addr + I2SMOD); mod = (mod & ~mask) | val; - writel(mod, i2s->addr + I2SMOD); - spin_unlock_irqrestore(i2s->lock, flags); + writel(mod, priv->addr + I2SMOD); + spin_unlock_irqrestore(&priv->lock, flags); snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); i2s->frmclk = params_rate(params); - rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC]; - if (!rclksrc || IS_ERR(rclksrc)) - rclksrc = other->clk_table[CLK_I2S_RCLK_SRC]; - + rclksrc = priv->clk_table[CLK_I2S_RCLK_SRC]; if (rclksrc && !IS_ERR(rclksrc)) - i2s->rclk_srcrate = clk_get_rate(rclksrc); + priv->rclk_srcrate = clk_get_rate(rclksrc); return 0; } -/* We set constraints on the substream acc to the version of I2S */ +/* We set constraints on the substream according to the version of I2S */ static int i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = get_other_dai(i2s); unsigned long flags; pm_runtime_get_sync(dai->dev); - spin_lock_irqsave(&lock, flags); + spin_lock_irqsave(&priv->pcm_lock, flags); i2s->mode |= DAI_OPENED; @@ -815,10 +837,10 @@ static int i2s_startup(struct snd_pcm_substream *substream, else i2s->mode |= DAI_MANAGER; - if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR)) - writel(CON_RSTCLR, i2s->addr + I2SCON); + if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR)) + writel(CON_RSTCLR, i2s->priv->addr + I2SCON); - spin_unlock_irqrestore(&lock, flags); + spin_unlock_irqrestore(&priv->pcm_lock, flags); return 0; } @@ -826,11 +848,12 @@ static int i2s_startup(struct snd_pcm_substream *substream, static void i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = get_other_dai(i2s); unsigned long flags; - spin_lock_irqsave(&lock, flags); + spin_lock_irqsave(&priv->pcm_lock, flags); i2s->mode &= ~DAI_OPENED; i2s->mode &= ~DAI_MANAGER; @@ -842,13 +865,14 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, i2s->rfs = 0; i2s->bfs = 0; - spin_unlock_irqrestore(&lock, flags); + spin_unlock_irqrestore(&priv->pcm_lock, flags); pm_runtime_put(dai->dev); } static int config_setup(struct i2s_dai *i2s) { + struct samsung_i2s_priv *priv = i2s->priv; struct i2s_dai *other = get_other_dai(i2s); unsigned rfs, bfs, blc; u32 psr; @@ -896,12 +920,12 @@ static int config_setup(struct i2s_dai *i2s) if (is_slave(i2s)) return 0; - if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { - psr = i2s->rclk_srcrate / i2s->frmclk / rfs; - writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR); + if (!(priv->quirks & QUIRK_NO_MUXPSR)) { + psr = priv->rclk_srcrate / i2s->frmclk / rfs; + writel(((psr - 1) << 8) | PSR_PSREN, priv->addr + I2SPSR); dev_dbg(&i2s->pdev->dev, "RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n", - i2s->rclk_srcrate, psr, rfs, bfs); + priv->rclk_srcrate, psr, rfs, bfs); } return 0; @@ -910,6 +934,7 @@ static int config_setup(struct i2s_dai *i2s) static int i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); struct snd_soc_pcm_runtime *rtd = substream->private_data; struct i2s_dai *i2s = to_info(rtd->cpu_dai); @@ -920,10 +945,10 @@ static int i2s_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: pm_runtime_get_sync(dai->dev); - spin_lock_irqsave(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); if (config_setup(i2s)) { - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); return -EINVAL; } @@ -932,12 +957,12 @@ static int i2s_trigger(struct snd_pcm_substream *substream, else i2s_txctrl(i2s, 1); - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - spin_lock_irqsave(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); if (capture) { i2s_rxctrl(i2s, 0); @@ -947,7 +972,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream, i2s_fifo(i2s, FIC_TXFLUSH); } - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); pm_runtime_put(dai->dev); break; } @@ -986,19 +1011,19 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai, static snd_pcm_sframes_t i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); - u32 reg = readl(i2s->addr + I2SFIC); + u32 reg = readl(priv->addr + I2SFIC); snd_pcm_sframes_t delay; - const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; WARN_ON(!pm_runtime_active(dai->dev)); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) delay = FIC_RXCOUNT(reg); else if (is_secondary(i2s)) - delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS)); + delay = FICS_TXCOUNT(readl(priv->addr + I2SFICS)); else - delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f; + delay = (reg >> priv->variant_regs->ftx0cnt_off) & 0x7f; return delay; } @@ -1020,39 +1045,39 @@ static int i2s_resume(struct snd_soc_dai *dai) static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = get_other_dai(i2s); unsigned long flags; pm_runtime_get_sync(dai->dev); - if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ - snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback, - NULL); + if (is_secondary(i2s)) { + /* If this is probe on the secondary DAI */ + snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, NULL); } else { snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, - &i2s->dma_capture); + &i2s->dma_capture); - if (i2s->quirks & QUIRK_NEED_RSTCLR) - writel(CON_RSTCLR, i2s->addr + I2SCON); + if (priv->quirks & QUIRK_NEED_RSTCLR) + writel(CON_RSTCLR, priv->addr + I2SCON); - if (i2s->quirks & QUIRK_SUPPORTS_IDMA) - idma_reg_addr_init(i2s->addr, - i2s->sec_dai->idma_playback.addr); + if (priv->quirks & QUIRK_SUPPORTS_IDMA) + idma_reg_addr_init(priv->addr, + other->idma_playback.addr); } /* Reset any constraint on RFS and BFS */ i2s->rfs = 0; i2s->bfs = 0; - i2s->rclk_srcrate = 0; - spin_lock_irqsave(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); i2s_txctrl(i2s, 0); i2s_rxctrl(i2s, 0); i2s_fifo(i2s, FIC_TXFLUSH); i2s_fifo(other, FIC_TXFLUSH); i2s_fifo(i2s, FIC_RXFLUSH); - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); /* Gate CDCLK by default */ if (!is_opened(other)) @@ -1065,16 +1090,17 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) { - struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); + struct i2s_dai *i2s = to_info(dai); unsigned long flags; pm_runtime_get_sync(dai->dev); if (!is_secondary(i2s)) { - if (i2s->quirks & QUIRK_NEED_RSTCLR) { - spin_lock_irqsave(i2s->lock, flags); - writel(0, i2s->addr + I2SCON); - spin_unlock_irqrestore(i2s->lock, flags); + if (priv->quirks & QUIRK_NEED_RSTCLR) { + spin_lock_irqsave(&priv->lock, flags); + writel(0, priv->addr + I2SCON); + spin_unlock_irqrestore(&priv->lock, flags); } } @@ -1094,118 +1120,157 @@ static const struct snd_soc_dai_ops samsung_i2s_dai_ops = { .delay = i2s_delay, }; +static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { + /* Backend DAI */ + SND_SOC_DAPM_AIF_OUT("Mixer DAI TX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Mixer DAI RX", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* Playback Mixer */ + SND_SOC_DAPM_MIXER("Playback Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { + { "Playback Mixer", NULL, "Primary" }, + { "Playback Mixer", NULL, "Secondary" }, + + { "Mixer DAI TX", NULL, "Playback Mixer" }, + { "Playback Mixer", NULL, "Mixer DAI RX" }, +}; + static const struct snd_soc_component_driver samsung_i2s_component = { - .name = "samsung-i2s", + .name = "samsung-i2s", + + .dapm_widgets = samsung_i2s_widgets, + .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), + + .dapm_routes = samsung_i2s_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(samsung_i2s_dapm_routes), }; -#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \ - SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE) +#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) -static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, - const struct samsung_i2s_dai_data *i2s_dai_data, - bool sec) +static int i2s_alloc_dais(struct samsung_i2s_priv *priv, + const struct samsung_i2s_dai_data *i2s_dai_data, + int num_dais) { - struct i2s_dai *i2s; - - i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL); - if (i2s == NULL) - return NULL; - - i2s->pdev = pdev; - i2s->pri_dai = NULL; - i2s->sec_dai = NULL; - i2s->i2s_dai_drv.id = 1; - i2s->i2s_dai_drv.symmetric_rates = 1; - i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe; - i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove; - i2s->i2s_dai_drv.ops = &samsung_i2s_dai_ops; - i2s->i2s_dai_drv.suspend = i2s_suspend; - i2s->i2s_dai_drv.resume = i2s_resume; - i2s->i2s_dai_drv.playback.channels_min = 1; - i2s->i2s_dai_drv.playback.channels_max = 2; - i2s->i2s_dai_drv.playback.rates = i2s_dai_data->pcm_rates; - i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS; - - if (!sec) { - i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI; - i2s->i2s_dai_drv.capture.channels_min = 1; - i2s->i2s_dai_drv.capture.channels_max = 2; - i2s->i2s_dai_drv.capture.rates = i2s_dai_data->pcm_rates; - i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; - } else { - i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI_SEC; + static const char *dai_names[] = { "samsung-i2s", "samsung-i2s-sec" }; + static const char *stream_names[] = { "Primary", "Secondary" }; + struct snd_soc_dai_driver *dai_drv; + struct i2s_dai *dai; + int i; + + priv->dai = devm_kcalloc(&priv->pdev->dev, num_dais, + sizeof(*dai), GFP_KERNEL); + if (!priv->dai) + return -ENOMEM; + + priv->dai_drv = devm_kcalloc(&priv->pdev->dev, num_dais, + sizeof(*dai_drv), GFP_KERNEL); + if (!priv->dai_drv) + return -ENOMEM; + + for (i = 0; i < num_dais; i++) { + dai_drv = &priv->dai_drv[i]; + + dai_drv->probe = samsung_i2s_dai_probe; + dai_drv->remove = samsung_i2s_dai_remove; + dai_drv->suspend = i2s_suspend; + dai_drv->resume = i2s_resume; + + dai_drv->symmetric_rates = 1; + dai_drv->ops = &samsung_i2s_dai_ops; + + dai_drv->playback.channels_min = 1; + dai_drv->playback.channels_max = 2; + dai_drv->playback.rates = i2s_dai_data->pcm_rates; + dai_drv->playback.formats = SAMSUNG_I2S_FMTS; + dai_drv->playback.stream_name = stream_names[i]; + + dai_drv->id = i + 1; + dai_drv->name = dai_names[i]; + + priv->dai[i].drv = &priv->dai_drv[i]; + priv->dai[i].pdev = priv->pdev; } - return i2s; + + /* Initialize capture only for the primary DAI */ + dai_drv = &priv->dai_drv[SAMSUNG_I2S_ID_PRIMARY - 1]; + + dai_drv->capture.channels_min = 1; + dai_drv->capture.channels_max = 2; + dai_drv->capture.rates = i2s_dai_data->pcm_rates; + dai_drv->capture.formats = SAMSUNG_I2S_FMTS; + + return 0; } #ifdef CONFIG_PM static int i2s_runtime_suspend(struct device *dev) { - struct i2s_dai *i2s = dev_get_drvdata(dev); + struct samsung_i2s_priv *priv = dev_get_drvdata(dev); - i2s->suspend_i2smod = readl(i2s->addr + I2SMOD); - i2s->suspend_i2scon = readl(i2s->addr + I2SCON); - i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR); + priv->suspend_i2smod = readl(priv->addr + I2SMOD); + priv->suspend_i2scon = readl(priv->addr + I2SCON); + priv->suspend_i2spsr = readl(priv->addr + I2SPSR); - if (i2s->op_clk) - clk_disable_unprepare(i2s->op_clk); - clk_disable_unprepare(i2s->clk); + if (priv->op_clk) + clk_disable_unprepare(priv->op_clk); + clk_disable_unprepare(priv->clk); return 0; } static int i2s_runtime_resume(struct device *dev) { - struct i2s_dai *i2s = dev_get_drvdata(dev); + struct samsung_i2s_priv *priv = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(i2s->clk); + ret = clk_prepare_enable(priv->clk); if (ret) return ret; - if (i2s->op_clk) { - ret = clk_prepare_enable(i2s->op_clk); + if (priv->op_clk) { + ret = clk_prepare_enable(priv->op_clk); if (ret) { - clk_disable_unprepare(i2s->clk); + clk_disable_unprepare(priv->clk); return ret; } } - writel(i2s->suspend_i2scon, i2s->addr + I2SCON); - writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); - writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); + writel(priv->suspend_i2scon, priv->addr + I2SCON); + writel(priv->suspend_i2smod, priv->addr + I2SMOD); + writel(priv->suspend_i2spsr, priv->addr + I2SPSR); return 0; } #endif /* CONFIG_PM */ -static void i2s_unregister_clocks(struct i2s_dai *i2s) +static void i2s_unregister_clocks(struct samsung_i2s_priv *priv) { int i; - for (i = 0; i < i2s->clk_data.clk_num; i++) { - if (!IS_ERR(i2s->clk_table[i])) - clk_unregister(i2s->clk_table[i]); + for (i = 0; i < priv->clk_data.clk_num; i++) { + if (!IS_ERR(priv->clk_table[i])) + clk_unregister(priv->clk_table[i]); } } -static void i2s_unregister_clock_provider(struct platform_device *pdev) +static void i2s_unregister_clock_provider(struct samsung_i2s_priv *priv) { - struct i2s_dai *i2s = dev_get_drvdata(&pdev->dev); - - of_clk_del_provider(pdev->dev.of_node); - i2s_unregister_clocks(i2s); + of_clk_del_provider(priv->pdev->dev.of_node); + i2s_unregister_clocks(priv); } -static int i2s_register_clock_provider(struct platform_device *pdev) + +static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) { + const char * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" }; const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" }; const char *p_names[2] = { NULL }; - struct device *dev = &pdev->dev; - struct i2s_dai *i2s = dev_get_drvdata(dev); - const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs; + struct device *dev = &priv->pdev->dev; + const struct samsung_i2s_variant_regs *reg_info = priv->variant_regs; const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)]; struct clk *rclksrc; int ret, i; @@ -1230,110 +1295,170 @@ static int i2s_register_clock_provider(struct platform_device *pdev) return -ENOMEM; } - if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { + if (!(priv->quirks & QUIRK_NO_MUXPSR)) { /* Activate the prescaler */ - u32 val = readl(i2s->addr + I2SPSR); - writel(val | PSR_PSREN, i2s->addr + I2SPSR); + u32 val = readl(priv->addr + I2SPSR); + writel(val | PSR_PSREN, priv->addr + I2SPSR); - i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev, + priv->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev, i2s_clk_name[CLK_I2S_RCLK_SRC], p_names, ARRAY_SIZE(p_names), CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, - i2s->addr + I2SMOD, reg_info->rclksrc_off, - 1, 0, i2s->lock); + priv->addr + I2SMOD, reg_info->rclksrc_off, + 1, 0, &priv->lock); - i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev, + priv->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev, i2s_clk_name[CLK_I2S_RCLK_PSR], i2s_clk_name[CLK_I2S_RCLK_SRC], CLK_SET_RATE_PARENT, - i2s->addr + I2SPSR, 8, 6, 0, i2s->lock); + priv->addr + I2SPSR, 8, 6, 0, &priv->lock); p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR]; - i2s->clk_data.clk_num = 2; + priv->clk_data.clk_num = 2; } - i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, + priv->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, i2s_clk_name[CLK_I2S_CDCLK], p_names[0], CLK_SET_RATE_PARENT, - i2s->addr + I2SMOD, reg_info->cdclkcon_off, - CLK_GATE_SET_TO_DISABLE, i2s->lock); + priv->addr + I2SMOD, reg_info->cdclkcon_off, + CLK_GATE_SET_TO_DISABLE, &priv->lock); - i2s->clk_data.clk_num += 1; - i2s->clk_data.clks = i2s->clk_table; + priv->clk_data.clk_num += 1; + priv->clk_data.clks = priv->clk_table; ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, - &i2s->clk_data); + &priv->clk_data); if (ret < 0) { dev_err(dev, "failed to add clock provider: %d\n", ret); - i2s_unregister_clocks(i2s); + i2s_unregister_clocks(priv); } return ret; } +/* Create platform device for the secondary PCM */ +static int i2s_create_secondary_device(struct samsung_i2s_priv *priv) +{ + struct platform_device *pdev_sec; + const char *devname; + int ret; + + devname = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, "%s-sec", + dev_name(&priv->pdev->dev)); + if (!devname) + return -ENOMEM; + + pdev_sec = platform_device_alloc(devname, -1); + if (!pdev_sec) + return -ENOMEM; + + pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL); + + ret = platform_device_add(pdev_sec); + if (ret < 0) { + platform_device_put(pdev_sec); + return ret; + } + + ret = device_attach(&pdev_sec->dev); + if (ret <= 0) { + platform_device_unregister(priv->pdev_sec); + dev_info(&pdev_sec->dev, "device_attach() failed\n"); + return ret; + } + + priv->pdev_sec = pdev_sec; + + return 0; +} + +static void i2s_delete_secondary_device(struct samsung_i2s_priv *priv) +{ + platform_device_unregister(priv->pdev_sec); + priv->pdev_sec = NULL; +} + static int samsung_i2s_probe(struct platform_device *pdev) { struct i2s_dai *pri_dai, *sec_dai = NULL; struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; - struct resource *res; - u32 regs_base, quirks = 0, idma_addr = 0; + u32 regs_base, idma_addr = 0; struct device_node *np = pdev->dev.of_node; const struct samsung_i2s_dai_data *i2s_dai_data; - int ret; + const struct platform_device_id *id; + struct samsung_i2s_priv *priv; + struct resource *res; + int num_dais, ret; - if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { i2s_dai_data = of_device_get_match_data(&pdev->dev); - else - i2s_dai_data = (struct samsung_i2s_dai_data *) - platform_get_device_id(pdev)->driver_data; + } else { + id = platform_get_device_id(pdev); - pri_dai = i2s_alloc_dai(pdev, i2s_dai_data, false); - if (!pri_dai) { - dev_err(&pdev->dev, "Unable to alloc I2S_pri\n"); - return -ENOMEM; + /* Nothing to do if it is the secondary device probe */ + if (!id) + return 0; + + i2s_dai_data = (struct samsung_i2s_dai_data *)id->driver_data; } - spin_lock_init(&pri_dai->spinlock); - pri_dai->lock = &pri_dai->spinlock; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - if (!np) { - if (i2s_pdata == NULL) { - dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); + if (np) { + priv->quirks = i2s_dai_data->quirks; + } else { + if (!i2s_pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); return -EINVAL; } + priv->quirks = i2s_pdata->type.quirks; + } + + num_dais = (priv->quirks & QUIRK_SEC_DAI) ? 2 : 1; + priv->pdev = pdev; + priv->variant_regs = i2s_dai_data->i2s_variant_regs; + ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais); + if (ret < 0) + return ret; + + pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1]; + + spin_lock_init(&priv->lock); + spin_lock_init(&priv->pcm_lock); + + if (!np) { pri_dai->dma_playback.filter_data = i2s_pdata->dma_playback; pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture; pri_dai->filter = i2s_pdata->dma_filter; - quirks = i2s_pdata->type.quirks; idma_addr = i2s_pdata->type.idma_addr; } else { - quirks = i2s_dai_data->quirks; if (of_property_read_u32(np, "samsung,idma-addr", &idma_addr)) { - if (quirks & QUIRK_SUPPORTS_IDMA) { + if (priv->quirks & QUIRK_SUPPORTS_IDMA) { dev_info(&pdev->dev, "idma address is not"\ "specified"); } } } - quirks &= ~(QUIRK_SEC_DAI | QUIRK_SUPPORTS_IDMA); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pri_dai->addr = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pri_dai->addr)) - return PTR_ERR(pri_dai->addr); + priv->addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->addr)) + return PTR_ERR(priv->addr); regs_base = res->start; - pri_dai->clk = devm_clk_get(&pdev->dev, "iis"); - if (IS_ERR(pri_dai->clk)) { + priv->clk = devm_clk_get(&pdev->dev, "iis"); + if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "Failed to get iis clock\n"); - return PTR_ERR(pri_dai->clk); + return PTR_ERR(priv->clk); } - ret = clk_prepare_enable(pri_dai->clk); + ret = clk_prepare_enable(priv->clk); if (ret != 0) { dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); return ret; @@ -1344,33 +1469,19 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai->dma_capture.chan_name = "rx"; pri_dai->dma_playback.addr_width = 4; pri_dai->dma_capture.addr_width = 4; - pri_dai->quirks = quirks; - pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs; + pri_dai->priv = priv; - if (quirks & QUIRK_PRI_6CHAN) - pri_dai->i2s_dai_drv.playback.channels_max = 6; + if (priv->quirks & QUIRK_PRI_6CHAN) + pri_dai->drv->playback.channels_max = 6; ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, - NULL, NULL); + "tx", "rx", NULL); if (ret < 0) goto err_disable_clk; - ret = devm_snd_soc_register_component(&pdev->dev, - &samsung_i2s_component, - &pri_dai->i2s_dai_drv, 1); - if (ret < 0) - goto err_disable_clk; - - if (quirks & QUIRK_SEC_DAI) { - sec_dai = i2s_alloc_dai(pdev, i2s_dai_data, true); - if (!sec_dai) { - dev_err(&pdev->dev, "Unable to alloc I2S_sec\n"); - ret = -ENOMEM; - goto err_disable_clk; - } + if (priv->quirks & QUIRK_SEC_DAI) { + sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1]; - sec_dai->lock = &pri_dai->spinlock; - sec_dai->variant_regs = pri_dai->variant_regs; sec_dai->dma_playback.addr = regs_base + I2STXDS; sec_dai->dma_playback.chan_name = "tx-sec"; @@ -1380,62 +1491,72 @@ static int samsung_i2s_probe(struct platform_device *pdev) } sec_dai->dma_playback.addr_width = 4; - sec_dai->addr = pri_dai->addr; - sec_dai->clk = pri_dai->clk; - sec_dai->quirks = quirks; sec_dai->idma_playback.addr = idma_addr; sec_dai->pri_dai = pri_dai; + sec_dai->priv = priv; pri_dai->sec_dai = sec_dai; - ret = samsung_asoc_dma_platform_register(&pdev->dev, - sec_dai->filter, "tx-sec", NULL); + ret = i2s_create_secondary_device(priv); if (ret < 0) goto err_disable_clk; - ret = devm_snd_soc_register_component(&pdev->dev, - &samsung_i2s_component, - &sec_dai->i2s_dai_drv, 1); + ret = samsung_asoc_dma_platform_register(&priv->pdev_sec->dev, + sec_dai->filter, "tx-sec", NULL, + &pdev->dev); if (ret < 0) - goto err_disable_clk; + goto err_del_sec; + } if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { dev_err(&pdev->dev, "Unable to configure gpio\n"); ret = -EINVAL; - goto err_disable_clk; + goto err_del_sec; } - dev_set_drvdata(&pdev->dev, pri_dai); + dev_set_drvdata(&pdev->dev, priv); + + ret = devm_snd_soc_register_component(&pdev->dev, + &samsung_i2s_component, + priv->dai_drv, num_dais); + if (ret < 0) + goto err_del_sec; pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - ret = i2s_register_clock_provider(pdev); + ret = i2s_register_clock_provider(priv); if (ret < 0) goto err_disable_pm; - pri_dai->op_clk = clk_get_parent(pri_dai->clk_table[CLK_I2S_RCLK_SRC]); + priv->op_clk = clk_get_parent(priv->clk_table[CLK_I2S_RCLK_SRC]); return 0; err_disable_pm: pm_runtime_disable(&pdev->dev); +err_del_sec: + i2s_delete_secondary_device(priv); err_disable_clk: - clk_disable_unprepare(pri_dai->clk); + clk_disable_unprepare(priv->clk); return ret; } static int samsung_i2s_remove(struct platform_device *pdev) { - struct i2s_dai *pri_dai; + struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev); - pri_dai = dev_get_drvdata(&pdev->dev); + /* The secondary device has no driver data assigned */ + if (!priv) + return 0; pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - i2s_unregister_clock_provider(pdev); - clk_disable_unprepare(pri_dai->clk); + i2s_unregister_clock_provider(priv); + i2s_delete_secondary_device(priv); + clk_disable_unprepare(priv->clk); + pm_runtime_put_noidle(&pdev->dev); return 0; diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index e7b371b07230..694512f980fd 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -7,6 +7,7 @@ */ #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/module.h> @@ -17,26 +18,52 @@ struct odroid_priv { struct snd_soc_card card; - struct snd_soc_dai_link dai_link; - struct clk *clk_i2s_bus; struct clk *sclk_i2s; + + /* Spinlock protecting fields below */ + spinlock_t lock; + unsigned int be_sample_rate; + bool be_active; }; -static int odroid_card_startup(struct snd_pcm_substream *substream) +static int odroid_card_fe_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); + return 0; } -static int odroid_card_hw_params(struct snd_pcm_substream *substream, +static int odroid_card_fe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->be_active && priv->be_sample_rate != params_rate(params)) + ret = -EINVAL; + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static const struct snd_soc_ops odroid_card_fe_ops = { + .startup = odroid_card_fe_startup, + .hw_params = odroid_card_fe_hw_params, +}; + +static int odroid_card_be_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); unsigned int pll_freq, rclk_freq, rfs; + unsigned long flags; int ret; switch (params_rate(params)) { @@ -83,22 +110,97 @@ static int odroid_card_hw_params(struct snd_pcm_substream *substream, return ret; } + spin_lock_irqsave(&priv->lock, flags); + priv->be_sample_rate = params_rate(params); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int odroid_card_be_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + priv->be_active = true; + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + priv->be_active = false; + break; + } + + spin_unlock_irqrestore(&priv->lock, flags); + return 0; } -static const struct snd_soc_ops odroid_card_ops = { - .startup = odroid_card_startup, - .hw_params = odroid_card_hw_params, +static const struct snd_soc_ops odroid_card_be_ops = { + .hw_params = odroid_card_be_hw_params, + .trigger = odroid_card_be_trigger, +}; + +/* DAPM routes for backward compatibility with old DTS */ +static const struct snd_soc_dapm_route odroid_dapm_routes[] = { + { "I2S Playback", NULL, "Mixer DAI TX" }, + { "HiFi Playback", NULL, "Mixer DAI TX" }, +}; + +static struct snd_soc_dai_link odroid_card_dais[] = { + { + /* Primary FE <-> BE link */ + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ops = &odroid_card_fe_ops, + .name = "Primary", + .stream_name = "Primary", + .platform_name = "3830000.i2s", + .dynamic = 1, + .dpcm_playback = 1, + }, { + /* BE <-> CODECs link */ + .name = "I2S Mixer", + .cpu_name = "snd-soc-dummy", + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .ops = &odroid_card_be_ops, + .no_pcm = 1, + .dpcm_playback = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, { + /* Secondary FE <-> BE link */ + .playback_only = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ops = &odroid_card_fe_ops, + .name = "Secondary", + .stream_name = "Secondary", + .platform_name = "3830000.i2s-sec", + .dynamic = 1, + .dpcm_playback = 1, + } }; static int odroid_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *cpu_dai = NULL; struct device_node *cpu, *codec; struct odroid_priv *priv; - struct snd_soc_dai_link *link; struct snd_soc_card *card; - int ret; + struct snd_soc_dai_link *link, *codec_link; + int num_pcms, ret, i; + struct of_phandle_args args = {}; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -110,6 +212,7 @@ static int odroid_audio_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->fully_routed = true; + spin_lock_init(&priv->lock); snd_soc_card_set_drvdata(card, priv); ret = snd_soc_of_parse_card_name(card, "model"); @@ -130,45 +233,78 @@ static int odroid_audio_probe(struct platform_device *pdev) return ret; } - link = &priv->dai_link; - - link->ops = &odroid_card_ops; - link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; - - card->dai_link = &priv->dai_link; - card->num_links = 1; + card->dai_link = odroid_card_dais; + card->num_links = ARRAY_SIZE(odroid_card_dais); cpu = of_get_child_by_name(dev->of_node, "cpu"); codec = of_get_child_by_name(dev->of_node, "codec"); + link = card->dai_link; + codec_link = &card->dai_link[1]; - link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); - if (!link->cpu_of_node) { - dev_err(dev, "Failed parsing cpu/sound-dai property\n"); - return -EINVAL; + /* + * For backwards compatibility create the secondary CPU DAI link only + * if there are 2 CPU DAI entries in the cpu sound-dai property in DT. + * Also add required DAPM routes not available in old DTS. + */ + num_pcms = of_count_phandle_with_args(cpu, "sound-dai", + "#sound-dai-cells"); + if (num_pcms == 1) { + card->dapm_routes = odroid_dapm_routes; + card->num_dapm_routes = ARRAY_SIZE(odroid_dapm_routes); + card->num_links--; } - ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); - if (ret < 0) - goto err_put_codec_n; + for (i = 0; i < num_pcms; i++, link += 2) { + ret = of_parse_phandle_with_args(cpu, "sound-dai", + "#sound-dai-cells", i, &args); + if (ret < 0) + break; + + if (!args.np) { + dev_err(dev, "sound-dai property parse error: %d\n", ret); + ret = -EINVAL; + break; + } - link->platform_of_node = link->cpu_of_node; + ret = snd_soc_get_dai_name(&args, &link->cpu_dai_name); + of_node_put(args.np); - link->name = "Primary"; - link->stream_name = link->name; + if (ret < 0) + break; + } + if (ret == 0) { + cpu_dai = of_parse_phandle(cpu, "sound-dai", 0); + if (!cpu_dai) + ret = -EINVAL; + } + of_node_put(cpu); + of_node_put(codec); + if (ret < 0) + return ret; + + ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link); + if (ret < 0) + goto err_put_cpu_dai; + + /* Set capture capability only for boards with the MAX98090 CODEC */ + if (codec_link->num_codecs > 1) { + card->dai_link[0].dpcm_capture = 1; + card->dai_link[1].dpcm_capture = 1; + } - priv->sclk_i2s = of_clk_get_by_name(link->cpu_of_node, "i2s_opclk1"); + priv->sclk_i2s = of_clk_get_by_name(cpu_dai, "i2s_opclk1"); if (IS_ERR(priv->sclk_i2s)) { ret = PTR_ERR(priv->sclk_i2s); - goto err_put_i2s_n; + goto err_put_cpu_dai; } - priv->clk_i2s_bus = of_clk_get_by_name(link->cpu_of_node, "iis"); + priv->clk_i2s_bus = of_clk_get_by_name(cpu_dai, "iis"); if (IS_ERR(priv->clk_i2s_bus)) { ret = PTR_ERR(priv->clk_i2s_bus); goto err_put_sclk; } + of_node_put(cpu_dai); ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { @@ -182,10 +318,9 @@ err_put_clk_i2s: clk_put(priv->clk_i2s_bus); err_put_sclk: clk_put(priv->sclk_i2s); -err_put_i2s_n: - of_node_put(link->cpu_of_node); -err_put_codec_n: - snd_soc_of_put_dai_link_codecs(link); +err_put_cpu_dai: + of_node_put(cpu_dai); + snd_soc_of_put_dai_link_codecs(codec_link); return ret; } @@ -193,8 +328,7 @@ static int odroid_audio_remove(struct platform_device *pdev) { struct odroid_priv *priv = platform_get_drvdata(pdev); - of_node_put(priv->dai_link.cpu_of_node); - snd_soc_of_put_dai_link_codecs(&priv->dai_link); + snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]); clk_put(priv->sclk_i2s); clk_put(priv->clk_i2s_bus); diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index 37f95eee1558..3c7baa561084 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -553,7 +553,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev) pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, - NULL, NULL); + NULL, NULL, NULL); if (ret) { dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); goto err_dis_pclk; diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index cc0840fff5aa..c08638b0e458 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -177,7 +177,7 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev) ret = samsung_asoc_dma_platform_register(&pdev->dev, pdata->dma_filter, - NULL, NULL); + "tx", "rx", NULL); if (ret) { pr_err("failed to register the DMA: %d\n", ret); return ret; diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 8d58d02183bf..a8026b640c95 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -446,7 +446,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev) s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO; ret = samsung_asoc_dma_platform_register(&pdev->dev, NULL, - NULL, NULL); + "tx", "rx", NULL); if (ret) { dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret); return ret; diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index cb59911e65c0..5e4afb330416 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -430,7 +430,7 @@ static int spdif_probe(struct platform_device *pdev) spdif->dma_playback = &spdif_stereo_out; ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, - NULL, NULL); + NULL, NULL, NULL); if (ret) { dev_err(&pdev->dev, "failed to register DMA: %d\n", ret); goto err4; diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index aa7e902f0c02..db929b00ae5e 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -780,7 +780,7 @@ static int fsi_clk_init(struct device *dev, return -EINVAL; } if (clock->div == clock->own) { - dev_err(dev, "cpu doens't support div clock\n"); + dev_err(dev, "cpu doesn't support div clock\n"); return -EINVAL; } } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index e819e965e1db..9834474684b1 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1031,25 +1031,19 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { .prepare = rsnd_soc_dai_prepare, }; -static void rsnd_parse_connect_simple(struct rsnd_priv *priv, - struct device_node *dai_np, - int dai_i, int is_play) +static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *dai_np) { struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); - struct rsnd_dai_stream *io = is_play ? - &rdai->playback : - &rdai->capture; struct device_node *ssiu_np = rsnd_ssiu_of_node(priv); struct device_node *np; + int is_play = rsnd_io_is_play(io); int i, j; if (!ssiu_np) return; - if (!rsnd_io_to_mod_ssi(io)) - return; - /* * This driver assumes that it is TDM Split mode * if it includes ssiu node @@ -1074,12 +1068,21 @@ static void rsnd_parse_connect_simple(struct rsnd_priv *priv, } } +static void rsnd_parse_connect_simple(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *dai_np) +{ + if (!rsnd_io_to_mod_ssi(io)) + return; + + rsnd_parse_tdm_split_mode(priv, io, dai_np); +} + static void rsnd_parse_connect_graph(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct device_node *endpoint) { struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *remote_port = of_graph_get_remote_port(endpoint); struct device_node *remote_node = of_graph_get_remote_port_parent(endpoint); if (!rsnd_io_to_mod_ssi(io)) @@ -1097,14 +1100,7 @@ static void rsnd_parse_connect_graph(struct rsnd_priv *priv, dev_dbg(dev, "%s connected to HDMI1\n", io->name); } - /* - * This driver assumes that it is TDM Split mode - * if remote node has multi endpoint - */ - if (of_get_child_count(remote_port) > 1) { - rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); - dev_dbg(dev, "%s is part of TDM Split\n", io->name); - } + rsnd_parse_tdm_split_mode(priv, io, endpoint); } void rsnd_parse_connect_common(struct rsnd_dai *rdai, @@ -1292,8 +1288,10 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) for_each_child_of_node(dai_node, dai_np) { __rsnd_dai_probe(priv, dai_np, dai_i); if (rsnd_is_gen3(priv)) { - rsnd_parse_connect_simple(priv, dai_np, dai_i, 1); - rsnd_parse_connect_simple(priv, dai_np, dai_i, 0); + struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); + rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); } dai_i++; } diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 50348a2c9203..db81e066b92e 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -14,6 +14,7 @@ */ #include "rsnd.h" +#include <linux/sys_soc.h> #define SRC_NAME "src" @@ -134,20 +135,83 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, return rate; } +const static u32 bsdsr_table_pattern1[] = { + 0x01800000, /* 6 - 1/6 */ + 0x01000000, /* 6 - 1/4 */ + 0x00c00000, /* 6 - 1/3 */ + 0x00800000, /* 6 - 1/2 */ + 0x00600000, /* 6 - 2/3 */ + 0x00400000, /* 6 - 1 */ +}; + +const static u32 bsdsr_table_pattern2[] = { + 0x02400000, /* 6 - 1/6 */ + 0x01800000, /* 6 - 1/4 */ + 0x01200000, /* 6 - 1/3 */ + 0x00c00000, /* 6 - 1/2 */ + 0x00900000, /* 6 - 2/3 */ + 0x00600000, /* 6 - 1 */ +}; + +const static u32 bsisr_table[] = { + 0x00100060, /* 6 - 1/6 */ + 0x00100040, /* 6 - 1/4 */ + 0x00100030, /* 6 - 1/3 */ + 0x00100020, /* 6 - 1/2 */ + 0x00100020, /* 6 - 2/3 */ + 0x00100020, /* 6 - 1 */ +}; + +const static u32 chan288888[] = { + 0x00000006, /* 1 to 2 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ +}; + +const static u32 chan244888[] = { + 0x00000006, /* 1 to 2 */ + 0x0000001e, /* 1 to 4 */ + 0x0000001e, /* 1 to 4 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ +}; + +const static u32 chan222222[] = { + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ +}; + +static const struct soc_device_attribute ov_soc[] = { + { .soc_id = "r8a77990" }, /* E3 */ + { /* sentinel */ } +}; + static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + const struct soc_device_attribute *soc = soc_device_match(ov_soc); int is_play = rsnd_io_is_play(io); int use_src = 0; u32 fin, fout; u32 ifscr, fsrate, adinr; u32 cr, route; - u32 bsdsr, bsisr; u32 i_busif, o_busif, tmp; + const u32 *bsdsr_table; + const u32 *chptn; uint ratio; + int chan; + int idx; if (!runtime) return; @@ -155,6 +219,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, fin = rsnd_src_get_in_rate(priv, io); fout = rsnd_src_get_out_rate(priv, io); + chan = rsnd_runtime_channel_original(io); + /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ if (fin == fout) ratio = 0; @@ -173,8 +239,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, /* * SRC_ADINR */ - adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_runtime_channel_original(io); + adinr = rsnd_get_adinr_bit(mod, io) | chan; /* * SRC_IFSCR / SRC_IFSVR @@ -207,21 +272,56 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, /* * SRC_BSDSR / SRC_BSISR + * + * see + * Combination of Register Setting Related to + * FSO/FSI Ratio and Channel, Latency */ switch (rsnd_mod_id(mod)) { + case 0: + chptn = chan288888; + bsdsr_table = bsdsr_table_pattern1; + break; + case 1: + case 3: + case 4: + chptn = chan244888; + bsdsr_table = bsdsr_table_pattern1; + break; + case 2: + case 9: + chptn = chan222222; + bsdsr_table = bsdsr_table_pattern1; + break; case 5: case 6: case 7: case 8: - bsdsr = 0x02400000; /* 6 - 1/6 */ - bsisr = 0x00100060; /* 6 - 1/6 */ + chptn = chan222222; + bsdsr_table = bsdsr_table_pattern2; break; default: - bsdsr = 0x01800000; /* 6 - 1/6 */ - bsisr = 0x00100060 ;/* 6 - 1/6 */ - break; + goto convert_rate_err; } + /* + * E3 need to overwrite + */ + if (soc) + switch (rsnd_mod_id(mod)) { + case 0: + case 4: + chptn = chan222222; + } + + for (idx = 0; idx < ARRAY_SIZE(chan222222); idx++) + if (chptn[idx] & (1 << chan)) + break; + + if (chan > 8 || + idx >= ARRAY_SIZE(chan222222)) + goto convert_rate_err; + /* BUSIF_MODE */ tmp = rsnd_get_busif_shift(io, mod); i_busif = ( is_play ? tmp : 0) | 1; @@ -234,8 +334,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_IFSCR, ifscr); rsnd_mod_write(mod, SRC_IFSVR, fsrate); rsnd_mod_write(mod, SRC_SRCCR, cr); - rsnd_mod_write(mod, SRC_BSDSR, bsdsr); - rsnd_mod_write(mod, SRC_BSISR, bsisr); + rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]); + rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]); rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif); @@ -244,6 +344,11 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); + + return; + +convert_rate_err: + dev_err(dev, "unknown BSDSR/BSDIR settings\n"); } static int rsnd_src_irq(struct rsnd_mod *mod, diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 699397a09167..03d5b9ccd3fc 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -345,17 +345,13 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) return 0; } -static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) +static int soc_compr_components_trigger(struct snd_compr_stream *cstream, + int cmd) { - struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0, __ret; - - mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + int ret; for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; @@ -364,10 +360,24 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) !component->driver->compr_ops->trigger) continue; - __ret = component->driver->compr_ops->trigger(cstream, cmd); - if (__ret < 0) - ret = __ret; + ret = component->driver->compr_ops->trigger(cstream, cmd); + if (ret < 0) + return ret; } + + return 0; +} + +static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + ret = soc_compr_components_trigger(cstream, cmd); if (ret < 0) goto out; @@ -391,27 +401,12 @@ out: static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) { struct snd_soc_pcm_runtime *fe = cstream->private_data; - struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; - int ret = 0, __ret, stream; + int ret, stream; if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || - cmd == SND_COMPR_TRIGGER_DRAIN) { - - for_each_rtdcom(fe, rtdcom) { - component = rtdcom->component; - - if (!component->driver->compr_ops || - !component->driver->compr_ops->trigger) - continue; - - __ret = component->driver->compr_ops->trigger(cstream, cmd); - if (__ret < 0) - ret = __ret; - } - return ret; - } + cmd == SND_COMPR_TRIGGER_DRAIN) + return soc_compr_components_trigger(cstream, cmd); if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -426,17 +421,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) goto out; } - for_each_rtdcom(fe, rtdcom) { - component = rtdcom->component; - - if (!component->driver->compr_ops || - !component->driver->compr_ops->trigger) - continue; - - __ret = component->driver->compr_ops->trigger(cstream, cmd); - if (__ret < 0) - ret = __ret; - } + ret = soc_compr_components_trigger(cstream, cmd); if (ret < 0) goto out; @@ -465,14 +450,35 @@ out: return ret; } -static int soc_compr_set_params(struct snd_compr_stream *cstream, - struct snd_compr_params *params) +static int soc_compr_components_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; + int ret; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_params) + continue; + + ret = component->driver->compr_ops->set_params(cstream, params); + if (ret < 0) + return ret; + } + + return 0; +} + +static int soc_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0, __ret; + int ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -489,17 +495,7 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, goto err; } - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->compr_ops || - !component->driver->compr_ops->set_params) - continue; - - __ret = component->driver->compr_ops->set_params(cstream, params); - if (__ret < 0) - ret = __ret; - } + ret = soc_compr_components_set_params(cstream, params); if (ret < 0) goto err; @@ -522,7 +518,7 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, cancel_delayed_work_sync(&rtd->delayed_work); - return ret; + return 0; err: mutex_unlock(&rtd->pcm_mutex); @@ -535,10 +531,8 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; - struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; - int ret = 0, __ret, stream; + int ret, stream; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -571,17 +565,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } - for_each_rtdcom(fe, rtdcom) { - component = rtdcom->component; - - if (!component->driver->compr_ops || - !component->driver->compr_ops->set_params) - continue; - - __ret = component->driver->compr_ops->set_params(cstream, params); - if (__ret < 0) - ret = __ret; - } + ret = soc_compr_components_set_params(cstream, params); if (ret < 0) goto out; @@ -607,7 +591,7 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0, __ret; + int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -624,9 +608,8 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, !component->driver->compr_ops->get_params) continue; - __ret = component->driver->compr_ops->get_params(cstream, params); - if (__ret < 0) - ret = __ret; + ret = component->driver->compr_ops->get_params(cstream, params); + break; } err: @@ -640,7 +623,7 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - int ret = 0, __ret; + int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -651,9 +634,8 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, !component->driver->compr_ops->get_caps) continue; - __ret = component->driver->compr_ops->get_caps(cstream, caps); - if (__ret < 0) - ret = __ret; + ret = component->driver->compr_ops->get_caps(cstream, caps); + break; } mutex_unlock(&rtd->pcm_mutex); @@ -666,7 +648,7 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - int ret = 0, __ret; + int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -677,9 +659,9 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, !component->driver->compr_ops->get_codec_caps) continue; - __ret = component->driver->compr_ops->get_codec_caps(cstream, codec); - if (__ret < 0) - ret = __ret; + ret = component->driver->compr_ops->get_codec_caps(cstream, + codec); + break; } mutex_unlock(&rtd->pcm_mutex); @@ -692,7 +674,7 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0, __ret; + int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -709,9 +691,9 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) !component->driver->compr_ops->ack) continue; - __ret = component->driver->compr_ops->ack(cstream, bytes); - if (__ret < 0) - ret = __ret; + ret = component->driver->compr_ops->ack(cstream, bytes); + if (ret < 0) + goto err; } err: @@ -725,7 +707,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - int ret = 0, __ret; + int ret = 0; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -740,9 +722,8 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, !component->driver->compr_ops->pointer) continue; - __ret = component->driver->compr_ops->pointer(cstream, tstamp); - if (__ret < 0) - ret = __ret; + ret = component->driver->compr_ops->pointer(cstream, tstamp); + break; } mutex_unlock(&rtd->pcm_mutex); @@ -781,7 +762,7 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0, __ret; + int ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_metadata) { ret = cpu_dai->driver->cops->set_metadata(cstream, metadata, cpu_dai); @@ -796,12 +777,13 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, !component->driver->compr_ops->set_metadata) continue; - __ret = component->driver->compr_ops->set_metadata(cstream, metadata); - if (__ret < 0) - ret = __ret; + ret = component->driver->compr_ops->set_metadata(cstream, + metadata); + if (ret < 0) + return ret; } - return ret; + return 0; } static int soc_compr_get_metadata(struct snd_compr_stream *cstream, @@ -811,7 +793,7 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0, __ret; + int ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->get_metadata) { ret = cpu_dai->driver->cops->get_metadata(cstream, metadata, cpu_dai); @@ -826,12 +808,11 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, !component->driver->compr_ops->get_metadata) continue; - __ret = component->driver->compr_ops->get_metadata(cstream, metadata); - if (__ret < 0) - ret = __ret; + return component->driver->compr_ops->get_metadata(cstream, + metadata); } - return ret; + return 0; } /* ASoC Compress operations */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 50617db05c46..93d316d5bf8e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -425,6 +425,14 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); +static void snd_soc_flush_all_delayed_work(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd; + + for_each_card_rtds(card, rtd) + flush_delayed_work(&rtd->delayed_work); +} + static void codec2codec_close_delayed_work(struct work_struct *work) { /* @@ -494,8 +502,7 @@ int snd_soc_suspend(struct device *dev) } /* close any waiting streams */ - for_each_card_rtds(card, rtd) - flush_delayed_work(&rtd->delayed_work); + snd_soc_flush_all_delayed_work(card); for_each_card_rtds(card, rtd) { @@ -868,7 +875,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai_link_component *codecs = dai_link->codecs; + struct snd_soc_dai_link_component *codecs; struct snd_soc_dai_link_component cpu_dai_component; struct snd_soc_component *component; struct snd_soc_dai **codec_dais; @@ -903,13 +910,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, rtd->num_codecs = dai_link->num_codecs; /* Find CODEC from registered CODECs */ - /* we can use for_each_rtd_codec_dai() after this */ codec_dais = rtd->codec_dais; - for (i = 0; i < rtd->num_codecs; i++) { - codec_dais[i] = snd_soc_find_dai(&codecs[i]); + for_each_link_codecs(dai_link, i, codecs) { + codec_dais[i] = snd_soc_find_dai(codecs); if (!codec_dais[i]) { - dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", - codecs[i].dai_name); + dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n", + codecs->dai_name); goto _err_defer; } snd_soc_rtdcom_add(rtd, codec_dais[i]->component); @@ -920,7 +926,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, /* find one from the set of registered platforms */ for_each_component(component) { - if (!snd_soc_is_matching_component(dai_link->platform, + if (!snd_soc_is_matching_component(dai_link->platforms, component)) continue; @@ -935,21 +941,25 @@ _err_defer: return -EPROBE_DEFER; } +static void soc_cleanup_component(struct snd_soc_component *component) +{ + list_del(&component->card_list); + snd_soc_dapm_free(snd_soc_component_get_dapm(component)); + soc_cleanup_component_debugfs(component); + component->card = NULL; + if (!component->driver->ignore_module_refcount) + module_put(component->dev->driver->owner); +} + static void soc_remove_component(struct snd_soc_component *component) { if (!component->card) return; - list_del(&component->card_list); - if (component->driver->remove) component->driver->remove(component); - snd_soc_dapm_free(snd_soc_component_get_dapm(component)); - - soc_cleanup_component_debugfs(component); - component->card = NULL; - module_put(component->dev->driver->owner); + soc_cleanup_component(component); } static void soc_remove_dai(struct snd_soc_dai *dai, int order) @@ -1031,22 +1041,28 @@ static void soc_remove_dai_links(struct snd_soc_card *card) static int snd_soc_init_platform(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { - struct snd_soc_dai_link_component *platform = dai_link->platform; + struct snd_soc_dai_link_component *platform = dai_link->platforms; /* - * FIXME + * REMOVE ME * - * this function should be removed in the future + * This is glue code for Legacy vs Modern dai_link. + * This function will be removed if all derivers are switched to + * modern style dai_link. + * Driver shouldn't use both legacy and modern style in the same time. + * see + * soc.h :: struct snd_soc_dai_link */ /* convert Legacy platform link */ - if (!platform || dai_link->legacy_platform) { + if (!platform) { platform = devm_kzalloc(card->dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); if (!platform) return -ENOMEM; - dai_link->platform = platform; + dai_link->platforms = platform; + dai_link->num_platforms = 1; dai_link->legacy_platform = 1; platform->name = dai_link->platform_name; platform->of_node = dai_link->platform_of_node; @@ -1061,9 +1077,38 @@ static int snd_soc_init_platform(struct snd_soc_card *card, return 0; } +static void soc_cleanup_platform(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link; + int i; + /* + * FIXME + * + * this function should be removed with snd_soc_init_platform + */ + + for_each_card_prelinks(card, i, link) { + if (link->legacy_platform) { + link->legacy_platform = 0; + link->platforms = NULL; + } + } +} + static int snd_soc_init_multicodec(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { + /* + * REMOVE ME + * + * This is glue code for Legacy vs Modern dai_link. + * This function will be removed if all derivers are switched to + * modern style dai_link. + * Driver shouldn't use both legacy and modern style in the same time. + * see + * soc.h :: struct snd_soc_dai_link + */ + /* Legacy codec/codec_dai link is a single entry in multicodec */ if (dai_link->codec_name || dai_link->codec_of_node || dai_link->codec_dai_name) { @@ -1125,11 +1170,19 @@ static int soc_init_dai_link(struct snd_soc_card *card, } } + /* FIXME */ + if (link->num_platforms > 1) { + dev_err(card->dev, + "ASoC: multi platform is not yet supported %s\n", + link->name); + return -EINVAL; + } + /* * Platform may be specified by either name or OF node, but * can be left unspecified, and a dummy platform will be used. */ - if (link->platform->name && link->platform->of_node) { + if (link->platforms->name && link->platforms->of_node) { dev_err(card->dev, "ASoC: Both platform name/of_node are set for %s\n", link->name); @@ -1140,8 +1193,8 @@ static int soc_init_dai_link(struct snd_soc_card *card, * Defer card registartion if platform dai component is not added to * component list. */ - if ((link->platform->of_node || link->platform->name) && - !soc_find_component(link->platform->of_node, link->platform->name)) + if ((link->platforms->of_node || link->platforms->name) && + !soc_find_component(link->platforms->of_node, link->platforms->name)) return -EPROBE_DEFER; /* @@ -1328,11 +1381,14 @@ static int soc_probe_component(struct snd_soc_card *card, return 0; } - if (!try_module_get(component->dev->driver->owner)) + if (!component->driver->ignore_module_refcount && + !try_module_get(component->dev->driver->owner)) return -ENODEV; component->card = card; dapm->card = card; + INIT_LIST_HEAD(&component->card_list); + INIT_LIST_HEAD(&dapm->list); soc_set_name_prefix(card, component); soc_init_component_debugfs(component); @@ -1395,12 +1451,9 @@ static int soc_probe_component(struct snd_soc_card *card, /* see for_each_card_components */ list_add(&component->card_list, &card->component_dev_list); - return 0; - err_probe: - soc_cleanup_component_debugfs(component); - component->card = NULL; - module_put(component->dev->driver->owner); + if (ret < 0) + soc_cleanup_component(component); return ret; } @@ -1585,27 +1638,24 @@ static int soc_probe_link_dais(struct snd_soc_card *card, dai_link->stream_name); return ret; } - } else { - - if (!dai_link->params) { - /* create the pcm */ - ret = soc_new_pcm(rtd, num); - if (ret < 0) { - dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", - dai_link->stream_name, ret); - return ret; - } - ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); - if (ret < 0) - return ret; - ret = soc_link_dai_pcm_new(rtd->codec_dais, - rtd->num_codecs, rtd); - if (ret < 0) - return ret; - } else { - INIT_DELAYED_WORK(&rtd->delayed_work, - codec2codec_close_delayed_work); + } else if (!dai_link->params) { + /* create the pcm */ + ret = soc_new_pcm(rtd, num); + if (ret < 0) { + dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", + dai_link->stream_name, ret); + return ret; } + ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); + if (ret < 0) + return ret; + ret = soc_link_dai_pcm_new(rtd->codec_dais, + rtd->num_codecs, rtd); + if (ret < 0) + return ret; + } else { + INIT_DELAYED_WORK(&rtd->delayed_work, + codec2codec_close_delayed_work); } return 0; @@ -1945,7 +1995,7 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) dev_err(card->dev, "init platform error"); continue; } - dai_link->platform->name = component->name; + dai_link->platforms->name = component->name; /* convert non BE into BE */ dai_link->no_pcm = 1; @@ -1981,6 +2031,30 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) } } +static int soc_cleanup_card_resources(struct snd_soc_card *card) +{ + /* free the ALSA card at first; this syncs with pending operations */ + if (card->snd_card) + snd_card_free(card->snd_card); + + /* remove and free each DAI */ + soc_remove_dai_links(card); + soc_remove_pcm_runtimes(card); + soc_cleanup_platform(card); + + /* remove auxiliary devices */ + soc_remove_aux_devices(card); + + snd_soc_dapm_free(&card->dapm); + soc_cleanup_card_debugfs(card); + + /* remove the card */ + if (card->remove) + card->remove(card); + + return 0; +} + static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -1990,6 +2064,11 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) mutex_lock(&client_mutex); mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + card->dapm.bias_level = SND_SOC_BIAS_OFF; + card->dapm.dev = card->dev; + card->dapm.card = card; + list_add(&card->dapm.list, &card->dapm_list); + /* check whether any platform is ignore machine FE and using topology */ soc_check_tplg_fes(card); @@ -1997,14 +2076,14 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) for_each_card_prelinks(card, i, dai_link) { ret = soc_bind_dai_link(card, dai_link); if (ret != 0) - goto base_error; + goto probe_end; } /* bind aux_devs too */ for (i = 0; i < card->num_aux_devs; i++) { ret = soc_bind_aux_dev(card, i); if (ret != 0) - goto base_error; + goto probe_end; } /* add predefined DAI links to the list */ @@ -2018,16 +2097,11 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) dev_err(card->dev, "ASoC: can't create sound card for card %s: %d\n", card->name, ret); - goto base_error; + goto probe_end; } soc_init_card_debugfs(card); - card->dapm.bias_level = SND_SOC_BIAS_OFF; - card->dapm.dev = card->dev; - card->dapm.card = card; - list_add(&card->dapm.list, &card->dapm_list); - #ifdef CONFIG_DEBUG_FS snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); #endif @@ -2049,7 +2123,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) if (card->probe) { ret = card->probe(card); if (ret < 0) - goto card_probe_error; + goto probe_end; } /* probe all components used by DAI links on this card */ @@ -2060,7 +2134,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) dev_err(card->dev, "ASoC: failed to instantiate card %d\n", ret); - goto probe_dai_err; + goto probe_end; } } } @@ -2068,7 +2142,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) /* probe auxiliary components */ ret = soc_probe_aux_devices(card); if (ret < 0) - goto probe_dai_err; + goto probe_end; /* * Find new DAI links added during probing components and bind them. @@ -2080,10 +2154,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) ret = soc_init_dai_link(card, dai_link); if (ret) - goto probe_dai_err; + goto probe_end; ret = soc_bind_dai_link(card, dai_link); if (ret) - goto probe_dai_err; + goto probe_end; } /* probe all DAI links on this card */ @@ -2094,7 +2168,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) dev_err(card->dev, "ASoC: failed to instantiate card %d\n", ret); - goto probe_dai_err; + goto probe_end; } } } @@ -2141,7 +2215,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) if (ret < 0) { dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n", card->name, ret); - goto probe_aux_dev_err; + goto probe_end; } } @@ -2151,33 +2225,17 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) if (ret < 0) { dev_err(card->dev, "ASoC: failed to register soundcard %d\n", ret); - goto probe_aux_dev_err; + goto probe_end; } card->instantiated = 1; dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); - mutex_unlock(&card->mutex); - mutex_unlock(&client_mutex); - return 0; - -probe_aux_dev_err: - soc_remove_aux_devices(card); - -probe_dai_err: - soc_remove_dai_links(card); - -card_probe_error: - if (card->remove) - card->remove(card); - - snd_soc_dapm_free(&card->dapm); - soc_cleanup_card_debugfs(card); - snd_card_free(card->snd_card); +probe_end: + if (ret < 0) + soc_cleanup_card_resources(card); -base_error: - soc_remove_pcm_runtimes(card); mutex_unlock(&card->mutex); mutex_unlock(&client_mutex); @@ -2206,34 +2264,6 @@ static int soc_probe(struct platform_device *pdev) return snd_soc_register_card(card); } -static int soc_cleanup_card_resources(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - /* make sure any delayed work runs */ - for_each_card_rtds(card, rtd) - flush_delayed_work(&rtd->delayed_work); - - /* free the ALSA card at first; this syncs with pending operations */ - snd_card_free(card->snd_card); - - /* remove and free each DAI */ - soc_remove_dai_links(card); - soc_remove_pcm_runtimes(card); - - /* remove auxiliary devices */ - soc_remove_aux_devices(card); - - snd_soc_dapm_free(&card->dapm); - soc_cleanup_card_debugfs(card); - - /* remove the card */ - if (card->remove) - card->remove(card); - - return 0; -} - /* removes a socdev */ static int soc_remove(struct platform_device *pdev) { @@ -2255,8 +2285,7 @@ int snd_soc_poweroff(struct device *dev) * Flush out pmdown_time work - we actually do want to run it * now, we're shutting down so no imminent restart. */ - for_each_card_rtds(card, rtd) - flush_delayed_work(&rtd->delayed_work); + snd_soc_flush_all_delayed_work(card); snd_soc_dapm_shutdown(card); @@ -2800,6 +2829,7 @@ static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) if (card->instantiated) { card->instantiated = false; snd_soc_dapm_shutdown(card); + snd_soc_flush_all_delayed_work(card); soc_cleanup_card_resources(card); if (!unregister) list_add(&card->list, &unbind_card_list); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 20bad755888b..1ec06ef6d161 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -64,85 +64,85 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { - [snd_soc_dapm_pre] = 0, - [snd_soc_dapm_regulator_supply] = 1, - [snd_soc_dapm_pinctrl] = 1, - [snd_soc_dapm_clock_supply] = 1, - [snd_soc_dapm_supply] = 2, - [snd_soc_dapm_micbias] = 3, - [snd_soc_dapm_vmid] = 3, - [snd_soc_dapm_dai_link] = 2, - [snd_soc_dapm_dai_in] = 4, - [snd_soc_dapm_dai_out] = 4, - [snd_soc_dapm_aif_in] = 4, - [snd_soc_dapm_aif_out] = 4, - [snd_soc_dapm_mic] = 5, - [snd_soc_dapm_siggen] = 5, - [snd_soc_dapm_input] = 5, - [snd_soc_dapm_output] = 5, - [snd_soc_dapm_mux] = 6, - [snd_soc_dapm_demux] = 6, - [snd_soc_dapm_dac] = 7, - [snd_soc_dapm_switch] = 8, - [snd_soc_dapm_mixer] = 8, - [snd_soc_dapm_mixer_named_ctl] = 8, - [snd_soc_dapm_pga] = 9, - [snd_soc_dapm_buffer] = 9, - [snd_soc_dapm_scheduler] = 9, - [snd_soc_dapm_effect] = 9, - [snd_soc_dapm_src] = 9, - [snd_soc_dapm_asrc] = 9, - [snd_soc_dapm_encoder] = 9, - [snd_soc_dapm_decoder] = 9, - [snd_soc_dapm_adc] = 10, - [snd_soc_dapm_out_drv] = 11, - [snd_soc_dapm_hp] = 11, - [snd_soc_dapm_spk] = 11, - [snd_soc_dapm_line] = 11, - [snd_soc_dapm_sink] = 11, - [snd_soc_dapm_kcontrol] = 12, - [snd_soc_dapm_post] = 13, + [snd_soc_dapm_pre] = 1, + [snd_soc_dapm_regulator_supply] = 2, + [snd_soc_dapm_pinctrl] = 2, + [snd_soc_dapm_clock_supply] = 2, + [snd_soc_dapm_supply] = 3, + [snd_soc_dapm_micbias] = 4, + [snd_soc_dapm_vmid] = 4, + [snd_soc_dapm_dai_link] = 3, + [snd_soc_dapm_dai_in] = 5, + [snd_soc_dapm_dai_out] = 5, + [snd_soc_dapm_aif_in] = 5, + [snd_soc_dapm_aif_out] = 5, + [snd_soc_dapm_mic] = 6, + [snd_soc_dapm_siggen] = 6, + [snd_soc_dapm_input] = 6, + [snd_soc_dapm_output] = 6, + [snd_soc_dapm_mux] = 7, + [snd_soc_dapm_demux] = 7, + [snd_soc_dapm_dac] = 8, + [snd_soc_dapm_switch] = 9, + [snd_soc_dapm_mixer] = 9, + [snd_soc_dapm_mixer_named_ctl] = 9, + [snd_soc_dapm_pga] = 10, + [snd_soc_dapm_buffer] = 10, + [snd_soc_dapm_scheduler] = 10, + [snd_soc_dapm_effect] = 10, + [snd_soc_dapm_src] = 10, + [snd_soc_dapm_asrc] = 10, + [snd_soc_dapm_encoder] = 10, + [snd_soc_dapm_decoder] = 10, + [snd_soc_dapm_adc] = 11, + [snd_soc_dapm_out_drv] = 12, + [snd_soc_dapm_hp] = 12, + [snd_soc_dapm_spk] = 12, + [snd_soc_dapm_line] = 12, + [snd_soc_dapm_sink] = 12, + [snd_soc_dapm_kcontrol] = 13, + [snd_soc_dapm_post] = 14, }; static int dapm_down_seq[] = { - [snd_soc_dapm_pre] = 0, - [snd_soc_dapm_kcontrol] = 1, - [snd_soc_dapm_adc] = 2, - [snd_soc_dapm_hp] = 3, - [snd_soc_dapm_spk] = 3, - [snd_soc_dapm_line] = 3, - [snd_soc_dapm_out_drv] = 3, - [snd_soc_dapm_sink] = 3, - [snd_soc_dapm_pga] = 4, - [snd_soc_dapm_buffer] = 4, - [snd_soc_dapm_scheduler] = 4, - [snd_soc_dapm_effect] = 4, - [snd_soc_dapm_src] = 4, - [snd_soc_dapm_asrc] = 4, - [snd_soc_dapm_encoder] = 4, - [snd_soc_dapm_decoder] = 4, - [snd_soc_dapm_switch] = 5, - [snd_soc_dapm_mixer_named_ctl] = 5, - [snd_soc_dapm_mixer] = 5, - [snd_soc_dapm_dac] = 6, - [snd_soc_dapm_mic] = 7, - [snd_soc_dapm_siggen] = 7, - [snd_soc_dapm_input] = 7, - [snd_soc_dapm_output] = 7, - [snd_soc_dapm_micbias] = 8, - [snd_soc_dapm_vmid] = 8, - [snd_soc_dapm_mux] = 9, - [snd_soc_dapm_demux] = 9, - [snd_soc_dapm_aif_in] = 10, - [snd_soc_dapm_aif_out] = 10, - [snd_soc_dapm_dai_in] = 10, - [snd_soc_dapm_dai_out] = 10, - [snd_soc_dapm_dai_link] = 11, - [snd_soc_dapm_supply] = 12, - [snd_soc_dapm_clock_supply] = 13, - [snd_soc_dapm_pinctrl] = 13, - [snd_soc_dapm_regulator_supply] = 13, - [snd_soc_dapm_post] = 14, + [snd_soc_dapm_pre] = 1, + [snd_soc_dapm_kcontrol] = 2, + [snd_soc_dapm_adc] = 3, + [snd_soc_dapm_hp] = 4, + [snd_soc_dapm_spk] = 4, + [snd_soc_dapm_line] = 4, + [snd_soc_dapm_out_drv] = 4, + [snd_soc_dapm_sink] = 4, + [snd_soc_dapm_pga] = 5, + [snd_soc_dapm_buffer] = 5, + [snd_soc_dapm_scheduler] = 5, + [snd_soc_dapm_effect] = 5, + [snd_soc_dapm_src] = 5, + [snd_soc_dapm_asrc] = 5, + [snd_soc_dapm_encoder] = 5, + [snd_soc_dapm_decoder] = 5, + [snd_soc_dapm_switch] = 6, + [snd_soc_dapm_mixer_named_ctl] = 6, + [snd_soc_dapm_mixer] = 6, + [snd_soc_dapm_dac] = 7, + [snd_soc_dapm_mic] = 8, + [snd_soc_dapm_siggen] = 8, + [snd_soc_dapm_input] = 8, + [snd_soc_dapm_output] = 8, + [snd_soc_dapm_micbias] = 9, + [snd_soc_dapm_vmid] = 9, + [snd_soc_dapm_mux] = 10, + [snd_soc_dapm_demux] = 10, + [snd_soc_dapm_aif_in] = 11, + [snd_soc_dapm_aif_out] = 11, + [snd_soc_dapm_dai_in] = 11, + [snd_soc_dapm_dai_out] = 11, + [snd_soc_dapm_dai_link] = 12, + [snd_soc_dapm_supply] = 13, + [snd_soc_dapm_clock_supply] = 14, + [snd_soc_dapm_pinctrl] = 14, + [snd_soc_dapm_regulator_supply] = 14, + [snd_soc_dapm_post] = 15, }; static void dapm_assert_locked(struct snd_soc_dapm_context *dapm) @@ -157,6 +157,7 @@ static void pop_wait(u32 pop_time) schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time)); } +__printf(3, 4) static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...) { va_list args; @@ -319,7 +320,24 @@ EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); static inline struct snd_soc_dapm_widget *dapm_cnew_widget( const struct snd_soc_dapm_widget *_widget) { - return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); + struct snd_soc_dapm_widget *w; + + w = kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); + if (!w) + return NULL; + + /* + * w->name is duplicated in caller, but w->sname isn't. + * Duplicate it here if defined + */ + if (_widget->sname) { + w->sname = kstrdup_const(_widget->sname, GFP_KERNEL); + if (!w->sname) { + kfree(w); + return NULL; + } + } + return w; } struct dapm_kcontrol_data { @@ -1410,11 +1428,17 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a, { int *sort; + BUILD_BUG_ON(ARRAY_SIZE(dapm_up_seq) != SND_SOC_DAPM_TYPE_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(dapm_down_seq) != SND_SOC_DAPM_TYPE_COUNT); + if (power_up) sort = dapm_up_seq; else sort = dapm_down_seq; + WARN_ONCE(sort[a->id] == 0, "offset a->id %d not initialized\n", a->id); + WARN_ONCE(sort[b->id] == 0, "offset b->id %d not initialized\n", b->id); + if (sort[a->id] != sort[b->id]) return sort[a->id] - sort[b->id]; if (a->subseq != b->subseq) { @@ -2436,6 +2460,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) kfree(w->kcontrols); kfree_const(w->name); + kfree_const(w->sname); kfree(w); } @@ -2549,6 +2574,81 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); +static int dapm_update_dai_chan(struct snd_soc_dapm_path *p, + struct snd_soc_dapm_widget *w, + int channels) +{ + switch (w->id) { + case snd_soc_dapm_aif_out: + case snd_soc_dapm_aif_in: + break; + default: + return 0; + } + + dev_dbg(w->dapm->dev, "%s DAI route %s -> %s\n", + w->channel < channels ? "Connecting" : "Disconnecting", + p->source->name, p->sink->name); + + if (w->channel < channels) + soc_dapm_connect_path(p, true, "dai update"); + else + soc_dapm_connect_path(p, false, "dai update"); + + return 0; +} + +static int dapm_update_dai_unlocked(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int dir = substream->stream; + int channels = params_channels(params); + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *w; + int ret; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + w = dai->playback_widget; + else + w = dai->capture_widget; + + if (!w) + return 0; + + dev_dbg(dai->dev, "Update DAI routes for %s %s\n", dai->name, + dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + ret = dapm_update_dai_chan(p, p->sink, channels); + if (ret < 0) + return ret; + } + + snd_soc_dapm_widget_for_each_source_path(w, p) { + ret = dapm_update_dai_chan(p, p->source, channels); + if (ret < 0) + return ret; + } + + return 0; +} + +int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret; + + mutex_lock_nested(&rtd->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + ret = dapm_update_dai_unlocked(substream, params, dai); + mutex_unlock(&rtd->card->dapm_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_update_dai); + /* * dapm_update_widget_flags() - Re-compute widget sink and source flags * @w: The widget for which to update the flags @@ -2765,6 +2865,8 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, char prefixed_sink[80]; char prefixed_source[80]; const char *prefix; + unsigned int sink_ref = 0; + unsigned int source_ref = 0; int ret; prefix = soc_dapm_prefix(dapm); @@ -2798,6 +2900,11 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, if (wsource) break; } + sink_ref++; + if (sink_ref > 1) + dev_warn(dapm->dev, + "ASoC: sink widget %s overwritten\n", + w->name); continue; } if (!wsource && !(strcmp(w->name, source))) { @@ -2807,6 +2914,11 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, if (wsink) break; } + source_ref++; + if (source_ref > 1) + dev_warn(dapm->dev, + "ASoC: source widget %s overwritten\n", + w->name); } } /* use widget from another DAPM context if not found from this */ @@ -3493,6 +3605,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, else w->name = kstrdup_const(widget->name, GFP_KERNEL); if (w->name == NULL) { + kfree_const(w->sname); kfree(w); return ERR_PTR(-ENOMEM); } @@ -3713,6 +3826,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, ret = soc_dai_hw_params(&substream, params, source); if (ret < 0) goto out; + + dapm_update_dai_unlocked(&substream, params, source); } substream.stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -3733,6 +3848,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, ret = soc_dai_hw_params(&substream, params, sink); if (ret < 0) goto out; + + dapm_update_dai_unlocked(&substream, params, sink); } break; diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 30e791a53352..f1ab6285a085 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -265,7 +265,6 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) struct dmaengine_pcm *pcm = soc_component_to_pcm(component); const struct snd_dmaengine_pcm_config *config = pcm->config; struct device *dev = component->dev; - struct snd_dmaengine_dai_dma_data *dma_data; struct snd_pcm_substream *substream; size_t prealloc_buffer_size; size_t max_buffer_size; @@ -285,12 +284,9 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) if (!substream) continue; - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - if (!pcm->chan[i] && - (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) + if (!pcm->chan[i] && config && config->chan_names[i]) pcm->chan[i] = dma_request_slave_channel(dev, - dma_data->chan_name); + config->chan_names[i]); if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) { pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, @@ -413,9 +409,8 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, const char *name; struct dma_chan *chan; - if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | - SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || - !dev->of_node) + if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || (!dev->of_node && + !(config && config->dma_dev && config->dma_dev->of_node))) return 0; if (config && config->dma_dev) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 03f36e534050..a5b40e82dea4 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -969,6 +969,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, codec_dai->channels = params_channels(&codec_params); codec_dai->sample_bits = snd_pcm_format_physical_width( params_format(&codec_params)); + + snd_soc_dapm_update_dai(substream, &codec_params, codec_dai); } ret = soc_dai_hw_params(substream, params, cpu_dai); @@ -998,6 +1000,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, cpu_dai->sample_bits = snd_pcm_format_physical_width(params_format(params)); + snd_soc_dapm_update_dai(substream, params, cpu_dai); + ret = soc_pcm_params_symmetry(substream, params); if (ret) goto component_err; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 731b963b6995..25fca7055464 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -382,10 +382,10 @@ static void remove_mixer(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->control_unload) dobj->ops->control_unload(comp, dobj); - if (sm->dobj.control.kcontrol->tlv.p) - p = sm->dobj.control.kcontrol->tlv.p; - snd_ctl_remove(card, sm->dobj.control.kcontrol); - list_del(&sm->dobj.list); + if (dobj->control.kcontrol->tlv.p) + p = dobj->control.kcontrol->tlv.p; + snd_ctl_remove(card, dobj->control.kcontrol); + list_del(&dobj->list); kfree(sm); kfree(p); } @@ -404,12 +404,13 @@ static void remove_enum(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->control_unload) dobj->ops->control_unload(comp, dobj); - snd_ctl_remove(card, se->dobj.control.kcontrol); - list_del(&se->dobj.list); + snd_ctl_remove(card, dobj->control.kcontrol); + list_del(&dobj->list); - kfree(se->dobj.control.dvalues); + kfree(dobj->control.dvalues); for (i = 0; i < se->items; i++) - kfree(se->dobj.control.dtexts[i]); + kfree(dobj->control.dtexts[i]); + kfree(dobj->control.dtexts); kfree(se); } @@ -427,11 +428,28 @@ static void remove_bytes(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->control_unload) dobj->ops->control_unload(comp, dobj); - snd_ctl_remove(card, sb->dobj.control.kcontrol); - list_del(&sb->dobj.list); + snd_ctl_remove(card, dobj->control.kcontrol); + list_del(&dobj->list); kfree(sb); } +/* remove a route */ +static void remove_route(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + struct snd_soc_dapm_route *route = + container_of(dobj, struct snd_soc_dapm_route, dobj); + + if (pass != SOC_TPLG_PASS_GRAPH) + return; + + if (dobj->ops && dobj->ops->dapm_route_unload) + dobj->ops->dapm_route_unload(comp, dobj); + + list_del(&dobj->list); + kfree(route); +} + /* remove a widget and it's kcontrols - routes must be removed first */ static void remove_widget(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) @@ -464,9 +482,10 @@ static void remove_widget(struct snd_soc_component *comp, snd_ctl_remove(card, kcontrol); - kfree(se->dobj.control.dvalues); + kfree(dobj->control.dvalues); for (j = 0; j < se->items; j++) - kfree(se->dobj.control.dtexts[j]); + kfree(dobj->control.dtexts[j]); + kfree(dobj->control.dtexts); kfree(se); kfree(w->kcontrol_news[i].name); @@ -493,6 +512,8 @@ static void remove_widget(struct snd_soc_component *comp, free_news: kfree(w->kcontrol_news); + list_del(&dobj->list); + /* widget w is freed by soc-dapm.c */ } @@ -541,6 +562,25 @@ static void remove_link(struct snd_soc_component *comp, kfree(link); } +/* unload dai link */ +static void remove_backend_link(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + if (pass != SOC_TPLG_PASS_LINK) + return; + + if (dobj->ops && dobj->ops->link_unload) + dobj->ops->link_unload(comp, dobj); + + /* + * We don't free the link here as what remove_link() do since BE + * links are not allocated by topology. + * We however need to reset the dobj type to its initial values + */ + dobj->type = SND_SOC_DOBJ_NONE; + list_del(&dobj->list); +} + /* bind a kcontrol to it's IO handlers */ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, struct snd_kcontrol_new *k, @@ -1120,9 +1160,10 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; - struct snd_soc_dapm_route route; struct snd_soc_tplg_dapm_graph_elem *elem; - int count = hdr->count, i; + struct snd_soc_dapm_route **routes; + int count = hdr->count, i, j; + int ret = 0; if (tplg->pass != SOC_TPLG_PASS_GRAPH) { tplg->pos += hdr->size + hdr->payload_size; @@ -1141,36 +1182,85 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count, hdr->index); + /* allocate memory for pointer to array of dapm routes */ + routes = kcalloc(count, sizeof(struct snd_soc_dapm_route *), + GFP_KERNEL); + if (!routes) + return -ENOMEM; + + /* + * allocate memory for each dapm route in the array. + * This needs to be done individually so that + * each route can be freed when it is removed in remove_route(). + */ + for (i = 0; i < count; i++) { + routes[i] = kzalloc(sizeof(*routes[i]), GFP_KERNEL); + if (!routes[i]) { + /* free previously allocated memory */ + for (j = 0; j < i; j++) + kfree(routes[j]); + + kfree(routes); + return -ENOMEM; + } + } + for (i = 0; i < count; i++) { elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos; tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem); /* validate routes */ if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { + ret = -EINVAL; + break; + } if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { + ret = -EINVAL; + break; + } if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { + ret = -EINVAL; + break; + } + + routes[i]->source = elem->source; + routes[i]->sink = elem->sink; - route.source = elem->source; - route.sink = elem->sink; - route.connected = NULL; /* set to NULL atm for tplg users */ + /* set to NULL atm for tplg users */ + routes[i]->connected = NULL; if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0) - route.control = NULL; + routes[i]->control = NULL; else - route.control = elem->control; + routes[i]->control = elem->control; - soc_tplg_add_route(tplg, &route); + /* add route dobj to dobj_list */ + routes[i]->dobj.type = SND_SOC_DOBJ_GRAPH; + routes[i]->dobj.ops = tplg->ops; + routes[i]->dobj.index = tplg->index; + list_add(&routes[i]->dobj.list, &tplg->comp->dobj_list); + + soc_tplg_add_route(tplg, routes[i]); /* add route, but keep going if some fail */ - snd_soc_dapm_add_routes(dapm, &route, 1); + snd_soc_dapm_add_routes(dapm, routes[i], 1); } - return 0; + /* free memory allocated for all dapm routes in case of error */ + if (ret < 0) + for (i = 0; i < count ; i++) + kfree(routes[i]); + + /* + * free pointer to array of dapm routes as this is no longer needed. + * The memory allocated for each dapm route will be freed + * when it is removed in remove_route(). + */ + kfree(routes); + + return ret; } static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( @@ -1364,6 +1454,7 @@ err_se: kfree(se->dobj.control.dvalues); for (j = 0; j < ec->items; j++) kfree(se->dobj.control.dtexts[j]); + kfree(se->dobj.control.dtexts); kfree(se); kfree(kc[i].name); @@ -1583,6 +1674,9 @@ widget: if (ret < 0) goto ready_err; + kfree(template.sname); + kfree(template.name); + return 0; ready_err: @@ -2093,6 +2187,12 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, return ret; } + /* for unloading it in snd_soc_tplg_component_remove */ + link->dobj.index = tplg->index; + link->dobj.ops = tplg->ops; + link->dobj.type = SND_SOC_DOBJ_BACKEND_LINK; + list_add(&link->dobj.list, &tplg->comp->dobj_list); + return 0; } @@ -2573,6 +2673,9 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) case SND_SOC_DOBJ_BYTES: remove_bytes(comp, dobj, pass); break; + case SND_SOC_DOBJ_GRAPH: + remove_route(comp, dobj, pass); + break; case SND_SOC_DOBJ_WIDGET: remove_widget(comp, dobj, pass); break; @@ -2582,6 +2685,13 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) case SND_SOC_DOBJ_DAI_LINK: remove_link(comp, dobj, pass); break; + case SND_SOC_DOBJ_BACKEND_LINK: + /* + * call link_unload ops if extra + * deinitialization is needed. + */ + remove_backend_link(comp, dobj, pass); + break; default: dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", dobj->type); diff --git a/sound/soc/sprd/Kconfig b/sound/soc/sprd/Kconfig new file mode 100644 index 000000000000..43ece7daf0e9 --- /dev/null +++ b/sound/soc/sprd/Kconfig @@ -0,0 +1,6 @@ +config SND_SOC_SPRD + tristate "SoC Audio for the Spreadtrum SoC chips" + depends on ARCH_SPRD || COMPILE_TEST + help + Say Y or M if you want to add support for codecs attached to + the Spreadtrum SoCs' Audio interfaces. diff --git a/sound/soc/sprd/Makefile b/sound/soc/sprd/Makefile new file mode 100644 index 000000000000..47620e57a9f2 --- /dev/null +++ b/sound/soc/sprd/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +# Spreadtrum Audio Support + +obj-$(CONFIG_SND_SOC_SPRD) += sprd-pcm-dma.o diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c new file mode 100644 index 000000000000..cbb27c4abeba --- /dev/null +++ b/sound/soc/sprd/sprd-pcm-dma.c @@ -0,0 +1,562 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2019 Spreadtrum Communications Inc. + +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/dma/sprd-dma.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "sprd-pcm-dma.h" + +#define DRV_NAME "sprd_pcm_dma" +#define SPRD_PCM_DMA_LINKLIST_SIZE 64 +#define SPRD_PCM_DMA_BRUST_LEN 640 + +struct sprd_pcm_dma_data { + struct dma_chan *chan; + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + dma_addr_t phys; + void *virt; + int pre_pointer; +}; + +struct sprd_pcm_dma_private { + struct snd_pcm_substream *substream; + struct sprd_pcm_dma_params *params; + struct sprd_pcm_dma_data data[SPRD_PCM_CHANNEL_MAX]; + int hw_chan; + int dma_addr_offset; +}; + +static const struct snd_pcm_hardware sprd_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .period_bytes_min = 1, + .period_bytes_max = 64 * 1024, + .periods_min = 1, + .periods_max = PAGE_SIZE / SPRD_PCM_DMA_LINKLIST_SIZE, + .buffer_bytes_max = 64 * 1024, +}; + +static int sprd_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct device *dev = component->dev; + struct sprd_pcm_dma_private *dma_private; + int hw_chan = SPRD_PCM_CHANNEL_MAX; + int size, ret, i; + + snd_soc_set_runtime_hwparams(substream, &sprd_pcm_hardware); + + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + SPRD_PCM_DMA_BRUST_LEN); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + SPRD_PCM_DMA_BRUST_LEN); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + dma_private = devm_kzalloc(dev, sizeof(*dma_private), GFP_KERNEL); + if (!dma_private) + return -ENOMEM; + + size = runtime->hw.periods_max * SPRD_PCM_DMA_LINKLIST_SIZE; + + for (i = 0; i < hw_chan; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + data->virt = dmam_alloc_coherent(dev, size, &data->phys, + GFP_KERNEL); + if (!data->virt) { + ret = -ENOMEM; + goto error; + } + } + + dma_private->hw_chan = hw_chan; + runtime->private_data = dma_private; + dma_private->substream = substream; + + return 0; + +error: + for (i = 0; i < hw_chan; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + if (data->virt) + dmam_free_coherent(dev, size, data->virt, data->phys); + } + + devm_kfree(dev, dma_private); + return ret; +} + +static int sprd_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sprd_pcm_dma_private *dma_private = runtime->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct device *dev = component->dev; + int size = runtime->hw.periods_max * SPRD_PCM_DMA_LINKLIST_SIZE; + int i; + + for (i = 0; i < dma_private->hw_chan; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + dmam_free_coherent(dev, size, data->virt, data->phys); + } + + devm_kfree(dev, dma_private); + + return 0; +} + +static void sprd_pcm_dma_complete(void *data) +{ + struct sprd_pcm_dma_private *dma_private = data; + struct snd_pcm_substream *substream = dma_private->substream; + + snd_pcm_period_elapsed(substream); +} + +static void sprd_pcm_release_dma_channel(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sprd_pcm_dma_private *dma_private = runtime->private_data; + int i; + + for (i = 0; i < SPRD_PCM_CHANNEL_MAX; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + if (data->chan) { + dma_release_channel(data->chan); + data->chan = NULL; + } + } +} + +static int sprd_pcm_request_dma_channel(struct snd_pcm_substream *substream, + int channels) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sprd_pcm_dma_private *dma_private = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct device *dev = component->dev; + struct sprd_pcm_dma_params *dma_params = dma_private->params; + int i; + + if (channels > SPRD_PCM_CHANNEL_MAX) { + dev_err(dev, "invalid dma channel number:%d\n", channels); + return -EINVAL; + } + + for (i = 0; i < channels; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + data->chan = dma_request_slave_channel(dev, + dma_params->chan_name[i]); + if (!data->chan) { + dev_err(dev, "failed to request dma channel:%s\n", + dma_params->chan_name[i]); + sprd_pcm_release_dma_channel(substream); + return -ENODEV; + } + } + + return 0; +} + +static int sprd_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sprd_pcm_dma_private *dma_private = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct sprd_pcm_dma_params *dma_params; + size_t totsize = params_buffer_bytes(params); + size_t period = params_period_bytes(params); + int channels = params_channels(params); + int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct scatterlist *sg; + unsigned long flags; + int ret, i, j, sg_num; + + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) { + dev_warn(component->dev, "no dma parameters setting\n"); + dma_private->params = NULL; + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = totsize; + return 0; + } + + if (!dma_private->params) { + dma_private->params = dma_params; + ret = sprd_pcm_request_dma_channel(substream, channels); + if (ret) + return ret; + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + runtime->dma_bytes = totsize; + sg_num = totsize / period; + dma_private->dma_addr_offset = totsize / channels; + + sg = devm_kcalloc(component->dev, sg_num, sizeof(*sg), GFP_KERNEL); + if (!sg) { + ret = -ENOMEM; + goto sg_err; + } + + for (i = 0; i < channels; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + struct dma_chan *chan = data->chan; + struct dma_slave_config config = { }; + struct sprd_dma_linklist link = { }; + enum dma_transfer_direction dir; + struct scatterlist *sgt = sg; + + config.src_maxburst = dma_params->fragment_len[i]; + config.src_addr_width = dma_params->datawidth[i]; + config.dst_addr_width = dma_params->datawidth[i]; + if (is_playback) { + config.src_addr = runtime->dma_addr + + i * dma_private->dma_addr_offset; + config.dst_addr = dma_params->dev_phys[i]; + dir = DMA_MEM_TO_DEV; + } else { + config.src_addr = dma_params->dev_phys[i]; + config.dst_addr = runtime->dma_addr + + i * dma_private->dma_addr_offset; + dir = DMA_DEV_TO_MEM; + } + + sg_init_table(sgt, sg_num); + for (j = 0; j < sg_num; j++, sgt++) { + u32 sg_len = period / channels; + + sg_dma_len(sgt) = sg_len; + sg_dma_address(sgt) = runtime->dma_addr + + i * dma_private->dma_addr_offset + sg_len * j; + } + + /* + * Configure the link-list address for the DMA engine link-list + * mode. + */ + link.virt_addr = (unsigned long)data->virt; + link.phy_addr = data->phys; + + ret = dmaengine_slave_config(chan, &config); + if (ret) { + dev_err(component->dev, + "failed to set slave configuration: %d\n", ret); + goto config_err; + } + + /* + * We configure the DMA request mode, interrupt mode, channel + * mode and channel trigger mode by the flags. + */ + flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE, SPRD_DMA_NO_TRG, + SPRD_DMA_FRAG_REQ, SPRD_DMA_TRANS_INT); + data->desc = chan->device->device_prep_slave_sg(chan, sg, + sg_num, dir, + flags, &link); + if (!data->desc) { + dev_err(component->dev, "failed to prepare slave sg\n"); + ret = -ENOMEM; + goto config_err; + } + + if (!runtime->no_period_wakeup) { + data->desc->callback = sprd_pcm_dma_complete; + data->desc->callback_param = dma_private; + } + } + + devm_kfree(component->dev, sg); + + return 0; + +config_err: + devm_kfree(component->dev, sg); +sg_err: + sprd_pcm_release_dma_channel(substream); + return ret; +} + +static int sprd_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + sprd_pcm_release_dma_channel(substream); + + return 0; +} + +static int sprd_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct sprd_pcm_dma_private *dma_private = + substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + int ret = 0, i; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + for (i = 0; i < dma_private->hw_chan; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + if (!data->desc) + continue; + + data->cookie = dmaengine_submit(data->desc); + ret = dma_submit_error(data->cookie); + if (ret) { + dev_err(component->dev, + "failed to submit dma request: %d\n", + ret); + return ret; + } + + dma_async_issue_pending(data->chan); + } + + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + for (i = 0; i < dma_private->hw_chan; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + if (data->chan) + dmaengine_resume(data->chan); + } + + break; + case SNDRV_PCM_TRIGGER_STOP: + for (i = 0; i < dma_private->hw_chan; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + if (data->chan) + dmaengine_terminate_async(data->chan); + } + + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + for (i = 0; i < dma_private->hw_chan; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + if (data->chan) + dmaengine_pause(data->chan); + } + + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t sprd_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sprd_pcm_dma_private *dma_private = runtime->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + int pointer[SPRD_PCM_CHANNEL_MAX]; + int bytes_of_pointer = 0, sel_max = 0, i; + snd_pcm_uframes_t x; + struct dma_tx_state state; + enum dma_status status; + + for (i = 0; i < dma_private->hw_chan; i++) { + struct sprd_pcm_dma_data *data = &dma_private->data[i]; + + if (!data->chan) + continue; + + status = dmaengine_tx_status(data->chan, data->cookie, &state); + if (status == DMA_ERROR) { + dev_err(component->dev, + "failed to get dma channel %d status\n", i); + return 0; + } + + /* + * We just get current transfer address from the DMA engine, so + * we need convert to current pointer. + */ + pointer[i] = state.residue - runtime->dma_addr - + i * dma_private->dma_addr_offset; + + if (i == 0) { + bytes_of_pointer = pointer[i]; + sel_max = pointer[i] < data->pre_pointer ? 1 : 0; + } else { + sel_max ^= pointer[i] < data->pre_pointer ? 1 : 0; + + if (sel_max) + bytes_of_pointer = + max(pointer[i], pointer[i - 1]) << 1; + else + bytes_of_pointer = + min(pointer[i], pointer[i - 1]) << 1; + } + + data->pre_pointer = pointer[i]; + } + + x = bytes_to_frames(runtime, bytes_of_pointer); + if (x == runtime->buffer_size) + x = 0; + + return x; +} + +static int sprd_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + return remap_pfn_range(vma, vma->vm_start, + runtime->dma_addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static struct snd_pcm_ops sprd_pcm_ops = { + .open = sprd_pcm_open, + .close = sprd_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sprd_pcm_hw_params, + .hw_free = sprd_pcm_hw_free, + .trigger = sprd_pcm_trigger, + .pointer = sprd_pcm_pointer, + .mmap = sprd_pcm_mmap, +}; + +static int sprd_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_substream *substream; + int ret; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (substream) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, + sprd_pcm_hardware.buffer_bytes_max, + &substream->dma_buffer); + if (ret) { + dev_err(card->dev, + "can't alloc playback dma buffer: %d\n", ret); + return ret; + } + } + + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, + sprd_pcm_hardware.buffer_bytes_max, + &substream->dma_buffer); + if (ret) { + dev_err(card->dev, + "can't alloc capture dma buffer: %d\n", ret); + snd_dma_free_pages(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer); + return ret; + } + } + + return 0; +} + +static void sprd_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + int i; + + for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + substream = pcm->streams[i].substream; + if (substream) { + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } +} + +static const struct snd_soc_component_driver sprd_soc_component = { + .name = DRV_NAME, + .ops = &sprd_pcm_ops, + .pcm_new = sprd_pcm_new, + .pcm_free = sprd_pcm_free, +}; + +static int sprd_soc_platform_probe(struct platform_device *pdev) +{ + int ret; + + ret = devm_snd_soc_register_component(&pdev->dev, &sprd_soc_component, + NULL, 0); + if (ret) + dev_err(&pdev->dev, "could not register platform:%d\n", ret); + + return ret; +} + +static const struct of_device_id sprd_pcm_of_match[] = { + { .compatible = "sprd,pcm-platform", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sprd_pcm_of_match); + +static struct platform_driver sprd_pcm_driver = { + .driver = { + .name = "sprd-pcm-audio", + .of_match_table = sprd_pcm_of_match, + }, + + .probe = sprd_soc_platform_probe, +}; + +module_platform_driver(sprd_pcm_driver); + +MODULE_DESCRIPTION("Spreadtrum ASoC PCM DMA"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sprd-audio"); diff --git a/sound/soc/sprd/sprd-pcm-dma.h b/sound/soc/sprd/sprd-pcm-dma.h new file mode 100644 index 000000000000..d85a34f1461d --- /dev/null +++ b/sound/soc/sprd/sprd-pcm-dma.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifndef __SPRD_PCM_DMA_H +#define __SPRD_PCM_DMA_H + +#define SPRD_PCM_CHANNEL_MAX 2 + +struct sprd_pcm_dma_params { + dma_addr_t dev_phys[SPRD_PCM_CHANNEL_MAX]; + u32 datawidth[SPRD_PCM_CHANNEL_MAX]; + u32 fragment_len[SPRD_PCM_CHANNEL_MAX]; + const char *chan_name[SPRD_PCM_CHANNEL_MAX]; +}; + +#endif /* __SPRD_PCM_DMA_H */ diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 6d0bf78d114d..47c334de6b09 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -186,8 +186,9 @@ enum i2s_datlen { #define STM32_I2S_IS_SLAVE(x) ((x)->ms_flg == I2S_MS_SLAVE) /** + * struct stm32_i2s_data - private data of I2S * @regmap_conf: I2S register map configuration pointer - * @egmap: I2S register map pointer + * @regmap: I2S register map pointer * @pdev: device data pointer * @dai_drv: DAI driver pointer * @dma_data_tx: dma configuration data for tx channel @@ -200,6 +201,7 @@ enum i2s_datlen { * @base: mmio register base virtual address * @phys_addr: I2S registers physical base address * @lock_fd: lock to manage race conditions in full duplex mode + * @irq_lock: prevent race condition with IRQ * @dais_name: DAI name * @mclk_rate: master clock frequency (Hz) * @fmt: DAI protocol @@ -221,6 +223,7 @@ struct stm32_i2s_data { void __iomem *base; dma_addr_t phys_addr; spinlock_t lock_fd; /* Manage race conditions for full duplex */ + spinlock_t irq_lock; /* used to prevent race condition with IRQ */ char dais_name[STM32_I2S_DAI_NAME_SIZE]; unsigned int mclk_rate; unsigned int fmt; @@ -246,8 +249,8 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid) return IRQ_NONE; } - regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, flags); + regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, flags); if (flags & I2S_SR_OVR) { dev_dbg(&pdev->dev, "Overrun\n"); @@ -262,8 +265,10 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid) if (flags & I2S_SR_TIFRE) dev_dbg(&pdev->dev, "Frame error\n"); - if (err) + spin_lock(&i2s->irq_lock); + if (err && i2s->substream) snd_pcm_stop_xrun(i2s->substream); + spin_unlock(&i2s->irq_lock); return IRQ_HANDLED; } @@ -276,7 +281,6 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) case STM32_I2S_CFG2_REG: case STM32_I2S_IER_REG: case STM32_I2S_SR_REG: - case STM32_I2S_IFCR_REG: case STM32_I2S_TXDR_REG: case STM32_I2S_RXDR_REG: case STM32_I2S_CGFR_REG: @@ -488,7 +492,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); int format = params_width(params); - u32 cfgr, cfgr_mask, cfg1, cfg1_mask; + u32 cfgr, cfgr_mask, cfg1; unsigned int fthlv; int ret; @@ -501,7 +505,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, switch (format) { case 16: cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16); - cfgr_mask = I2S_CGFR_DATLEN_MASK; + cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN; break; case 32: cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) | @@ -529,30 +533,32 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, if (ret < 0) return ret; - cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; - cfg1_mask = cfg1; - fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4; - cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1); - cfg1_mask |= I2S_CFG1_FTHVL_MASK; + cfg1 = I2S_CFG1_FTHVL_SET(fthlv - 1); return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, - cfg1_mask, cfg1); + I2S_CFG1_FTHVL_MASK, cfg1); } static int stm32_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + int ret; + spin_lock_irqsave(&i2s->irq_lock, flags); i2s->substream = substream; + spin_unlock_irqrestore(&i2s->irq_lock, flags); - spin_lock(&i2s->lock_fd); - i2s->refcount++; - spin_unlock(&i2s->lock_fd); + ret = clk_prepare_enable(i2s->i2sclk); + if (ret < 0) { + dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); + return ret; + } - return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, I2S_IFCR_MASK); + return regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); } static int stm32_i2s_hw_params(struct snd_pcm_substream *substream, @@ -589,6 +595,10 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, /* Enable i2s */ dev_dbg(cpu_dai->dev, "start I2S\n"); + cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; + regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, + cfg1_mask, cfg1_mask); + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, I2S_CR1_SPE, I2S_CR1_SPE); if (ret < 0) { @@ -596,28 +606,29 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } - ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, - I2S_CR1_CSTART, I2S_CR1_CSTART); + ret = regmap_write_bits(i2s->regmap, STM32_I2S_CR1_REG, + I2S_CR1_CSTART, I2S_CR1_CSTART); if (ret < 0) { dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret); return ret; } - regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, I2S_IFCR_MASK); + regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); + spin_lock(&i2s->lock_fd); + i2s->refcount++; if (playback_flg) { ier = I2S_IER_UDRIE; } else { ier = I2S_IER_OVRIE; - spin_lock(&i2s->lock_fd); - if (i2s->refcount == 1) - /* dummy write to trigger capture */ + if (STM32_I2S_IS_MASTER(i2s) && i2s->refcount == 1) + /* dummy write to gate bus clocks */ regmap_write(i2s->regmap, STM32_I2S_TXDR_REG, 0); - spin_unlock(&i2s->lock_fd); } + spin_unlock(&i2s->lock_fd); if (STM32_I2S_IS_SLAVE(i2s)) ier |= I2S_IER_TIFREIE; @@ -642,7 +653,6 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, spin_unlock(&i2s->lock_fd); break; } - spin_unlock(&i2s->lock_fd); dev_dbg(cpu_dai->dev, "stop I2S\n"); @@ -650,8 +660,10 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, I2S_CR1_SPE, 0); if (ret < 0) { dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret); + spin_unlock(&i2s->lock_fd); return ret; } + spin_unlock(&i2s->lock_fd); cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, @@ -668,11 +680,16 @@ static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); - - i2s->substream = NULL; + unsigned long flags; regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); + + clk_disable_unprepare(i2s->i2sclk); + + spin_lock_irqsave(&i2s->irq_lock, flags); + i2s->substream = NULL; + spin_unlock_irqrestore(&i2s->irq_lock, flags); } static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) @@ -703,6 +720,7 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = { .volatile_reg = stm32_i2s_volatile_reg, .writeable_reg = stm32_i2s_writeable_reg, .fast_io = true, + .cache_type = REGCACHE_FLAT, }; static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { @@ -866,76 +884,68 @@ static int stm32_i2s_probe(struct platform_device *pdev) i2s->pdev = pdev; i2s->ms_flg = I2S_MS_NOT_SET; spin_lock_init(&i2s->lock_fd); + spin_lock_init(&i2s->irq_lock); platform_set_drvdata(pdev, i2s); ret = stm32_i2s_dais_init(pdev, i2s); if (ret) return ret; - i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base, - i2s->regmap_conf); + i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk", + i2s->base, i2s->regmap_conf); if (IS_ERR(i2s->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); return PTR_ERR(i2s->regmap); } - ret = clk_prepare_enable(i2s->pclk); - if (ret) { - dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(i2s->i2sclk); - if (ret) { - dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret); - goto err_pclk_disable; - } - ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component, i2s->dai_drv, 1); if (ret) - goto err_clocks_disable; + return ret; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); if (ret) - goto err_clocks_disable; + return ret; /* Set SPI/I2S in i2s mode */ - ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, - I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); - if (ret) - goto err_clocks_disable; + return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); +} - return ret; +MODULE_DEVICE_TABLE(of, stm32_i2s_ids); -err_clocks_disable: - clk_disable_unprepare(i2s->i2sclk); -err_pclk_disable: - clk_disable_unprepare(i2s->pclk); +#ifdef CONFIG_PM_SLEEP +static int stm32_i2s_suspend(struct device *dev) +{ + struct stm32_i2s_data *i2s = dev_get_drvdata(dev); - return ret; + regcache_cache_only(i2s->regmap, true); + regcache_mark_dirty(i2s->regmap); + + return 0; } -static int stm32_i2s_remove(struct platform_device *pdev) +static int stm32_i2s_resume(struct device *dev) { - struct stm32_i2s_data *i2s = platform_get_drvdata(pdev); - - clk_disable_unprepare(i2s->i2sclk); - clk_disable_unprepare(i2s->pclk); + struct stm32_i2s_data *i2s = dev_get_drvdata(dev); - return 0; + regcache_cache_only(i2s->regmap, false); + return regcache_sync(i2s->regmap); } +#endif /* CONFIG_PM_SLEEP */ -MODULE_DEVICE_TABLE(of, stm32_i2s_ids); +static const struct dev_pm_ops stm32_i2s_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stm32_i2s_suspend, stm32_i2s_resume) +}; static struct platform_driver stm32_i2s_driver = { .driver = { .name = "st,stm32-i2s", .of_match_table = stm32_i2s_ids, + .pm = &stm32_i2s_pm_ops, }, .probe = stm32_i2s_probe, - .remove = stm32_i2s_remove, }; module_platform_driver(stm32_i2s_driver); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index bcb35cae2a2c..14c9591aae42 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -112,16 +112,21 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client, if (!sai_provider) { dev_err(&sai_client->pdev->dev, "SAI sync provider data not found\n"); - return -EINVAL; + ret = -EINVAL; + goto out_put_dev; } /* Configure sync client */ ret = stm32_sai_sync_conf_client(sai_client, synci); if (ret < 0) - return ret; + goto out_put_dev; /* Configure sync provider */ - return stm32_sai_sync_conf_provider(sai_provider, synco); + ret = stm32_sai_sync_conf_provider(sai_provider, synco); + +out_put_dev: + put_device(&pdev->dev); + return ret; } static int stm32_sai_probe(struct platform_device *pdev) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index d4825700b63f..f9297228c41c 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -898,7 +898,7 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); - int cr1, mask, div = 0; + int div = 0; int sai_clk_rate, mclk_ratio, den; unsigned int rate = params_rate(params); @@ -943,10 +943,8 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, } else { if (sai->mclk_rate) { mclk_ratio = sai->mclk_rate / rate; - if (mclk_ratio == 512) { - mask = SAI_XCR1_OSR; - cr1 = SAI_XCR1_OSR; - } else if (mclk_ratio != 256) { + if ((mclk_ratio != 512) && + (mclk_ratio != 256)) { dev_err(cpu_dai->dev, "Wrong mclk ratio %d\n", mclk_ratio); diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 9a3cb7704810..15d08e343b47 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -64,9 +64,20 @@ #define SUN4I_CODEC_DAC_ACTL_DACAENR (31) #define SUN4I_CODEC_DAC_ACTL_DACAENL (30) #define SUN4I_CODEC_DAC_ACTL_MIXEN (29) +#define SUN4I_CODEC_DAC_ACTL_LNG (26) +#define SUN4I_CODEC_DAC_ACTL_FMG (23) +#define SUN4I_CODEC_DAC_ACTL_MICG (20) +#define SUN4I_CODEC_DAC_ACTL_LLNS (19) +#define SUN4I_CODEC_DAC_ACTL_RLNS (18) +#define SUN4I_CODEC_DAC_ACTL_LFMS (17) +#define SUN4I_CODEC_DAC_ACTL_RFMS (16) #define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15) #define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14) #define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13) +#define SUN4I_CODEC_DAC_ACTL_MIC1LS (12) +#define SUN4I_CODEC_DAC_ACTL_MIC1RS (11) +#define SUN4I_CODEC_DAC_ACTL_MIC2LS (10) +#define SUN4I_CODEC_DAC_ACTL_MIC2RS (9) #define SUN4I_CODEC_DAC_ACTL_DACPAS (8) #define SUN4I_CODEC_DAC_ACTL_MIXPAS (7) #define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6) @@ -94,8 +105,11 @@ #define SUN4I_CODEC_ADC_ACTL_PREG1EN (29) #define SUN4I_CODEC_ADC_ACTL_PREG2EN (28) #define SUN4I_CODEC_ADC_ACTL_VMICEN (27) +#define SUN4I_CODEC_ADC_ACTL_PREG1 (25) +#define SUN4I_CODEC_ADC_ACTL_PREG2 (23) #define SUN4I_CODEC_ADC_ACTL_VADCG (20) #define SUN4I_CODEC_ADC_ACTL_ADCIS (17) +#define SUN4I_CODEC_ADC_ACTL_LNPREG (13) #define SUN4I_CODEC_ADC_ACTL_PA_EN (4) #define SUN4I_CODEC_ADC_ACTL_DDE (3) #define SUN4I_CODEC_ADC_DEBUG (0x2c) @@ -110,6 +124,9 @@ /* Microphone controls (sun7i only) */ #define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c) +#define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1 (29) +#define SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2 (26) + /* * sun6i specific registers * @@ -673,23 +690,91 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mute = SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1); +static DECLARE_TLV_DB_SCALE(sun4i_codec_linein_loopback_gain_scale, -150, 150, + 0); +static DECLARE_TLV_DB_SCALE(sun4i_codec_linein_preamp_gain_scale, -1200, 300, + 0); +static DECLARE_TLV_DB_SCALE(sun4i_codec_fmin_loopback_gain_scale, -450, 150, + 0); +static DECLARE_TLV_DB_SCALE(sun4i_codec_micin_loopback_gain_scale, -450, 150, + 0); +static DECLARE_TLV_DB_RANGE(sun4i_codec_micin_preamp_gain_scale, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 7, TLV_DB_SCALE_ITEM(3500, 300, 0)); +static DECLARE_TLV_DB_RANGE(sun7i_codec_micin_preamp_gain_scale, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0)); static const struct snd_kcontrol_new sun4i_codec_controls[] = { SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, sun4i_codec_pa_volume_scale), + SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_LNG, 1, 0, + sun4i_codec_linein_loopback_gain_scale), + SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0, + sun4i_codec_linein_preamp_gain_scale), + SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_FMG, 3, 0, + sun4i_codec_fmin_loopback_gain_scale), + SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_MICG, 7, 0, + sun4i_codec_micin_loopback_gain_scale), + SOC_SINGLE_TLV("Mic1 Boost Volume", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_PREG1, 3, 0, + sun4i_codec_micin_preamp_gain_scale), + SOC_SINGLE_TLV("Mic2 Boost Volume", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_PREG2, 3, 0, + sun4i_codec_micin_preamp_gain_scale), }; -static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = { - SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, - SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0), +static const struct snd_kcontrol_new sun7i_codec_controls[] = { + SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, + sun4i_codec_pa_volume_scale), + SOC_SINGLE_TLV("Line Playback Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_LNG, 1, 0, + sun4i_codec_linein_loopback_gain_scale), + SOC_SINGLE_TLV("Line Boost Volume", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_LNPREG, 7, 0, + sun4i_codec_linein_preamp_gain_scale), + SOC_SINGLE_TLV("FM Playback Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_FMG, 3, 0, + sun4i_codec_fmin_loopback_gain_scale), + SOC_SINGLE_TLV("Mic Playback Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_MICG, 7, 0, + sun4i_codec_micin_loopback_gain_scale), + SOC_SINGLE_TLV("Mic1 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL, + SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG1, 7, 0, + sun7i_codec_micin_preamp_gain_scale), + SOC_SINGLE_TLV("Mic2 Boost Volume", SUN7I_CODEC_AC_MIC_PHONE_CAL, + SUN7I_CODEC_AC_MIC_PHONE_CAL_PREG2, 7, 0, + sun7i_codec_micin_preamp_gain_scale), }; -static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = { - SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, - SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0), - SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, +static const struct snd_kcontrol_new sun4i_codec_mixer_controls[] = { + SOC_DAPM_SINGLE("Left Mixer Left DAC Playback Switch", + SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_LDACLMIXS, + 1, 0), + SOC_DAPM_SINGLE("Right Mixer Right DAC Playback Switch", + SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_RDACRMIXS, + 1, 0), + SOC_DAPM_SINGLE("Right Mixer Left DAC Playback Switch", + SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0), + SOC_DAPM_DOUBLE("Line Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_LLNS, + SUN4I_CODEC_DAC_ACTL_RLNS, 1, 0), + SOC_DAPM_DOUBLE("FM Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_LFMS, + SUN4I_CODEC_DAC_ACTL_RFMS, 1, 0), + SOC_DAPM_DOUBLE("Mic1 Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_MIC1LS, + SUN4I_CODEC_DAC_ACTL_MIC1RS, 1, 0), + SOC_DAPM_DOUBLE("Mic2 Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_MIC2LS, + SUN4I_CODEC_DAC_ACTL_MIC2RS, 1, 0), }; static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = { @@ -724,11 +809,11 @@ static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = { /* Mixers */ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, - sun4i_codec_left_mixer_controls, - ARRAY_SIZE(sun4i_codec_left_mixer_controls)), + sun4i_codec_mixer_controls, + ARRAY_SIZE(sun4i_codec_mixer_controls)), SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, - sun4i_codec_right_mixer_controls, - ARRAY_SIZE(sun4i_codec_right_mixer_controls)), + sun4i_codec_mixer_controls, + ARRAY_SIZE(sun4i_codec_mixer_controls)), /* Global Mixer Enable */ SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL, @@ -741,6 +826,8 @@ static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = { /* Mic Pre-Amplifiers */ SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC2 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_PREG2EN, 0, NULL, 0), /* Power Amplifier */ SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL, @@ -750,7 +837,12 @@ static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = { SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0, &sun4i_codec_pa_mute), + SND_SOC_DAPM_INPUT("Line Right"), + SND_SOC_DAPM_INPUT("Line Left"), + SND_SOC_DAPM_INPUT("FM Right"), + SND_SOC_DAPM_INPUT("FM Left"), SND_SOC_DAPM_INPUT("Mic1"), + SND_SOC_DAPM_INPUT("Mic2"), SND_SOC_DAPM_OUTPUT("HP Right"), SND_SOC_DAPM_OUTPUT("HP Left"), @@ -767,12 +859,20 @@ static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = { /* Right Mixer Routes */ { "Right Mixer", NULL, "Mixer Enable" }, - { "Right Mixer", "Left DAC Playback Switch", "Left DAC" }, - { "Right Mixer", "Right DAC Playback Switch", "Right DAC" }, + { "Right Mixer", "Right Mixer Left DAC Playback Switch", "Left DAC" }, + { "Right Mixer", "Right Mixer Right DAC Playback Switch", "Right DAC" }, + { "Right Mixer", "Line Playback Switch", "Line Right" }, + { "Right Mixer", "FM Playback Switch", "FM Right" }, + { "Right Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" }, + { "Right Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" }, /* Left Mixer Routes */ { "Left Mixer", NULL, "Mixer Enable" }, - { "Left Mixer", "Left DAC Playback Switch", "Left DAC" }, + { "Left Mixer", "Left Mixer Left DAC Playback Switch", "Left DAC" }, + { "Left Mixer", "Line Playback Switch", "Line Left" }, + { "Left Mixer", "FM Playback Switch", "FM Left" }, + { "Left Mixer", "Mic1 Playback Switch", "MIC1 Pre-Amplifier" }, + { "Left Mixer", "Mic2 Playback Switch", "MIC2 Pre-Amplifier" }, /* Power Amplifier Routes */ { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" }, @@ -790,6 +890,12 @@ static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = { { "Right ADC", NULL, "MIC1 Pre-Amplifier" }, { "MIC1 Pre-Amplifier", NULL, "Mic1"}, { "Mic1", NULL, "VMIC" }, + + /* Mic2 Routes */ + { "Left ADC", NULL, "MIC2 Pre-Amplifier" }, + { "Right ADC", NULL, "MIC2 Pre-Amplifier" }, + { "MIC2 Pre-Amplifier", NULL, "Mic2"}, + { "Mic2", NULL, "VMIC" }, }; static const struct snd_soc_component_driver sun4i_codec_codec = { @@ -805,6 +911,19 @@ static const struct snd_soc_component_driver sun4i_codec_codec = { .non_legacy_dai_naming = 1, }; +static const struct snd_soc_component_driver sun7i_codec_codec = { + .controls = sun7i_codec_controls, + .num_controls = ARRAY_SIZE(sun7i_codec_controls), + .dapm_widgets = sun4i_codec_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets), + .dapm_routes = sun4i_codec_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + /*** sun6i Codec ***/ /* mixer controls */ @@ -1485,7 +1604,7 @@ static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = { static const struct sun4i_codec_quirks sun7i_codec_quirks = { .regmap_config = &sun7i_codec_regmap_config, - .codec = &sun4i_codec_codec, + .codec = &sun7i_codec_codec, .create_card = sun4i_codec_create_card, .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index df1fed0aa001..d105c90c3706 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -274,7 +274,7 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { * stream widgets at the card level. */ - SND_SOC_DAPM_REGULATOR_SUPPLY("hpvcc", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0), SND_SOC_DAPM_MUX("Headphone Source Playback Route", SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN50I_ADDA_HP_CTRL, @@ -362,7 +362,7 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, { "Headphone Amp", NULL, "Headphone Source Playback Route" }, - { "Headphone Amp", NULL, "hpvcc" }, + { "Headphone Amp", NULL, "cpvdd" }, { "HP", NULL, "Headphone Amp" }, /* Microphone Routes */ diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index a10fcb5963c6..a3a67a8f0f54 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -29,6 +29,7 @@ #include <linux/platform_data/davinci_asp.h> #include <linux/math64.h> #include <linux/bitmap.h> +#include <linux/gpio/driver.h> #include <sound/asoundef.h> #include <sound/core.h> @@ -54,6 +55,7 @@ static u32 context_regs[] = { DAVINCI_MCASP_AHCLKXCTL_REG, DAVINCI_MCASP_AHCLKRCTL_REG, DAVINCI_MCASP_PDIR_REG, + DAVINCI_MCASP_PFUNC_REG, DAVINCI_MCASP_RXMASK_REG, DAVINCI_MCASP_TXMASK_REG, DAVINCI_MCASP_RXTDM_REG, @@ -108,6 +110,10 @@ struct davinci_mcasp { /* Used for comstraint setting on the second stream */ u32 channels; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif + #ifdef CONFIG_PM struct davinci_mcasp_context context; #endif @@ -818,9 +824,6 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, if (mcasp->version < MCASP_VERSION_3) mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT); - /* All PINS as McASP */ - mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); @@ -1845,6 +1848,147 @@ static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata) return offset; } +#ifdef CONFIG_GPIOLIB +static int davinci_mcasp_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_mcasp *mcasp = gpiochip_get_data(chip); + + if (mcasp->num_serializer && offset < mcasp->num_serializer && + mcasp->serial_dir[offset] != INACTIVE_MODE) { + dev_err(mcasp->dev, "AXR%u pin is used for audio\n", offset); + return -EBUSY; + } + + /* Do not change the PIN yet */ + + return pm_runtime_get_sync(mcasp->dev); +} + +static void davinci_mcasp_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_mcasp *mcasp = gpiochip_get_data(chip); + + /* Set the direction to input */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset)); + + /* Set the pin as McASP pin */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset)); + + pm_runtime_put_sync(mcasp->dev); +} + +static int davinci_mcasp_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct davinci_mcasp *mcasp = gpiochip_get_data(chip); + u32 val; + + if (value) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset)); + + val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PFUNC_REG); + if (!(val & BIT(offset))) { + /* Set the pin as GPIO pin */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset)); + + /* Set the direction to output */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset)); + } + + return 0; +} + +static void davinci_mcasp_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct davinci_mcasp *mcasp = gpiochip_get_data(chip); + + if (value) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset)); +} + +static int davinci_mcasp_gpio_direction_in(struct gpio_chip *chip, + unsigned offset) +{ + struct davinci_mcasp *mcasp = gpiochip_get_data(chip); + u32 val; + + val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PFUNC_REG); + if (!(val & BIT(offset))) { + /* Set the direction to input */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset)); + + /* Set the pin as GPIO pin */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset)); + } + + return 0; +} + +static int davinci_mcasp_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_mcasp *mcasp = gpiochip_get_data(chip); + u32 val; + + val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDSET_REG); + if (val & BIT(offset)) + return 1; + + return 0; +} + +static int davinci_mcasp_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + struct davinci_mcasp *mcasp = gpiochip_get_data(chip); + u32 val; + + val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG); + if (val & BIT(offset)) + return 0; + + return 1; +} + +static const struct gpio_chip davinci_mcasp_template_chip = { + .owner = THIS_MODULE, + .request = davinci_mcasp_gpio_request, + .free = davinci_mcasp_gpio_free, + .direction_output = davinci_mcasp_gpio_direction_out, + .set = davinci_mcasp_gpio_set, + .direction_input = davinci_mcasp_gpio_direction_in, + .get = davinci_mcasp_gpio_get, + .get_direction = davinci_mcasp_gpio_get_direction, + .base = -1, + .ngpio = 32, +}; + +static int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp) +{ + if (!of_property_read_bool(mcasp->dev->of_node, "gpio-controller")) + return 0; + + mcasp->gpio_chip = davinci_mcasp_template_chip; + mcasp->gpio_chip.label = dev_name(mcasp->dev); + mcasp->gpio_chip.parent = mcasp->dev; +#ifdef CONFIG_OF_GPIO + mcasp->gpio_chip.of_node = mcasp->dev->of_node; +#endif + + return devm_gpiochip_add_data(mcasp->dev, &mcasp->gpio_chip, mcasp); +} + +#else /* CONFIG_GPIOLIB */ +static inline int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp) +{ + return 0; +} +#endif /* CONFIG_GPIOLIB */ + static int davinci_mcasp_probe(struct platform_device *pdev) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -2069,6 +2213,15 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp_reparent_fck(pdev); + /* All PINS as McASP */ + pm_runtime_get_sync(mcasp->dev); + mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000); + pm_runtime_put(mcasp->dev); + + ret = davinci_mcasp_init_gpiochip(mcasp); + if (ret) + goto err; + ret = devm_snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, &davinci_mcasp_dai[pdata->op_mode], 1); @@ -2079,26 +2232,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = davinci_mcasp_get_dma_type(mcasp); switch (ret) { case PCM_EDMA: -#if IS_BUILTIN(CONFIG_SND_SOC_TI_EDMA_PCM) || \ - (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \ - IS_MODULE(CONFIG_SND_SOC_TI_EDMA_PCM)) ret = edma_pcm_platform_register(&pdev->dev); -#else - dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n"); - ret = -EINVAL; - goto err; -#endif break; case PCM_SDMA: -#if IS_BUILTIN(CONFIG_SND_SOC_TI_SDMA_PCM) || \ - (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \ - IS_MODULE(CONFIG_SND_SOC_TI_SDMA_PCM)) ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL); -#else - dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n"); - ret = -EINVAL; - goto err; -#endif break; default: dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret); diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig index 723a583a8d57..47f606b924e4 100644 --- a/sound/soc/xilinx/Kconfig +++ b/sound/soc/xilinx/Kconfig @@ -6,3 +6,17 @@ config SND_SOC_XILINX_I2S mode, IP receives audio in AES format, extracts PCM and sends PCM data. In receiver mode, IP receives PCM audio and encapsulates PCM in AES format and sends AES data. + +config SND_SOC_XILINX_AUDIO_FORMATTER + tristate "Audio support for the the Xilinx audio formatter" + help + Select this option to enable Xilinx audio formatter + support. This provides DMA platform device support for + audio functionality. + +config SND_SOC_XILINX_SPDIF + tristate "Audio support for the the Xilinx SPDIF" + help + Select this option to enable Xilinx SPDIF Audio. + This provides playback and capture of SPDIF audio in + AES format. diff --git a/sound/soc/xilinx/Makefile b/sound/soc/xilinx/Makefile index 6c1209b9ee75..d79fd38b094b 100644 --- a/sound/soc/xilinx/Makefile +++ b/sound/soc/xilinx/Makefile @@ -1,2 +1,6 @@ snd-soc-xlnx-i2s-objs := xlnx_i2s.o obj-$(CONFIG_SND_SOC_XILINX_I2S) += snd-soc-xlnx-i2s.o +snd-soc-xlnx-formatter-pcm-objs := xlnx_formatter_pcm.o +obj-$(CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER) += snd-soc-xlnx-formatter-pcm.o +snd-soc-xlnx-spdif-objs := xlnx_spdif.o +obj-$(CONFIG_SND_SOC_XILINX_SPDIF) += snd-soc-xlnx-spdif.o diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c new file mode 100644 index 000000000000..97177d35652e --- /dev/null +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Xilinx ASoC audio formatter support +// +// Copyright (C) 2018 Xilinx, Inc. +// +// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com> + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/sizes.h> + +#include <sound/asoundef.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#define DRV_NAME "xlnx_formatter_pcm" + +#define XLNX_S2MM_OFFSET 0 +#define XLNX_MM2S_OFFSET 0x100 + +#define XLNX_AUD_CORE_CONFIG 0x4 +#define XLNX_AUD_CTRL 0x10 +#define XLNX_AUD_STS 0x14 + +#define AUD_CTRL_RESET_MASK BIT(1) +#define AUD_CFG_MM2S_MASK BIT(15) +#define AUD_CFG_S2MM_MASK BIT(31) + +#define XLNX_AUD_FS_MULTIPLIER 0x18 +#define XLNX_AUD_PERIOD_CONFIG 0x1C +#define XLNX_AUD_BUFF_ADDR_LSB 0x20 +#define XLNX_AUD_BUFF_ADDR_MSB 0x24 +#define XLNX_AUD_XFER_COUNT 0x28 +#define XLNX_AUD_CH_STS_START 0x2C +#define XLNX_BYTES_PER_CH 0x44 + +#define AUD_STS_IOC_IRQ_MASK BIT(31) +#define AUD_STS_CH_STS_MASK BIT(29) +#define AUD_CTRL_IOC_IRQ_MASK BIT(13) +#define AUD_CTRL_TOUT_IRQ_MASK BIT(14) +#define AUD_CTRL_DMA_EN_MASK BIT(0) + +#define CFG_MM2S_CH_MASK GENMASK(11, 8) +#define CFG_MM2S_CH_SHIFT 8 +#define CFG_MM2S_XFER_MASK GENMASK(14, 13) +#define CFG_MM2S_XFER_SHIFT 13 +#define CFG_MM2S_PKG_MASK BIT(12) + +#define CFG_S2MM_CH_MASK GENMASK(27, 24) +#define CFG_S2MM_CH_SHIFT 24 +#define CFG_S2MM_XFER_MASK GENMASK(30, 29) +#define CFG_S2MM_XFER_SHIFT 29 +#define CFG_S2MM_PKG_MASK BIT(28) + +#define AUD_CTRL_DATA_WIDTH_SHIFT 16 +#define AUD_CTRL_ACTIVE_CH_SHIFT 19 +#define PERIOD_CFG_PERIODS_SHIFT 16 + +#define PERIODS_MIN 2 +#define PERIODS_MAX 6 +#define PERIOD_BYTES_MIN 192 +#define PERIOD_BYTES_MAX (50 * 1024) +#define XLNX_PARAM_UNKNOWN 0 + +enum bit_depth { + BIT_DEPTH_8, + BIT_DEPTH_16, + BIT_DEPTH_20, + BIT_DEPTH_24, + BIT_DEPTH_32, +}; + +struct xlnx_pcm_drv_data { + void __iomem *mmio; + bool s2mm_presence; + bool mm2s_presence; + int s2mm_irq; + int mm2s_irq; + struct snd_pcm_substream *play_stream; + struct snd_pcm_substream *capture_stream; + struct clk *axi_clk; +}; + +/* + * struct xlnx_pcm_stream_param - stream configuration + * @mmio: base address offset + * @interleaved: audio channels arrangement in buffer + * @xfer_mode: data formatting mode during transfer + * @ch_limit: Maximum channels supported + * @buffer_size: stream ring buffer size + */ +struct xlnx_pcm_stream_param { + void __iomem *mmio; + bool interleaved; + u32 xfer_mode; + u32 ch_limit; + u64 buffer_size; +}; + +static const struct snd_pcm_hardware xlnx_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .buffer_bytes_max = PERIODS_MAX * PERIOD_BYTES_MAX, + .period_bytes_min = PERIOD_BYTES_MIN, + .period_bytes_max = PERIOD_BYTES_MAX, + .periods_min = PERIODS_MIN, + .periods_max = PERIODS_MAX, +}; + +enum { + AES_TO_AES, + AES_TO_PCM, + PCM_TO_PCM, + PCM_TO_AES +}; + +static void xlnx_parse_aes_params(u32 chsts_reg1_val, u32 chsts_reg2_val, + struct device *dev) +{ + u32 padded, srate, bit_depth, status[2]; + + if (chsts_reg1_val & IEC958_AES0_PROFESSIONAL) { + status[0] = chsts_reg1_val & 0xff; + status[1] = (chsts_reg1_val >> 16) & 0xff; + + switch (status[0] & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_44100: + srate = 44100; + break; + case IEC958_AES0_PRO_FS_48000: + srate = 48000; + break; + case IEC958_AES0_PRO_FS_32000: + srate = 32000; + break; + case IEC958_AES0_PRO_FS_NOTID: + default: + srate = XLNX_PARAM_UNKNOWN; + break; + } + + switch (status[1] & IEC958_AES2_PRO_SBITS) { + case IEC958_AES2_PRO_WORDLEN_NOTID: + case IEC958_AES2_PRO_SBITS_20: + padded = 0; + break; + case IEC958_AES2_PRO_SBITS_24: + padded = 4; + break; + default: + bit_depth = XLNX_PARAM_UNKNOWN; + goto log_params; + } + + switch (status[1] & IEC958_AES2_PRO_WORDLEN) { + case IEC958_AES2_PRO_WORDLEN_20_16: + bit_depth = 16 + padded; + break; + case IEC958_AES2_PRO_WORDLEN_22_18: + bit_depth = 18 + padded; + break; + case IEC958_AES2_PRO_WORDLEN_23_19: + bit_depth = 19 + padded; + break; + case IEC958_AES2_PRO_WORDLEN_24_20: + bit_depth = 20 + padded; + break; + case IEC958_AES2_PRO_WORDLEN_NOTID: + default: + bit_depth = XLNX_PARAM_UNKNOWN; + break; + } + + } else { + status[0] = (chsts_reg1_val >> 24) & 0xff; + status[1] = chsts_reg2_val & 0xff; + + switch (status[0] & IEC958_AES3_CON_FS) { + case IEC958_AES3_CON_FS_44100: + srate = 44100; + break; + case IEC958_AES3_CON_FS_48000: + srate = 48000; + break; + case IEC958_AES3_CON_FS_32000: + srate = 32000; + break; + default: + srate = XLNX_PARAM_UNKNOWN; + break; + } + + if (status[1] & IEC958_AES4_CON_MAX_WORDLEN_24) + padded = 4; + else + padded = 0; + + switch (status[1] & IEC958_AES4_CON_WORDLEN) { + case IEC958_AES4_CON_WORDLEN_20_16: + bit_depth = 16 + padded; + break; + case IEC958_AES4_CON_WORDLEN_22_18: + bit_depth = 18 + padded; + break; + case IEC958_AES4_CON_WORDLEN_23_19: + bit_depth = 19 + padded; + break; + case IEC958_AES4_CON_WORDLEN_24_20: + bit_depth = 20 + padded; + break; + case IEC958_AES4_CON_WORDLEN_21_17: + bit_depth = 17 + padded; + break; + case IEC958_AES4_CON_WORDLEN_NOTID: + default: + bit_depth = XLNX_PARAM_UNKNOWN; + break; + } + } + +log_params: + if (srate != XLNX_PARAM_UNKNOWN) + dev_info(dev, "sample rate = %d\n", srate); + else + dev_info(dev, "sample rate = unknown\n"); + + if (bit_depth != XLNX_PARAM_UNKNOWN) + dev_info(dev, "bit_depth = %d\n", bit_depth); + else + dev_info(dev, "bit_depth = unknown\n"); +} + +static int xlnx_formatter_pcm_reset(void __iomem *mmio_base) +{ + u32 val, retries = 0; + + val = readl(mmio_base + XLNX_AUD_CTRL); + val |= AUD_CTRL_RESET_MASK; + writel(val, mmio_base + XLNX_AUD_CTRL); + + val = readl(mmio_base + XLNX_AUD_CTRL); + /* Poll for maximum timeout of approximately 100ms (1 * 100)*/ + while ((val & AUD_CTRL_RESET_MASK) && (retries < 100)) { + mdelay(1); + retries++; + val = readl(mmio_base + XLNX_AUD_CTRL); + } + if (val & AUD_CTRL_RESET_MASK) + return -ENODEV; + + return 0; +} + +static void xlnx_formatter_disable_irqs(void __iomem *mmio_base, int stream) +{ + u32 val; + + val = readl(mmio_base + XLNX_AUD_CTRL); + val &= ~AUD_CTRL_IOC_IRQ_MASK; + if (stream == SNDRV_PCM_STREAM_CAPTURE) + val &= ~AUD_CTRL_TOUT_IRQ_MASK; + + writel(val, mmio_base + XLNX_AUD_CTRL); +} + +static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg) +{ + u32 val; + void __iomem *reg; + struct device *dev = arg; + struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); + + reg = adata->mmio + XLNX_MM2S_OFFSET + XLNX_AUD_STS; + val = readl(reg); + if (val & AUD_STS_IOC_IRQ_MASK) { + writel(val & AUD_STS_IOC_IRQ_MASK, reg); + if (adata->play_stream) + snd_pcm_period_elapsed(adata->play_stream); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) +{ + u32 val; + void __iomem *reg; + struct device *dev = arg; + struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); + + reg = adata->mmio + XLNX_S2MM_OFFSET + XLNX_AUD_STS; + val = readl(reg); + if (val & AUD_STS_IOC_IRQ_MASK) { + writel(val & AUD_STS_IOC_IRQ_MASK, reg); + if (adata->capture_stream) + snd_pcm_period_elapsed(adata->capture_stream); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int xlnx_formatter_pcm_open(struct snd_pcm_substream *substream) +{ + int err; + u32 val, data_format_mode; + u32 ch_count_mask, ch_count_shift, data_xfer_mode, data_xfer_shift; + struct xlnx_pcm_stream_param *stream_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); + struct xlnx_pcm_drv_data *adata = dev_get_drvdata(component->dev); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !adata->mm2s_presence) + return -ENODEV; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + !adata->s2mm_presence) + return -ENODEV; + + stream_data = kzalloc(sizeof(*stream_data), GFP_KERNEL); + if (!stream_data) + return -ENOMEM; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ch_count_mask = CFG_MM2S_CH_MASK; + ch_count_shift = CFG_MM2S_CH_SHIFT; + data_xfer_mode = CFG_MM2S_XFER_MASK; + data_xfer_shift = CFG_MM2S_XFER_SHIFT; + data_format_mode = CFG_MM2S_PKG_MASK; + stream_data->mmio = adata->mmio + XLNX_MM2S_OFFSET; + adata->play_stream = substream; + + } else { + ch_count_mask = CFG_S2MM_CH_MASK; + ch_count_shift = CFG_S2MM_CH_SHIFT; + data_xfer_mode = CFG_S2MM_XFER_MASK; + data_xfer_shift = CFG_S2MM_XFER_SHIFT; + data_format_mode = CFG_S2MM_PKG_MASK; + stream_data->mmio = adata->mmio + XLNX_S2MM_OFFSET; + adata->capture_stream = substream; + } + + val = readl(adata->mmio + XLNX_AUD_CORE_CONFIG); + + if (!(val & data_format_mode)) + stream_data->interleaved = true; + + stream_data->xfer_mode = (val & data_xfer_mode) >> data_xfer_shift; + stream_data->ch_limit = (val & ch_count_mask) >> ch_count_shift; + dev_info(component->dev, + "stream %d : format = %d mode = %d ch_limit = %d\n", + substream->stream, stream_data->interleaved, + stream_data->xfer_mode, stream_data->ch_limit); + + snd_soc_set_runtime_hwparams(substream, &xlnx_pcm_hardware); + runtime->private_data = stream_data; + + /* Resize the period size divisible by 64 */ + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); + if (err) { + dev_err(component->dev, + "unable to set constraint on period bytes\n"); + return err; + } + + /* enable DMA IOC irq */ + val = readl(stream_data->mmio + XLNX_AUD_CTRL); + val |= AUD_CTRL_IOC_IRQ_MASK; + writel(val, stream_data->mmio + XLNX_AUD_CTRL); + + return 0; +} + +static int xlnx_formatter_pcm_close(struct snd_pcm_substream *substream) +{ + int ret; + struct xlnx_pcm_stream_param *stream_data = + substream->runtime->private_data; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); + + ret = xlnx_formatter_pcm_reset(stream_data->mmio); + if (ret) { + dev_err(component->dev, "audio formatter reset failed\n"); + goto err_reset; + } + xlnx_formatter_disable_irqs(stream_data->mmio, substream->stream); + +err_reset: + kfree(stream_data); + return 0; +} + +static snd_pcm_uframes_t +xlnx_formatter_pcm_pointer(struct snd_pcm_substream *substream) +{ + u32 pos; + struct snd_pcm_runtime *runtime = substream->runtime; + struct xlnx_pcm_stream_param *stream_data = runtime->private_data; + + pos = readl(stream_data->mmio + XLNX_AUD_XFER_COUNT); + + if (pos >= stream_data->buffer_size) + pos = 0; + + return bytes_to_frames(runtime, pos); +} + +static int xlnx_formatter_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + u32 low, high, active_ch, val, bytes_per_ch, bits_per_sample; + u32 aes_reg1_val, aes_reg2_val; + int status; + u64 size; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); + struct snd_pcm_runtime *runtime = substream->runtime; + struct xlnx_pcm_stream_param *stream_data = runtime->private_data; + + active_ch = params_channels(params); + if (active_ch > stream_data->ch_limit) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + stream_data->xfer_mode == AES_TO_PCM) { + val = readl(stream_data->mmio + XLNX_AUD_STS); + if (val & AUD_STS_CH_STS_MASK) { + aes_reg1_val = readl(stream_data->mmio + + XLNX_AUD_CH_STS_START); + aes_reg2_val = readl(stream_data->mmio + + XLNX_AUD_CH_STS_START + 0x4); + + xlnx_parse_aes_params(aes_reg1_val, aes_reg2_val, + component->dev); + } + } + + size = params_buffer_bytes(params); + status = snd_pcm_lib_malloc_pages(substream, size); + if (status < 0) + return status; + + stream_data->buffer_size = size; + + low = lower_32_bits(substream->dma_buffer.addr); + high = upper_32_bits(substream->dma_buffer.addr); + writel(low, stream_data->mmio + XLNX_AUD_BUFF_ADDR_LSB); + writel(high, stream_data->mmio + XLNX_AUD_BUFF_ADDR_MSB); + + val = readl(stream_data->mmio + XLNX_AUD_CTRL); + bits_per_sample = params_width(params); + switch (bits_per_sample) { + case 8: + val |= (BIT_DEPTH_8 << AUD_CTRL_DATA_WIDTH_SHIFT); + break; + case 16: + val |= (BIT_DEPTH_16 << AUD_CTRL_DATA_WIDTH_SHIFT); + break; + case 20: + val |= (BIT_DEPTH_20 << AUD_CTRL_DATA_WIDTH_SHIFT); + break; + case 24: + val |= (BIT_DEPTH_24 << AUD_CTRL_DATA_WIDTH_SHIFT); + break; + case 32: + val |= (BIT_DEPTH_32 << AUD_CTRL_DATA_WIDTH_SHIFT); + break; + default: + return -EINVAL; + } + + val |= active_ch << AUD_CTRL_ACTIVE_CH_SHIFT; + writel(val, stream_data->mmio + XLNX_AUD_CTRL); + + val = (params_periods(params) << PERIOD_CFG_PERIODS_SHIFT) + | params_period_bytes(params); + writel(val, stream_data->mmio + XLNX_AUD_PERIOD_CONFIG); + bytes_per_ch = DIV_ROUND_UP(params_period_bytes(params), active_ch); + writel(bytes_per_ch, stream_data->mmio + XLNX_BYTES_PER_CH); + + return 0; +} + +static int xlnx_formatter_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int xlnx_formatter_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + u32 val; + struct xlnx_pcm_stream_param *stream_data = + substream->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + val = readl(stream_data->mmio + XLNX_AUD_CTRL); + val |= AUD_CTRL_DMA_EN_MASK; + writel(val, stream_data->mmio + XLNX_AUD_CTRL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + val = readl(stream_data->mmio + XLNX_AUD_CTRL); + val &= ~AUD_CTRL_DMA_EN_MASK; + writel(val, stream_data->mmio + XLNX_AUD_CTRL); + break; + } + + return 0; +} + +static int xlnx_formatter_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, + DRV_NAME); + return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, component->dev, + xlnx_pcm_hardware.buffer_bytes_max, + xlnx_pcm_hardware.buffer_bytes_max); +} + +static const struct snd_pcm_ops xlnx_formatter_pcm_ops = { + .open = xlnx_formatter_pcm_open, + .close = xlnx_formatter_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = xlnx_formatter_pcm_hw_params, + .hw_free = xlnx_formatter_pcm_hw_free, + .trigger = xlnx_formatter_pcm_trigger, + .pointer = xlnx_formatter_pcm_pointer, +}; + +static const struct snd_soc_component_driver xlnx_asoc_component = { + .name = DRV_NAME, + .ops = &xlnx_formatter_pcm_ops, + .pcm_new = xlnx_formatter_pcm_new, +}; + +static int xlnx_formatter_pcm_probe(struct platform_device *pdev) +{ + int ret; + u32 val; + struct xlnx_pcm_drv_data *aud_drv_data; + struct resource *res; + struct device *dev = &pdev->dev; + + aud_drv_data = devm_kzalloc(dev, sizeof(*aud_drv_data), GFP_KERNEL); + if (!aud_drv_data) + return -ENOMEM; + + aud_drv_data->axi_clk = devm_clk_get(dev, "s_axi_lite_aclk"); + if (IS_ERR(aud_drv_data->axi_clk)) { + ret = PTR_ERR(aud_drv_data->axi_clk); + dev_err(dev, "failed to get s_axi_lite_aclk(%d)\n", ret); + return ret; + } + ret = clk_prepare_enable(aud_drv_data->axi_clk); + if (ret) { + dev_err(dev, + "failed to enable s_axi_lite_aclk(%d)\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "audio formatter node:addr to resource failed\n"); + ret = -ENXIO; + goto clk_err; + } + aud_drv_data->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(aud_drv_data->mmio)) { + dev_err(dev, "audio formatter ioremap failed\n"); + ret = PTR_ERR(aud_drv_data->mmio); + goto clk_err; + } + + val = readl(aud_drv_data->mmio + XLNX_AUD_CORE_CONFIG); + if (val & AUD_CFG_MM2S_MASK) { + aud_drv_data->mm2s_presence = true; + ret = xlnx_formatter_pcm_reset(aud_drv_data->mmio + + XLNX_MM2S_OFFSET); + if (ret) { + dev_err(dev, "audio formatter reset failed\n"); + goto clk_err; + } + xlnx_formatter_disable_irqs(aud_drv_data->mmio + + XLNX_MM2S_OFFSET, + SNDRV_PCM_STREAM_PLAYBACK); + + aud_drv_data->mm2s_irq = platform_get_irq_byname(pdev, + "irq_mm2s"); + if (aud_drv_data->mm2s_irq < 0) { + dev_err(dev, "xlnx audio mm2s irq resource failed\n"); + ret = aud_drv_data->mm2s_irq; + goto clk_err; + } + ret = devm_request_irq(dev, aud_drv_data->mm2s_irq, + xlnx_mm2s_irq_handler, 0, + "xlnx_formatter_pcm_mm2s_irq", dev); + if (ret) { + dev_err(dev, "xlnx audio mm2s irq request failed\n"); + goto clk_err; + } + } + if (val & AUD_CFG_S2MM_MASK) { + aud_drv_data->s2mm_presence = true; + ret = xlnx_formatter_pcm_reset(aud_drv_data->mmio + + XLNX_S2MM_OFFSET); + if (ret) { + dev_err(dev, "audio formatter reset failed\n"); + goto clk_err; + } + xlnx_formatter_disable_irqs(aud_drv_data->mmio + + XLNX_S2MM_OFFSET, + SNDRV_PCM_STREAM_CAPTURE); + + aud_drv_data->s2mm_irq = platform_get_irq_byname(pdev, + "irq_s2mm"); + if (aud_drv_data->s2mm_irq < 0) { + dev_err(dev, "xlnx audio s2mm irq resource failed\n"); + ret = aud_drv_data->s2mm_irq; + goto clk_err; + } + ret = devm_request_irq(dev, aud_drv_data->s2mm_irq, + xlnx_s2mm_irq_handler, 0, + "xlnx_formatter_pcm_s2mm_irq", + dev); + if (ret) { + dev_err(dev, "xlnx audio s2mm irq request failed\n"); + goto clk_err; + } + } + + dev_set_drvdata(dev, aud_drv_data); + + ret = devm_snd_soc_register_component(dev, &xlnx_asoc_component, + NULL, 0); + if (ret) { + dev_err(dev, "pcm platform device register failed\n"); + goto clk_err; + } + + return 0; + +clk_err: + clk_disable_unprepare(aud_drv_data->axi_clk); + return ret; +} + +static int xlnx_formatter_pcm_remove(struct platform_device *pdev) +{ + int ret = 0; + struct xlnx_pcm_drv_data *adata = dev_get_drvdata(&pdev->dev); + + if (adata->s2mm_presence) + ret = xlnx_formatter_pcm_reset(adata->mmio + XLNX_S2MM_OFFSET); + + /* Try MM2S reset, even if S2MM reset fails */ + if (adata->mm2s_presence) + ret = xlnx_formatter_pcm_reset(adata->mmio + XLNX_MM2S_OFFSET); + + if (ret) + dev_err(&pdev->dev, "audio formatter reset failed\n"); + + clk_disable_unprepare(adata->axi_clk); + return ret; +} + +static const struct of_device_id xlnx_formatter_pcm_of_match[] = { + { .compatible = "xlnx,audio-formatter-1.0"}, + {}, +}; +MODULE_DEVICE_TABLE(of, xlnx_formatter_pcm_of_match); + +static struct platform_driver xlnx_formatter_pcm_driver = { + .probe = xlnx_formatter_pcm_probe, + .remove = xlnx_formatter_pcm_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = xlnx_formatter_pcm_of_match, + }, +}; + +module_platform_driver(xlnx_formatter_pcm_driver); +MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c new file mode 100644 index 000000000000..3b9000fd8c49 --- /dev/null +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Xilinx ASoC SPDIF audio support +// +// Copyright (C) 2018 Xilinx, Inc. +// +// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com> +// + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define XLNX_SPDIF_RATES \ + (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +#define XLNX_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +#define XSPDIF_IRQ_STS_REG 0x20 +#define XSPDIF_IRQ_ENABLE_REG 0x28 +#define XSPDIF_SOFT_RESET_REG 0x40 +#define XSPDIF_CONTROL_REG 0x44 +#define XSPDIF_CHAN_0_STS_REG 0x4C +#define XSPDIF_GLOBAL_IRQ_ENABLE_REG 0x1C +#define XSPDIF_CH_A_USER_DATA_REG_0 0x64 + +#define XSPDIF_CORE_ENABLE_MASK BIT(0) +#define XSPDIF_FIFO_FLUSH_MASK BIT(1) +#define XSPDIF_CH_STS_MASK BIT(5) +#define XSPDIF_GLOBAL_IRQ_ENABLE BIT(31) +#define XSPDIF_CLOCK_CONFIG_BITS_MASK GENMASK(5, 2) +#define XSPDIF_CLOCK_CONFIG_BITS_SHIFT 2 +#define XSPDIF_SOFT_RESET_VALUE 0xA + +#define MAX_CHANNELS 2 +#define AES_SAMPLE_WIDTH 32 +#define CH_STATUS_UPDATE_TIMEOUT 40 + +struct spdif_dev_data { + u32 mode; + u32 aclk; + bool rx_chsts_updated; + void __iomem *base; + struct clk *axi_clk; + wait_queue_head_t chsts_q; +}; + +static irqreturn_t xlnx_spdifrx_irq_handler(int irq, void *arg) +{ + u32 val; + struct spdif_dev_data *ctx = arg; + + val = readl(ctx->base + XSPDIF_IRQ_STS_REG); + if (val & XSPDIF_CH_STS_MASK) { + writel(val & XSPDIF_CH_STS_MASK, + ctx->base + XSPDIF_IRQ_STS_REG); + val = readl(ctx->base + + XSPDIF_IRQ_ENABLE_REG); + writel(val & ~XSPDIF_CH_STS_MASK, + ctx->base + XSPDIF_IRQ_ENABLE_REG); + + ctx->rx_chsts_updated = true; + wake_up_interruptible(&ctx->chsts_q); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int xlnx_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + u32 val; + struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev); + + val = readl(ctx->base + XSPDIF_CONTROL_REG); + val |= XSPDIF_FIFO_FLUSH_MASK; + writel(val, ctx->base + XSPDIF_CONTROL_REG); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + writel(XSPDIF_CH_STS_MASK, + ctx->base + XSPDIF_IRQ_ENABLE_REG); + writel(XSPDIF_GLOBAL_IRQ_ENABLE, + ctx->base + XSPDIF_GLOBAL_IRQ_ENABLE_REG); + } + + return 0; +} + +static void xlnx_spdif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev); + + writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); +} + +static int xlnx_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u32 val, clk_div, clk_cfg; + struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev); + + clk_div = DIV_ROUND_CLOSEST(ctx->aclk, MAX_CHANNELS * AES_SAMPLE_WIDTH * + params_rate(params)); + + switch (clk_div) { + case 4: + clk_cfg = 0; + break; + case 8: + clk_cfg = 1; + break; + case 16: + clk_cfg = 2; + break; + case 24: + clk_cfg = 3; + break; + case 32: + clk_cfg = 4; + break; + case 48: + clk_cfg = 5; + break; + case 64: + clk_cfg = 6; + break; + default: + return -EINVAL; + } + + val = readl(ctx->base + XSPDIF_CONTROL_REG); + val &= ~XSPDIF_CLOCK_CONFIG_BITS_MASK; + val |= clk_cfg << XSPDIF_CLOCK_CONFIG_BITS_SHIFT; + writel(val, ctx->base + XSPDIF_CONTROL_REG); + + return 0; +} + +static int rx_stream_detect(struct snd_soc_dai *dai) +{ + int err; + struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev); + unsigned long jiffies = msecs_to_jiffies(CH_STATUS_UPDATE_TIMEOUT); + + /* start capture only if stream is detected within 40ms timeout */ + err = wait_event_interruptible_timeout(ctx->chsts_q, + ctx->rx_chsts_updated, + jiffies); + if (!err) { + dev_err(dai->dev, "No streaming audio detected!\n"); + return -EINVAL; + } + ctx->rx_chsts_updated = false; + + return 0; +} + +static int xlnx_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + u32 val; + int ret = 0; + struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev); + + val = readl(ctx->base + XSPDIF_CONTROL_REG); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val |= XSPDIF_CORE_ENABLE_MASK; + writel(val, ctx->base + XSPDIF_CONTROL_REG); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = rx_stream_detect(dai); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val &= ~XSPDIF_CORE_ENABLE_MASK; + writel(val, ctx->base + XSPDIF_CONTROL_REG); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct snd_soc_dai_ops xlnx_spdif_dai_ops = { + .startup = xlnx_spdif_startup, + .shutdown = xlnx_spdif_shutdown, + .trigger = xlnx_spdif_trigger, + .hw_params = xlnx_spdif_hw_params, +}; + +static struct snd_soc_dai_driver xlnx_spdif_tx_dai = { + .name = "xlnx_spdif_tx", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = XLNX_SPDIF_RATES, + .formats = XLNX_SPDIF_FORMATS, + }, + .ops = &xlnx_spdif_dai_ops, +}; + +static struct snd_soc_dai_driver xlnx_spdif_rx_dai = { + .name = "xlnx_spdif_rx", + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = XLNX_SPDIF_RATES, + .formats = XLNX_SPDIF_FORMATS, + }, + .ops = &xlnx_spdif_dai_ops, +}; + +static const struct snd_soc_component_driver xlnx_spdif_component = { + .name = "xlnx-spdif", +}; + +static const struct of_device_id xlnx_spdif_of_match[] = { + { .compatible = "xlnx,spdif-2.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xlnx_spdif_of_match); + +static int xlnx_spdif_probe(struct platform_device *pdev) +{ + int ret; + struct resource *res; + struct snd_soc_dai_driver *dai_drv; + struct spdif_dev_data *ctx; + + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->axi_clk = devm_clk_get(dev, "s_axi_aclk"); + if (IS_ERR(ctx->axi_clk)) { + ret = PTR_ERR(ctx->axi_clk); + dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret); + return ret; + } + ret = clk_prepare_enable(ctx->axi_clk); + if (ret) { + dev_err(dev, "failed to enable s_axi_aclk(%d)\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->base)) { + ret = PTR_ERR(ctx->base); + goto clk_err; + } + ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); + if (ret < 0) { + dev_err(dev, "cannot get SPDIF mode\n"); + goto clk_err; + } + if (ctx->mode) { + dai_drv = &xlnx_spdif_tx_dai; + } else { + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "No IRQ resource found\n"); + ret = -ENODEV; + goto clk_err; + } + ret = devm_request_irq(dev, res->start, + xlnx_spdifrx_irq_handler, + 0, "XLNX_SPDIF_RX", ctx); + if (ret) { + dev_err(dev, "spdif rx irq request failed\n"); + ret = -ENODEV; + goto clk_err; + } + + init_waitqueue_head(&ctx->chsts_q); + dai_drv = &xlnx_spdif_rx_dai; + } + + ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); + if (ret < 0) { + dev_err(dev, "cannot get aud_clk_i value\n"); + goto clk_err; + } + + dev_set_drvdata(dev, ctx); + + ret = devm_snd_soc_register_component(dev, &xlnx_spdif_component, + dai_drv, 1); + if (ret) { + dev_err(dev, "SPDIF component registration failed\n"); + goto clk_err; + } + + writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); + dev_info(dev, "%s DAI registered\n", dai_drv->name); + +clk_err: + clk_disable_unprepare(ctx->axi_clk); + return ret; +} + +static int xlnx_spdif_remove(struct platform_device *pdev) +{ + struct spdif_dev_data *ctx = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(ctx->axi_clk); + return 0; +} + +static struct platform_driver xlnx_spdif_driver = { + .driver = { + .name = "xlnx-spdif", + .of_match_table = xlnx_spdif_of_match, + }, + .probe = xlnx_spdif_probe, + .remove = xlnx_spdif_remove, +}; +module_platform_driver(xlnx_spdif_driver); + +MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>"); +MODULE_DESCRIPTION("XILINX SPDIF driver"); +MODULE_LICENSE("GPL v2"); |