diff options
author | Walker Chen <walker.chen@starfivetech.com> | 2021-12-29 11:02:54 +0300 |
---|---|---|
committer | Walker Chen <walker.chen@starfivetech.com> | 2021-12-29 11:02:54 +0300 |
commit | 2db1d325be7acf9aa7e626873b4d677bedeeff96 (patch) | |
tree | 7132f116c069434a13a169fc490bcddc49471d92 | |
parent | 689e2e087b179c0a151e766b2be31225c680992d (diff) | |
download | linux-2db1d325be7acf9aa7e626873b4d677bedeeff96.tar.xz |
modify clock tree and support i2s audio
-rwxr-xr-x | arch/riscv/boot/dts/starfive/codecs/sf_wm8960.dtsi | 38 | ||||
-rw-r--r-- | arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight-a1.dts | 4 | ||||
-rw-r--r-- | arch/riscv/boot/dts/starfive/jh7100-common.dtsi | 18 | ||||
-rw-r--r-- | arch/riscv/boot/dts/starfive/jh7100.dtsi | 314 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-starfive-jh7100-audio.c | 59 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-starfive-jh7100.c | 18 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-starfive-jh7100.h | 2 | ||||
-rw-r--r-- | include/dt-bindings/clock/starfive-jh7100-audio.h | 7 | ||||
-rw-r--r-- | sound/soc/codecs/wm8960.c | 43 | ||||
-rw-r--r-- | sound/soc/dwc/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/dwc/dwc-i2s.c | 387 | ||||
-rw-r--r-- | sound/soc/dwc/local.h | 49 |
12 files changed, 761 insertions, 186 deletions
diff --git a/arch/riscv/boot/dts/starfive/codecs/sf_wm8960.dtsi b/arch/riscv/boot/dts/starfive/codecs/sf_wm8960.dtsi new file mode 100755 index 000000000000..151fb1bf3270 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/codecs/sf_wm8960.dtsi @@ -0,0 +1,38 @@ +&sound{ + /* i2s + wm8960 */ + simple-audio-card,dai-link@1 { + reg = <1>; + status = "okay"; + format = "i2s"; + bitclock-master = <&sndcodec1>; + frame-master = <&sndcodec1>; + + widgets = + "Microphone", "Mic Jack", + "Line", "Line In", + "Line", "Line Out", + "Speaker", "Speaker", + "Headphone", "Headphone Jack"; + routing = + "Headphone Jack", "HP_L", + "Headphone Jack", "HP_R", + "Speaker", "SPK_LP", + "Speaker", "SPK_LN", + "LINPUT1", "Mic Jack", + "LINPUT3", "Mic Jack", + "RINPUT1", "Mic Jack", + "RINPUT2", "Mic Jack"; + cpu0 { + sound-dai = <&i2sadc0>; + }; + cpu1 { + sound-dai = <&i2sdac0>; + }; + + sndcodec1:codec { + sound-dai = <&wm8960>; + clocks = <&wm8960_mclk>; + clock-names = "mclk"; + }; + }; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight-a1.dts b/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight-a1.dts index d307e44590f3..c7e6b793d763 100644 --- a/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight-a1.dts +++ b/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight-a1.dts @@ -20,5 +20,7 @@ &gpio { /* don't reset gpio mux for serial console and reset gpio */ - starfive,keep-gpiomux = <13 14 63>; + starfive,keep-gpiomux = <13 14 63 0 2 3 45>; }; + +#include "codecs/sf_wm8960.dtsi" diff --git a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi index 1144dfd65e0e..ff3e6b746132 100644 --- a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi @@ -130,6 +130,9 @@ }; &gpio { + /* don't reset gpio mux for serial console on uart3 */ + starfive,keep-gpiomux = <13 14 0 2 3 45>; + gmac_pins: gmac-0 { gtxclk-pins { pins = <PAD_FUNC_SHARE(115)>; @@ -266,11 +269,11 @@ <GPIOMUX(5, GPO_PWM_PAD_OUT_BIT1, GPO_PWM_PAD_OE_N_BIT1, - GPI_NONE)>, - <GPIOMUX(45, - GPO_PWM_PAD_OUT_BIT2, - GPO_PWM_PAD_OE_N_BIT2, GPI_NONE)>; + // <GPIOMUX(45, + // GPO_PWM_PAD_OUT_BIT2, + // GPO_PWM_PAD_OE_N_BIT2, + // GPI_NONE)>; bias-disable; drive-strength = <35>; input-disable; @@ -468,6 +471,13 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c1_pins>; status = "okay"; + + wm8960: codec@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + #sound-dai-cells = <0>; + wlf,shared-lrclk; + }; }; &i2c2 { diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi index d4c88766f99b..48f87141f00e 100644 --- a/arch/riscv/boot/dts/starfive/jh7100.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi @@ -243,155 +243,6 @@ interrupts = <31>; }; - i2sadc0: i2sadc0@10400000 { - compatible = "snps,designware-i2sadc0"; - reg = <0x0 0x10400000 0x0 0x1000>; - clocks = <&clkgen JH7100_CLK_APB1_BUS>; - clock-names = "i2sclk"; - interrupt-parent = <&plic>; - #sound-dai-cells = <0>; - dmas = <&dma2p 28>; - dma-names = "rx"; - }; - - i2svad: i2svad@10420000 { - compatible = "starfive,sf-i2svad"; - reg = <0x0 0x10420000 0x0 0x1000> ; - clocks = <&audclk JH7100_AUDCLK_I2SVAD_APB>; - clock-names = "i2svad_apb"; - resets = <&audrst JH7100_AUDRSTN_I2SVAD_APB>, - <&audrst JH7100_AUDRSTN_I2SVAD_SRST>; - reset-names = "apb_i2svad", "i2svad_srst"; - interrupts = <60>, <61>; - interrupt-names = "spintr", "slintr"; - #sound-dai-cells = <0>; - }; - - pwmdac: pwmdac@10440000 { - compatible = "starfive,pwmdac"; - reg = <0x0 0x10440000 0x0 0x1000>; - clocks = <&clkgen JH7100_CLK_AUDIO_ROOT>, - <&clkgen JH7100_CLK_AUDIO_SRC>, - <&clkgen JH7100_CLK_AUDIO_12288>, - <&audclk JH7100_AUDCLK_DMA1P_AHB>, - <&audclk JH7100_AUDCLK_PWMDAC_APB>, - <&audclk JH7100_AUDCLK_DAC_MCLK>; - clock-names = "audio_root", - "audio_src", - "audio_12288", - "dma1p_ahb", - "pwmdac_apb", - "dac_mclk"; - resets = <&audrst JH7100_AUDRSTN_APB_BUS>, - <&audrst JH7100_AUDRSTN_DMA1P_AHB>, - <&audrst JH7100_AUDRSTN_PWMDAC_APB>; - reset-names = "apb_bus", "dma1p_ahb", "apb_pwmdac"; - dmas = <&dma2p 23>; - dma-names = "tx"; - #sound-dai-cells = <0>; - }; - - i2sdac0: i2sdac0@10450000 { - compatible = "snps,designware-i2sdac0"; - reg = <0x0 0x10450000 0x0 0x1000>; - clocks = <&audclk JH7100_AUDCLK_DAC_MCLK>, - <&audclk JH7100_AUDCLK_I2SDAC_BCLK>, - <&audclk JH7100_AUDCLK_I2SDAC_LRCLK>, - <&audclk JH7100_AUDCLK_I2SDAC_APB>; - clock-names = "dac_mclk", "i2sdac0_bclk", "i2sdac0_lrclk", "i2sdac_apb"; - resets = <&audrst JH7100_AUDRSTN_I2SDAC_APB>, - <&audrst JH7100_AUDRSTN_I2SDAC_SRST>; - reset-names = "apb_i2sdac", "i2sdac_srst"; - #sound-dai-cells = <0>; - dmas = <&dma2p 30>; - dma-names = "tx"; - }; - - i2sdac1: i2sdac1@10460000 { - compatible = "snps,designware-i2sdac1"; - reg = <0x0 0x10460000 0x0 0x1000>; - clocks = <&audclk JH7100_AUDCLK_DAC_MCLK>, - <&audclk JH7100_AUDCLK_I2S1_BCLK>, - <&audclk JH7100_AUDCLK_I2S1_LRCLK>, - <&audclk JH7100_AUDCLK_I2S1_APB>; - clock-names = "dac_mclk", "i2sdac1_bclk", "i2sdac1_lrclk", "i2s1_apb"; - resets = <&audrst JH7100_AUDRSTN_I2S1_APB>, - <&audrst JH7100_AUDRSTN_I2S1_SRST>; - #sound-dai-cells = <0>; - dmas = <&dma2p 31>; - dma-names = "tx"; - }; - - i2sdac16k: i2sdac16k@10470000 { - compatible = "snps,designware-i2sdac16k"; - reg = <0x0 0x10470000 0x0 0x1000>; - clocks = <&clkgen JH7100_CLK_APB1_BUS>; - clock-names = "i2sclk"; - #sound-dai-cells = <0>; - dmas = <&dma2p 29>; - dma-names = "tx"; - }; - - audclk: clock-controller@10480000 { - compatible = "starfive,jh7100-audclk"; - reg = <0x0 0x10480000 0x0 0x10000>; - clocks = <&clkgen JH7100_CLK_AUDIO_SRC>, - <&clkgen JH7100_CLK_AUDIO_12288>, - <&clkgen JH7100_CLK_DOM7AHB_BUS>; - clock-names = "audio_src", "audio_12288", "dom7ahb_bus"; - #clock-cells = <1>; - }; - - audrst: reset-controller@10490000 { - compatible = "starfive,jh7100-audrst"; - reg = <0x0 0x10490000 0x0 0x10000>; - #reset-cells = <1>; - }; - - spdif_transmitter: spdif-transmitter { - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0>; - }; - - spdif_receiver: spdif-receiver { - compatible = "linux,spdif-dir"; - #sound-dai-cells = <0>; - }; - - pwmdac_codec: pwmdac-transmitter { - compatible = "linux,pwmdac-dit"; - #sound-dai-cells = <0>; - }; - - dmic_codec: dmic { - compatible = "dmic-codec"; - #sound-dai-cells = <0>; - }; - - sound: snd-card { - compatible = "simple-audio-card"; - simple-audio-card,name = "Starfive-Multi-Sound-Card"; - #address-cells = <1>; - #size-cells = <0>; - - /* pwmdac */ - simple-audio-card,dai-link@0 { - reg = <0>; - status = "okay"; - format = "left_j"; - bitclock-master = <&sndcpu0>; - frame-master = <&sndcpu0>; - - sndcpu0: cpu { - sound-dai = <&pwmdac>; - }; - - codec { - sound-dai = <&pwmdac_codec>; - }; - }; - }; - usb3: usb@104c0000 { compatible = "cdns,usb3"; reg = <0x0 0x104c0000 0x0 0x10000>, // memory area for HOST registers @@ -802,5 +653,170 @@ dsp@0 { }; }; + + wm8960_mclk: wm8960_mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + }; + + i2sadc0: i2sadc0@10400000 { + compatible = "snps,designware-i2sadc0"; + reg = <0x0 0x10400000 0x0 0x1000>; + interrupt-parent = <&plic>; + clocks = <&clkgen JH7100_CLK_AUDIO_ROOT>, + <&clkgen JH7100_CLK_AUDIO_SRC>, + <&clkgen JH7100_CLK_AUDIO_12288>, + <&audclk JH7100_AUDCLK_DMA1P_AHB>, + <&audclk JH7100_AUDCLK_ADC_MCLK>, + <&audclk JH7100_AUDCLK_I2SADC_APB>, + <&audclk JH7100_AUDCLK_I2SVAD_APB>, + <&audclk JH7100_AUDCLK_I2SADC_BCLK>, + <&audclk JH7100_AUDCLK_I2SADC_LRCLK>, + <&audclk JH7100_AUDCLK_I2SADC_BCLK_IOPAD>, + <&audclk JH7100_AUDCLK_I2SADC_LRCLK_IOPAD>; + clock-names = "audio_root", "audio_src", + "audio_12288", "dma1p_ahb", + "adc_mclk", "i2sadc_apb", + "i2svad_apb", "i2sadc0_bclk", "i2sadc0_lrclk", + "i2sadc_bclk_iopad", "i2sadc_lrclk_iopad"; + resets = <&audrst JH7100_AUDRSTN_APB_BUS>, + <&audrst JH7100_AUDRSTN_DMA1P_AHB>, + <&audrst JH7100_AUDRSTN_I2SADC_APB>, + <&audrst JH7100_AUDRSTN_I2SADC_SRST>, + <&audrst JH7100_AUDRSTN_I2SVAD_APB>, + <&audrst JH7100_AUDRSTN_I2SVAD_SRST>; + reset-names = "apb_bus", "dma1p_ahb", "apb_i2sadc", + "i2sadc_srst", "apb_i2svad", "i2svad_srst"; + #sound-dai-cells = <0>; + dmas = <&dma2p 28>; + dma-names = "rx"; + }; + + i2svad: i2svad@10420000 { + compatible = "sf,sf-i2svad"; + reg = <0x0 0x10420000 0x0 0x1000> ; + interrupt-parent = <&plic>; + interrupts = <60>, <61>; + interrupt-names = "spintr", "slintr"; + clocks = <&clkgen JH7100_CLK_APB1_BUS>; + clock-names = "i2sclk"; + #sound-dai-cells = <0>; + }; + i2sdac0: i2sdac0@10450000 { + compatible = "snps,designware-i2sdac0"; + reg = <0x0 0x10450000 0x0 0x1000>; + interrupt-parent = <&plic>; + clocks = <&audclk JH7100_AUDCLK_DAC_MCLK>, + <&audclk JH7100_AUDCLK_I2SDAC_BCLK>, + <&audclk JH7100_AUDCLK_I2SDAC_LRCLK>, + <&audclk JH7100_AUDCLK_I2SDAC_APB>, + <&audclk JH7100_AUDCLK_I2SDAC_BCLK_IOPAD>, + <&audclk JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD>; + clock-names = "dac_mclk", "i2sdac0_bclk", "i2sdac0_lrclk", + "i2sdac_apb", "i2sdac_bclk_iopad", "i2sdac_lrclk_iopad"; + resets = <&audrst JH7100_AUDRSTN_I2SDAC_APB>, + <&audrst JH7100_AUDRSTN_I2SDAC_SRST>; + reset-names = "apb_i2sdac", "i2sdac_srst"; + #sound-dai-cells = <0>; + dmas = <&dma2p 30>; + dma-names = "tx"; + }; + + i2sdac16k: i2sdac16k@10470000 { + compatible = "snps,designware-i2sdac16k"; + reg = <0x0 0x10470000 0x0 0x1000>; + interrupt-parent = <&plic>; + /* interrupts = <68>; */ + /* interrupt-names = "tx"; */ + clocks = <&clkgen JH7100_CLK_APB1_BUS>; + clock-names = "i2sclk"; + #sound-dai-cells = <0>; + dmas = <&dma2p 29>; + dma-names = "tx"; + }; + + audclk: clock-controller@10480000 { + compatible = "starfive,jh7100-audclk"; + reg = <0x0 0x10480000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_AUDIO_SRC>, + <&clkgen JH7100_CLK_AUDIO_12288>, + <&clkgen JH7100_CLK_DOM7AHB_BUS>; + clock-names = "audio_src", "audio_12288", "dom7ahb_bus"; + #clock-cells = <1>; + }; + + audrst: reset-controller@10490000 { + compatible = "starfive,jh7100-audrst"; + reg = <0x0 0x10490000 0x0 0x10000>; + #reset-cells = <1>; + }; + + spdif_transmitter: spdif_transmitter { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + spdif_receiver: spdif_receiver { + compatible = "linux,spdif-dir"; + #sound-dai-cells = <0>; + }; + + pwmdac: pwmdac@10440000 { + compatible = "starfive,pwmdac"; + reg = <0x0 0x10440000 0x0 0x1000>; + clocks = <&clkgen JH7100_CLK_AUDIO_ROOT>, + <&clkgen JH7100_CLK_AUDIO_SRC>, + <&clkgen JH7100_CLK_AUDIO_12288>, + <&audclk JH7100_AUDCLK_DMA1P_AHB>, + <&audclk JH7100_AUDCLK_PWMDAC_APB>, + <&audclk JH7100_AUDCLK_DAC_MCLK>; + clock-names = "audio_root", + "audio_src", + "audio_12288", + "dma1p_ahb", + "pwmdac_apb", + "dac_mclk"; + resets = <&audrst JH7100_AUDRSTN_APB_BUS>, + <&audrst JH7100_AUDRSTN_DMA1P_AHB>, + <&audrst JH7100_AUDRSTN_PWMDAC_APB>; + reset-names = "apb_bus", "dma1p_ahb", "apb_pwmdac"; + dmas = <&dma2p 23>; + dma-names = "tx"; + #sound-dai-cells = <0>; + }; + + pwmdac_codec: pwmdac-transmitter { + compatible = "linux,pwmdac-dit"; + #sound-dai-cells = <0>; + }; + + dmic_codec: dmic_codec { + compatible = "dmic-codec"; + #sound-dai-cells = <0>; + }; + + sound:snd-card{ + compatible = "simple-audio-card"; + simple-audio-card,name = "Starfive-Multi-Sound-Card"; + #address-cells = <1>; + #size-cells = <0>; + + simple-audio-card,dai-link@0 { + reg = <0>; + status = "okay"; + format = "left_j"; + bitclock-master = <&sndcpu0>; + frame-master = <&sndcpu0>; + + sndcpu0: cpu { + sound-dai = <&pwmdac>; + }; + + codec { + sound-dai = <&pwmdac_codec>; + }; + }; + }; }; }; diff --git a/drivers/clk/starfive/clk-starfive-jh7100-audio.c b/drivers/clk/starfive/clk-starfive-jh7100-audio.c index e5a59bccda60..0808aac4f64b 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100-audio.c +++ b/drivers/clk/starfive/clk-starfive-jh7100-audio.c @@ -3,10 +3,12 @@ * StarFive JH7100 Audio Clock Driver * * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk> + * Copyright (C) 2021 Walker Chen <walker.chen@starfivetech.com> */ #include <linux/bits.h> #include <linux/clk-provider.h> +#include <linux/io.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/mod_devicetable.h> @@ -22,11 +24,7 @@ #define JH7100_AUDCLK_AUDIO_SRC (JH7100_AUDCLK_END + 0) #define JH7100_AUDCLK_AUDIO_12288 (JH7100_AUDCLK_END + 1) #define JH7100_AUDCLK_DOM7AHB_BUS (JH7100_AUDCLK_END + 2) -#define JH7100_AUDCLK_I2SADC_BCLK_IOPAD (JH7100_AUDCLK_END + 3) -#define JH7100_AUDCLK_I2SADC_LRCLK_IOPAD (JH7100_AUDCLK_END + 4) -#define JH7100_AUDCLK_I2SDAC_BCLK_IOPAD (JH7100_AUDCLK_END + 5) -#define JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD (JH7100_AUDCLK_END + 6) -#define JH7100_AUDCLK_VAD_INTMEM (JH7100_AUDCLK_END + 7) +#define JH7100_AUDCLK_VAD_INTMEM (JH7100_AUDCLK_END + 3) static const struct jh7100_clk_data jh7100_audclk_data[] = { JH7100__GMD(JH7100_AUDCLK_ADC_MCLK, "adc_mclk", 0, 15, 2, @@ -62,9 +60,10 @@ static const struct jh7100_clk_data jh7100_audclk_data[] = { JH7100_AUDCLK_DAC_MCLK, JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), JH7100__INV(JH7100_AUDCLK_I2SDAC_BCLK_N, "i2sdac_bclk_n", JH7100_AUDCLK_I2SDAC_BCLK), - JH7100_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk", 31, 2, - JH7100_AUDCLK_I2S1_MCLK, - JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), + JH7100_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk", 63, 3, + JH7100_AUDCLK_I2SDAC_BCLK_N, + JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD, + JH7100_AUDCLK_I2SDAC_BCLK), JH7100_GATE(JH7100_AUDCLK_I2S1_APB, "i2s1_apb", 0, JH7100_AUDCLK_APB0_BUS), JH7100_MDIV(JH7100_AUDCLK_I2S1_BCLK, "i2s1_bclk", 31, 2, JH7100_AUDCLK_I2S1_MCLK, @@ -72,7 +71,8 @@ static const struct jh7100_clk_data jh7100_audclk_data[] = { JH7100__INV(JH7100_AUDCLK_I2S1_BCLK_N, "i2s1_bclk_n", JH7100_AUDCLK_I2S1_BCLK), JH7100_MDIV(JH7100_AUDCLK_I2S1_LRCLK, "i2s1_lrclk", 63, 3, JH7100_AUDCLK_I2S1_BCLK_N, - JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD), + JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD, + JH7100_AUDCLK_I2S1_BCLK), JH7100_GATE(JH7100_AUDCLK_I2SDAC16K_APB, "i2s1dac16k_apb", 0, JH7100_AUDCLK_APB0_BUS), JH7100__DIV(JH7100_AUDCLK_APB0_BUS, "apb0_bus", 8, JH7100_AUDCLK_DOM7AHB_BUS), JH7100_GATE(JH7100_AUDCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7100_AUDCLK_DOM7AHB_BUS), @@ -85,14 +85,33 @@ static const struct jh7100_clk_data jh7100_audclk_data[] = { JH7100_AUDCLK_AUDIO_12288), }; +static void jh7100_clk_set_divider(struct jh7100_clk_priv *priv, + unsigned int idx, + unsigned int mask, + unsigned int div) +{ + unsigned long flags; + unsigned int value; + void __iomem *reg = priv->base + 4 * idx; + + spin_lock_irqsave(&priv->rmw_lock, flags); + value = readl_relaxed(reg) & ~mask; + value |= div; + writel_relaxed(value, reg); + spin_unlock_irqrestore(&priv->rmw_lock, flags); +} + static struct clk_hw *jh7100_audclk_get(struct of_phandle_args *clkspec, void *data) { struct jh7100_clk_priv *priv = data; unsigned int idx = clkspec->args[0]; - if (idx < JH7100_AUDCLK_END) + if (idx < JH7100_AUDCLK_I2SADC_BCLK_IOPAD) return &priv->reg[idx].hw; + if (idx < JH7100_AUDCLK_END) + return priv->pll[idx - JH7100_AUDCLK_I2SADC_BCLK_IOPAD]; + return ERR_PTR(-EINVAL); } @@ -102,7 +121,7 @@ static int jh7100_audclk_probe(struct platform_device *pdev) unsigned int idx; int ret; - priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7100_AUDCLK_END), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7100_AUDCLK_I2SADC_BCLK_IOPAD), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -112,7 +131,18 @@ static int jh7100_audclk_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - for (idx = 0; idx < JH7100_AUDCLK_END; idx++) { + priv->pll[0] = clk_hw_register_fixed_rate(priv->dev, "i2sadc_bclk_iopad", NULL, 0, 12288000); + priv->pll[1] = clk_hw_register_fixed_rate(priv->dev, "i2sadc_lrclk_iopad", NULL, 0, 12288000); + priv->pll[2] = clk_hw_register_fixed_rate(priv->dev, "i2sdac_bclk_iopad", NULL, 0, 12288000); + priv->pll[3] = clk_hw_register_fixed_rate(priv->dev, "i2sdac_lrclk_iopad", NULL, 0, 12288000); + + // for special initialization + jh7100_clk_set_divider(priv, JH7100_AUDCLK_I2SADC_BCLK, 0x1f, 0x1); + jh7100_clk_set_divider(priv, JH7100_AUDCLK_I2SADC_LRCLK, 0x3f, 0x1); + jh7100_clk_set_divider(priv, JH7100_AUDCLK_I2SDAC_BCLK, 0x1f, 0x1); + jh7100_clk_set_divider(priv, JH7100_AUDCLK_I2SDAC_LRCLK, 0x3f, 0x1); + + for (idx = 0; idx < JH7100_AUDCLK_I2SADC_BCLK_IOPAD; idx++) { u32 max = jh7100_audclk_data[idx].max; struct clk_parent_data parents[4] = {}; struct clk_init_data init = { @@ -128,8 +158,10 @@ static int jh7100_audclk_probe(struct platform_device *pdev) for (i = 0; i < init.num_parents; i++) { unsigned int pidx = jh7100_audclk_data[idx].parents[i]; - if (pidx < JH7100_AUDCLK_END) + if (pidx < JH7100_AUDCLK_I2SADC_BCLK_IOPAD) parents[i].hw = &priv->reg[pidx].hw; + else if (pidx < JH7100_AUDCLK_END) + parents[i].hw = priv->pll[pidx - JH7100_AUDCLK_I2SADC_BCLK_IOPAD]; else if (pidx == JH7100_AUDCLK_AUDIO_SRC) parents[i].fw_name = "audio_src"; else if (pidx == JH7100_AUDCLK_AUDIO_12288) @@ -166,5 +198,6 @@ static struct platform_driver jh7100_audclk_driver = { module_platform_driver(jh7100_audclk_driver); MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_AUTHOR("Walker Chen"); MODULE_DESCRIPTION("StarFive JH7100 audio clock driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index 831938baac05..57ff561a186d 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -321,6 +321,22 @@ static int jh7100_clk_is_enabled(struct clk_hw *hw) return !!(jh7100_clk_reg_get(clk) & JH7100_CLK_ENABLE); } +static void jh7100_clk_set_divider(struct jh7100_clk_priv *priv, + unsigned int idx, + unsigned int mask, + unsigned int div) +{ + unsigned long flags; + unsigned int value; + void __iomem *reg = priv->base + 4 * idx; + + spin_lock_irqsave(&priv->rmw_lock, flags); + value = readl_relaxed(reg) & ~mask; + value |= div; + writel_relaxed(value, reg); + spin_unlock_irqrestore(&priv->rmw_lock, flags); +} + static unsigned long jh7100_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -634,6 +650,8 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) if (IS_ERR(priv->pll[2])) return PTR_ERR(priv->pll[2]); + jh7100_clk_set_divider(priv, JH7100_CLK_AHB_BUS, 0xf, 0x4); + for (idx = 0; idx < JH7100_CLK_PLL0_OUT; idx++) { u32 max = jh7100_clk_data[idx].max; struct clk_parent_data parents[4] = {}; diff --git a/drivers/clk/starfive/clk-starfive-jh7100.h b/drivers/clk/starfive/clk-starfive-jh7100.h index 5c9812c58392..c2d594c8ce5b 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.h +++ b/drivers/clk/starfive/clk-starfive-jh7100.h @@ -109,7 +109,7 @@ struct jh7100_clk_priv { spinlock_t rmw_lock; struct device *dev; void __iomem *base; - struct clk_hw *pll[3]; + struct clk_hw *pll[4]; struct jh7100_clk reg[]; }; diff --git a/include/dt-bindings/clock/starfive-jh7100-audio.h b/include/dt-bindings/clock/starfive-jh7100-audio.h index fbb4eae6572b..929f37ad1107 100644 --- a/include/dt-bindings/clock/starfive-jh7100-audio.h +++ b/include/dt-bindings/clock/starfive-jh7100-audio.h @@ -36,6 +36,11 @@ #define JH7100_AUDCLK_APB_EN 27 #define JH7100_AUDCLK_VAD_MEM 28 -#define JH7100_AUDCLK_END 29 +#define JH7100_AUDCLK_I2SADC_BCLK_IOPAD 29 +#define JH7100_AUDCLK_I2SADC_LRCLK_IOPAD 30 +#define JH7100_AUDCLK_I2SDAC_BCLK_IOPAD 31 +#define JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD 32 + +#define JH7100_AUDCLK_END 33 #endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7100_AUDIO_H__ */ diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 499604f1e178..b090b4012f5d 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -25,6 +25,8 @@ #include "wm8960.h" +#define WM8960_MCLK 24000000 + /* R25 - Power 1 */ #define WM8960_VMID_MASK 0x180 #define WM8960_VREF 0x40 @@ -742,16 +744,9 @@ static int wm8960_configure_clocking(struct snd_soc_component *component) int i, j, k; int ret; - /* - * For Slave mode clocking should still be configured, - * so this if statement should be removed, but some platform - * may not work if the sysclk is not configured, to avoid such - * compatible issue, just add '!wm8960->sysclk' condition in - * this if statement. - */ - if (!(iface1 & (1 << 6)) && !wm8960->sysclk) { - dev_warn(component->dev, - "slave mode, but proceeding with no clock configuration\n"); + if (!(iface1 & (1<<6))) { + dev_dbg(component->dev, + "Codec is slave mode, no need to configure clock\n"); return 0; } @@ -1287,6 +1282,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, { struct snd_soc_component *component = dai->component; struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + clk_id = WM8960_SYSCLK_PLL; switch (clk_id) { case WM8960_SYSCLK_MCLK: @@ -1302,7 +1298,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, default: return -EINVAL; } - + wm8960->freq_in = WM8960_MCLK; wm8960->sysclk = freq; wm8960->clk_id = clk_id; @@ -1354,6 +1350,22 @@ static int wm8960_probe(struct snd_soc_component *component) else wm8960->set_bias_level = wm8960_set_bias_level_out3; +#if 1 + snd_soc_component_update_bits(component, WM8960_LDAC, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8960_RDAC, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8960_LOUT1, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8960_ROUT1, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8960_LOUT2, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8960_ROUT2, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8960_POWER2, 0x1fB, 0x198); + snd_soc_component_update_bits(component, WM8960_LOUTMIX, 0x1F0, 0x100); + snd_soc_component_update_bits(component, WM8960_ROUTMIX, 0x1F0, 0x100); + snd_soc_component_update_bits(component, WM8960_LOUT1, 0x7f, 0x6f); + snd_soc_component_update_bits(component, WM8960_ROUT1, 0x7f, 0x6f); + snd_soc_component_update_bits(component, WM8960_LOUT2, 0x7f, 0x7f); + snd_soc_component_update_bits(component, WM8960_ROUT2, 0x7f, 0x7f); +#endif + snd_soc_add_component_controls(component, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls)); wm8960_add_widgets(component); @@ -1413,6 +1425,8 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, if (wm8960 == NULL) return -ENOMEM; + wm8960->clk_id = WM8960_SYSCLK_PLL; + wm8960->mclk = devm_clk_get(&i2c->dev, "mclk"); if (IS_ERR(wm8960->mclk)) { if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER) @@ -1456,6 +1470,13 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LINPATH, 0x138, 0x138); + regmap_update_bits(wm8960->regmap, WM8960_RINPATH, 0x138, 0x138); + regmap_update_bits(wm8960->regmap, WM8960_POWER1, 0x7E, 0x7E); + regmap_update_bits(wm8960->regmap, WM8960_POWER3, 0x30, 0x30); + regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x19F, 0x197); + regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x19F, 0x197); + /* ADCLRC pin configured as GPIO. */ regmap_update_bits(wm8960->regmap, WM8960_IFACE2, 1 << 6, wm8960->pdata.gpio_cfg[0] << 6); diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig index 71a58f7ac13a..ec5dd5e44452 100644 --- a/sound/soc/dwc/Kconfig +++ b/sound/soc/dwc/Kconfig @@ -18,3 +18,11 @@ config SND_DESIGNWARE_PCM This functionality is specially suited for I2S devices that don't have DMA support. +config SND_DESIGNWARE_I2S_STARFIVE_JH7100 + bool "Synopsys I2S Device Driver for Starfive JH7100 SOC platform" + depends on SND_DESIGNWARE_I2S + help + Say Y or N if you want to use on Starfive JH7100 SOC platform. + + This functionality is specially suited for I2S devices that run on + Starfive JH7100 SOC platform. diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 33ce257ae198..dc51ec841052 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -13,6 +13,7 @@ #include <linux/clk.h> #include <linux/device.h> +#include <linux/reset.h> #include <linux/init.h> #include <linux/io.h> #include <linux/interrupt.h> @@ -26,6 +27,37 @@ #include <sound/dmaengine_pcm.h> #include "local.h" +static const char *clk_name[CLK_AUDIO_NUM] = { + [CLK_AUDIO_ROOT] = "audio_root", + [CLK_AUDIO_SRC] = "audio_src", + [CLK_AUDIO_12288] = "audio_12288", + [CLK_DMA1P_AHB] = "dma1p_ahb", + [CLK_ADC_MCLK] = "adc_mclk", + [CLK_APB_I2SADC] = "i2sadc_apb", + [CLK_I2SVAD] = "i2svad_apb", + [CLK_ADC_BCLK] = "i2sadc0_bclk", + [CLK_ADC_LRCLK] = "i2sadc0_lrclk", + [CLK_ADC_BCLK_IOPAD] = "i2sadc_bclk_iopad", + [CLK_ADC_LRCLK_IOPAD] = "i2sadc_lrclk_iopad", + [CLK_DAC_MCLK] = "dac_mclk", + [CLK_DAC_BCLK] = "i2sdac0_bclk", + [CLK_DAC_LRCLK] = "i2sdac0_lrclk", + [CLK_DAC_BCLK_IOPAD] = "i2sdac_bclk_iopad", + [CLK_DAC_LRCLK_IOPAD] = "i2sdac_lrclk_iopad", + [CLK_APB_I2SDAC] = "i2sdac_apb", +}; + +static const char * const rst_name[RST_AUDIO_NUM] = { + [RST_APB_BUS] = "apb_bus", + [RST_DMA1P_AHB] = "dma1p_ahb", + [RST_APB_I2SADC] = "apb_i2sadc", + [RST_I2SADC_SRST] = "i2sadc_srst", + [RST_APB_I2SVAD] = "apb_i2svad", + [RST_I2SVAD_SRST] = "i2svad_srst", + [RST_APB_I2SDAC] = "apb_i2sdac", + [RST_I2SDAC_SRST] = "i2sdac_srst", +}; + static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val) { writel(val, io_base + reg); @@ -187,7 +219,9 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); +#ifndef CONFIG_SND_DESIGNWARE_I2S_STARFIVE_JH7100 union dw_i2s_snd_dma_data *dma_data = NULL; +#endif if (!(dev->capability & DWC_I2S_RECORD) && (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) @@ -197,13 +231,14 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream, (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) return -EINVAL; +#ifndef CONFIG_SND_DESIGNWARE_I2S_STARFIVE_JH7100 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &dev->play_dma_data; else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) dma_data = &dev->capture_dma_data; snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data); - +#endif return 0; } @@ -233,6 +268,291 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) } } +#define CLKGEN_BASE_ADDR 0x11800000UL +#define AUDIO_DIV_CTRL 0x17C +static int init_audio_subsys(struct platform_device *pdev, struct dw_i2s_dev *dev) +{ + int ret = 0; + int i = 0; + + static struct clk_bulk_data clks[] = { + { .id = "audio_root" }, //clock-names in dts file + { .id = "audio_src" }, + { .id = "audio_12288" }, + { .id = "dma1p_ahb" }, + }; + struct reset_control_bulk_data resets[] = { + { .id = "apb_bus" }, + { .id = "dma1p_ahb" }, + }; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks); + if (ret) { + printk(KERN_INFO "%s: failed to get audio_subsys clocks\n", __func__); + goto err_out_clock; + } + + for (i = 0; i < CLK_ADC_MCLK; i++) + dev->clks[i] = clks[i].clk; + + ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(resets), resets); + if (ret) { + printk(KERN_INFO "%s: failed to get audio_subsys resets\n", __func__); + goto err_out_clock; + } + + dev->rstc[RST_APB_BUS] = resets[0].rstc; + dev->rstc[RST_DMA1P_AHB] = resets[1].rstc; + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(clks), clks); + if (ret) { + printk(KERN_INFO "%s: failed to enable clocks.\n", __func__); + goto err_out_clock; + } + + reset_control_deassert(dev->rstc[RST_APB_BUS]); + reset_control_deassert(dev->rstc[RST_DMA1P_AHB]); + + ret = clk_set_rate(dev->clks[CLK_AUDIO_SRC], 12288000); + if (ret) { + dev_err(&pdev->dev, "%s: failed to set 12.28 MHz rate for clk_audio_src\n", __func__); + goto err_out_clock; + } + + reset_control_bulk_put(ARRAY_SIZE(resets), resets); + return 0; + +err_out_clock: + return ret; +} + +static int init_i2srx_3ch(struct platform_device *pdev, struct dw_i2s_dev *dev) +{ + int ret = 0; + + dev->rstc[RST_APB_I2SADC] = devm_reset_control_get_exclusive(&pdev->dev, rst_name[RST_APB_I2SADC]); + if (IS_ERR(dev->rstc[RST_APB_I2SADC])) { + dev_err(&pdev->dev, "%s: failed to get apb_i2sadc reset control\n", __func__); + return PTR_ERR(dev->rstc[RST_APB_I2SADC]); + } + dev->rstc[RST_I2SADC_SRST] = devm_reset_control_get_exclusive(&pdev->dev, rst_name[RST_I2SADC_SRST]); + if (IS_ERR(dev->rstc[RST_I2SADC_SRST])) { + dev_err(&pdev->dev, "%s: failed to get i2sadc_srst reset control\n", __func__); + return PTR_ERR(dev->rstc[RST_I2SADC_SRST]); + } + reset_control_assert(dev->rstc[RST_APB_I2SADC]); + reset_control_assert(dev->rstc[RST_I2SADC_SRST]); + + dev->clks[CLK_ADC_MCLK] = devm_clk_get(&pdev->dev, clk_name[CLK_ADC_MCLK]); + if (IS_ERR(dev->clks[CLK_ADC_MCLK])) { + dev_err(&pdev->dev, "%s: failed to get clk_adc_mclk: %ld\n", __func__, + PTR_ERR(dev->clks[CLK_ADC_MCLK])); + return PTR_ERR(dev->clks[CLK_ADC_MCLK]); + } + ret = clk_prepare_enable(dev->clks[CLK_ADC_MCLK]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to prepare enable adc_mclk\n", __func__); + return ret; + } + + dev->clks[CLK_APB_I2SADC] = devm_clk_get(&pdev->dev, clk_name[CLK_APB_I2SADC]); + if (IS_ERR(dev->clks[CLK_APB_I2SADC])) { + dev_err(&pdev->dev, "%s: failed to get clk_apb_i2sadc: %ld\n", __func__, + PTR_ERR(dev->clks[CLK_APB_I2SADC])); + return PTR_ERR(dev->clks[CLK_APB_I2SADC]); + } + ret = clk_prepare_enable(dev->clks[CLK_APB_I2SADC]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to prepare enable i2sadc_apb\n", __func__); + return ret; + } + + reset_control_deassert(dev->rstc[RST_APB_I2SADC]); + reset_control_deassert(dev->rstc[RST_I2SADC_SRST]); + + return 0; +} + +static int init_i2svad(struct platform_device *pdev, struct dw_i2s_dev *dev) +{ + int ret = 0; + + dev->clks[CLK_I2SVAD] = devm_clk_get(&pdev->dev, clk_name[CLK_I2SVAD]); + if (IS_ERR(dev->clks[CLK_I2SVAD])) { + dev_err(&pdev->dev, "%s: failed to get clk_i2svad_apb: %ld\n", __func__, + PTR_ERR(dev->clks[CLK_I2SVAD])); + return PTR_ERR(dev->clks[CLK_I2SVAD]); + } + + dev->rstc[RST_APB_I2SVAD] = devm_reset_control_get_exclusive(&pdev->dev, rst_name[RST_APB_I2SVAD]); + if (IS_ERR(dev->rstc[RST_APB_I2SVAD])) { + dev_err(&pdev->dev, "%s: failed to get apb_i2svad reset control\n", __func__); + return PTR_ERR(dev->rstc[RST_APB_I2SVAD]); + } + + dev->rstc[RST_I2SVAD_SRST] = devm_reset_control_get_exclusive(&pdev->dev, rst_name[RST_I2SVAD_SRST]); + if (IS_ERR(dev->rstc[RST_I2SVAD_SRST])) { + dev_err(&pdev->dev, "%s: failed to get i2svad_srst reset control\n", __func__); + return PTR_ERR(dev->rstc[RST_I2SVAD_SRST]); + } + + ret = clk_prepare_enable(dev->clks[CLK_I2SVAD]); + if (ret < 0) { + printk(KERN_INFO "%s: failed to enable clk_i2svad_apb\n", __func__); + return ret; + } + + reset_control_deassert(dev->rstc[RST_APB_I2SVAD]); + reset_control_deassert(dev->rstc[RST_I2SVAD_SRST]); + + return 0; +} + +static int dw_i2sdac_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev) +{ + static struct clk_bulk_data i2sclk[] = { + { .id = "dac_mclk" }, //clock-names in dts file + { .id = "i2sdac0_bclk" }, + { .id = "i2sdac0_lrclk" }, + { .id = "i2sdac_apb" }, + { .id = "i2sdac_bclk_iopad" }, + { .id = "i2sdac_lrclk_iopad" }, + }; + + int ret = 0; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(i2sclk), i2sclk); + if (ret) { + printk(KERN_INFO "%s: failed to get i2sdac clocks\n", __func__); + return ret; + } + + dev->clks[CLK_DAC_MCLK] = i2sclk[0].clk; + dev->clks[CLK_DAC_BCLK] = i2sclk[1].clk; + dev->clks[CLK_DAC_LRCLK] = i2sclk[2].clk; + dev->clks[CLK_APB_I2SDAC] = i2sclk[3].clk; + dev->clks[CLK_DAC_BCLK_IOPAD] = i2sclk[4].clk; + dev->clks[CLK_DAC_LRCLK_IOPAD] = i2sclk[5].clk; + + dev->rstc[RST_APB_I2SDAC] = devm_reset_control_get_exclusive(&pdev->dev, rst_name[RST_APB_I2SDAC]); + if (IS_ERR(dev->rstc[RST_APB_I2SDAC])) { + dev_err(&pdev->dev, "%s: failed to get apb_i2sdac reset control\n", __func__); + return PTR_ERR(dev->rstc[RST_APB_I2SDAC]); + } + + dev->rstc[RST_I2SDAC_SRST] = devm_reset_control_get_exclusive(&pdev->dev, rst_name[RST_I2SDAC_SRST]); + if (IS_ERR(dev->rstc[RST_I2SDAC_SRST])) { + dev_err(&pdev->dev, "%s: failed to get i2sdac_srst reset control\n", __func__); + return PTR_ERR(dev->rstc[RST_I2SDAC_SRST]); + } + reset_control_assert(dev->rstc[RST_APB_I2SDAC]); + reset_control_assert(dev->rstc[RST_I2SDAC_SRST]); + + ret = clk_prepare_enable(dev->clks[CLK_DAC_MCLK]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable dac_mclk\n", __func__); + goto err_clk_i2s; + } + + ret = clk_prepare_enable(dev->clks[CLK_APB_I2SDAC]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable clk_apb_i2sdac\n", __func__); + goto err_clk_i2s; + } + reset_control_deassert(dev->rstc[RST_APB_I2SDAC]); // ---> clk_apb_i2sdac + reset_control_deassert(dev->rstc[RST_I2SDAC_SRST]); // ---> clk_i2sdac_bclk + + ret = clk_set_parent(dev->clks[CLK_DAC_BCLK], dev->clks[CLK_DAC_BCLK_IOPAD]); + if (ret) { + printk(KERN_INFO "%s: failed to set parent for clk_i2sdac0_bclk\n", __func__); + goto err_clk_i2s; + } + ret = clk_prepare_enable(dev->clks[CLK_DAC_BCLK]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to prepare enable i2sdac0_bclk\n", __func__); + goto err_clk_i2s; + } + + ret = clk_set_parent(dev->clks[CLK_DAC_LRCLK], dev->clks[CLK_DAC_LRCLK_IOPAD]); + if (ret) { + printk(KERN_INFO "%s: failed to set parent for clk_i2sdac0_lrclk\n", __func__); + goto err_clk_i2s; + } + + ret = clk_prepare_enable(dev->clks[CLK_DAC_LRCLK]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable i2sdac0_lrclk\n", __func__); + goto err_clk_i2s; + } + +err_clk_i2s: + return ret; +} + +#define VAD_BASE 0x10420000UL +#define VAD_SW 0x844 +#define VAD_I2S_CTRL 0x884 +static int dw_i2sadc_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev) +{ + static struct clk_bulk_data i2sclk[] = { + { .id = "i2sadc0_bclk" }, + { .id = "i2sadc0_lrclk" }, + { .id = "i2sadc_bclk_iopad" }, + { .id = "i2sadc_lrclk_iopad" }, + }; + + int ret = 0; + unsigned int val = 0; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(i2sclk), i2sclk); + if (ret) { + printk(KERN_INFO "%s: failed to get i2sadc clocks\n", __func__); + goto err_clk_i2s; + } + + dev->clks[CLK_ADC_BCLK] = i2sclk[0].clk; + dev->clks[CLK_ADC_LRCLK] = i2sclk[1].clk; + dev->clks[CLK_ADC_BCLK_IOPAD] = i2sclk[2].clk; + dev->clks[CLK_ADC_LRCLK_IOPAD] = i2sclk[3].clk; + + ret = clk_set_parent(dev->clks[CLK_ADC_BCLK], dev->clks[CLK_ADC_BCLK_IOPAD]); + if (ret) { + printk(KERN_INFO "%s: failed to set parent for clk_i2sdac0_bclk\n", __func__); + goto err_clk_i2s; + } + ret = clk_prepare_enable(dev->clks[CLK_ADC_BCLK]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to prepare enable i2sdac0_bclk\n", __func__); + goto err_clk_i2s; + } + + ret = clk_set_parent(dev->clks[CLK_ADC_LRCLK], dev->clks[CLK_ADC_LRCLK_IOPAD]); + if (ret) { + printk(KERN_INFO "%s: failed to set parent for clk_i2sdac0_lrclk\n", __func__); + goto err_clk_i2s; + } + + ret = clk_prepare_enable(dev->clks[CLK_ADC_LRCLK]); + if (ret) { + dev_err(&pdev->dev, "%s: failed to prepare enable i2sdac0_lrclk\n", __func__); + goto err_clk_i2s; + } + + // _SET_SYSCON_REG_SCFG_ctrl_i2sadc_enable + val = readl(dev->vad_base + VAD_SW); + val |= (0x1<<1); + writel(val, dev->vad_base + VAD_SW); + + // _SET_SYSCON_REG_SCFG_aon_i2s_ctrl_adci2s_d0_sel + val = readl(dev->vad_base + VAD_I2S_CTRL); + val &= ~(0x7); + val |= (AUDIO_IN_SPIO_SD0 & 0x7); + writel(val, dev->vad_base + VAD_I2S_CTRL); + +err_clk_i2s: + return ret; +} + static int dw_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -306,9 +626,11 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, } static void dw_i2s_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { +#ifndef CONFIG_SND_DESIGNWARE_I2S_STARFIVE_JH7100 snd_soc_dai_set_dma_data(dai, substream, NULL); +#endif } static int dw_i2s_prepare(struct snd_pcm_substream *substream, @@ -383,7 +705,7 @@ static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) static const struct snd_soc_dai_ops dw_i2s_dai_ops = { .startup = dw_i2s_startup, - .shutdown = dw_i2s_shutdown, + .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, @@ -617,9 +939,20 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, } +#ifdef CONFIG_SND_DESIGNWARE_I2S_STARFIVE_JH7100 +static int dw_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data); + return 0; +} +#endif + static int dw_i2s_probe(struct platform_device *pdev) { const struct i2s_platform_data *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct dw_i2s_dev *dev; struct resource *res; int ret, irq; @@ -635,14 +968,24 @@ static int dw_i2s_probe(struct platform_device *pdev) return -ENOMEM; dw_i2s_dai->ops = &dw_i2s_dai_ops; +#ifdef CONFIG_SND_DESIGNWARE_I2S_STARFIVE_JH7100 + dw_i2s_dai->probe = dw_i2s_dai_probe; +#endif - dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); + + dev->vad_base = ioremap(VAD_BASE, 0x900); + if (IS_ERR(dev->vad_base)) { + printk(KERN_INFO "%s: failed to alloc memory for vad_base\n", __func__); + return PTR_ERR(dev->vad_base); + } dev->dev = &pdev->dev; - irq = platform_get_irq_optional(pdev, 0); + irq = platform_get_irq(pdev, 0); if (irq >= 0) { ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0, pdev->name, dev); @@ -688,6 +1031,34 @@ static int dw_i2s_probe(struct platform_device *pdev) return ret; } + if (of_device_is_compatible(np, "snps,designware-i2sadc0")) { //record + ret = init_audio_subsys(pdev, dev); + if (ret) { + printk(KERN_INFO "%s: failed to init_audio_subsys!\n", __func__); + goto err_clk_disable; + } + + ret = init_i2srx_3ch(pdev, dev); + if (ret) { + printk(KERN_INFO "%s: failed to init_i2srx_3ch\n", __func__); + goto err_clk_disable; + } + + ret = init_i2svad(pdev, dev); + if (ret) { + printk(KERN_INFO "%s: failed to init_i2svad\n", __func__); + goto err_clk_disable; + } + + ret = dw_i2sadc_clk_init(pdev, dev); + if (ret < 0) + goto err_clk_disable; + } else if (of_device_is_compatible(np, "snps,designware-i2sdac0")) { //playback + ret = dw_i2sdac_clk_init(pdev, dev); + if (ret < 0) + goto err_clk_disable; + } + dev_set_drvdata(&pdev->dev, dev); ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component, dw_i2s_dai, 1); @@ -714,6 +1085,7 @@ static int dw_i2s_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + return 0; err_clk_disable: @@ -735,7 +1107,9 @@ static int dw_i2s_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id dw_i2s_of_match[] = { - { .compatible = "snps,designware-i2s", }, + { .compatible = "snps,designware-i2sadc0", }, + { .compatible = "snps,designware-i2sdac0", }, + //{ .compatible = "snps,designware-i2sdac1", }, {}, }; @@ -759,6 +1133,7 @@ static struct platform_driver dw_i2s_driver = { module_platform_driver(dw_i2s_driver); MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>"); +MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>"); MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:designware_i2s"); diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h index 1c361eb6127e..4a05357728c6 100644 --- a/sound/soc/dwc/local.h +++ b/sound/soc/dwc/local.h @@ -81,6 +81,51 @@ #define MAX_CHANNEL_NUM 8 #define MIN_CHANNEL_NUM 2 +enum { + CLK_AUDIO_ROOT = 0, + CLK_AUDIO_SRC, + CLK_AUDIO_12288, + CLK_DMA1P_AHB, + CLK_ADC_MCLK, + CLK_APB_I2SADC, + CLK_I2SVAD, + CLK_ADC_BCLK, + CLK_ADC_LRCLK, + CLK_ADC_BCLK_IOPAD, + CLK_ADC_LRCLK_IOPAD, + CLK_DAC_MCLK, + CLK_DAC_BCLK, + CLK_DAC_LRCLK, + CLK_DAC_BCLK_IOPAD, + CLK_DAC_LRCLK_IOPAD, + CLK_APB_I2SDAC, + CLK_AUDIO_NUM, +}; + +enum { + RST_APB_BUS = 0, + RST_DMA1P_AHB, + RST_APB_I2SADC, + RST_I2SADC_SRST, + RST_APB_I2SVAD, + RST_I2SVAD_SRST, + RST_APB_I2SDAC, + RST_I2SDAC_SRST, + RST_AUDIO_NUM, +}; + +enum audio_mode { + AUDIO_IN_NONE = -1, + AUDIO_IN_GPIO_SD2 = 0, + AUDIO_IN_GPIO_SD1, + AUDIO_IN_SPIO_SD0, + AUDIO_IN_DAC16K_SD0, + AUDIO_IN_ANA_ADC_SD1, + AUDIO_IN_ANA_ADC_SD0, + AUDIO_IN_PDM_SD1, + AUDIO_IN_PDM_SD0, +}; + union dw_i2s_snd_dma_data { struct i2s_dma_data pd; struct snd_dmaengine_dai_dma_data dt; @@ -88,6 +133,7 @@ union dw_i2s_snd_dma_data { struct dw_i2s_dev { void __iomem *i2s_base; + void __iomem *vad_base; struct clk *clk; int active; unsigned int capability; @@ -99,6 +145,9 @@ struct dw_i2s_dev { u32 xfer_resolution; u32 fifo_th; + struct clk *clks[CLK_AUDIO_NUM]; + struct reset_control *rstc[RST_AUDIO_NUM]; + /* data related to DMA transfers b/w i2s and DMAC */ union dw_i2s_snd_dma_data play_dma_data; union dw_i2s_snd_dma_data capture_dma_data; |