diff --git a/Documentation/devicetree/bindings/sound/tlv320adc3101.txt b/Documentation/devicetree/bindings/sound/tlv320adc3101.txt new file mode 100644 index 000000000000..69b6e2ad5d1e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tlv320adc3101.txt @@ -0,0 +1,20 @@ +Texas Instruments - tlv320adc3101 Codec module + +The tlv320adc3101 serial control bus communicates through I2C protocols + +Required properties: + - compatible: Should be "ti,tlv320adc3101" + - reg: I2C slave address + +Optional properties: + - reset-gpios: Reset-GPIO phandle with args as described in gpio/gpio.txt + - clocks/clock-names: Clock named 'mclk' for the master clock of the codec. + See clock/clock-bindings.txt for information about the detailed format. + + +Example: + +codec: tlv320adc3101@18 { + compatible = "ti,tlv320adc3101"; + reg = <0x18>; +}; diff --git a/MAINTAINERS b/MAINTAINERS index ede77369a8fd..aa6783e0fa90 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13916,3 +13916,12 @@ M: Long Yu F: drivers/amlogic/mmc/aml_sd_emmc_v3.c F: include/linux/amlogic/aml_sd_emmc_internal.h F: include/linux/amlogic/aml_sd_emmc_v3.h + +AMLOGIC Asoc driver +M: shuai li +M: xing wang +F: sound/soc/amlogic/meson/* +F: sound/soc/amlogic/auge/* +F: sound/soc/codecs/amlogic/* +F: include/dt-bindings/clock/amlogic,axg-audio-clk.h + diff --git a/arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts b/arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts index 23484f011e2a..263d6f8dddae 100644 --- a/arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts +++ b/arch/arm64/boot/dts/amlogic/axg_a113d_skt.dts @@ -302,7 +302,130 @@ pinctrl-names = "default"; pinctrl-0 = <&b_uart_pins>; }; + /* Sound iomap */ + aml_snd_iomap { + compatible = "amlogic, snd_iomap"; + status = "okay"; + #address-cells=<2>; + #size-cells=<2>; + ranges; + pdm_bus { + reg = <0x0 0xFF632000 0x0 0x20>; + }; + audiobus_base { + reg = <0x0 0xFF642000 0x0 0x2000>; + }; + }; + pdm_codec:dummy{ + #sound-dai-cells = <0>; + compatible = "amlogic, pdm_dummy_codec"; + status = "okay"; + }; + dummy_codec:dummy{ + #sound-dai-cells = <0>; + compatible = "amlogic, aml_dummy_codec"; + status = "okay"; + }; + meson_sound { + compatible = "amlogic, sound-card"; + aml-audio-card,name = "AML-AXGSOUND"; + //aml-audio-card,mclk-fs = <256>; + + aml-audio-card,dai-link@0 { + format = "dsp_a"; + mclk-fs = <512>; + continuous-clock; + //bitclock-inversion; + //frame-inversion; + //bitclock-master = <&tdmacodec>; + //frame-master = <&tdmacodec>; + tdmacpu: cpu { + sound-dai = <&aml_tdma>; + dai-tdm-slot-tx-mask = + <1 1 1 1 1 1 1 1>; + dai-tdm-slot-rx-mask = + <1 1 1 1 1 1 1 1>; + dai-tdm-slot-num = <8>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <24576000>; + }; + tdmacodec: codec { + sound-dai = <&dummy_codec &dummy_codec>; + }; + }; + + aml-audio-card,dai-link@1 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + bitclock-inversion; + frame-inversion; + //bitclock-master = <&aml_tdmb>; + //frame-master = <&aml_tdmb>; + cpu { + sound-dai = <&aml_tdmb>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + sound-dai = <&dummy_codec &dummy_codec>; + }; + }; + + aml-audio-card,dai-link@2 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + bitclock-inversion; + frame-inversion; + //bitclock-master = <&aml_tdmc>; + //frame-master = <&aml_tdmc>; + cpu { + sound-dai = <&aml_tdmc>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + sound-dai = <&tas5707_36 &tas5707_3a>; + }; + }; + + aml-audio-card,dai-link@3 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + //bitclock-inversion; + //frame-inversion; + bitclock-master = <&aml_pdm>; + frame-master = <&aml_pdm>; + cpu { + sound-dai = <&aml_pdm>; + }; + codec { + sound-dai = <&pdm_codec>; + }; + }; + + aml-audio-card,dai-link@4 { + //format = "i2s"; + mclk-fs = <128>; + continuous-clock; + cpu { + sound-dai = <&aml_spdif>; + system-clock-frequency = <12288000>; + }; + codec { + sound-dai = <&dummy_codec>; + }; + }; + }; sd_emmc_c: emmc@ffe07000 { status = "okay"; compatible = "amlogic, meson-aml-mmc"; @@ -428,6 +551,244 @@ }; /* end of / */ +/* Audio Related start */ +/* for spk board */ +&i2c_b { + status = "okay"; + //pinctrl-names="default"; + //pinctrl-0=<&b_i2c_master>; + tlv320adc3101_32: tlv320adc3101_32@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x32>; + status = "disabled"; + }; + + tas5707_36: tas5707_36@36 { + compatible = "ti,tas5707"; + #sound-dai-cells = <0>; + reg = <0x1b>; + status = "okay"; + reset_pin = <&gpio_ao GPIOAO_4 0>; + }; + + tas5707_3a: tas5707_3a@3a { + compatible = "ti,tas5707"; + #sound-dai-cells = <0>; + reg = <0x1d>; + status = "okay"; + }; +}; + +/* for mic board */ +&i2c_ao { + status = "okay"; + pinctrl-names="default"; + pinctrl-0=<&ao_i2c_master_pin2>; + + tlv320adc3101_30: tlv320adc3101_30@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x30>; + status = "okay"; + }; + tlv320adc3101_34: tlv320adc3101_34@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x34>; + status = "okay"; + }; + tlv320adc3101_36: tlv320adc3101_36@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x36>; + status = "okay"; + }; +}; + +&audiobus { + aml_tdma: tdma { + compatible = "amlogic, snd-tdma"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask = <1>; + dai-tdm-clk-sel = <0>; + tdm_from_ddr = <0>; + tdm_to_ddr = <0>; + clocks = <&clkc CLKID_MPLL0>; + //&clkaudio CLKID_AUDIO_TDMOUTA + //&clkaudio CLKID_AUDIO_MCLK_A>; + clock-names = "mpll0", "gate", "mclk"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmout_a &tdmin_a>; + }; + + aml_tdmb: tdmb { + compatible = "amlogic, snd-tdmb"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask = <1 1 1 1>; + dai-tdm-clk-sel = <1>; + tdm_from_ddr = <1>; + tdm_to_ddr = <1>; + clocks = <&clkc CLKID_MPLL1>; + clock-names = "mpll1"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmb_mclk &tdmout_b>; + }; + + aml_tdmc: tdmc { + compatible = "amlogic, snd-tdmc"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask-in = <0 1 0 0>; + dai-tdm-lane-slot-mask-out = <1 0 1 1>; + dai-tdm-clk-sel = <2>; + tdm_from_ddr = <2>; + tdm_to_ddr = <2>; + clocks = <&clkc CLKID_MPLL2>; + clock-names = "mpll2"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmc_mclk &tdmout_c>;// &tdmin_c>; + }; + + aml_spdif: spdif { + compatible = "amlogic, snd-spdif"; + #sound-dai-cells = <0>; + spdif_from_ddr = <0>; + spdif_to_ddr = <0>; + clocks = <&clkc CLKID_MPLL0 + &clkc CLKID_FCLK_DIV4 + &clkaudio CLKID_AUDIO_SPDIFIN + &clkaudio CLKID_AUDIO_SPDIFOUT + &clkaudio CLKID_AUDIO_SPDIFIN_CTRL + &clkaudio CLKID_AUDIO_SPDIFOUT_CTRL>; + clock-names = "sysclk", "fixed_clk", "gate_spdifin", + "gate_spdifout", "clk_spdifin", "clk_spdifout"; + interrupts = + ; + + interrupt-names = "irq_spdifin", "irq_toddr", "irq_frddr"; + pinctrl-names = "spdif_pins"; + pinctrl-0 = <&spdifout &spdifin>; + status = "okay"; + }; + aml_pdm: pdm { + compatible = "amlogic, snd-pdm"; + #sound-dai-cells = <0>; + to_ddr = <2>; + clocks = <&clkaudio CLKID_AUDIO_PDM + &clkc CLKID_MPLL3 + &clkaudio CLKID_AUDIO_PDMIN0 + &clkaudio CLKID_AUDIO_PDMIN1>; + clock-names = "gate", "pll_clk", "pdm_dclk", "pdm_sysclk"; + interrupts = ; + interrupt-names = "pdmin_irq"; + pinctrl-names = "pdm_pins"; + pinctrl-0 = <&pdmin>; + filter_mode = <1>; /* mode 0~4, defalut:1 */ + status = "okay"; + }; +}; /* end of audiobus */ + +&pinctrl_periphs { + tdmout_a: tdmout_a { + mux { + pins = "GPIOX_12", "GPIOX_13", "GPIOX_15"; + //pins = "GPIOX_15";//slave + function = "tdma_out"; + }; + }; + + tdmin_a: tdmin_a { + mux { + pins = "GPIOX_14"; + //pins = "GPIOX_12", "GPIOX_13", "GPIOX_14";//slave + function = "tdma_in"; + }; + }; + + tdmb_mclk: tdmb_mclk { + mux { + pins = "GPIOA_1"; + function = "mclk"; + }; + }; + + tdmout_b: tdmout_b { + mux { + pins = "GPIOA_8", "GPIOA_9", "GPIOA_10", + "GPIOA_11", "GPIOA_12", "GPIOA_13"; + function = "tdmb_out"; + }; + }; + // tdmin and tdmout are the same pins. can't use at same time + /* + *tdmin_b:tdmin_b { + * mux { + * pins = "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13"; + * function = "tdmb_in"; + * }; + *}; + */ + + tdmc_mclk: tdmc_mclk { + mux { + pins = "GPIOA_0"; + function = "mclk"; + }; + }; + + tdmout_c:tdmout_c { + mux { + pins = "GPIOA_2", "GPIOA_3", "GPIOA_4", + "GPIOA_5", "GPIOA_6", "GPIOA_7"; + function = "tdmc_out"; + }; + }; + + + //tdmin_c:tdmin_c { + // mux { + // pins = "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7"; + // function = "tdmc_in"; + // }; + //}; + + spdifout: spidfout { + mux { + pins = "GPIOA_20"; + function = "spdif_out"; + }; + }; + + spdifin: spidfin { + mux { + pins = "GPIOA_19"; + function = "spdif_in"; + }; + }; + + pdmin: pdmin { + mux { + pins = "GPIOA_14", "GPIOA_15", "GPIOA_16", + "GPIOA_17", "GPIOA_18"; + function = "pdm"; + }; + }; +}; /* end of pinctrl_periphs */ +/* Audio Related End */ &spicc_a{ status = "okay"; num_chipselect = <1>; diff --git a/arch/arm64/boot/dts/amlogic/axg_s400.dts b/arch/arm64/boot/dts/amlogic/axg_s400.dts index 589c03c0aad4..8ffd7243064c 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400.dts @@ -299,6 +299,131 @@ pinctrl-names = "default"; pinctrl-0 = <&b_uart_pins>; }; + /* Sound iomap */ + aml_snd_iomap { + compatible = "amlogic, snd_iomap"; + status = "okay"; + #address-cells=<2>; + #size-cells=<2>; + ranges; + pdm_bus { + reg = <0x0 0xFF632000 0x0 0x20>; + }; + audiobus_base { + reg = <0x0 0xFF642000 0x0 0x2000>; + }; + }; + pdm_codec:dummy{ + #sound-dai-cells = <0>; + compatible = "amlogic, pdm_dummy_codec"; + status = "okay"; + }; + dummy_codec:dummy{ + #sound-dai-cells = <0>; + compatible = "amlogic, aml_dummy_codec"; + status = "okay"; + }; + + meson_sound { + compatible = "amlogic, sound-card"; + aml-audio-card,name = "AML-AXGSOUND"; + //aml-audio-card,mclk-fs = <256>; + + aml-audio-card,dai-link@0 { + format = "dsp_a"; + mclk-fs = <512>; + continuous-clock; + //bitclock-inversion; + //frame-inversion; + //bitclock-master = <&tdmacodec>; + //frame-master = <&tdmacodec>; + tdmacpu: cpu { + sound-dai = <&aml_tdma>; + dai-tdm-slot-tx-mask = + <1 1 1 1 1 1 1 1>; + dai-tdm-slot-rx-mask = + <1 1 1 1 1 1 1 1>; + dai-tdm-slot-num = <8>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <24576000>; + }; + tdmacodec: codec { + sound-dai = <&dummy_codec &dummy_codec>; + }; + }; + + aml-audio-card,dai-link@1 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + bitclock-inversion; + frame-inversion; + //bitclock-master = <&aml_tdmb>; + //frame-master = <&aml_tdmb>; + cpu { + sound-dai = <&aml_tdmb>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + sound-dai = <&dummy_codec &dummy_codec>; + }; + }; + + aml-audio-card,dai-link@2 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + bitclock-inversion; + frame-inversion; + //bitclock-master = <&aml_tdmc>; + //frame-master = <&aml_tdmc>; + cpu { + sound-dai = <&aml_tdmc>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + sound-dai = <&tas5707_36 &tas5707_3a>; + }; + }; + + aml-audio-card,dai-link@3 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + //bitclock-inversion; + //frame-inversion; + bitclock-master = <&aml_pdm>; + frame-master = <&aml_pdm>; + cpu { + sound-dai = <&aml_pdm>; + }; + codec { + sound-dai = <&pdm_codec>; + }; + }; + + aml-audio-card,dai-link@4 { + //format = "i2s"; + mclk-fs = <128>; + continuous-clock; + cpu { + sound-dai = <&aml_spdif>; + system-clock-frequency = <12288000>; + }; + codec { + sound-dai = <&dummy_codec>; + }; + }; + }; + wifi{ compatible = "amlogic, aml_wifi"; dev_name = "aml_wifi"; @@ -488,6 +613,246 @@ }; /* end of / */ +/* Audio Related start */ +/* for spk board */ +&i2c_b { + status = "okay"; + //pinctrl-names="default"; + //pinctrl-0=<&b_i2c_master>; + tlv320adc3101_32: tlv320adc3101_32@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x32>; + status = "disabled"; + }; + + tas5707_36: tas5707_36@36 { + compatible = "ti,tas5707"; + #sound-dai-cells = <0>; + reg = <0x1b>; + status = "okay"; + reset_pin = <&gpio_ao GPIOAO_4 0>; + }; + + tas5707_3a: tas5707_3a@3a { + compatible = "ti,tas5707"; + #sound-dai-cells = <0>; + reg = <0x1d>; + status = "okay"; + }; +}; + +/* for mic board */ +&i2c_ao { + status = "okay"; + pinctrl-names="default"; + pinctrl-0=<&ao_i2c_master_pin2>; + + tlv320adc3101_30: tlv320adc3101_30@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x30>; + status = "okay"; + }; + tlv320adc3101_34: tlv320adc3101_34@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x34>; + status = "okay"; + }; + tlv320adc3101_36: tlv320adc3101_36@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x36>; + status = "okay"; + }; +}; + +&audiobus { + aml_tdma: tdma { + compatible = "amlogic, snd-tdma"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask = <1>; + dai-tdm-clk-sel = <0>; + tdm_from_ddr = <0>; + tdm_to_ddr = <0>; + clocks = <&clkc CLKID_MPLL0>; + //&clkaudio CLKID_AUDIO_TDMOUTA + //&clkaudio CLKID_AUDIO_MCLK_A>; + clock-names = "mpll0", "gate", "mclk"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmout_a &tdmin_a>; + }; + + aml_tdmb: tdmb { + compatible = "amlogic, snd-tdmb"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask = <1 1 1 1>; + dai-tdm-clk-sel = <1>; + tdm_from_ddr = <1>; + tdm_to_ddr = <1>; + clocks = <&clkc CLKID_MPLL1>; + clock-names = "mpll1"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmb_mclk &tdmout_b>; + }; + + aml_tdmc: tdmc { + compatible = "amlogic, snd-tdmc"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask-in = <0 1 0 0>; + dai-tdm-lane-slot-mask-out = <1 0 1 1>; + dai-tdm-clk-sel = <2>; + tdm_from_ddr = <2>; + tdm_to_ddr = <2>; + clocks = <&clkc CLKID_MPLL2>; + clock-names = "mpll2"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmc_mclk &tdmout_c>;// &tdmin_c>; + }; + + aml_spdif: spdif { + compatible = "amlogic, snd-spdif"; + #sound-dai-cells = <0>; + spdif_from_ddr = <0>; + spdif_to_ddr = <0>; + clocks = <&clkc CLKID_MPLL0 + &clkc CLKID_FCLK_DIV4 + &clkaudio CLKID_AUDIO_SPDIFIN + &clkaudio CLKID_AUDIO_SPDIFOUT + &clkaudio CLKID_AUDIO_SPDIFIN_CTRL + &clkaudio CLKID_AUDIO_SPDIFOUT_CTRL>; + clock-names = "sysclk", "fixed_clk", "gate_spdifin", + "gate_spdifout", "clk_spdifin", "clk_spdifout"; + interrupts = + ; + + interrupt-names = "irq_spdifin", "irq_toddr", "irq_frddr"; + pinctrl-names = "spdif_pins"; + pinctrl-0 = <&spdifout &spdifin>; + status = "okay"; + }; + aml_pdm: pdm { + compatible = "amlogic, snd-pdm"; + #sound-dai-cells = <0>; + to_ddr = <2>; + clocks = <&clkaudio CLKID_AUDIO_PDM + &clkc CLKID_MPLL3 + &clkaudio CLKID_AUDIO_PDMIN0 + &clkaudio CLKID_AUDIO_PDMIN1>; + clock-names = "gate", "pll_clk", "pdm_dclk", "pdm_sysclk"; + interrupts = ; + interrupt-names = "pdmin_irq"; + pinctrl-names = "pdm_pins"; + pinctrl-0 = <&pdmin>; + filter_mode = <1>; /* mode 0~4, defalut:1 */ + status = "okay"; + }; +}; /* end of audiobus */ + +&pinctrl_periphs { + tdmout_a: tdmout_a { + mux { + pins = "GPIOX_12", "GPIOX_13", "GPIOX_15"; + //pins = "GPIOX_15";//slave + function = "tdma_out"; + }; + }; + + tdmin_a: tdmin_a { + mux { + pins = "GPIOX_14"; + //pins = "GPIOX_12", "GPIOX_13", "GPIOX_14";//slave + function = "tdma_in"; + }; + }; + + tdmb_mclk: tdmb_mclk { + mux { + pins = "GPIOA_1"; + function = "mclk"; + }; + }; + + tdmout_b: tdmout_b { + mux { + pins = "GPIOA_8", "GPIOA_9", "GPIOA_10", + "GPIOA_11", "GPIOA_12", "GPIOA_13"; + function = "tdmb_out"; + }; + }; + // tdmin and tdmout are the same pins. can't use at same time + /* + *tdmin_b:tdmin_b { + * mux { + * pins = "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13"; + * function = "tdmb_in"; + * }; + *}; + */ + + tdmc_mclk: tdmc_mclk { + mux { + pins = "GPIOA_0"; + function = "mclk"; + }; + }; + + tdmout_c:tdmout_c { + mux { + pins = "GPIOA_2", "GPIOA_3", "GPIOA_4", + "GPIOA_5", "GPIOA_6", "GPIOA_7"; + function = "tdmc_out"; + }; + }; + + + //tdmin_c:tdmin_c { + // mux { + // pins = "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7"; + // function = "tdmc_in"; + // }; + //}; + + spdifout: spidfout { + mux { + pins = "GPIOA_20"; + function = "spdif_out"; + }; + }; + + spdifin: spidfin { + mux { + pins = "GPIOA_19"; + function = "spdif_in"; + }; + }; + + pdmin: pdmin { + mux { + pins = "GPIOA_14", "GPIOA_15", "GPIOA_16", + "GPIOA_17", "GPIOA_18"; + function = "pdm"; + }; + }; +}; /* end of pinctrl_periphs */ +/* Audio Related End */ + + &spicc_a{ status = "disabled"; num_chipselect = <1>; diff --git a/arch/arm64/boot/dts/amlogic/axg_s420.dts b/arch/arm64/boot/dts/amlogic/axg_s420.dts index 4847493dd557..7267b378536d 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s420.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s420.dts @@ -224,6 +224,132 @@ pinctrl-names = "default"; pinctrl-0 = <&b_uart_pins>; }; + + /* Sound iomap */ + aml_snd_iomap { + compatible = "amlogic, snd_iomap"; + status = "okay"; + #address-cells=<2>; + #size-cells=<2>; + ranges; + pdm_bus { + reg = <0x0 0xFF632000 0x0 0x20>; + }; + audiobus_base { + reg = <0x0 0xFF642000 0x0 0x2000>; + }; + }; + pdm_codec:dummy{ + #sound-dai-cells = <0>; + compatible = "amlogic, pdm_dummy_codec"; + status = "okay"; + }; + dummy_codec:dummy{ + #sound-dai-cells = <0>; + compatible = "amlogic, aml_dummy_codec"; + status = "okay"; + }; + + meson_sound { + compatible = "amlogic, sound-card"; + aml-audio-card,name = "AML-AXGSOUND"; + //aml-audio-card,mclk-fs = <256>; + + aml-audio-card,dai-link@0 { + format = "i2s";//"dsp_a"; + mclk-fs = <256>;//512 + continuous-clock; + bitclock-inversion; + //frame-inversion; + bitclock-master = <&aml_tdma>; + frame-master = <&aml_tdma>; + cpu { + sound-dai = <&aml_tdma>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + sound-dai = <&dummy_codec &dummy_codec>; + }; + }; + + aml-audio-card,dai-link@1 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + bitclock-inversion; + //frame-inversion; + bitclock-master = <&aml_tdmb>; + frame-master = <&aml_tdmb>; + cpu { + sound-dai = <&aml_tdmb>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + //sound-dai = <&dummy_codec &dummy_codec>; + sound-dai = <&tas5707_36>; + }; + }; + + aml-audio-card,dai-link@2 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + bitclock-inversion; + //frame-inversion; + bitclock-master = <&aml_tdmc>; + frame-master = <&aml_tdmc>; + cpu { + sound-dai = <&aml_tdmc>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + //sound-dai = <&tas5707_36 &tas5707_3a>; + sound-dai = <&dummy_codec &dummy_codec>; + }; + }; + + aml-audio-card,dai-link@3 { + format = "i2s"; + mclk-fs = <256>; + continuous-clock; + //bitclock-inversion; + //frame-inversion; + bitclock-master = <&aml_pdm>; + frame-master = <&aml_pdm>; + cpu { + sound-dai = <&aml_pdm>; + }; + codec { + sound-dai = <&pdm_codec>; + }; + }; + + /*aml-audio-card,dai-link@4 { + * //format = "i2s"; + * mclk-fs = <128>; + * continuous-clock; + * cpu { + * sound-dai = <&aml_spdif>; + * system-clock-frequency = <12288000>; + * }; + * codec { + * sound-dai = <&dummy_codec>; + * }; + *}; + */ + }; wifi{ compatible = "amlogic, aml_wifi"; dev_name = "aml_wifi"; @@ -411,6 +537,244 @@ }; }; /* end of / */ +/* for spk board */ +&i2c_b { + status = "okay"; + //pinctrl-names="default"; + //pinctrl-0=<&b_i2c_master>; + tlv320adc3101_32: tlv320adc3101_32@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x19>; + status = "disabled"; + }; + + tas5707_36: tas5707_36@36 { + compatible = "ti,tas5707"; + #sound-dai-cells = <0>; + reg = <0x1b>; + status = "okay"; + reset_pin = <&gpio_ao GPIOAO_4 0>; + }; + + tas5707_3a: tas5707_3a@3a { + compatible = "ti,tas5707"; + #sound-dai-cells = <0>; + reg = <0x1d>; + status = "disable"; + }; +}; + +/* for mic board */ +&i2c_ao { + status = "okay"; + pinctrl-names="default"; + pinctrl-0=<&ao_i2c_master_pin2>; + + tlv320adc3101_30: tlv320adc3101_30@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x18>; + status = "okay"; + }; + tlv320adc3101_34: tlv320adc3101_34@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x1a>; + status = "okay"; + }; + tlv320adc3101_36: tlv320adc3101_36@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x1b>; + status = "okay"; + }; +}; + +&audiobus { + aml_tdma: tdma { + compatible = "amlogic, snd-tdma"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask = <1 1 1 1>; + /* select tdm lr/bclk src, see aml_axg_tdm.c */ + dai-tdm-clk-sel = <0>; + tdm_from_ddr = <0>; + tdm_to_ddr = <0>; + clocks = <&clkc CLKID_MPLL2>; + //&clkaudio CLKID_AUDIO_TDMOUTA + //&clkaudio CLKID_AUDIO_MCLK_A>; + clock-names = "mpll0", "gate", "mclk"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmin_a &tdmout_a>; + }; + + aml_tdmb: tdmb { + compatible = "amlogic, snd-tdmb"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask = <1 1 1 1>; + dai-tdm-clk-sel = <2>; + tdm_from_ddr = <1>; + tdm_to_ddr = <1>; + clocks = <&clkc CLKID_MPLL1>; + clock-names = "mpll1"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmb_mclk &tdmout_b>; + }; + + aml_tdmc: tdmc { + compatible = "amlogic, snd-tdmc"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask = <1 1 1 1>; + dai-tdm-clk-sel = <2>; + tdm_from_ddr = <2>; + tdm_to_ddr = <2>; + clocks = <&clkc CLKID_MPLL2>; + clock-names = "mpll2"; + interrupts = + ; + interrupt-names = "tdmin", "tdmout"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmc_mclk &tdmout_c>; + }; + + aml_spdif: spdif { + compatible = "amlogic, snd-spdif"; + #sound-dai-cells = <0>; + spdif_from_ddr = <0>; + spdif_to_ddr = <0>; + clocks = <&clkc CLKID_MPLL0 + &clkc CLKID_FCLK_DIV4 + &clkaudio CLKID_AUDIO_SPDIFIN + &clkaudio CLKID_AUDIO_SPDIFOUT + &clkaudio CLKID_AUDIO_SPDIFIN_CTRL + &clkaudio CLKID_AUDIO_SPDIFOUT_CTRL>; + clock-names = "sysclk", "fixed_clk", "gate_spdifin", + "gate_spdifout", "clk_spdifin", "clk_spdifout"; + interrupts = + ; + + interrupt-names = "irq_spdifin", "irq_toddr", "irq_frddr"; + pinctrl-names = "spdif_pins"; + pinctrl-0 = <&spdifout &spdifin>; + status = "disabled"; + }; + + aml_pdm: pdm { + compatible = "amlogic, snd-pdm"; + #sound-dai-cells = <0>; + to_ddr = <2>; + clocks = <&clkaudio CLKID_AUDIO_PDM + &clkc CLKID_MPLL3 + &clkaudio CLKID_AUDIO_PDMIN0 + &clkaudio CLKID_AUDIO_PDMIN1>; + clock-names = "gate", "pll_clk", "pdm_dclk", "pdm_sysclk"; + interrupts = ; + interrupt-names = "pdmin_irq"; + pinctrl-names = "pdm_pins"; + pinctrl-0 = <&pdmin>; + filter_mode = <1>; /* mode 0~4, defalut:1 */ + status = "okay"; + }; +}; /* end of audiobus */ + +&pinctrl_periphs { + tdmout_a: tdmout_a { + mux { + pins = "GPIOX_12", "GPIOX_13", "GPIOX_15"; + //pins = "GPIOX_15";//slave + function = "tdma_out"; + }; + }; + + tdmin_a: tdmin_a { + mux { + pins = "GPIOX_14"; + //pins = "GPIOX_12", "GPIOX_13", "GPIOX_14";//slave + function = "tdma_in"; + }; + }; + tdmb_mclk: tdmb_mclk { + mux { + pins = "GPIOA_1"; + function = "mclk"; + }; + }; + + tdmout_b: tdmout_b { + mux { + pins = "GPIOA_8", "GPIOA_9", "GPIOA_10", + "GPIOA_11"; + function = "tdmb_out"; + }; + }; + // tdmin and tdmout are the same pins. can't use at same time + /* + *tdmin_b:tdmin_b { + * mux { + * pins = "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13"; + * function = "tdmb_in"; + * }; + *}; + */ + + tdmc_mclk: tdmc_mclk { + mux { + pins = "GPIOA_0"; + function = "mclk"; + }; + }; + + tdmout_c:tdmout_c { + mux { + pins = "GPIOA_2", "GPIOA_3", "GPIOA_4", + "GPIOA_5"; + function = "tdmc_out"; + }; + }; + + /* + *tdmin_c:tdmin_c { + * mux { + * pins = "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7"; + * function = "tdmc_in"; + * }; + *}; + */ + + spdifout: spidfout { + mux { + pins = "GPIOA_20"; + function = "spdif_out"; + }; + }; + + spdifin: spidfin { + mux { + pins = "GPIOA_19"; + function = "spdif_in"; + }; + }; + + pdmin: pdmin { + mux { + pins = "GPIOA_14", "GPIOA_15", "GPIOA_16", + "GPIOA_17", "GPIOA_18"; + function = "pdm"; + }; + }; +}; /* end of pinctrl_periphs */ + &spicc_a{ status = "disabled"; num_chipselect = <1>; diff --git a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi index d37e5aa50d72..5c15369bdf74 100644 --- a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -320,6 +321,19 @@ reg = <0x0 0x0 0x0 0x320>; }; };/* end of hiubus*/ + + audiobus: audiobus@0xff642000 { + compatible = "amlogic, audio-controller", "simple-bus"; + reg = <0x0 0xff642000 0x0 0x2000>; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0xff642000 0x0 0x2000>; + clkaudio: audio_clocks { + compatible = "amlogic, audio_clocks"; + #clock-cells = <1>; + reg = <0x0 0x0 0x0 0xb0>; + }; + };/* end of audiobus*/ }; /* end of soc*/ pwm:meson-pwm { diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 2846bd522bab..758f65103c78 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -372,9 +372,13 @@ CONFIG_SND_SOC=y CONFIG_AMLOGIC_SND_SOC_CODECS=y CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC=y CONFIG_AMLOGIC_SND_CODEC_PCM2BT=y +CONFIG_AMLOGIC_SND_CODEC_PDM_DUMMY_CODEC=y CONFIG_AMLOGIC_SND_CODEC_AMLT9015=y CONFIG_AMLOGIC_SND_SOC_TAS5707=y +CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101=y CONFIG_AMLOGIC_SND_SOC=y +CONFIG_AMLOGIC_SND_SOC_MESON=y +CONFIG_AMLOGIC_SND_SOC_AUGE=y CONFIG_AMLOGIC_SND_SPLIT_MODE=y CONFIG_UHID=y CONFIG_USB_HIDDEV=y diff --git a/drivers/amlogic/pinctrl/pinctrl-mesonaxg.c b/drivers/amlogic/pinctrl/pinctrl-mesonaxg.c index 0cbb5b398b9f..88db2bb0cc2b 100644 --- a/drivers/amlogic/pinctrl/pinctrl-mesonaxg.c +++ b/drivers/amlogic/pinctrl/pinctrl-mesonaxg.c @@ -128,12 +128,12 @@ static const struct meson_desc_pin mesonaxg_periphs_pins[] = { MESON_FUNCTION(0x3, "norflash")), /*NOR_CS */ MESON_PINCTRL_PIN(MESON_PIN(GPIOA_0, EE_OFF), 0xb, 0, MESON_FUNCTION(0x0, "gpio"), - MESON_FUNCTION(0x1, "mclk_a"), /*MCLK_A*/ + MESON_FUNCTION(0x1, "mclk"), /*MCLK_A*/ MESON_FUNCTION(0x2, "pwm_vs"), /*PWM_VS*/ MESON_FUNCTION(0x5, "wifi")), /*WIFI_BEACON*/ MESON_PINCTRL_PIN(MESON_PIN(GPIOA_1, EE_OFF), 0xb, 4, MESON_FUNCTION(0x0, "gpio"), - MESON_FUNCTION(0x1, "mclk_a"), /*MCLK_A*/ + MESON_FUNCTION(0x1, "mclk"), /*MCLK_B*/ MESON_FUNCTION(0x3, "spdif_in"), /*SPDIF_IN*/ MESON_FUNCTION(0x4, "spdif_out"), /*SPDIF_OUT*/ MESON_FUNCTION(0x5, "wifi")), /*WIFI_BEACON*/ @@ -286,15 +286,24 @@ static const struct meson_desc_pin mesonaxg_periphs_pins[] = { MESON_FUNCTION(0x3, "pwm_d")), /*PWM_D*/ MESON_PINCTRL_PIN(MESON_PIN(GPIOX_12, EE_OFF), 0x5, 16, MESON_FUNCTION(0x0, "gpio"), + MESON_FUNCTION(0x1, "tdma_out"), /*TDMA_OUT*/ + MESON_FUNCTION(0x2, "tdma_in"), /*TDMA_IN*/ MESON_FUNCTION(0x4, "eth")), /*ETH_RGMII_RX_CLK*/ MESON_PINCTRL_PIN(MESON_PIN(GPIOX_13, EE_OFF), 0x5, 20, MESON_FUNCTION(0x0, "gpio"), + MESON_FUNCTION(0x1, "tdma_out"), /*TDMA_OUT*/ + MESON_FUNCTION(0x2, "tdma_in"), /*TDMA_IN*/ MESON_FUNCTION(0x4, "eth")), /*ETH_RXD0*/ MESON_PINCTRL_PIN(MESON_PIN(GPIOX_14, EE_OFF), 0x5, 24, MESON_FUNCTION(0x0, "gpio"), + MESON_FUNCTION(0x1, "tdma_in"), /*TDMA_IN*/ + MESON_FUNCTION(0x2, "tdma_out"), /*TDMA_OUT*/ MESON_FUNCTION(0x4, "eth")), /*ETH_RXD1*/ MESON_PINCTRL_PIN(MESON_PIN(GPIOX_15, EE_OFF), 0x5, 28, MESON_FUNCTION(0x0, "gpio"), + MESON_FUNCTION(0x1, "tdma_out"), /*TDMA_OUT*/ + MESON_FUNCTION(0x2, "tdma_out1"), /*TDMA_OUT1*/ + MESON_FUNCTION(0x3, "tdma_in"), /*TDMA_IN*/ MESON_FUNCTION(0x4, "eth")), /*ETH_RX_DV*/ MESON_PINCTRL_PIN(MESON_PIN(GPIOX_16, EE_OFF), 0x6, 0, MESON_FUNCTION(0x0, "gpio"), diff --git a/include/dt-bindings/clock/amlogic,axg-audio-clk.h b/include/dt-bindings/clock/amlogic,axg-audio-clk.h new file mode 100644 index 000000000000..5c2ad553bd59 --- /dev/null +++ b/include/dt-bindings/clock/amlogic,axg-audio-clk.h @@ -0,0 +1,60 @@ +/* + * include/dt-bindings/clock/amlogic,axg-audio-clk.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AGX_AUDIO_CLK_H +#define __AGX_AUDIO_CLK_H + +/* + * CLKID audio index values + */ + +#define CLKID_AUDIO_DDR_ARB 0 +#define CLKID_AUDIO_PDM 1 +#define CLKID_AUDIO_TDMINA 2 +#define CLKID_AUDIO_TDMINB 3 +#define CLKID_AUDIO_TDMINC 4 +#define CLKID_AUDIO_TDMINLB 5 +#define CLKID_AUDIO_TDMOUTA 6 +#define CLKID_AUDIO_TDMOUTB 7 +#define CLKID_AUDIO_TDMOUTC 8 +#define CLKID_AUDIO_FRDDRA 9 +#define CLKID_AUDIO_FRDDRB 10 +#define CLKID_AUDIO_FRDDRC 11 +#define CLKID_AUDIO_TODDRA 12 +#define CLKID_AUDIO_TODDRB 13 +#define CLKID_AUDIO_TODDRC 14 +#define CLKID_AUDIO_LOOPBACK 15 +#define CLKID_AUDIO_SPDIFIN 16 +#define CLKID_AUDIO_SPDIFOUT 17 +#define CLKID_AUDIO_RESAMPLE 18 +#define CLKID_AUDIO_POWER_DETECT 19 + +#define MCLK_BASE 20 +#define CLKID_AUDIO_MCLK_A (MCLK_BASE + 0) +#define CLKID_AUDIO_MCLK_B (MCLK_BASE + 1) +#define CLKID_AUDIO_MCLK_C (MCLK_BASE + 2) +#define CLKID_AUDIO_MCLK_D (MCLK_BASE + 3) +#define CLKID_AUDIO_MCLK_E (MCLK_BASE + 4) +#define CLKID_AUDIO_MCLK_F (MCLK_BASE + 5) + +#define CLKID_AUDIO_SPDIFIN_CTRL (MCLK_BASE + 6) +#define CLKID_AUDIO_SPDIFOUT_CTRL (MCLK_BASE + 7) +#define CLKID_AUDIO_PDMIN0 (MCLK_BASE + 8) +#define CLKID_AUDIO_PDMIN1 (MCLK_BASE + 9) + +#define NUM_AUDIO_CLKS (MCLK_BASE + 10) +#endif /* __AGX_AUDIO_CLK_H */ diff --git a/sound/soc/amlogic/Kconfig b/sound/soc/amlogic/Kconfig index 58d5799b51f6..41d8d56f94d4 100644 --- a/sound/soc/amlogic/Kconfig +++ b/sound/soc/amlogic/Kconfig @@ -1,5 +1,9 @@ +# +# Amlogic SoC audio configuration +# + menuconfig AMLOGIC_SND_SOC - bool "Amlogic Meson ASoC" + bool "Amlogic ASoC" default n help Say Y or M if you want to add support for codecs attached to @@ -8,20 +12,26 @@ menuconfig AMLOGIC_SND_SOC if AMLOGIC_SND_SOC -config AMLOGIC_SND_SPLIT_MODE - bool "AIU split mode, otherwise normal mode" +config AMLOGIC_SND_SOC_MESON + bool "Amlogic Meson Asoc" depends on AMLOGIC_SND_SOC default n help - Say 'Y' to enable AIU split mode. If not, it's normal mode. + Say Y or M if you want to add support for meson arch to the + Amlogic Asoc Interface. it is for old audio arch, less than + kernel4.9. -config AMLOGIC_SND_SPLIT_MODE_MMAP - bool "AIU split mode, mmap" - depends on AMLOGIC_SND_SPLIT_MODE +config AMLOGIC_SND_SOC_AUGE + bool "Amlogic Auge Asoc" depends on AMLOGIC_SND_SOC default n help - Say 'Y' or 'N' to enable/disable AIU split mmap + Say Y or M if you want to add support for audio arch Auge + to the Amlogic Asoc Interface. it is a new audio arch to + distinguish Meson audio arch. it is base on kernel 4.9 + +# All the supported SoCs +source "sound/soc/amlogic/meson/Kconfig" +source "sound/soc/amlogic/auge/Kconfig" endif # AMLOGIC_SND_SOC - diff --git a/sound/soc/amlogic/Makefile b/sound/soc/amlogic/Makefile index a430b00d0895..d1fa3c8cfe2a 100644 --- a/sound/soc/amlogic/Makefile +++ b/sound/soc/amlogic/Makefile @@ -1,31 +1,2 @@ -# AML Platform Support -snd-soc-aml-pcm-objs := aml_pcm.o -snd-soc-aml-i2s-objs := aml_i2s.o -snd-soc-aml-i2s-dai-objs := aml_i2s_dai.o -snd-soc-aml-pcm-dai-objs := aml_pcm_dai.o -snd-soc-aml-spdif-dai-objs := aml_spdif_dai.o -snd-soc-aml-hw-objs := aml_audio_hw.o -snd-soc-aml-hw-pcm2bt-objs := aml_audio_hw_pcm.o -snd-soc-aml-dmic-objs := aml_dmic.o - -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-pcm.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-i2s.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-i2s-dai.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-pcm-dai.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-hw.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += aml_notify.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-hw-pcm2bt.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-spdif-dai.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-dmic.o - -# AML spdif codec support -snd-soc-aml-spdif-codec-objs := aml_spdif_codec.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-spdif-codec.o - -#AML M8 Machine support -snd-soc-aml-meson-objs := aml_meson.o -obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-meson.o - -#AML G9TV Machine support -snd-soc-aml-tv-objs := aml_tv.o -#obj-$(CONFIG_AMLOGIC_SND_SOC) += snd-soc-aml-tv.o +obj-$(CONFIG_AMLOGIC_SND_SOC) += meson/ +obj-$(CONFIG_AMLOGIC_SND_SOC) += auge/ diff --git a/sound/soc/amlogic/auge/Kconfig b/sound/soc/amlogic/auge/Kconfig new file mode 100644 index 000000000000..eafeb0d45de5 --- /dev/null +++ b/sound/soc/amlogic/auge/Kconfig @@ -0,0 +1,7 @@ +menuconfig AMLOGIC_SND_SOC_AUGE + bool "Amlogic Auge ASoC" + default n + help + Say Y or M if you want to add support for codecs attached to + the Amlogic Asoc interface. You will also need + to select the audio interfaces to support below. diff --git a/sound/soc/amlogic/auge/Makefile b/sound/soc/amlogic/auge/Makefile new file mode 100644 index 000000000000..ec978fb913d6 --- /dev/null +++ b/sound/soc/amlogic/auge/Makefile @@ -0,0 +1,14 @@ +obj-$(CONFIG_AMLOGIC_SND_SOC_AUGE) += audio_controller.o \ + audio_io.o \ + clocks.o \ + card.o \ + card_utils.o \ + tdm.o \ + tdm_hw.o \ + spdif.o \ + spdif_hw.o \ + pdm.o \ + pdm_hw.o \ + pdm_hw_coeff.o \ + iomap.o \ + ddr_mngr.o diff --git a/sound/soc/amlogic/auge/audio_controller.c b/sound/soc/amlogic/auge/audio_controller.c new file mode 100644 index 000000000000..eeb37d200baa --- /dev/null +++ b/sound/soc/amlogic/auge/audio_controller.c @@ -0,0 +1,136 @@ +/* + * sound/soc/amlogic/auge/audio_controller.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audio_io.h" +#include "regs.h" + +#define DRV_NAME "aml-audio-controller" + +static unsigned int aml_audio_mmio_read(struct aml_audio_controller *actrlr, + unsigned int reg) +{ + struct regmap *regmap = actrlr->regmap; + unsigned int val; + + regmap_read(regmap, (reg << 2), &val); + + return val; +} + +static int aml_audio_mmio_write(struct aml_audio_controller *actrlr, + unsigned int reg, unsigned int value) +{ + struct regmap *regmap = actrlr->regmap; + + return regmap_write(regmap, (reg << 2), value); +} + +static int aml_audio_mmio_update_bits(struct aml_audio_controller *actrlr, + unsigned int reg, unsigned int mask, unsigned int value) +{ + struct regmap *regmap = actrlr->regmap; + + return regmap_update_bits(regmap, (reg << 2), mask, value); +} + +struct aml_audio_ctrl_ops aml_actrl_mmio_ops = { + .read = aml_audio_mmio_read, + .write = aml_audio_mmio_write, + .update_bits = aml_audio_mmio_update_bits, +}; + +static struct regmap_config aml_audio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static const struct of_device_id amlogic_audio_controller_of_match[] = { + { .compatible = "amlogic, audio-controller" }, + {}, +}; + +static int register_audio_controller(struct platform_device *pdev, + struct aml_audio_controller *actrl) +{ + struct resource *res_mem; + void __iomem *regs; + struct regmap *regmap; + + /* get platform res from dtb */ + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENOENT; + + regs = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + aml_audio_regmap_config.max_register = 4 * resource_size(res_mem); + + regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &aml_audio_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* init aml audio bus mmio controller */ + aml_init_audio_controller(actrl, regmap, &aml_actrl_mmio_ops); + platform_set_drvdata(pdev, actrl); + + /* gate on all clks on bringup stage, need gate separately */ + aml_audiobus_write(actrl, EE_AUDIO_CLK_GATE_EN, 0xfffff); + + return 0; +} + +static int aml_audio_controller_probe(struct platform_device *pdev) +{ + struct aml_audio_controller *actrl; + + pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + actrl = devm_kzalloc(&pdev->dev, sizeof(*actrl), GFP_KERNEL); + if (!actrl) + return -ENOMEM; + + return register_audio_controller(pdev, actrl); +} + +static struct platform_driver aml_audio_controller_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = amlogic_audio_controller_of_match, + }, + .probe = aml_audio_controller_probe, +}; +module_platform_driver(aml_audio_controller_driver); + +MODULE_AUTHOR("Amlogic, Inc."); +MODULE_DESCRIPTION("Amlogic audio controller ASoc driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, amlogic_audio_controller_of_match); diff --git a/sound/soc/amlogic/auge/audio_controller.h b/sound/soc/amlogic/auge/audio_controller.h new file mode 100644 index 000000000000..75480f81020d --- /dev/null +++ b/sound/soc/amlogic/auge/audio_controller.h @@ -0,0 +1,23 @@ +/* + * sound/soc/amlogic/auge/audio_controller.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_AUDIO_CONTROLLER_H_ +#define __AML_AUDIO_CONTROLLER_H_ + +struct aml_audio_controller; + +#endif diff --git a/sound/soc/amlogic/auge/audio_io.c b/sound/soc/amlogic/auge/audio_io.c new file mode 100644 index 000000000000..feb75a1d8127 --- /dev/null +++ b/sound/soc/amlogic/auge/audio_io.c @@ -0,0 +1,60 @@ +/* + * sound/soc/amlogic/auge/audio_io.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include "audio_io.h" + +int aml_init_audio_controller(struct aml_audio_controller *actrlr, + struct regmap *regmap, struct aml_audio_ctrl_ops *ops) +{ + actrlr->regmap = regmap; + actrlr->ops = ops; + + return 0; +} + +int aml_audiobus_write(struct aml_audio_controller *actrlr, + unsigned int reg, unsigned int value) +{ + if (actrlr->ops->write) + return actrlr->ops->write(actrlr, reg, value); + + return -1; +} + +unsigned int aml_audiobus_read(struct aml_audio_controller *actrlr, + unsigned int reg) +{ + if (actrlr->ops->read) + return actrlr->ops->read(actrlr, reg); + + return 0; +} + +int aml_audiobus_update_bits(struct aml_audio_controller *actrlr, + unsigned int reg, unsigned int mask, unsigned int value) +{ + if (actrlr->ops->update_bits) + return actrlr->ops->update_bits(actrlr, reg, mask, value); + + return -1; +} + +/* Module information */ +MODULE_AUTHOR("Amlogic, Inc."); +MODULE_DESCRIPTION("ALSA Soc Aml Audio Utils"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amlogic/auge/audio_io.h b/sound/soc/amlogic/auge/audio_io.h new file mode 100644 index 000000000000..97b068a8ed37 --- /dev/null +++ b/sound/soc/amlogic/auge/audio_io.h @@ -0,0 +1,47 @@ +/* + * sound/soc/amlogic/auge/audio_io.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_AUDIO_IO_H_ +#define __AML_AUDIO_IO_H_ + +struct aml_audio_controller; + +struct aml_audio_ctrl_ops { + unsigned int (*read)(struct aml_audio_controller *actrlr, + unsigned int reg); + int (*write)(struct aml_audio_controller *actrlr, + unsigned int reg, unsigned int value); + int (*update_bits)(struct aml_audio_controller *actrlr, + unsigned int reg, unsigned int mask, unsigned int value); +}; + +struct aml_audio_controller { + struct regmap *regmap; + const struct aml_audio_ctrl_ops *ops; +}; + +/* audio io controller */ +int aml_init_audio_controller(struct aml_audio_controller *actrlr, + struct regmap *regmap, struct aml_audio_ctrl_ops *ops); +int aml_audiobus_write(struct aml_audio_controller *actrlr, + unsigned int reg, unsigned int value); +unsigned int aml_audiobus_read(struct aml_audio_controller *actrlr, + unsigned int reg); +int aml_audiobus_update_bits(struct aml_audio_controller *actrlr, + unsigned int reg, unsigned int mask, unsigned int value); + +#endif diff --git a/sound/soc/amlogic/auge/card.c b/sound/soc/amlogic/auge/card.c new file mode 100644 index 000000000000..97604e2c81c7 --- /dev/null +++ b/sound/soc/amlogic/auge/card.c @@ -0,0 +1,550 @@ +/* + * sound/soc/amlogic/auge/card.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "card.h" + +struct aml_jack { + struct snd_soc_jack jack; + struct snd_soc_jack_pin pin; + struct snd_soc_jack_gpio gpio; +}; + +struct aml_card_data { + struct snd_soc_card snd_card; + struct aml_dai_props { + struct aml_dai cpu_dai; + struct aml_dai codec_dai; + unsigned int mclk_fs; + } *dai_props; + unsigned int mclk_fs; + struct aml_jack hp_jack; + struct aml_jack mic_jack; + struct snd_soc_dai_link *dai_link; +}; + +#define aml_priv_to_dev(priv) ((priv)->snd_card.dev) +#define aml_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) +#define aml_priv_to_props(priv, i) ((priv)->dai_props + (i)) + +#define DAI "sound-dai" +#define CELL "#sound-dai-cells" +#define PREFIX "aml-audio-card," + +#define aml_card_init_hp(card, sjack, prefix)\ + aml_card_init_jack(card, sjack, 1, prefix) +#define aml_card_init_mic(card, sjack, prefix)\ + aml_card_init_jack(card, sjack, 0, prefix) +static int aml_card_init_jack(struct snd_soc_card *card, + struct aml_jack *sjack, + int is_hp, char *prefix) +{ + struct device *dev = card->dev; + enum of_gpio_flags flags; + char prop[128]; + char *pin_name; + char *gpio_name; + int mask; + int det; + + sjack->gpio.gpio = -ENOENT; + + if (is_hp) { + snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix); + pin_name = "Headphones"; + gpio_name = "Headphone detection"; + mask = SND_JACK_HEADPHONE; + } else { + snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix); + pin_name = "Mic Jack"; + gpio_name = "Mic detection"; + mask = SND_JACK_MICROPHONE; + } + + det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags); + if (det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (gpio_is_valid(det)) { + sjack->pin.pin = pin_name; + sjack->pin.mask = mask; + + sjack->gpio.name = gpio_name; + sjack->gpio.report = mask; + sjack->gpio.gpio = det; + sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW); + sjack->gpio.debounce_time = 150; + + snd_soc_card_jack_new(card, pin_name, mask, + &sjack->jack, + &sjack->pin, 1); + + snd_soc_jack_add_gpios(&sjack->jack, 1, + &sjack->gpio); + } + + return 0; +} + +static void aml_card_remove_jack(struct aml_jack *sjack) +{ + if (gpio_is_valid(sjack->gpio.gpio)) + snd_soc_jack_free_gpios(&sjack->jack, 1, &sjack->gpio); +} + +static int aml_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct aml_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct aml_dai_props *dai_props = + aml_priv_to_props(priv, rtd->num); + int ret; + + ret = clk_prepare_enable(dai_props->cpu_dai.clk); + if (ret) + return ret; + + ret = clk_prepare_enable(dai_props->codec_dai.clk); + if (ret) + clk_disable_unprepare(dai_props->cpu_dai.clk); + + return ret; +} + +static void aml_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct aml_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct aml_dai_props *dai_props = + aml_priv_to_props(priv, rtd->num); + + clk_disable_unprepare(dai_props->cpu_dai.clk); + + clk_disable_unprepare(dai_props->codec_dai.clk); +} + +static int aml_card_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 aml_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct aml_dai_props *dai_props = + aml_priv_to_props(priv, rtd->num); + unsigned int mclk, mclk_fs = 0; + int ret = 0; + + if (priv->mclk_fs) + mclk_fs = priv->mclk_fs; + else if (dai_props->mclk_fs) + mclk_fs = dai_props->mclk_fs; + + if (mclk_fs) { + mclk = params_rate(params) * mclk_fs; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + goto err; + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + goto err; + } + return 0; +err: + return ret; +} + +static struct snd_soc_ops aml_card_ops = { + .startup = aml_card_startup, + .shutdown = aml_card_shutdown, + .hw_params = aml_card_hw_params, +}; + +static int aml_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct aml_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + struct aml_dai_props *dai_props = + aml_priv_to_props(priv, rtd->num); + int ret; + + ret = aml_card_init_dai(codec, &dai_props->codec_dai); + if (ret < 0) + return ret; + + ret = aml_card_init_dai(cpu, &dai_props->cpu_dai); + if (ret < 0) + return ret; + + ret = aml_card_init_hp(rtd->card, &priv->hp_jack, PREFIX); + if (ret < 0) + return ret; + + ret = aml_card_init_mic(rtd->card, &priv->hp_jack, PREFIX); + if (ret < 0) + return ret; + + return 0; +} + +static int aml_card_dai_link_of(struct device_node *node, + struct aml_card_data *priv, + int idx, + bool is_top_level_node) +{ + struct device *dev = aml_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = aml_priv_to_link(priv, idx); + struct aml_dai_props *dai_props = aml_priv_to_props(priv, idx); + struct aml_dai *cpu_dai = &dai_props->cpu_dai; + struct aml_dai *codec_dai = &dai_props->codec_dai; + struct device_node *cpu = 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; + + snprintf(prop, sizeof(prop), "%scpu", prefix); + cpu = of_get_child_by_name(node, prop); + + 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 (!cpu || !codec) { + ret = -EINVAL; + dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); + goto dai_link_of_err; + } + + ret = aml_card_parse_daifmt(dev, node, codec, + prefix, &dai_link->dai_fmt); + if (ret < 0) + goto dai_link_of_err; + + of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); + + ret = aml_card_parse_cpu(cpu, dai_link, + DAI, CELL, &single_cpu); + if (ret < 0) + goto dai_link_of_err; + +#if 0 + ret = aml_card_parse_codec(codec, dai_link, DAI, CELL); +#else + ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link); +#endif + if (ret < 0) + goto dai_link_of_err; + + ret = aml_card_parse_platform(plat, dai_link, DAI, CELL); + if (ret < 0) + goto dai_link_of_err; + + ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask, + &cpu_dai->rx_slot_mask, + &cpu_dai->slots, + &cpu_dai->slot_width); + if (ret < 0) + goto dai_link_of_err; + + ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask, + &codec_dai->rx_slot_mask, + &codec_dai->slots, + &codec_dai->slot_width); + if (ret < 0) + goto dai_link_of_err; + + ret = aml_card_parse_clk_cpu(cpu, dai_link, cpu_dai); + if (ret < 0) + goto dai_link_of_err; + +#if 0 + ret = aml_card_parse_clk_codec(codec, dai_link, codec_dai); + if (ret < 0) + goto dai_link_of_err; +#endif + + ret = aml_card_canonicalize_dailink(dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = aml_card_set_dailink_name(dev, dai_link, + "%s-%s", + dai_link->cpu_dai_name, + dai_link->codecs->dai_name); + if (ret < 0) + goto dai_link_of_err; + + dai_link->ops = &aml_card_ops; + dai_link->init = aml_card_dai_init; + + dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); + dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); + dev_dbg(dev, "\tcpu : %s / %d\n", + dai_link->cpu_dai_name, + dai_props->cpu_dai.sysclk); + dev_dbg(dev, "\tcodec : %s / %d\n", + dai_link->codecs->dai_name, + dai_props->codec_dai.sysclk); + + aml_card_canonicalize_cpu(dai_link, single_cpu); + +dai_link_of_err: + of_node_put(cpu); + of_node_put(codec); + + return ret; +} + +static int aml_card_parse_aux_devs(struct device_node *node, + struct aml_card_data *priv) +{ + struct device *dev = aml_priv_to_dev(priv); + struct device_node *aux_node; + int i, n, len; + + if (!of_find_property(node, PREFIX "aux-devs", &len)) + return 0; /* Ok to have no aux-devs */ + + n = len / sizeof(__be32); + if (n <= 0) + return -EINVAL; + + priv->snd_card.aux_dev = devm_kzalloc(dev, + n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL); + if (!priv->snd_card.aux_dev) + return -ENOMEM; + + for (i = 0; i < n; i++) { + aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); + if (!aux_node) + return -EINVAL; + priv->snd_card.aux_dev[i].codec_of_node = aux_node; + } + + priv->snd_card.num_aux_devs = n; + return 0; +} + +static int aml_card_parse_of(struct device_node *node, + struct aml_card_data *priv) +{ + struct device *dev = aml_priv_to_dev(priv); + struct device_node *dai_link; + int ret; + + if (!node) + return -EINVAL; + + dai_link = of_get_child_by_name(node, PREFIX "dai-link"); + + /* The off-codec widgets */ + if (of_property_read_bool(node, PREFIX "widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, + PREFIX "widgets"); + if (ret) + goto card_parse_end; + } + + /* DAPM routes */ + if (of_property_read_bool(node, PREFIX "routing")) { + ret = snd_soc_of_parse_audio_routing(&priv->snd_card, + PREFIX "routing"); + if (ret) + goto card_parse_end; + } + + /* Factor to mclk, used in hw_params() */ + of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); + + /* Single/Muti DAI link(s) & New style of DT node */ + if (dai_link) { + struct device_node *np = NULL; + int i = 0; + + for_each_child_of_node(node, np) { + dev_dbg(dev, "\tlink %d:\n", i); + ret = aml_card_dai_link_of(np, priv, + i, false); + if (ret < 0) { + of_node_put(np); + goto card_parse_end; + } + i++; + } + } else { + /* For single DAI link & old style of DT node */ + ret = aml_card_dai_link_of(node, priv, 0, true); + if (ret < 0) + goto card_parse_end; + } + + ret = aml_card_parse_card_name(&priv->snd_card, PREFIX); + if (ret < 0) + goto card_parse_end; + + ret = aml_card_parse_aux_devs(node, priv); + +card_parse_end: + of_node_put(dai_link); + + return ret; +} + +static int aml_card_probe(struct platform_device *pdev) +{ + struct aml_card_data *priv; + struct snd_soc_dai_link *dai_link; + struct aml_dai_props *dai_props; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int num, ret; + + /* Get the number of DAI links */ + if (np && of_get_child_by_name(np, PREFIX "dai-link")) + num = of_get_child_count(np); + else + num = 1; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_props || !dai_link) + return -ENOMEM; + + priv->dai_props = dai_props; + priv->dai_link = dai_link; + + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = priv->dai_link; + priv->snd_card.num_links = num; + + if (np && of_device_is_available(np)) { + + ret = aml_card_parse_of(np, priv); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "%s, parse error %d\n", + __func__, ret); + goto err; + } + + } else { + struct aml_card_info *cinfo; + + cinfo = dev->platform_data; + if (!cinfo) { + dev_err(dev, "no info for asoc-aml-card\n"); + return -EINVAL; + } + + if (!cinfo->name || + !cinfo->codec_dai.name || + !cinfo->codec || + !cinfo->platform || + !cinfo->cpu_dai.name) { + dev_err(dev, "insufficient aml_card_info settings\n"); + return -EINVAL; + } + + priv->snd_card.name = + (cinfo->card) ? cinfo->card : cinfo->name; + dai_link->name = cinfo->name; + dai_link->stream_name = cinfo->name; + dai_link->platform_name = cinfo->platform; + dai_link->codec_name = cinfo->codec; + dai_link->cpu_dai_name = cinfo->cpu_dai.name; + dai_link->codec_dai_name = cinfo->codec_dai.name; + dai_link->dai_fmt = cinfo->daifmt; + dai_link->init = aml_card_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, + sizeof(priv->dai_props->codec_dai)); + } + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); + if (ret >= 0) + return ret; +err: + aml_card_clean_reference(&priv->snd_card); + + return ret; +} + +static int aml_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct aml_card_data *priv = snd_soc_card_get_drvdata(card); + + aml_card_remove_jack(&priv->hp_jack); + aml_card_remove_jack(&priv->mic_jack); + + return aml_card_clean_reference(card); +} + +static const struct of_device_id aml_of_match[] = { + { .compatible = "amlogic, sound-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, aml_of_match); + +static struct platform_driver aml_card = { + .driver = { + .name = "asoc-aml-card", + .pm = &snd_soc_pm_ops, + .of_match_table = aml_of_match, + }, + .probe = aml_card_probe, + .remove = aml_card_remove, +}; + +module_platform_driver(aml_card); + +MODULE_ALIAS("platform:asoc-aml-card"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC aml Sound Card"); +MODULE_AUTHOR("AMLogic, Inc."); diff --git a/sound/soc/amlogic/auge/card.h b/sound/soc/amlogic/auge/card.h new file mode 100644 index 000000000000..1199d25ea16f --- /dev/null +++ b/sound/soc/amlogic/auge/card.h @@ -0,0 +1,35 @@ +/* + * sound/soc/amlogic/auge/card.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_CARD_H_ +#define __AML_CARD_H_ + +#include +#include "card_utils.h" + +struct aml_card_info { + const char *name; + const char *card; + const char *codec; + const char *platform; + + unsigned int daifmt; + struct aml_dai cpu_dai; + struct aml_dai codec_dai; +}; + +#endif /* __AML_CARD_H_ */ diff --git a/sound/soc/amlogic/auge/card_utils.c b/sound/soc/amlogic/auge/card_utils.c new file mode 100644 index 000000000000..3b6b8e1a8e6f --- /dev/null +++ b/sound/soc/amlogic/auge/card_utils.c @@ -0,0 +1,240 @@ +/* + * sound/soc/amlogic/auge/card_utils.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include "card_utils.h" + +int aml_card_parse_daifmt(struct device *dev, + struct device_node *node, + struct device_node *codec, + char *prefix, + unsigned int *retfmt) +{ + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + int prefix_len = prefix ? strlen(prefix) : 0; + unsigned int daifmt; + + daifmt = snd_soc_of_parse_daifmt(node, prefix, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + + if (prefix_len && !bitclkmaster && !framemaster) { + /* + * No dai-link level and master setting was not found from + * sound node level, revert back to legacy DT parsing and + * take the settings from codec node. + */ + dev_dbg(dev, "Revert to legacy daifmt parsing\n"); + + daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | + (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); + } else { + if (codec == bitclkmaster) + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; + else + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; + } + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + *retfmt = daifmt; + + return 0; +} + +int aml_card_set_dailink_name(struct device *dev, + struct snd_soc_dai_link *dai_link, + const char *fmt, ...) +{ + va_list ap; + char *name = NULL; + int ret = -ENOMEM; + + va_start(ap, fmt); + name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap); + va_end(ap); + + if (name) { + ret = 0; + + dai_link->name = name; + dai_link->stream_name = name; + } + + return ret; +} + +int aml_card_parse_card_name(struct snd_soc_card *card, + char *prefix) +{ + char prop[128]; + int ret; + + snprintf(prop, sizeof(prop), "%sname", prefix); + + /* Parse the card name from DT */ + ret = snd_soc_of_parse_card_name(card, prop); + if (ret < 0) + return ret; + + if (!card->name && card->dai_link) + card->name = card->dai_link->name; + + return 0; +} + +int aml_card_parse_clk(struct device_node *node, + struct device_node *dai_of_node, + struct aml_dai *aml_dai) +{ + struct clk *clk; + u32 val; + + /* + * Parse dai->sysclk come from "clocks = <&xxx>" + * (if system has common clock) + * or "system-clock-frequency = " + * or device's module clock. + */ + clk = of_clk_get(node, 0); + if (!IS_ERR(clk)) { + aml_dai->sysclk = clk_get_rate(clk); + aml_dai->clk = clk; + } else if (!of_property_read_u32(node, + "system-clock-frequency", &val)) { + aml_dai->sysclk = val; + } else { + clk = of_clk_get(dai_of_node, 0); + if (!IS_ERR(clk)) + aml_dai->sysclk = clk_get_rate(clk); + } + + return 0; +} + +int aml_card_parse_dai(struct device_node *node, + struct device_node **dai_of_node, + const char **dai_name, + const char *list_name, + const char *cells_name, + int *is_single_link) +{ + struct of_phandle_args args; + int ret; + + if (!node) + return 0; + + /* + * Get node via "sound-dai = <&phandle port>" + * it will be used as xxx_of_node on soc_bind_dai_link() + */ + ret = of_parse_phandle_with_args(node, list_name, cells_name, 0, &args); + if (ret) + return ret; + + /* Get dai->name */ + if (dai_name) { + ret = snd_soc_of_get_dai_name(node, dai_name); + if (ret < 0) + return ret; + } + + *dai_of_node = args.np; + + if (is_single_link) + *is_single_link = !args.args_count; + + return 0; +} + +int aml_card_init_dai(struct snd_soc_dai *dai, + struct aml_dai *aml_dai) +{ + int ret; + + if (aml_dai->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, aml_dai->sysclk, 0); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "aml-card: set_sysclk error\n"); + return ret; + } + } + + if (aml_dai->slots) { + ret = snd_soc_dai_set_tdm_slot(dai, + aml_dai->tx_slot_mask, + aml_dai->rx_slot_mask, + aml_dai->slots, + aml_dai->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "aml-card: set_tdm_slot error\n"); + return ret; + } + } + + return 0; +} + +int aml_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link) +{ + if (!dai_link->cpu_dai_name || + (!dai_link->codec_dai_name && !dai_link->codecs)) + return -EINVAL; + + /* Assumes platform == cpu */ + if (!dai_link->platform_of_node) + dai_link->platform_of_node = dai_link->cpu_of_node; + + return 0; +} + +void aml_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, + int is_single_links) +{ + /* + * In soc_bind_dai_link() will check cpu name after + * of_node matching if dai_link has cpu_dai_name. + * but, it will never match if name was created by + * fmt_single_name() remove cpu_dai_name if cpu_args + * was 0. See: + * fmt_single_name() + * fmt_multiple_name() + */ + if (is_single_links) + dai_link->cpu_dai_name = NULL; +} + +int aml_card_clean_reference(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + int num_links; + + for (num_links = 0, dai_link = card->dai_link; + num_links < card->num_links; + num_links++, dai_link++) { + of_node_put(dai_link->cpu_of_node); + of_node_put(dai_link->codec_of_node); + } + return 0; +} diff --git a/sound/soc/amlogic/auge/card_utils.h b/sound/soc/amlogic/auge/card_utils.h new file mode 100644 index 000000000000..c88dcf885ce4 --- /dev/null +++ b/sound/soc/amlogic/auge/card_utils.h @@ -0,0 +1,79 @@ +/* + * sound/soc/amlogic/auge/card_utils.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_CARD_CORE_H +#define __AML_CARD_CORE_H + +#include + +struct aml_dai { + const char *name; + unsigned int sysclk; + int slots; + int slot_width; + unsigned int tx_slot_mask; + unsigned int rx_slot_mask; + struct clk *clk; +}; + +int aml_card_parse_daifmt(struct device *dev, + struct device_node *node, + struct device_node *codec, + char *prefix, + unsigned int *retfmt); +__printf(3, 4) +int aml_card_set_dailink_name(struct device *dev, + struct snd_soc_dai_link *dai_link, + const char *fmt, ...); +int aml_card_parse_card_name(struct snd_soc_card *card, + char *prefix); + +#define aml_card_parse_clk_cpu(node, dai_link, aml_dai) \ + aml_card_parse_clk(node, dai_link->cpu_of_node, aml_dai) +#define aml_card_parse_clk_codec(node, dai_link, aml_dai) \ + aml_card_parse_clk(node, dai_link->codec_of_node, aml_dai) +int aml_card_parse_clk(struct device_node *node, + struct device_node *dai_of_node, + struct aml_dai *aml_dai); + +#define aml_card_parse_cpu(node, dai_link, \ + list_name, cells_name, is_single_link) \ + aml_card_parse_dai(node, &dai_link->cpu_of_node, \ + &dai_link->cpu_dai_name, list_name, cells_name, is_single_link) +#define aml_card_parse_codec(node, dai_link, list_name, cells_name) \ + aml_card_parse_dai(node, &dai_link->codec_of_node, \ + &dai_link->codec_dai_name, list_name, cells_name, NULL) +#define aml_card_parse_platform(node, dai_link, list_name, cells_name) \ + aml_card_parse_dai(node, &dai_link->platform_of_node, \ + NULL, list_name, cells_name, NULL) +int aml_card_parse_dai(struct device_node *node, + struct device_node **endpoint_np, + const char **dai_name, + const char *list_name, + const char *cells_name, + int *is_single_links); + +int aml_card_init_dai(struct snd_soc_dai *dai, + struct aml_dai *aml_dai); + +int aml_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link); +void aml_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, + int is_single_links); + +int aml_card_clean_reference(struct snd_soc_card *card); + +#endif /* __AML_CARD_CORE_H */ diff --git a/sound/soc/amlogic/auge/clocks.c b/sound/soc/amlogic/auge/clocks.c new file mode 100644 index 000000000000..ab6f8f997eef --- /dev/null +++ b/sound/soc/amlogic/auge/clocks.c @@ -0,0 +1,308 @@ +/* + * sound/soc/amlogic/auge/audio_clocks.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "regs.h" + +#define DRV_NAME "aml-audio-clocks" + +spinlock_t aclk_lock; + +#define CLOCK_GATE(_name, _reg, _bit) \ +struct clk_gate _name = { \ + .reg = (void __iomem *)(_reg), \ + .bit_idx = (_bit), \ + .lock = &aclk_lock, \ + .hw.init = &(struct clk_init_data) { \ + .name = #_name, \ + .ops = &clk_gate_ops, \ + .parent_names = (const char *[]){ "clk81" }, \ + .num_parents = 1, \ + .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ + }, \ +} + +static CLOCK_GATE(audio_ddr_arb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 0); +static CLOCK_GATE(audio_pdm, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 1); +static CLOCK_GATE(audio_tdmina, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 2); +static CLOCK_GATE(audio_tdminb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 3); +static CLOCK_GATE(audio_tdminc, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 4); +static CLOCK_GATE(audio_tdminlb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 5); +static CLOCK_GATE(audio_tdmouta, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 6); +static CLOCK_GATE(audio_tdmoutb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 7); +static CLOCK_GATE(audio_tdmoutc, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 8); +static CLOCK_GATE(audio_frddra, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 9); +static CLOCK_GATE(audio_frddrb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 10); +static CLOCK_GATE(audio_frddrc, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 11); +static CLOCK_GATE(audio_toddra, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 12); +static CLOCK_GATE(audio_toddrb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 13); +static CLOCK_GATE(audio_toddrc, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 14); +static CLOCK_GATE(audio_loopback, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 15); +static CLOCK_GATE(audio_spdifin, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 16); +static CLOCK_GATE(audio_spdifout, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 17); +static CLOCK_GATE(audio_resample, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 18); +static CLOCK_GATE(audio_power_detect, + AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN), 19); + +static struct clk_gate *audio_clk_gates[] = { + &audio_ddr_arb, + &audio_pdm, + &audio_tdmina, + &audio_tdminb, + &audio_tdminc, + &audio_tdminlb, + &audio_tdmouta, + &audio_tdmoutb, + &audio_tdmoutc, + &audio_frddra, + &audio_frddrb, + &audio_frddrc, + &audio_toddra, + &audio_toddrb, + &audio_toddrc, + &audio_loopback, + &audio_spdifin, + &audio_spdifout, + &audio_resample, + &audio_power_detect, +}; + +/* Array of all clocks provided by this provider */ +static struct clk_hw *audio_clk_hws[] = { + [CLKID_AUDIO_DDR_ARB] = &audio_ddr_arb.hw, + [CLKID_AUDIO_PDM] = &audio_pdm.hw, + [CLKID_AUDIO_TDMINA] = &audio_tdmina.hw, + [CLKID_AUDIO_TDMINB] = &audio_tdminb.hw, + [CLKID_AUDIO_TDMINC] = &audio_tdminc.hw, + [CLKID_AUDIO_TDMINLB] = &audio_tdminlb.hw, + [CLKID_AUDIO_TDMOUTA] = &audio_tdmouta.hw, + [CLKID_AUDIO_TDMOUTB] = &audio_tdmoutb.hw, + [CLKID_AUDIO_TDMOUTC] = &audio_tdmoutc.hw, + [CLKID_AUDIO_FRDDRA] = &audio_frddra.hw, + [CLKID_AUDIO_FRDDRB] = &audio_frddrb.hw, + [CLKID_AUDIO_FRDDRC] = &audio_frddrc.hw, + [CLKID_AUDIO_TODDRA] = &audio_toddra.hw, + [CLKID_AUDIO_TODDRB] = &audio_toddrb.hw, + [CLKID_AUDIO_TODDRC] = &audio_toddrc.hw, + [CLKID_AUDIO_LOOPBACK] = &audio_loopback.hw, + [CLKID_AUDIO_SPDIFIN] = &audio_spdifin.hw, + [CLKID_AUDIO_SPDIFOUT] = &audio_spdifout.hw, + [CLKID_AUDIO_RESAMPLE] = &audio_resample.hw, + [CLKID_AUDIO_POWER_DETECT] = &audio_power_detect.hw, +}; + +const char *mclk_parent_names[] = {"mpll0", "mpll1", "mpll2", "mpll3", + "hifi_pll", "fclk_div3", "fclk_div4", "gp0_pll"}; + +#define CLOCK_COM_MUX(_name, _reg, _mask, _shift) \ +struct clk_mux _name##_mux = { \ + .reg = (void __iomem *)(_reg), \ + .mask = (_mask), \ + .shift = (_shift), \ + .lock = &aclk_lock, \ +} +#define CLOCK_COM_DIV(_name, _reg, _shift, _width) \ +struct clk_divider _name##_div = { \ + .reg = (void __iomem *)(_reg), \ + .shift = (_shift), \ + .width = (_width), \ + .lock = &aclk_lock, \ +} +#define CLOCK_COM_GATE(_name, _reg, _bit) \ +struct clk_gate _name##_gate = { \ + .reg = (void __iomem *)(_reg), \ + .bit_idx = (_bit), \ + .lock = &aclk_lock, \ +} + +/* mclk_a */ +CLOCK_COM_MUX(mclk_a, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_A_CTRL), 0x7, 24); +CLOCK_COM_DIV(mclk_a, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_A_CTRL), 0, 16); +CLOCK_COM_GATE(mclk_a, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_A_CTRL), 31); +/* mclk_b */ +CLOCK_COM_MUX(mclk_b, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_B_CTRL), 0x7, 24); +CLOCK_COM_DIV(mclk_b, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_B_CTRL), 0, 16); +CLOCK_COM_GATE(mclk_b, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_B_CTRL), 31); +/* mclk_c */ +CLOCK_COM_MUX(mclk_c, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_C_CTRL), 0x7, 24); +CLOCK_COM_DIV(mclk_c, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_C_CTRL), 0, 16); +CLOCK_COM_GATE(mclk_c, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_C_CTRL), 31); +/* mclk_d */ +CLOCK_COM_MUX(mclk_d, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_D_CTRL), 0x7, 24); +CLOCK_COM_DIV(mclk_d, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_D_CTRL), 0, 16); +CLOCK_COM_GATE(mclk_d, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_D_CTRL), 31); +/* mclk_e */ +CLOCK_COM_MUX(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL), 0x7, 24); +CLOCK_COM_DIV(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL), 0, 16); +CLOCK_COM_GATE(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL), 31); +/* mclk_f */ +CLOCK_COM_MUX(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL), 0x7, 24); +CLOCK_COM_DIV(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL), 0, 16); +CLOCK_COM_GATE(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL), 31); +/* spdifin */ +CLOCK_COM_MUX(spdifin, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFIN_CTRL), 0x7, 24); +CLOCK_COM_DIV(spdifin, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFIN_CTRL), 0, 8); +CLOCK_COM_GATE(spdifin, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFIN_CTRL), 31); +/* spdifout */ +CLOCK_COM_MUX(spdifout, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFOUT_CTRL), 0x7, 24); +CLOCK_COM_DIV(spdifout, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFOUT_CTRL), 0, 10); +CLOCK_COM_GATE(spdifout, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFOUT_CTRL), 31); +/* pdmin0 */ +CLOCK_COM_MUX(pdmin0, AUD_ADDR_OFFSET(EE_AUDIO_CLK_PDMIN_CTRL0), 0x7, 24); +CLOCK_COM_DIV(pdmin0, AUD_ADDR_OFFSET(EE_AUDIO_CLK_PDMIN_CTRL0), 0, 16); +CLOCK_COM_GATE(pdmin0, AUD_ADDR_OFFSET(EE_AUDIO_CLK_PDMIN_CTRL0), 31); +/* pdmin1 */ +CLOCK_COM_MUX(pdmin1, AUD_ADDR_OFFSET(EE_AUDIO_CLK_PDMIN_CTRL1), 0x7, 24); +CLOCK_COM_DIV(pdmin1, AUD_ADDR_OFFSET(EE_AUDIO_CLK_PDMIN_CTRL1), 0, 16); +CLOCK_COM_GATE(pdmin1, AUD_ADDR_OFFSET(EE_AUDIO_CLK_PDMIN_CTRL1), 31); + +#define IOMAP_COM_CLK(_name, _iobase) \ +do { \ + _name##_mux.reg += (unsigned long)(iobase); \ + _name##_div.reg += (unsigned long)(iobase); \ + _name##_gate.reg += (unsigned long)(iobase); \ +} while (0) + +#define REGISTER_CLK_COM(_name) \ + clk_register_composite(NULL, #_name, \ + mclk_parent_names, ARRAY_SIZE(mclk_parent_names), \ + &_name##_mux.hw, &clk_mux_ops, \ + &_name##_div.hw, &clk_divider_ops, \ + &_name##_gate.hw, &clk_gate_ops, \ + CLK_SET_RATE_NO_REPARENT) + +static int register_audio_clk(struct clk **clks, void __iomem *iobase) +{ + IOMAP_COM_CLK(mclk_a, iobase); + clks[CLKID_AUDIO_MCLK_A] = REGISTER_CLK_COM(mclk_a); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_MCLK_A])); + + IOMAP_COM_CLK(mclk_b, iobase); + clks[CLKID_AUDIO_MCLK_B] = REGISTER_CLK_COM(mclk_b); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_MCLK_B])); + + IOMAP_COM_CLK(mclk_c, iobase); + clks[CLKID_AUDIO_MCLK_C] = REGISTER_CLK_COM(mclk_c); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_MCLK_C])); + + IOMAP_COM_CLK(mclk_d, iobase); + clks[CLKID_AUDIO_MCLK_D] = REGISTER_CLK_COM(mclk_d); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_MCLK_D])); + + IOMAP_COM_CLK(mclk_e, iobase); + clks[CLKID_AUDIO_MCLK_E] = REGISTER_CLK_COM(mclk_e); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_MCLK_E])); + + IOMAP_COM_CLK(mclk_f, iobase); + clks[CLKID_AUDIO_MCLK_F] = REGISTER_CLK_COM(mclk_f); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_MCLK_F])); + + IOMAP_COM_CLK(spdifin, iobase); + clks[CLKID_AUDIO_SPDIFIN_CTRL] = REGISTER_CLK_COM(spdifin); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_SPDIFIN_CTRL])); + + IOMAP_COM_CLK(spdifout, iobase); + clks[CLKID_AUDIO_SPDIFOUT_CTRL] = REGISTER_CLK_COM(spdifout); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_SPDIFOUT_CTRL])); + + IOMAP_COM_CLK(pdmin0, iobase); + clks[CLKID_AUDIO_PDMIN0] = REGISTER_CLK_COM(pdmin0); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_PDMIN0])); + + IOMAP_COM_CLK(pdmin1, iobase); + clks[CLKID_AUDIO_PDMIN1] = REGISTER_CLK_COM(pdmin1); + WARN_ON(IS_ERR(clks[CLKID_AUDIO_PDMIN1])); + + return 0; +} + +static int aml_audio_clocks_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct clk **clks; + struct clk_onecell_data *clk_data; + void __iomem *clk_base; + int clkid, ret; + + clk_base = of_iomap(np, 0); + if (!clk_base) { + dev_err(dev, "%s: Unable to map clk base\n", __func__); + return -ENXIO; + } + + clk_data = devm_kmalloc(dev, sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clks = devm_kmalloc(dev, NUM_AUDIO_CLKS * sizeof(*clks), GFP_KERNEL); + if (!clks) + return -ENOMEM; + + /* register all audio clks */ + if (ARRAY_SIZE(audio_clk_gates) == MCLK_BASE + && ARRAY_SIZE(audio_clk_gates) == MCLK_BASE) { + dev_info(dev, "%s register audio gate clks\n", __func__); + for (clkid = 0; clkid < MCLK_BASE; clkid++) { + audio_clk_gates[clkid]->reg += (unsigned long)clk_base; + clks[clkid] = clk_register(NULL, audio_clk_hws[clkid]); + WARN_ON(IS_ERR(clks[clkid])); + } + } else { + return -EINVAL; + } + + /* register composite audio clks */ + register_audio_clk(clks, clk_base); + + clk_data->clks = clks; + clk_data->clk_num = NUM_AUDIO_CLKS; + + ret = of_clk_add_provider(np, of_clk_src_onecell_get, + clk_data); + if (ret < 0) + dev_err(dev, "%s fail ret: %d\n", __func__, ret); + + return 0; +} + +static const struct of_device_id amlogic_audio_clocks_of_match[] = { + { .compatible = "amlogic, audio_clocks" }, + {}, +}; + +static struct platform_driver aml_audio_clocks_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = amlogic_audio_clocks_of_match, + }, + .probe = aml_audio_clocks_probe, +}; +module_platform_driver(aml_audio_clocks_driver); + +MODULE_AUTHOR("Amlogic, Inc."); +MODULE_DESCRIPTION("Amlogic audio clocks ASoc driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, amlogic_audio_clocks_of_match); diff --git a/sound/soc/amlogic/auge/ddr_mngr.c b/sound/soc/amlogic/auge/ddr_mngr.c new file mode 100644 index 000000000000..3f4ff751e515 --- /dev/null +++ b/sound/soc/amlogic/auge/ddr_mngr.c @@ -0,0 +1,420 @@ +/* + * sound/soc/amlogic/auge/ddr_mngr.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include "regs.h" +#include "ddr_mngr.h" + +static DEFINE_MUTEX(ddr_mutex); + +struct ddr_desc { + /* start address of DDR */ + unsigned int start; + /* finish address of DDR */ + unsigned int finish; + /* interrupt address or counts of DDR blocks */ + unsigned int intrpt; + /* fifo total counts */ + unsigned int fifo_depth; + /* fifo start threshold */ + unsigned int fifo_thr; + enum ddr_types data_type; + unsigned int edian; + unsigned int pp_mode; + //unsigned int reg_base; + struct clk *ddr; + struct clk *ddr_arb; +}; + +struct toddr { + struct ddr_desc dscrpt; + struct device *dev; + unsigned int resample: 1; + unsigned int ext_signed: 1; + unsigned int msb_bit; + unsigned int lsb_bit; + unsigned int reg_base; + enum toddr_src src; + struct aml_audio_controller *actrl; +}; + +struct frddr { + struct ddr_desc dscrpt; + struct device *dev; + enum frddr_dest dest; + struct aml_audio_controller *actrl; + unsigned int reg_base; +}; + +#define DDRMAX 3 +static struct frddr *frddrs[DDRMAX]; +static struct toddr *toddrs[DDRMAX]; + +/* to DDRS */ +static struct toddr *register_toddr_l(struct device *dev, + struct aml_audio_controller *actrl, enum ddr_num id) +{ + struct toddr *to; + unsigned int mask_bit; + + if (toddrs[id] != NULL) + return NULL; + + to = kzalloc(sizeof(struct toddr), GFP_KERNEL); + if (!to) + return NULL; + + switch (id) { + case DDR_A: + to->reg_base = EE_AUDIO_TODDR_A_CTRL0; + break; + case DDR_B: + to->reg_base = EE_AUDIO_TODDR_B_CTRL0; + break; + case DDR_C: + to->reg_base = EE_AUDIO_TODDR_C_CTRL0; + break; + default: + return NULL; + } + + /* enable audio ddr arb */ + mask_bit = id; + aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL, + 1<<31|1<dev = dev; + to->actrl = actrl; + toddrs[id] = to; + pr_info("toddrs[%d] occupied by device %s\n", id, dev_name(dev)); + return to; +} + +static int unregister_toddr_l(struct device *dev, enum ddr_num id) +{ + struct toddr *to; + struct aml_audio_controller *actrl; + unsigned int mask_bit; + unsigned int value; + + if (dev == NULL) + return -EINVAL; + + to = toddrs[id]; + if (to->dev != dev) + return -EINVAL; + + /* disable audio ddr arb */ + mask_bit = id; + actrl = to->actrl; + aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL, + 1<actrl; + unsigned int reg_base = to->reg_base; + unsigned int reg; + + reg = calc_toddr_address(EE_AUDIO_TODDR_A_START_ADDR, reg_base); + aml_audiobus_write(actrl, reg, start); + reg = calc_toddr_address(EE_AUDIO_TODDR_A_FINISH_ADDR, reg_base); + aml_audiobus_write(actrl, reg, end); + + return 0; +} + +int aml_toddr_set_intrpt(struct toddr *to, unsigned int intrpt) +{ + struct aml_audio_controller *actrl = to->actrl; + unsigned int reg_base = to->reg_base; + unsigned int reg; + + reg = calc_toddr_address(EE_AUDIO_TODDR_A_INT_ADDR, reg_base); + aml_audiobus_write(actrl, reg, intrpt); + reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base); + aml_audiobus_update_bits(actrl, reg, 0xff<<16, 4<<16); + + return 0; +} + +unsigned int aml_toddr_get_position(struct toddr *to) +{ + struct aml_audio_controller *actrl = to->actrl; + unsigned int reg_base = to->reg_base; + unsigned int reg; + + reg = calc_toddr_address(EE_AUDIO_TODDR_A_STATUS2, reg_base); + return aml_audiobus_read(actrl, reg); +} + +void aml_toddr_enable(struct toddr *to, bool enable) +{ + struct aml_audio_controller *actrl = to->actrl; + unsigned int reg_base = to->reg_base; + unsigned int reg; + + reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base); + aml_audiobus_update_bits(actrl, reg, 1<<31, enable<<31); +} + +void aml_toddr_select_src(struct toddr *to, enum toddr_src src) +{ + struct aml_audio_controller *actrl = to->actrl; + unsigned int reg_base = to->reg_base; + unsigned int reg; + + reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base); + aml_audiobus_update_bits(actrl, reg, 0x7, src & 0x7); +} + +void aml_toddr_set_fifos(struct toddr *to, unsigned int thresh) +{ + struct aml_audio_controller *actrl = to->actrl; + unsigned int reg_base = to->reg_base; + unsigned int reg; + + reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base); + aml_audiobus_write(actrl, reg, (thresh-1)<<16|2<<8); +} + +void aml_toddr_set_format(struct toddr *to, + unsigned int type, unsigned int msb, unsigned int lsb) +{ + struct aml_audio_controller *actrl = to->actrl; + unsigned int reg_base = to->reg_base; + unsigned int reg; + + reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base); + aml_audiobus_update_bits(actrl, reg, 0x1fff<<3, + type<<13|msb<<8|lsb<<3); +} + +/* from DDRS */ +static struct frddr *register_frddr_l(struct device *dev, + struct aml_audio_controller *actrl, enum ddr_num id) +{ + struct frddr *from; + unsigned int mask_bit; + + if (frddrs[id] != NULL) + return NULL; + + from = kzalloc(sizeof(struct frddr), GFP_KERNEL); + if (!from) + return NULL; + + switch (id) { + case DDR_A: + from->reg_base = EE_AUDIO_FRDDR_A_CTRL0; + break; + case DDR_B: + from->reg_base = EE_AUDIO_FRDDR_B_CTRL0; + break; + case DDR_C: + from->reg_base = EE_AUDIO_FRDDR_C_CTRL0; + break; + default: + return NULL; + } + + /* enable audio ddr arb */ + mask_bit = id + 4; + aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL, + 1<<31|1<dev = dev; + from->actrl = actrl; + frddrs[id] = from; + pr_info("frddrs[%d] claimed by device %s\n", id, dev_name(dev)); + return from; +} + +static int unregister_frddr_l(struct device *dev, enum ddr_num id) +{ + struct frddr *from; + struct aml_audio_controller *actrl; + unsigned int mask_bit; + unsigned int value; + + if (dev == NULL) + return -EINVAL; + + from = frddrs[id]; + if (from->dev != dev) + return -EINVAL; + + /* disable audio ddr arb */ + mask_bit = id + 4; + actrl = from->actrl; + aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL, + 1<actrl; + unsigned int reg_base = fr->reg_base; + unsigned int reg; + + reg = calc_frddr_address(EE_AUDIO_FRDDR_A_START_ADDR, reg_base); + aml_audiobus_write(actrl, reg, start); + reg = calc_frddr_address(EE_AUDIO_FRDDR_A_FINISH_ADDR, reg_base); + aml_audiobus_write(actrl, reg, end); + + return 0; +} + +int aml_frddr_set_intrpt(struct frddr *fr, unsigned int intrpt) +{ + struct aml_audio_controller *actrl = fr->actrl; + unsigned int reg_base = fr->reg_base; + unsigned int reg; + + reg = calc_frddr_address(EE_AUDIO_FRDDR_A_INT_ADDR, reg_base); + aml_audiobus_write(actrl, reg, intrpt); + reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0, reg_base); + aml_audiobus_update_bits(actrl, reg, 0xff<<16, 4<<16); + + return 0; +} + +unsigned int aml_frddr_get_position(struct frddr *fr) +{ + struct aml_audio_controller *actrl = fr->actrl; + unsigned int reg_base = fr->reg_base; + unsigned int reg; + + reg = calc_frddr_address(EE_AUDIO_FRDDR_A_STATUS2, reg_base); + return aml_audiobus_read(actrl, reg); +} + +void aml_frddr_enable(struct frddr *fr, bool enable) +{ + struct aml_audio_controller *actrl = fr->actrl; + unsigned int reg_base = fr->reg_base; + unsigned int reg; + + reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0, reg_base); + aml_audiobus_update_bits(actrl, reg, 1<<31, enable<<31); +} + +void aml_frddr_select_dst(struct frddr *fr, enum frddr_dest dst) +{ + struct aml_audio_controller *actrl = fr->actrl; + unsigned int reg_base = fr->reg_base; + unsigned int reg; + + reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0, reg_base); + aml_audiobus_update_bits(actrl, reg, 0x7, dst & 0x7); +} + +void aml_frddr_set_fifos(struct frddr *fr, + unsigned int depth, unsigned int thresh) +{ + struct aml_audio_controller *actrl = fr->actrl; + unsigned int reg_base = fr->reg_base; + unsigned int reg; + + reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL1, reg_base); + aml_audiobus_update_bits(actrl, reg, + 0xffff<<16 | 0xf<<8, + (depth - 1)<<24 | (thresh - 1)<<16 | 2<<8); +} + +/* Module information */ +MODULE_AUTHOR("Amlogic, Inc."); +MODULE_DESCRIPTION("ALSA Soc Aml Audio DDR Manager"); +MODULE_LICENSE("GPL v2"); + diff --git a/sound/soc/amlogic/auge/ddr_mngr.h b/sound/soc/amlogic/auge/ddr_mngr.h new file mode 100644 index 000000000000..7927744b8936 --- /dev/null +++ b/sound/soc/amlogic/auge/ddr_mngr.h @@ -0,0 +1,84 @@ +/* + * sound/soc/amlogic/auge/ddr_mngr.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_AUDIO_DDR_MANAGER_H_ +#define __AML_AUDIO_DDR_MANAGER_H_ + +#include +#include "audio_io.h" + +enum ddr_num { + DDR_A, + DDR_B, + DDR_C, +}; + +enum ddr_types { + LJ_8BITS, + LJ_16BITS, + RJ_16BITS, + LJ_32BITS, + RJ_32BITS, +}; + +enum toddr_src { + TDMIN_A, + TDMIN_B, + TDMIN_C, + SPDIFIN, + PDMIN, + NONE, + TDMIN_LB, + LOOPBACK, +}; + +enum frddr_dest { + TDMOUT_A, + TDMOUT_B, + TDMOUT_C, + SPDIFOUT, +}; + +/* to ddrs */ +struct toddr *aml_audio_register_toddr(struct device *dev, + struct aml_audio_controller *actrl, enum ddr_num); +int aml_audio_unregister_toddr(struct device *dev, enum ddr_num); +int aml_toddr_set_buf(struct toddr *to, unsigned int start, + unsigned int end); +int aml_toddr_set_intrpt(struct toddr *to, unsigned int intrpt); +unsigned int aml_toddr_get_position(struct toddr *to); +void aml_toddr_select_src(struct toddr *to, enum toddr_src); +void aml_toddr_enable(struct toddr *to, bool enable); +void aml_toddr_set_fifos(struct toddr *to, unsigned int thresh); +void aml_toddr_set_format(struct toddr *to, + unsigned int type, unsigned int msb, unsigned int lsb); + +/* from ddrs */ +struct frddr *aml_audio_register_frddr(struct device *dev, + struct aml_audio_controller *actrl, enum ddr_num); +int aml_audio_unregister_frddr(struct device *dev, enum ddr_num); +int aml_frddr_set_buf(struct frddr *fr, unsigned int start, + unsigned int end); +int aml_frddr_set_intrpt(struct frddr *fr, unsigned int intrpt); +unsigned int aml_frddr_get_position(struct frddr *fr); +void aml_frddr_enable(struct frddr *fr, bool enable); +void aml_frddr_select_dst(struct frddr *fr, enum frddr_dest); +void aml_frddr_set_fifos(struct frddr *fr, + unsigned int depth, unsigned int thresh); + +#endif + diff --git a/sound/soc/amlogic/auge/iomap.c b/sound/soc/amlogic/auge/iomap.c new file mode 100644 index 000000000000..a457ba428f02 --- /dev/null +++ b/sound/soc/amlogic/auge/iomap.c @@ -0,0 +1,145 @@ +/* + * sound/soc/amlogic/auge/iomap.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include + +#include "iomap.h" + +static void __iomem *aml_snd_reg_map[IO_MAX]; + + +static int aml_snd_read(u32 base_type, unsigned int reg, unsigned int *val) +{ + if ((base_type >= IO_PDM_BUS) && (base_type < IO_MAX)) { + *val = readl((aml_snd_reg_map[base_type] + (reg << 2))); + + return 0; + } + + return -1; +} + +static void aml_snd_write(u32 base_type, unsigned int reg, unsigned int val) +{ + + if ((base_type >= IO_PDM_BUS) && (base_type < IO_MAX)) { + writel(val, (aml_snd_reg_map[base_type] + (reg << 2))); + + return; + } + + pr_err("write snd reg %x error\n", reg); +} + +static void aml_snd_update_bits(u32 base_type, + unsigned int reg, unsigned int mask, + unsigned int val) +{ + if ((base_type >= IO_PDM_BUS) && (base_type < IO_MAX)) { + unsigned int tmp, orig; + + aml_snd_read(base_type, reg, &orig); + tmp = orig & ~mask; + tmp |= val & mask; + aml_snd_write(base_type, reg, tmp); + + return; + } + pr_err("write snd reg %x error\n", reg); + +} + +int aml_pdm_read(unsigned int reg) +{ + int ret, val = 0; + + ret = aml_snd_read(IO_PDM_BUS, reg, &val); + + if (ret) { + pr_err("read pdm reg %x error %d\n", reg, ret); + return -1; + } + return val; +} +EXPORT_SYMBOL(aml_pdm_read); + +void aml_pdm_write(unsigned int reg, unsigned int val) +{ + aml_snd_write(IO_PDM_BUS, reg, val); +} +EXPORT_SYMBOL(aml_pdm_write); + +void aml_pdm_update_bits(unsigned int reg, + unsigned int mask, unsigned int val) +{ + aml_snd_update_bits(IO_PDM_BUS, reg, mask, val); +} +EXPORT_SYMBOL(aml_pdm_update_bits); + +static int snd_iomap_probe(struct platform_device *pdev) +{ + struct resource res; + struct device_node *np, *child; + int i = 0; + int ret = 0; + + np = pdev->dev.of_node; + for_each_child_of_node(np, child) { + if (of_address_to_resource(child, 0, &res)) { + ret = -1; + pr_err("%s could not get resource", + __func__); + break; + } + aml_snd_reg_map[i] = + ioremap_nocache(res.start, resource_size(&res)); + pr_info("aml_snd_reg_map[%d], reg:%x, size:%x\n", + i, (u32)res.start, (u32)resource_size(&res)); + + i++; + } + pr_info("aml snd iomap probe done\n"); + + return ret; +} + +static const struct of_device_id snd_iomap_dt_match[] = { + { .compatible = "amlogic, snd_iomap" }, + {}, +}; + +static struct platform_driver snd_iomap_platform_driver = { + .probe = snd_iomap_probe, + .driver = { + .owner = THIS_MODULE, + .name = "snd_iomap", + .of_match_table = snd_iomap_dt_match, + }, +}; + +int __init meson_snd_iomap_init(void) +{ + int ret; + + ret = platform_driver_register(&snd_iomap_platform_driver); + + return ret; +} +core_initcall(meson_snd_iomap_init); diff --git a/sound/soc/amlogic/auge/iomap.h b/sound/soc/amlogic/auge/iomap.h new file mode 100644 index 000000000000..4d5bdd7b3832 --- /dev/null +++ b/sound/soc/amlogic/auge/iomap.h @@ -0,0 +1,35 @@ +/* + * sound/soc/amlogic/auge/iomap.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_SND_IOMAP_H__ +#define __AML_SND_IOMAP_H__ + +enum{ + IO_PDM_BUS = 0, + IO_AUDIO_BUS, + + IO_MAX, +}; + +extern int aml_pdm_read(unsigned int reg); + +extern void aml_pdm_write(unsigned int reg, unsigned int val); + +extern void aml_pdm_update_bits(unsigned int reg, + unsigned int mask, unsigned int val); + +#endif diff --git a/sound/soc/amlogic/auge/pdm.c b/sound/soc/amlogic/auge/pdm.c new file mode 100644 index 000000000000..6a6d863946c7 --- /dev/null +++ b/sound/soc/amlogic/auge/pdm.c @@ -0,0 +1,826 @@ +/* + * sound/soc/amlogic/auge/pdm.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pdm.h" +#include "pdm_hw.h" +#include "audio_io.h" +#include "iomap.h" +#include "regs.h" +#include "ddr_mngr.h" + +static struct snd_pcm_hardware aml_pdm_hardware = { + .info = + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + + .rate_min = 8000, + .rate_max = 48000, + + .channels_min = 1, + .channels_max = 8, + + .buffer_bytes_max = 32 * 1024, + .period_bytes_max = 16 * 1024, + .period_bytes_min = 32, + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +static unsigned int period_sizes[] = { + 64, 128, 256, 512, 1024, 2048, 4096, + 8192, 16384, 32768, 65536, 65536 * 2, 65536 * 4 +}; + +static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = { + .count = ARRAY_SIZE(period_sizes), + .list = period_sizes, + .mask = 0 +}; + +static int s_pdm_filter_mode; + +static const char *const pdm_filter_mode_texts[] = { + "Filter Mode 0", + "Filter Mode 1", + "Filter Mode 2", + "Filter Mode 3", + "Filter Mode 4" +}; + +static const struct soc_enum pdm_filter_mode_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(pdm_filter_mode_texts), + pdm_filter_mode_texts); + +static int aml_pdm_filter_mode_get_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = s_pdm_filter_mode; + + return 0; +} + +static int aml_pdm_filter_mode_set_enum( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + s_pdm_filter_mode = ucontrol->value.enumerated.item[0]; + + return 0; +} + +static int aml_pdm_cntrl_get_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct soc_mixer_control *mixcntrl = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mixcntrl->reg; + unsigned int shift = mixcntrl->shift; + unsigned int max = mixcntrl->max; + unsigned int invert = mixcntrl->invert; + unsigned int value = (((unsigned int) + aml_pdm_read(reg)) + >> shift) & max; + + if (invert) + value = (~value) & max; + ucontrol->value.integer.value[0] = value; + + return 0; +} + +static int aml_pdm_cntrl_set_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct soc_mixer_control *mixcntrl = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mixcntrl->reg; + unsigned int shift = mixcntrl->shift; + unsigned int max = mixcntrl->max; + unsigned int invert = mixcntrl->invert; + unsigned int value = ucontrol->value.integer.value[0]; + unsigned int reg_value = (unsigned int) + aml_pdm_read(reg); + + if (invert) + value = (~value) & mixcntrl->max; + max = ~(max << shift); + reg_value &= max; + reg_value |= (value << shift); + aml_pdm_write(reg, reg_value); + + return 0; +} + +static const struct snd_kcontrol_new snd_pdm_controls[] = { + /* which set */ + SOC_ENUM_EXT("PDM Filter Mode", + pdm_filter_mode_enum, + aml_pdm_filter_mode_get_enum, + aml_pdm_filter_mode_set_enum), + + /* hcis gain controls */ + SOC_SINGLE_EXT("HCIC shift gain", + PDM_HCIC_CTRL1, 24, 0x3F, 0, + aml_pdm_cntrl_get_reg, + aml_pdm_cntrl_set_reg + ), +}; + +static irqreturn_t aml_pdm_isr_handler(int irq, void *data) +{ + struct snd_pcm_substream *substream = + (struct snd_pcm_substream *)data; + + pr_debug("%s\n", __func__); + + snd_pcm_period_elapsed(substream); + + return IRQ_HANDLED; +} + +static int aml_pdm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; + struct aml_pdm *p_pdm = (struct aml_pdm *) + dev_get_drvdata(dev); + int ret; + + pr_info("%s, stream:%d, irq :%d\n", + __func__, substream->stream, p_pdm->irq_pdmin); + + snd_soc_set_runtime_hwparams(substream, &aml_pdm_hardware); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + /* Ensure that period size is a multiple of 32bytes */ + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_sizes); + if (ret < 0) { + dev_err(substream->pcm->card->dev, + "%s() setting constraints failed: %d\n", + __func__, ret); + return -EINVAL; + } + + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(substream->pcm->card->dev, + "%s() setting constraints failed: %d\n", + __func__, ret); + return -EINVAL; + } + + runtime->private_data = p_pdm; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + + p_pdm->tddr = aml_audio_register_toddr + (dev, p_pdm->actrl, p_pdm->to_ddr_num); + if (p_pdm->tddr == NULL) { + dev_err(dev, "failed to claim to ddr %u\n", + p_pdm->to_ddr_num); + return -ENXIO; + } + + ret = request_irq(p_pdm->irq_pdmin, + aml_pdm_isr_handler, + IRQF_SHARED, + "pdmin_irq", + substream); + + if (ret) { + aml_audio_unregister_toddr(p_pdm->dev, + p_pdm->to_ddr_num); + dev_err(p_pdm->dev, + "failed to claim irq %u\n", + p_pdm->irq_pdmin); + return ret; + } + } + + return 0; +} + +static int aml_pdm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; + struct aml_pdm *p_pdm = (struct aml_pdm *)dev_get_drvdata(dev); + + pr_info("enter %s type: %d, irq:%d\n", + __func__, substream->stream, p_pdm->irq_pdmin); + + aml_audio_unregister_toddr(p_pdm->dev, p_pdm->to_ddr_num); + free_irq(p_pdm->irq_pdmin, substream); + + return 0; +} + + +static int aml_pdm_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + pr_info("enter %s\n", __func__); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = params_buffer_bytes(params); + memset(runtime->dma_area, 0, runtime->dma_bytes); + + return ret; +} + +static int aml_pdm_hw_free(struct snd_pcm_substream *substream) +{ + pr_info("%s\n", __func__); + snd_pcm_lib_free_pages(substream); + + return 0; +} + +static int aml_pdm_prepare( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_pdm *p_pdm = runtime->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + struct toddr *to = p_pdm->tddr; + unsigned int start_addr, end_addr, int_addr; + + start_addr = runtime->dma_addr; + end_addr = start_addr + runtime->dma_bytes - 8; + int_addr = frames_to_bytes(runtime, runtime->period_size) / 8; + + aml_toddr_set_buf(to, start_addr, end_addr); + aml_toddr_set_intrpt(to, int_addr); + } + + return 0; +} + +static int aml_pdm_trigger( + struct snd_pcm_substream *substream, int cmd) +{ + return 0; +} + +static snd_pcm_uframes_t aml_pdm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_pdm *p_pdm = runtime->private_data; + unsigned int addr = 0, start_addr = 0; + + start_addr = runtime->dma_addr; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + addr = aml_toddr_get_position(p_pdm->tddr); + + return bytes_to_frames(runtime, addr - start_addr); +} + +static int aml_pdm_mmap( + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_coherent(substream->pcm->card->dev, vma, + runtime->dma_area, runtime->dma_addr, + runtime->dma_bytes); +} + +static int aml_pdm_silence( + struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned char *ppos = NULL; + ssize_t n; + + pr_info("%s\n", __func__); + + n = frames_to_bytes(runtime, count); + ppos = runtime->dma_area + frames_to_bytes(runtime, pos); + memset(ppos, 0, n); + + return 0; +} + +static int aml_pdm_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) +{ + struct snd_pcm *pcm = soc_runtime->pcm; + struct snd_pcm_substream *substream; + struct snd_soc_dai *dai = soc_runtime->cpu_dai; + int size = aml_pdm_hardware.buffer_bytes_max; + int ret = -EINVAL; + + pr_info("%s dai->name: %s dai->id: %d\n", + __func__, dai->name, dai->id); + + /* only capture for PDM */ + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + soc_runtime->platform->dev, + size, &substream->dma_buffer); + if (ret) { + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + return ret; + } + } + + return 0; +} + +static void aml_pdm_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + + pr_info("%s\n", __func__); + + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) { + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } +} + +static struct snd_pcm_ops aml_pdm_ops = { + .open = aml_pdm_open, + .close = aml_pdm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = aml_pdm_hw_params, + .hw_free = aml_pdm_hw_free, + .prepare = aml_pdm_prepare, + .trigger = aml_pdm_trigger, + .pointer = aml_pdm_pointer, + .mmap = aml_pdm_mmap, + .silence = aml_pdm_silence, +}; + +static int aml_pdm_probe(struct snd_soc_platform *platform) +{ + pr_info("%s\n", __func__); + + return 0; +} + +struct snd_soc_platform_driver aml_soc_platform_pdm = { + .pcm_new = aml_pdm_pcm_new, + .pcm_free = aml_pdm_pcm_free, + .ops = &aml_pdm_ops, + .probe = aml_pdm_probe, +}; +EXPORT_SYMBOL_GPL(aml_soc_platform_pdm); + +static int aml_pdm_dai_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int aml_pdm_dai_set_fmt( + struct snd_soc_dai *dai, unsigned int fmt) +{ + return 0; +} + + +static int aml_pdm_dai_prepare( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct aml_pdm *p_pdm = snd_soc_dai_get_drvdata(dai); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int bitwidth; + unsigned int toddr_type, lsb; + + /* set bclk */ + bitwidth = snd_pcm_format_width(runtime->format); + lsb = 32 - bitwidth; + + switch (bitwidth) { + case 16: + toddr_type = 2; + break; + case 24: + toddr_type = 3; + break; + case 32: + toddr_type = 4; + break; + default: + pr_err("invalid bit_depth: %d\n", + bitwidth); + return -1; + } + + pr_info("%s rate:%d, bits:%d, channels:%d\n", + __func__, + runtime->rate, + bitwidth, + runtime->channels); + + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + struct toddr *to = p_pdm->tddr; + unsigned int osr = 192; + + /* to ddr pdmin */ + aml_toddr_select_src(to, PDMIN); + aml_toddr_set_format(to, toddr_type, 31, lsb); + aml_toddr_set_fifos(to, 0x40); + + aml_pdm_ctrl(p_pdm->actrl, + bitwidth, runtime->channels); + + /* filter for pdm */ + if (runtime->rate == 48000) + osr = 64; + else if (runtime->rate == 32000) + osr = 96; + else if (runtime->rate == 16000) + osr = 192; + else if (runtime->rate == 8000) + osr = 384; + else + pr_err("Not support rate:%d\n", runtime->rate); + + p_pdm->filter_mode = s_pdm_filter_mode; + aml_pdm_filter_ctrl(osr, p_pdm->filter_mode); + } + + pr_info("%s\n", __func__); + + return 0; +} + +static int aml_pdm_dai_trigger( + struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct aml_pdm *p_pdm = snd_soc_dai_get_drvdata(dai); + struct aml_audio_controller *actrl = p_pdm->actrl; + + pr_info("%s\n", __func__); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* TODO */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + aml_pdm_set_clk(actrl, 1); + aml_pdm_enable(actrl, true); + } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dev_info(substream->pcm->card->dev, "pdm capture enable\n"); + aml_toddr_enable(p_pdm->tddr, 1); + } + + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /* do not disable pdm clk */ + /*aml_pdm_set_clk(actrl, 0);*/ + + aml_pdm_enable(actrl, false); + } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dev_info(substream->pcm->card->dev, "pdm capture enable\n"); + aml_toddr_enable(p_pdm->tddr, 0); + } + + break; + default: + return -EINVAL; + } + +return 0; +} + + +static int aml_pdm_dai_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aml_pdm *p_pdm = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int pll_freq = 0; + + pll_freq = freq * 50; + if (pll_freq > 196608000) + pll_freq = 196608000; + + pr_info("%s irq:%d freq:%d, pll_freq:%d\n", + __func__, p_pdm->irq_pdmin, freq, pll_freq); + + clk_set_rate(p_pdm->clk_pll, pll_freq); + clk_set_rate(p_pdm->clk_pdm_sysclk, pll_freq); + clk_set_rate(p_pdm->clk_pdm_dclk, pll_freq / 64); + + return 0; +} + +static int aml_pdm_dai_set_bclk_ratio(struct snd_soc_dai *cpu_dai, + unsigned int ratio) +{ + /* struct aml_pdm *p_pdm = snd_soc_dai_get_drvdata(cpu_dai); + * + * pr_info("%s ratio:%d\n", __func__, ratio); + * aml_pdm_set_bclk_ratio(p_pdm->actrl, ratio); + */ + + return 0; +} + +static int aml_pdm_dai_probe(struct snd_soc_dai *dai) +{ + int ret = 0; + + ret = snd_soc_add_dai_controls(dai, snd_pdm_controls, + ARRAY_SIZE(snd_pdm_controls)); + if (ret < 0) { + pr_err("%s, failed add snd pdm controls\n", __func__); + return ret; + } + + pr_info("%s\n", __func__); + + return 0; +} + +static struct snd_soc_dai_ops aml_pdm_dai_ops = { + .set_fmt = aml_pdm_dai_set_fmt, + .hw_params = aml_pdm_dai_hw_params, + .prepare = aml_pdm_dai_prepare, + .trigger = aml_pdm_dai_trigger, + .set_sysclk = aml_pdm_dai_set_sysclk, + .set_bclk_ratio = aml_pdm_dai_set_bclk_ratio, +}; + +struct snd_soc_dai_driver aml_pdm_dai[] = { + { + .name = "PDM", + .capture = { + .channels_min = PDM_CHANNELS_MIN, + .channels_max = PDM_CHANNELS_MAX, + .rates = PDM_RATES, + .formats = PDM_FORMATS, + }, + .probe = aml_pdm_dai_probe, + .ops = &aml_pdm_dai_ops, + }, +}; +EXPORT_SYMBOL_GPL(aml_pdm_dai); + +static const struct snd_soc_component_driver aml_pdm_component = { + .name = DRV_NAME, +}; + +static int aml_pdm_platform_probe(struct platform_device *pdev) +{ + struct aml_pdm *p_pdm; + struct device_node *node = pdev->dev.of_node; + struct device_node *node_prt = NULL; + struct platform_device *pdev_parent; + struct aml_audio_controller *actrl = NULL; + struct device *dev = &pdev->dev; + + int ret; + + p_pdm = devm_kzalloc(&pdev->dev, + sizeof(struct aml_pdm), + GFP_KERNEL); + if (!p_pdm) { + /*dev_err(&pdev->dev, "Can't allocate pcm_p\n");*/ + ret = -ENOMEM; + goto err; + } + + /* get audio controller */ + node_prt = of_get_parent(node); + if (node_prt == NULL) + return -ENXIO; + + pdev_parent = of_find_device_by_node(node_prt); + of_node_put(node_prt); + actrl = (struct aml_audio_controller *) + platform_get_drvdata(pdev_parent); + p_pdm->actrl = actrl; + + /* parse DTS configured ddr */ + ret = of_property_read_u32(node, "to_ddr", + &p_pdm->to_ddr_num); + if (ret < 0) { + dev_err(&pdev->dev, "Can't retrieve tdm_to_ddr\n"); + return -ENXIO; + } + + /* clock gate */ + p_pdm->clk_gate = devm_clk_get(&pdev->dev, "gate"); + if (IS_ERR(p_pdm->clk_gate)) { + dev_err(&pdev->dev, + "Can't get pdm gate\n"); + return PTR_ERR(p_pdm->clk_gate); + } + clk_prepare_enable(p_pdm->clk_gate); + + /* pinmux */ + p_pdm->pdm_pins = devm_pinctrl_get_select(&pdev->dev, "pdm_pins"); + if (IS_ERR(p_pdm->pdm_pins)) { + p_pdm->pdm_pins = NULL; + dev_err(&pdev->dev, + "Can't get pdm pinmux\n"); + return PTR_ERR(p_pdm->pdm_pins); + } + + /* irq */ + p_pdm->irq_pdmin = platform_get_irq_byname(pdev, "pdmin_irq"); + if (p_pdm->irq_pdmin < 0) { + dev_err(&pdev->dev, + "Can't get pdmin irq number\n"); + return -EINVAL; + } + + p_pdm->clk_pll = devm_clk_get(&pdev->dev, "pll_clk"); + if (IS_ERR(p_pdm->clk_pll)) { + dev_err(&pdev->dev, + "Can't retrieve pll clock\n"); + ret = PTR_ERR(p_pdm->clk_pll); + goto err; + } + + p_pdm->clk_pdm_sysclk = devm_clk_get(&pdev->dev, "pdm_sysclk"); + if (IS_ERR(p_pdm->clk_pdm_sysclk)) { + dev_err(&pdev->dev, + "Can't retrieve clk_pdm_sysclk clock\n"); + ret = PTR_ERR(p_pdm->clk_pdm_sysclk); + goto err; + } + + p_pdm->clk_pdm_dclk = devm_clk_get(&pdev->dev, "pdm_dclk"); + if (IS_ERR(p_pdm->clk_pdm_dclk)) { + dev_err(&pdev->dev, + "Can't retrieve clk_pdm_dclk clock\n"); + ret = PTR_ERR(p_pdm->clk_pdm_dclk); + goto err; + } + + ret = clk_set_parent(p_pdm->clk_pdm_sysclk, p_pdm->clk_pll); + if (ret) { + dev_err(&pdev->dev, + "Can't set clk_pdm_sysclk parent clock\n"); + ret = PTR_ERR(p_pdm->clk_pdm_sysclk); + goto err; + } + + ret = clk_set_parent(p_pdm->clk_pdm_dclk, p_pdm->clk_pll); + if (ret) { + dev_err(&pdev->dev, + "Can't set clk_pdm_dclk parent clock\n"); + ret = PTR_ERR(p_pdm->clk_pdm_dclk); + goto err; + } + + /* enable clock */ + ret = clk_prepare_enable(p_pdm->clk_pll); + if (ret) { + dev_err(&pdev->dev, + "Can't enable pcm clk_pll clock: %d\n", ret); + goto err; + } + + ret = clk_prepare_enable(p_pdm->clk_pdm_sysclk); + if (ret) { + dev_err(&pdev->dev, + "Can't enable pcm clk_pdm_sysclk clock: %d\n", ret); + goto err; + } + + ret = clk_prepare_enable(p_pdm->clk_pdm_dclk); + if (ret) { + dev_err(&pdev->dev, + "Can't enable pcm clk_pdm_dclk clock: %d\n", ret); + goto err; + } + + ret = of_property_read_u32(node, "filter_mode", + &p_pdm->filter_mode); + if (ret < 0) { + /* defulat set 1 */ + p_pdm->filter_mode = 1; + } + s_pdm_filter_mode = p_pdm->filter_mode; + pr_info("%s pdm filter mode from dts:%d\n", + __func__, p_pdm->filter_mode); + + p_pdm->dev = dev; + dev_set_drvdata(&pdev->dev, p_pdm); + + /*config ddr arb */ + aml_pdm_arb_config(p_pdm->actrl); + + ret = snd_soc_register_component(&pdev->dev, + &aml_pdm_component, + aml_pdm_dai, + ARRAY_SIZE(aml_pdm_dai)); + + if (ret) { + dev_err(&pdev->dev, + "snd_soc_register_component failed\n"); + goto err; + } + + pr_info("%s, register soc platform\n", __func__); + + return snd_soc_register_platform(&pdev->dev, &aml_soc_platform_pdm); + +err: + return ret; + +} + +static int aml_pdm_platform_remove(struct platform_device *pdev) +{ + struct aml_pdm *pdm_priv = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(pdm_priv->clk_pll); + clk_disable_unprepare(pdm_priv->clk_pdm_dclk); + + snd_soc_unregister_component(&pdev->dev); + + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static const struct of_device_id aml_pdm_device_id[] = { + { .compatible = "amlogic, snd-pdm" }, + {} +}; +MODULE_DEVICE_TABLE(of, aml_pdm_device_id); + +struct platform_driver aml_pdm_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aml_pdm_device_id), + }, + .probe = aml_pdm_platform_probe, + .remove = aml_pdm_platform_remove, +}; +module_platform_driver(aml_pdm_driver); + + +MODULE_AUTHOR("AMLogic, Inc."); +MODULE_DESCRIPTION("Amlogic PDM ASoc driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amlogic/auge/pdm.h b/sound/soc/amlogic/auge/pdm.h new file mode 100644 index 000000000000..fc5963516404 --- /dev/null +++ b/sound/soc/amlogic/auge/pdm.h @@ -0,0 +1,58 @@ +/* + * sound/soc/amlogic/auge/pdm.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_PDM_H__ +#define __AML_PDM_H__ + +#include +#include + + +#define DRV_NAME "snd_pdm" + +#define DEFAULT_FS_RATIO 256 + +#define PDM_CHANNELS_MIN 1 +#define PDM_CHANNELS_MAX 8 + +#define PDM_RATES SNDRV_PCM_RATE_8000_48000 +#define PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +#if 1 +struct aml_pdm { + struct device *dev; + struct aml_audio_controller *actrl; + struct pinctrl *pdm_pins; + struct clk *clk_gate; + struct clk *clk_pll; + struct clk *clk_pdm_sysclk; + struct clk *clk_pdm_dclk; + int irq_pdmin; + unsigned int to_ddr_num; + struct toddr *tddr; + /* + * filter mode:0~4, + * from mode 0 to 4, the performance is from high to low, + * the group delay (latency) is from high to low. + */ + int filter_mode; +}; +#endif + +#endif /*__AML_PDM_H__*/ diff --git a/sound/soc/amlogic/auge/pdm_hw.c b/sound/soc/amlogic/auge/pdm_hw.c new file mode 100644 index 000000000000..9bf4ab82e351 --- /dev/null +++ b/sound/soc/amlogic/auge/pdm_hw.c @@ -0,0 +1,410 @@ +/* + * sound/soc/amlogic/auge/pdm_hw.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include + +#include "pdm_hw.h" +#include "regs.h" +#include "iomap.h" +#include "pdm_hw_coeff.c" + +void aml_pdm_enable( + struct aml_audio_controller *actrl, + bool is_enalbe) +{ + aml_pdm_update_bits(PDM_CTRL, 1 << 31, is_enalbe << 31); +} + +void aml_pdm_ctrl( + struct aml_audio_controller *actrl, + int bitdepth, int channels) +{ + int mode, i, ch_mask = 0, sample_count = 28; + + if (bitdepth == 32) + mode = 0; + else + mode = 1; + + for (i = 0; i < channels; i++) + ch_mask |= (1 << i); + + pr_info("%s, channels mask:%x\n", __func__, ch_mask); + + aml_pdm_write(PDM_CTRL, 0); + + aml_pdm_write(PDM_CTRL, + (0 << 31) | + /* invert the PDM_DCLK or not */ + (0 << 30) | + /* output mode: 1: 24bits. 0: 32 bits */ + (mode << 29) | + /* bypass mode. + * 1: bypass all filter. 0: normal mode. + */ + (0 << 28) | + /* PDM Asynchronous FIFO soft reset. + * write 1 to soft reset AFIFO + */ + (1 << 16) | + /* PDM channel reset. */ + (ch_mask << 8) | + /* PDM channel enable */ + (ch_mask << 0) + ); + + aml_pdm_write(PDM_CHAN_CTRL, ((sample_count << 24) | + (sample_count << 16) | + (sample_count << 8) | + (sample_count << 0) + )); + aml_pdm_write(PDM_CHAN_CTRL1, ((sample_count << 24) | + (sample_count << 16) | + (sample_count << 8) | + (sample_count << 0) + )); +} + +void aml_pdm_arb_config(struct aml_audio_controller *actrl) +{ + /* config ddr arb */ + aml_audiobus_write(actrl, EE_AUDIO_ARB_CTRL, 1<<31|0xff<<0); +} + +void aml_pdm_set_clk( + struct aml_audio_controller *actrl, + int is_enable) +{ + return; + aml_audiobus_update_bits(actrl, + EE_AUDIO_CLK_PDMIN_CTRL0, 1 << 31, is_enable << 31); + aml_audiobus_update_bits(actrl, + EE_AUDIO_CLK_PDMIN_CTRL1, 1 << 31, is_enable << 31); +} + +void aml_pdm_set_bclk_ratio( + struct aml_audio_controller *actrl, + int ratio) +{ + unsigned int clk_id; + unsigned int mul; + unsigned int sample_count = ratio / 2; + + pr_info("%s, ratio:%d, count:%d\n", __func__, ratio, sample_count); + + clk_id = 2; /* according to dts, mpll2 */ + + /* sysclk */ + mul = 1; + aml_audiobus_write(actrl, EE_AUDIO_CLK_PDMIN_CTRL1, + 1 << 31 | /* clk enable */ + clk_id << 24 | /* clk src */ + (mul - 1)/* clk_div */ + ); + /* dclk */ + mul = ratio; + aml_audiobus_write(actrl, EE_AUDIO_CLK_PDMIN_CTRL0, + 1 << 31 | /* clk enable */ + clk_id << 24 | /* clk src */ + (mul - 1)); /* clk_div */ + + +} + + +/* config for hcic, lpf1,2,3, hpf */ +static void aml_pdm_filters_config(int osr, + int lpf1_len, int lpf2_len, int lpf3_len) +{ + int32_t hcic_dn_rate; + int32_t hcic_tap_num; + int32_t hcic_gain; + int32_t hcic_shift; + int32_t f1_tap_num; + int32_t f2_tap_num; + int32_t f3_tap_num; + int32_t f1_rnd_mode; + int32_t f2_rnd_mode; + int32_t f3_rnd_mode; + int32_t f1_ds_rate; + int32_t f2_ds_rate; + int32_t f3_ds_rate; + int32_t hpf_en; + int32_t hpf_shift_step; + int32_t hpf_out_factor; + int32_t pdm_out_mode; + + /* current Dclk: 3072000*/ + switch (osr) { + case 64: + hcic_dn_rate = 0x0008; + hcic_gain = 0x80; + hcic_shift = 0x15; + break; + case 96: + hcic_dn_rate = 0x000c; + hcic_gain = 0x78; + hcic_shift = 0x19; + break; + case 128: + hcic_dn_rate = 0x0010; + hcic_gain = 0x80; + hcic_shift = 0x1c; + break; + case 192: + hcic_dn_rate = 0x0018; + hcic_gain = 0x78; + hcic_shift = 0x20; + break; + case 384: + hcic_dn_rate = 0x0030; + hcic_gain = 0x78; + hcic_shift = 0x27; + break; + default: + pr_info("Not support osr:%d, translate to :192\n", osr); + hcic_dn_rate = 0x0018; + hcic_gain = 0x78; + hcic_shift = 0x20; + break; + } + + /* TODO: fixed hcic_shift 'cause of Dmic */ +#if 0 + hcic_shift -= 0x4; +#endif + + hcic_tap_num = 0x0007; + f1_tap_num = lpf1_len; + f2_tap_num = lpf2_len; + f3_tap_num = lpf3_len; + hpf_shift_step = 0xd; + hpf_en = 1; + pdm_out_mode = 0; + hpf_out_factor = 0x8000; + f1_rnd_mode = 1; + f2_rnd_mode = 0; + f3_rnd_mode = 1; + f1_ds_rate = 2; + f2_ds_rate = 2; + f3_ds_rate = 2; + + /* hcic */ + aml_pdm_write(PDM_HCIC_CTRL1, + (0x80000000 | + hcic_tap_num | + (hcic_dn_rate << 4) | + (hcic_gain << 16) | + (hcic_shift << 24)) + ); + + /* lpf */ + aml_pdm_write(PDM_F1_CTRL, + (0x80000000 | + f1_tap_num | + (2 << 12) | + (f1_rnd_mode << 16)) + ); + aml_pdm_write(PDM_F2_CTRL, + (0x80000000 | + f2_tap_num | + (2 << 12) | + (f2_rnd_mode << 16)) + ); + aml_pdm_write(PDM_F3_CTRL, + (0x80000000 | + f3_tap_num | + (2 << 12) | + (f3_rnd_mode << 16)) + ); + + /* hpf */ + aml_pdm_write(PDM_HPF_CTRL, + (hpf_out_factor | + (hpf_shift_step << 16) | + (hpf_en << 31)) + ); + +} + +/* coefficent for LPF1,2,3 */ +static void aml_pdm_LPF_coeff( + int lpf1_len, const int *lpf1_coeff, + int lpf2_len, const int *lpf2_coeff, + int lpf3_len, const int *lpf3_coeff) +{ + int i; + int32_t data; + int32_t data_tmp; + + aml_pdm_write(PDM_COEFF_ADDR, 0); + for (i = 0; + i < lpf1_len; + i++) + aml_pdm_write(PDM_COEFF_DATA, lpf1_coeff[i]); + for (i = 0; + i < lpf2_len; + i++) + aml_pdm_write(PDM_COEFF_DATA, lpf2_coeff[i]); + for (i = 0; + i < lpf3_len; + i++) + aml_pdm_write(PDM_COEFF_DATA, lpf3_coeff[i]); + + aml_pdm_write(PDM_COEFF_ADDR, 0); + for (i = 0; i < lpf1_len; i++) { + data = aml_pdm_read(PDM_COEFF_DATA); + data_tmp = lpf1_coeff[i]; + if (data != data_tmp) { + pr_info("FAILED coeff data verified wrong!\n"); + pr_info("Coeff = %x\n", data); + pr_info("DDR COEFF = %x\n", data_tmp); + } + } + for (i = 0; i < lpf2_len; i++) { + data = aml_pdm_read(PDM_COEFF_DATA); + data_tmp = lpf2_coeff[i]; + if (data != data_tmp) { + pr_info("FAILED coeff data verified wrong!\n"); + pr_info("Coeff = %x\n", data); + pr_info("DDR COEFF = %x\n", data_tmp); + } + } + for (i = 0; i < lpf3_len; i++) { + data = aml_pdm_read(PDM_COEFF_DATA); + data_tmp = lpf3_coeff[i]; + if (data != data_tmp) { + pr_info("FAILED coeff data verified wrong!\n"); + pr_info("Coeff = %x\n", data); + pr_info("DDR COEFF = %x\n", data_tmp); + } + } + +} + +void aml_pdm_filter_ctrl(int osr, int mode) +{ + int lpf1_len, lpf2_len, lpf3_len; + const int *lpf1_coeff, *lpf2_coeff, *lpf3_coeff; + + pr_info("%s, osr:%d, mode:%d\n", + __func__, osr, mode); + + /* select LPF coefficent + * For filter 1 and filter 3, + * it's only relative with coefficent mode + * For filter 2, + * it's only relative with osr and hcic stage number + */ + + /* mode and its latency + * mode | latency + * 0 | 47.7 + * 1 | 38.7 + * 2 | 26 + * 3 | 20.4 + * 4 | 15 + */ + + switch (osr) { + case 64: + lpf2_coeff = lpf2_osr64; + break; + case 96: + lpf2_coeff = lpf2_osr96; + break; + case 128: + lpf2_coeff = lpf2_osr128; + break; + case 192: + lpf2_coeff = lpf2_osr192; + break; + case 256: + lpf2_coeff = lpf2_osr256; + break; + case 384: + lpf2_coeff = lpf2_osr384; + break; + default: + pr_info("default mode 1, osr 64, 48k\n"); + lpf2_coeff = lpf2_osr64; + break; + } + + switch (mode) { + case 0: + lpf1_len = 110; + lpf2_len = 33; + lpf3_len = 147; + lpf1_coeff = lpf1_mode0; + lpf3_coeff = lpf3_mode0; + break; + case 1: + lpf1_len = 87; + lpf2_len = 33; + lpf3_len = 117; + lpf1_coeff = lpf1_mode1; + lpf3_coeff = lpf3_mode1; + break; + case 2: + lpf1_len = 87; + lpf2_len = 33; + lpf3_len = 66; + lpf1_coeff = lpf1_mode2; + lpf3_coeff = lpf3_mode2; + break; + case 3: + lpf1_len = 65; + lpf2_len = 33; + lpf3_len = 49; + lpf1_coeff = lpf1_mode3; + lpf3_coeff = lpf3_mode3; + break; + case 4: + lpf1_len = 43; + lpf2_len = 33; + lpf3_len = 32; + lpf1_coeff = lpf1_mode4; + lpf3_coeff = lpf3_mode4; + break; + default: + pr_info("default mode 1, osr 64\n"); + lpf1_len = 87; + lpf2_len = 33; + lpf3_len = 117; + lpf1_coeff = lpf1_mode1; + lpf3_coeff = lpf3_mode1; + break; + } + + /* config filter */ + aml_pdm_filters_config(osr, + lpf1_len, + lpf2_len, + lpf3_len); + + aml_pdm_LPF_coeff( + lpf1_len, lpf1_coeff, + lpf2_len, lpf2_coeff, + lpf3_len, lpf3_coeff + ); + + aml_pdm_update_bits(PDM_CTRL, 1 << 31, 1 << 31); +} diff --git a/sound/soc/amlogic/auge/pdm_hw.h b/sound/soc/amlogic/auge/pdm_hw.h new file mode 100644 index 000000000000..373354eee4d1 --- /dev/null +++ b/sound/soc/amlogic/auge/pdm_hw.h @@ -0,0 +1,44 @@ +/* + * sound/soc/amlogic/auge/pdm_hw.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_PDM_HW_H__ +#define __AML_PDM_HW_H__ +#include "audio_io.h" + +extern void aml_pdm_enable( + struct aml_audio_controller *actrl, + bool is_enalbe); + +extern void aml_pdm_ctrl( + struct aml_audio_controller *actrl, + int bitdepth, int channels); + +extern void aml_pdm_arb_config(struct aml_audio_controller *actrl); + +extern void aml_pdm_set_clk( + struct aml_audio_controller *actrl, + int is_enable); + +extern void aml_pdm_set_bclk_ratio( + struct aml_audio_controller *actrl, + int ratio); + +extern int aml_pmd_set_HPF_filter_parameters(void *array); + +extern void aml_pdm_filter_ctrl(int osr, int set); + +#endif /*__AML_PDM_HW_H__*/ diff --git a/sound/soc/amlogic/auge/pdm_hw_coeff.c b/sound/soc/amlogic/auge/pdm_hw_coeff.c new file mode 100644 index 000000000000..b44ba53f1e74 --- /dev/null +++ b/sound/soc/amlogic/auge/pdm_hw_coeff.c @@ -0,0 +1,276 @@ +/* + * sound/soc/amlogic/auge/pdm_hw_coeff.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +/* LPF coefficent + * For filter 1 and filter 3, it's only relative with coefficent mode + * For filter 2, it's only relative with osr and hcic stage number + */ + +static const int lpf2_osr64[] = { + 0x00050a, 0xfff004, 0x0002c1, 0x003c12, 0xffa818, + 0xffc87d, 0x010aef, 0xff5223, 0xfebd93, 0x028f41, + 0xff5c0e, 0xfc63f8, 0x055f81, 0x000000, 0xf478a0, + 0x11c5e3, 0x2ea74d, 0x11c5e3, 0xf478a0, 0x000000, + 0x055f81, 0xfc63f8, 0xff5c0e, 0x028f41, 0xfebd93, + 0xff5223, 0x010aef, 0xffc87d, 0xffa818, 0x003c12, + 0x0002c1, 0xfff004, 0x00050a, +}; + +static const int lpf2_osr96[] = { + 0x00050a, 0xfff004, 0x0002c1, 0x003c12, 0xffa818, + 0xffc87d, 0x010aef, 0xff5223, 0xfebd93, 0x028f41, + 0xff5c0e, 0xfc63f8, 0x055f81, 0x000000, 0xf478a0, + 0x11c5e3, 0x2ea74d, 0x11c5e3, 0xf478a0, 0x000000, + 0x055f81, 0xfc63f8, 0xff5c0e, 0x028f41, 0xfebd93, + 0xff5223, 0x010aef, 0xffc87d, 0xffa818, 0x003c12, + 0x0002c1, 0xfff004, 0x00050a +}; + +static const int lpf2_osr128[] = { + 0x00050b, 0xfff002, 0x0002c6, 0x003c15, 0xffa7fc, + 0xffc899, 0x010b29, 0xff518f, 0xfebd98, 0x02909c, + 0xff5ab8, 0xfc6254, 0x0563b2, 0x000000, 0xf46f16, + 0x11c6a2, 0x2eb45a, 0x11c6a2, 0xf46f16, 0x000000, + 0x0563b2, 0xfc6254, 0xff5ab8, 0x02909c, 0xfebd98, + 0xff518f, 0x010b29, 0xffc899, 0xffa7fc, 0x003c15, + 0x0002c6, 0xfff002, 0x00050b +}; + +static const int lpf2_osr192[] = { + 0x00050b, 0xfff002, 0x0002c7, 0x003c16, 0xffa7f7, + 0xffc89f, 0x010b35, 0xff516f, 0xfebd9a, 0x0290e8, + 0xff5a6d, 0xfc61f8, 0x05649b, 0x000000, 0xf46d02, + 0x11c6cc, 0x2eb731, 0x11c6cc, 0xf46d02, 0x000000, + 0x05649b, 0xfc61f8, 0xff5a6d, 0x0290e8, 0xfebd9a, + 0xff516f, 0x010b35, 0xffc89f, 0xffa7f7, 0x003c16, + 0x0002c7, 0xfff002, 0x00050b +}; + +static const int lpf2_osr256[] = { + 0x00050b, 0xfff002, 0x0002c7, 0x003c16, 0xffa7f6, + 0xffc8a0, 0x010b37, 0xff516a, 0xfebd9a, 0x0290f4, + 0xff5a62, 0xfc61ea, 0x0564bf, 0x000000, 0xf46cb0, + 0x11c6d2, 0x2eb7a1, 0x11c6d2, 0xf46cb0, 0x000000, + 0x0564bf, 0xfc61ea, 0xff5a62, 0x0290f4, 0xfebd9a, + 0xff516a, 0x010b37, 0xffc8a0, 0xffa7f6, 0x003c16, + 0x0002c7, 0xfff002, 0x00050b +}; + +static const int lpf2_osr384[] = { + 0x00050b, 0xfff002, 0x0002c7, 0x003c16, 0xffa7f6, + 0xffc8a0, 0x010b37, 0xff516a, 0xfebd9a, 0x0290f4, + 0xff5a62, 0xfc61ea, 0x0564bf, 0x000000, 0xf46cb0, + 0x11c6d2, 0x2eb7a1, 0x11c6d2, 0xf46cb0, 0x000000, + 0x0564bf, 0xfc61ea, 0xff5a62, 0x0290f4, 0xfebd9a, + 0xff516a, 0x010b37, 0xffc8a0, 0xffa7f6, 0x003c16, + 0x0002c7, 0xfff002, 0x00050b +}; + +static const int lpf1_mode0[] = { + 0x000006, 0x000002, 0xffffeb, 0xffffbe, 0xffff8a, + 0xffff76, 0xffffb4, 0x00006b, 0x000187, 0x0002a7, + 0x00031d, 0x00022c, 0xffff6b, 0xfffb35, 0xfff6e7, + 0xfff4aa, 0xfff6b8, 0xfffe3f, 0x000a43, 0x00172a, + 0x001f54, 0x001cfd, 0x000ce4, 0xfff0d0, 0xffd098, + 0xffb8cd, 0xffb6b8, 0xffd2b1, 0x000adb, 0x0050ad, + 0x008b46, 0x009ee9, 0x0077e0, 0x001506, 0xff8d7a, + 0xff0dc4, 0xfecb6c, 0xfef1b8, 0xff8da0, 0x0080db, + 0x0182b7, 0x0231a8, 0x023283, 0x0154bd, 0xffaf64, + 0xfdab1a, 0xfbf20a, 0xfb46e9, 0xfc4cb4, 0xff4f72, + 0x041fa5, 0x0a0e12, 0x100c37, 0x14eab3, 0x17a4fe, + 0x17a4fe, 0x14eab3, 0x100c37, 0x0a0e12, 0x041fa5, + 0xff4f72, 0xfc4cb4, 0xfb46e9, 0xfbf20a, 0xfdab1a, + 0xffaf64, 0x0154bd, 0x023283, 0x0231a8, 0x0182b7, + 0x0080db, 0xff8da0, 0xfef1b8, 0xfecb6c, 0xff0dc4, + 0xff8d7a, 0x001506, 0x0077e0, 0x009ee9, 0x008b46, + 0x0050ad, 0x000adb, 0xffd2b1, 0xffb6b8, 0xffb8cd, + 0xffd098, 0xfff0d0, 0x000ce4, 0x001cfd, 0x001f54, + 0x00172a, 0x000a43, 0xfffe3f, 0xfff6b8, 0xfff4aa, + 0xfff6e7, 0xfffb35, 0xffff6b, 0x00022c, 0x00031d, + 0x0002a7, 0x000187, 0x00006b, 0xffffb4, 0xffff76, + 0xffff8a, 0xffffbe, 0xffffeb, 0x000002, 0x000006, +}; + +static const int lpf3_mode0[] = { + 0x000007, 0x000000, 0xffffe8, 0x000000, 0x000038, + 0x000000, 0xffff94, 0x000000, 0x0000c0, 0x000000, + 0xfffec1, 0x000000, 0x0001f7, 0x000000, 0xfffd05, + 0x000000, 0x000460, 0x000000, 0xfff9c1, 0x000000, + 0x0008b7, 0x000000, 0xfff416, 0x000000, 0x000fff, + 0x000000, 0xffeadc, 0x000000, 0x001b8a, 0x000000, + 0xffdc97, 0x000000, 0x002d01, 0x000000, 0xffc767, + 0x000000, 0x00467f, 0x000000, 0xffa8f1, 0x000000, + 0x006ab0, 0x000000, 0xff7e26, 0x000000, 0x009d20, + 0x000000, 0xff42ce, 0x000000, 0x00e2f1, 0x000000, + 0xfef07e, 0x000000, 0x014470, 0x000000, 0xfe7c1b, + 0x000000, 0x01d10f, 0x000000, 0xfdcf39, 0x000000, + 0x02aaeb, 0x000000, 0xfcb2e7, 0x000000, 0x04311d, + 0x000000, 0xfa719c, 0x000000, 0x07f53f, 0x000000, + 0xf288b5, 0x000000, 0x28b482, 0x3fffff, 0x28b482, + 0x000000, 0xf288b5, 0x000000, 0x07f53f, 0x000000, + 0xfa719c, 0x000000, 0x04311d, 0x000000, 0xfcb2e7, + 0x000000, 0x02aaeb, 0x000000, 0xfdcf39, 0x000000, + 0x01d10f, 0x000000, 0xfe7c1b, 0x000000, 0x014470, + 0x000000, 0xfef07e, 0x000000, 0x00e2f1, 0x000000, + 0xff42ce, 0x000000, 0x009d20, 0x000000, 0xff7e26, + 0x000000, 0x006ab0, 0x000000, 0xffa8f1, 0x000000, + 0x00467f, 0x000000, 0xffc767, 0x000000, 0x002d01, + 0x000000, 0xffdc97, 0x000000, 0x001b8a, 0x000000, + 0xffeadc, 0x000000, 0x000fff, 0x000000, 0xfff416, + 0x000000, 0x0008b7, 0x000000, 0xfff9c1, 0x000000, + 0x000460, 0x000000, 0xfffd05, 0x000000, 0x0001f7, + 0x000000, 0xfffec1, 0x000000, 0x0000c0, 0x000000, + 0xffff94, 0x000000, 0x000038, 0x000000, 0xffffe8, + 0x000000, 0x000007 +}; + +static const int lpf1_mode1[] = { + 0x000014, 0xffffb2, 0xfffed9, 0xfffdce, 0xfffd45, + 0xfffe32, 0x000147, 0x000645, 0x000b86, 0x000e21, + 0x000ae3, 0x000000, 0xffeece, 0xffdca8, 0xffd212, + 0xffd7d1, 0xfff2a7, 0x001f4c, 0x0050c2, 0x0072aa, + 0x006ff1, 0x003c32, 0xffdc4e, 0xff6a18, 0xff0fef, + 0xfefbaf, 0xff4c40, 0x000000, 0x00ebc8, 0x01c077, + 0x02209e, 0x01c1a4, 0x008e60, 0xfebe52, 0xfcd690, + 0xfb8fa5, 0xfba498, 0xfd9812, 0x0181ce, 0x06f5f3, + 0x0d112f, 0x12a958, 0x169686, 0x18000e, 0x169686, + 0x12a958, 0x0d112f, 0x06f5f3, 0x0181ce, 0xfd9812, + 0xfba498, 0xfb8fa5, 0xfcd690, 0xfebe52, 0x008e60, + 0x01c1a4, 0x02209e, 0x01c077, 0x00ebc8, 0x000000, + 0xff4c40, 0xfefbaf, 0xff0fef, 0xff6a18, 0xffdc4e, + 0x003c32, 0x006ff1, 0x0072aa, 0x0050c2, 0x001f4c, + 0xfff2a7, 0xffd7d1, 0xffd212, 0xffdca8, 0xffeece, + 0x000000, 0x000ae3, 0x000e21, 0x000b86, 0x000645, + 0x000147, 0xfffe32, 0xfffd45, 0xfffdce, 0xfffed9, + 0xffffb2, 0x000014, +}; + +static const int lpf3_mode1[] = { + 0x000000, 0x000081, 0x000000, 0xfffedb, 0x000000, + 0x00022d, 0x000000, 0xfffc46, 0x000000, 0x0005f7, + 0x000000, 0xfff6eb, 0x000000, 0x000d4e, 0x000000, + 0xffed1e, 0x000000, 0x001a1c, 0x000000, 0xffdcb0, + 0x000000, 0x002ede, 0x000000, 0xffc2d1, 0x000000, + 0x004ebe, 0x000000, 0xff9beb, 0x000000, 0x007dd7, + 0x000000, 0xff633a, 0x000000, 0x00c1d2, 0x000000, + 0xff11d5, 0x000000, 0x012368, 0x000000, 0xfe9c45, + 0x000000, 0x01b252, 0x000000, 0xfdebf6, 0x000000, + 0x0290b8, 0x000000, 0xfcca0d, 0x000000, 0x041d7c, + 0x000000, 0xfa8152, 0x000000, 0x07e9c6, 0x000000, + 0xf28fb5, 0x000000, 0x28b216, 0x3fffde, 0x28b216, + 0x000000, 0xf28fb5, 0x000000, 0x07e9c6, 0x000000, + 0xfa8152, 0x000000, 0x041d7c, 0x000000, 0xfcca0d, + 0x000000, 0x0290b8, 0x000000, 0xfdebf6, 0x000000, + 0x01b252, 0x000000, 0xfe9c45, 0x000000, 0x012368, + 0x000000, 0xff11d5, 0x000000, 0x00c1d2, 0x000000, + 0xff633a, 0x000000, 0x007dd7, 0x000000, 0xff9beb, + 0x000000, 0x004ebe, 0x000000, 0xffc2d1, 0x000000, + 0x002ede, 0x000000, 0xffdcb0, 0x000000, 0x001a1c, + 0x000000, 0xffed1e, 0x000000, 0x000d4e, 0x000000, + 0xfff6eb, 0x000000, 0x0005f7, 0x000000, 0xfffc46, + 0x000000, 0x00022d, 0x000000, 0xfffedb, 0x000000, + 0x000081, 0x000000, +}; + +static const int lpf1_mode2[] = { + 0x000014, 0xffffb2, 0xfffed9, 0xfffdce, 0xfffd45, + 0xfffe32, 0x000147, 0x000645, 0x000b86, 0x000e21, + 0x000ae3, 0x000000, 0xffeece, 0xffdca8, 0xffd212, + 0xffd7d1, 0xfff2a7, 0x001f4c, 0x0050c2, 0x0072aa, + 0x006ff1, 0x003c32, 0xffdc4e, 0xff6a18, 0xff0fef, + 0xfefbaf, 0xff4c40, 0x000000, 0x00ebc8, 0x01c077, + 0x02209e, 0x01c1a4, 0x008e60, 0xfebe52, 0xfcd690, + 0xfb8fa5, 0xfba498, 0xfd9812, 0x0181ce, 0x06f5f3, + 0x0d112f, 0x12a958, 0x169686, 0x18000e, 0x169686, + 0x12a958, 0x0d112f, 0x06f5f3, 0x0181ce, 0xfd9812, + 0xfba498, 0xfb8fa5, 0xfcd690, 0xfebe52, 0x008e60, + 0x01c1a4, 0x02209e, 0x01c077, 0x00ebc8, 0x000000, + 0xff4c40, 0xfefbaf, 0xff0fef, 0xff6a18, 0xffdc4e, + 0x003c32, 0x006ff1, 0x0072aa, 0x0050c2, 0x001f4c, + 0xfff2a7, 0xffd7d1, 0xffd212, 0xffdca8, 0xffeece, + 0x000000, 0x000ae3, 0x000e21, 0x000b86, 0x000645, + 0x000147, 0xfffe32, 0xfffd45, 0xfffdce, 0xfffed9, + 0xffffb2, 0x000014 +}; + +static const int lpf3_mode2[] = { + 0x00005e, 0xffff19, 0xfffe3e, 0x00030a, 0x0004de, + 0xfff899, 0xfff531, 0x000f4a, 0x001510, 0xffe39e, + 0xffda78, 0x0030d1, 0x003e97, 0xffb0c5, 0xff9cd2, + 0x007aec, 0x009706, 0xff47dd, 0xff20f2, 0x010cbd, + 0x01426b, 0xfe7e4e, 0xfe3340, 0x0226a1, 0x0293d1, + 0xfce4d3, 0xfc382d, 0x04ad05, 0x05ef42, 0xf82315, + 0xf4bde1, 0x130bdd, 0x399128, 0x399128, 0x130bdd, + 0xf4bde1, 0xf82315, 0x05ef42, 0x04ad05, 0xfc382d, + 0xfce4d3, 0x0293d1, 0x0226a1, 0xfe3340, 0xfe7e4e, + 0x01426b, 0x010cbd, 0xff20f2, 0xff47dd, 0x009706, + 0x007aec, 0xff9cd2, 0xffb0c5, 0x003e97, 0x0030d1, + 0xffda78, 0xffe39e, 0x001510, 0x000f4a, 0xfff531, + 0xfff899, 0x0004de, 0x00030a, 0xfffe3e, 0xffff19, + 0x00005e +}; + +static const int lpf1_mode3[] = { + 0x000000, 0xfffc12, 0xfff5cb, 0xfff022, 0xffeffa, + 0xfffa03, 0x000f84, 0x002b96, 0x00429d, 0x00455c, + 0x00277a, 0xffe762, 0xff93e1, 0xff4bc4, 0xff3567, + 0xff6f81, 0x000000, 0x00c850, 0x018619, 0x01e3e3, + 0x01972b, 0x008323, 0xfed332, 0xfd00e8, 0xfbbe66, + 0xfbc791, 0xfda631, 0x017bb2, 0x06e40b, 0x0cfe8e, + 0x129dfd, 0x1693d6, 0x18011b, 0x1693d6, 0x129dfd, + 0x0cfe8e, 0x06e40b, 0x017bb2, 0xfda631, 0xfbc791, + 0xfbbe66, 0xfd00e8, 0xfed332, 0x008323, 0x01972b, + 0x01e3e3, 0x018619, 0x00c850, 0x000000, 0xff6f81, + 0xff3567, 0xff4bc4, 0xff93e1, 0xffe762, 0x00277a, + 0x00455c, 0x00429d, 0x002b96, 0x000f84, 0xfffa03, + 0xffeffa, 0xfff022, 0xfff5cb, 0xfffc12, 0x000000, +}; + +static const int lpf3_mode3[] = { + 0x000000, 0xfff4f6, 0x000000, 0x001e36, 0x000000, + 0xffbfb6, 0x000000, 0x007789, 0x000000, 0xff3423, + 0x000000, 0x0147cc, 0x000000, 0xfe0523, 0x000000, + 0x02ffbb, 0x000000, 0xfb728b, 0x000000, 0x0732af, + 0x000000, 0xf301d5, 0x000000, 0x288c99, 0x40023d, + 0x288c99, 0x000000, 0xf301d5, 0x000000, 0x0732af, + 0x000000, 0xfb728b, 0x000000, 0x02ffbb, 0x000000, + 0xfe0523, 0x000000, 0x0147cc, 0x000000, 0xff3423, + 0x000000, 0x007789, 0x000000, 0xffbfb6, 0x000000, + 0x001e36, 0x000000, 0xfff4f6, 0x000000 +}; + +static const int lpf1_mode4[] = { + 0xfff71d, 0xffd0ad, 0xffa4b6, 0xff8cdb, 0xffa5d4, + 0x000000, 0x009021, 0x012899, 0x0181ac, 0x0151f3, + 0x0070b6, 0xfef591, 0xfd46bc, 0xfc0b63, 0xfc00d4, + 0xfdbd02, 0x017215, 0x06c95c, 0x0ce665, 0x1295f9, + 0x169d63, 0x181214, 0x169d63, 0x1295f9, 0x0ce665, + 0x06c95c, 0x017215, 0xfdbd02, 0xfc00d4, 0xfc0b63, + 0xfd46bc, 0xfef591, 0x0070b6, 0x0151f3, 0x0181ac, + 0x012899, 0x009021, 0x000000, 0xffa5d4, 0xff8cdb, + 0xffa4b6, 0xffd0ad, 0xfff71d +}; + +static const int lpf3_mode4[] = { + 0xffd46b, 0xffb7a1, 0x006ded, 0x009ddb, 0xff25e9, + 0xfedaef, 0x018208, 0x01f591, 0xfd7972, 0xfcbff4, + 0x043527, 0x058a27, 0xf872cb, 0xf4f5a2, 0x12ef7c, + 0x399f1f, 0x399f1f, 0x12ef7c, 0xf4f5a2, 0xf872cb, + 0x058a27, 0x043527, 0xfcbff4, 0xfd7972, 0x01f591, + 0x018208, 0xfedaef, 0xff25e9, 0x009ddb, 0x006ded, + 0xffb7a1, 0xffd46b +}; diff --git a/sound/soc/amlogic/auge/regs.h b/sound/soc/amlogic/auge/regs.h new file mode 100644 index 000000000000..1be9d4a4497f --- /dev/null +++ b/sound/soc/amlogic/auge/regs.h @@ -0,0 +1,304 @@ +/* + * sound/soc/amlogic/auge/regs.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_REGS_H_ +#define __AML_REGS_H_ + +/* + * PDM - Registers + * PDM + * + * BASE_ADR 32'hFF632000 + */ + +#define PDM_CTRL 0x00 +#define PDM_HCIC_CTRL1 0x01 +#define PDM_HCIC_CTRL2 0x02 +#define PDM_F1_CTRL 0x03 +#define PDM_F2_CTRL 0x04 +#define PDM_F3_CTRL 0x05 +#define PDM_HPF_CTRL 0x06 +#define PDM_CHAN_CTRL 0x07 +#define PDM_CHAN_CTRL1 0x08 +#define PDM_COEFF_ADDR 0x09 +#define PDM_COEFF_DATA 0x0A +#define PDM_CLKG_CTRL 0x0B +#define PDM_STS 0x0C + + +/** + * AUDIO - Registers + * AUDIO CLOCK, TODDR, FRDDR, TDM, SPDIF, LOOPBACK, RESAMPLE, + * POWER DETECT, SECURITY + * + * BASE_ADR 32'hFF642000 + */ + +#define EE_AUDIO_CLK_GATE_EN 0x000 +#define EE_AUDIO_MCLK_A_CTRL 0x001 +#define EE_AUDIO_MCLK_B_CTRL 0x002 +#define EE_AUDIO_MCLK_C_CTRL 0x003 +#define EE_AUDIO_MCLK_D_CTRL 0x004 +#define EE_AUDIO_MCLK_E_CTRL 0x005 +#define EE_AUDIO_MCLK_F_CTRL 0x006 +#define EE_AUDIO_MST_A_SCLK_CTRL0 0x010 +#define EE_AUDIO_MST_A_SCLK_CTRL1 0x011 +#define EE_AUDIO_MST_B_SCLK_CTRL0 0x012 +#define EE_AUDIO_MST_B_SCLK_CTRL1 0x013 +#define EE_AUDIO_MST_C_SCLK_CTRL0 0x014 +#define EE_AUDIO_MST_C_SCLK_CTRL1 0x015 +#define EE_AUDIO_MST_D_SCLK_CTRL0 0x016 +#define EE_AUDIO_MST_D_SCLK_CTRL1 0x017 +#define EE_AUDIO_MST_E_SCLK_CTRL0 0x018 +#define EE_AUDIO_MST_E_SCLK_CTRL1 0x019 +#define EE_AUDIO_MST_F_SCLK_CTRL0 0x01a +#define EE_AUDIO_MST_F_SCLK_CTRL1 0x01b +#define EE_AUDIO_CLK_TDMIN_A_CTRL 0x020 +#define EE_AUDIO_CLK_TDMIN_B_CTRL 0x021 +#define EE_AUDIO_CLK_TDMIN_C_CTRL 0x022 +#define EE_AUDIO_CLK_TDMIN_LB_CTRL 0x023 +#define EE_AUDIO_CLK_TDMOUT_A_CTRL 0x024 +#define EE_AUDIO_CLK_TDMOUT_B_CTRL 0x025 +#define EE_AUDIO_CLK_TDMOUT_C_CTRL 0x026 +#define EE_AUDIO_CLK_SPDIFIN_CTRL 0x027 +#define EE_AUDIO_CLK_SPDIFOUT_CTRL 0x028 +#define EE_AUDIO_CLK_RESAMPLE_CTRL 0x029 +#define EE_AUDIO_CLK_LOCKER_CTRL 0x02a +#define EE_AUDIO_CLK_PDMIN_CTRL0 0x02b +#define EE_AUDIO_CLK_PDMIN_CTRL1 0x02c +#define EE_AUDIO_TODDR_A_CTRL0 0x040 +#define EE_AUDIO_TODDR_A_CTRL1 0x041 +#define EE_AUDIO_TODDR_A_START_ADDR 0x042 +#define EE_AUDIO_TODDR_A_FINISH_ADDR 0x043 +#define EE_AUDIO_TODDR_A_INT_ADDR 0x044 +#define EE_AUDIO_TODDR_A_STATUS1 0x045 +#define EE_AUDIO_TODDR_A_STATUS2 0x046 +#define EE_AUDIO_TODDR_A_START_ADDRB 0x047 +#define EE_AUDIO_TODDR_A_FINISH_ADDRB 0x048 +#define EE_AUDIO_TODDR_B_CTRL0 0x050 +#define EE_AUDIO_TODDR_B_CTRL1 0x051 +#define EE_AUDIO_TODDR_B_START_ADDR 0x052 +#define EE_AUDIO_TODDR_B_FINISH_ADDR 0x053 +#define EE_AUDIO_TODDR_B_INT_ADDR 0x054 +#define EE_AUDIO_TODDR_B_STATUS1 0x055 +#define EE_AUDIO_TODDR_B_STATUS2 0x056 +#define EE_AUDIO_TODDR_B_START_ADDRB 0x057 +#define EE_AUDIO_TODDR_B_FINISH_ADDRB 0x058 +#define EE_AUDIO_TODDR_C_CTRL0 0x060 +#define EE_AUDIO_TODDR_C_CTRL1 0x061 +#define EE_AUDIO_TODDR_C_START_ADDR 0x062 +#define EE_AUDIO_TODDR_C_FINISH_ADDR 0x063 +#define EE_AUDIO_TODDR_C_INT_ADDR 0x064 +#define EE_AUDIO_TODDR_C_STATUS1 0x065 +#define EE_AUDIO_TODDR_C_STATUS2 0x066 +#define EE_AUDIO_TODDR_C_START_ADDRB 0x067 +#define EE_AUDIO_TODDR_C_FINISH_ADDRB 0x068 +#define EE_AUDIO_FRDDR_A_CTRL0 0x070 +#define EE_AUDIO_FRDDR_A_CTRL1 0x071 +#define EE_AUDIO_FRDDR_A_START_ADDR 0x072 +#define EE_AUDIO_FRDDR_A_FINISH_ADDR 0x073 +#define EE_AUDIO_FRDDR_A_INT_ADDR 0x074 +#define EE_AUDIO_FRDDR_A_STATUS1 0x075 +#define EE_AUDIO_FRDDR_A_STATUS2 0x076 +#define EE_AUDIO_FRDDR_A_START_ADDRB 0x077 +#define EE_AUDIO_FRDDR_A_FINISH_ADDRB 0x078 +#define EE_AUDIO_FRDDR_B_CTRL0 0x080 +#define EE_AUDIO_FRDDR_B_CTRL1 0x081 +#define EE_AUDIO_FRDDR_B_START_ADDR 0x082 +#define EE_AUDIO_FRDDR_B_FINISH_ADDR 0x083 +#define EE_AUDIO_FRDDR_B_INT_ADDR 0x084 +#define EE_AUDIO_FRDDR_B_STATUS1 0x085 +#define EE_AUDIO_FRDDR_B_STATUS2 0x086 +#define EE_AUDIO_FRDDR_B_START_ADDRB 0x087 +#define EE_AUDIO_FRDDR_B_FINISH_ADDRB 0x088 +#define EE_AUDIO_FRDDR_C_CTRL0 0x090 +#define EE_AUDIO_FRDDR_C_CTRL1 0x091 +#define EE_AUDIO_FRDDR_C_START_ADDR 0x092 +#define EE_AUDIO_FRDDR_C_FINISH_ADDR 0x093 +#define EE_AUDIO_FRDDR_C_INT_ADDR 0x094 +#define EE_AUDIO_FRDDR_C_STATUS1 0x095 +#define EE_AUDIO_FRDDR_C_STATUS2 0x096 +#define EE_AUDIO_FRDDR_C_START_ADDRB 0x097 +#define EE_AUDIO_FRDDR_C_FINISH_ADDRB 0x098 +#define EE_AUDIO_ARB_CTRL 0x0a0 +#define EE_AUDIO_LB_CTRL0 0x0b0 +#define EE_AUDIO_LB_CTRL1 0x0b1 +#define EE_AUDIO_TDMIN_A_CTRL 0x0c0 +#define EE_AUDIO_TDMIN_A_SWAP 0x0c1 +#define EE_AUDIO_TDMIN_A_MASK0 0x0c2 +#define EE_AUDIO_TDMIN_A_MASK1 0x0c3 +#define EE_AUDIO_TDMIN_A_MASK2 0x0c4 +#define EE_AUDIO_TDMIN_A_MASK3 0x0c5 +#define EE_AUDIO_TDMIN_A_STAT 0x0c6 +#define EE_AUDIO_TDMIN_A_MUTE_VAL 0x0c7 +#define EE_AUDIO_TDMIN_A_MUTE0 0x0c8 +#define EE_AUDIO_TDMIN_A_MUTE1 0x0c9 +#define EE_AUDIO_TDMIN_A_MUTE2 0x0ca +#define EE_AUDIO_TDMIN_A_MUTE3 0x0cb +#define EE_AUDIO_TDMIN_B_CTRL 0x0d0 +#define EE_AUDIO_TDMIN_B_SWAP 0x0d1 +#define EE_AUDIO_TDMIN_B_MASK0 0x0d2 +#define EE_AUDIO_TDMIN_B_MASK1 0x0d3 +#define EE_AUDIO_TDMIN_B_MASK2 0x0d4 +#define EE_AUDIO_TDMIN_B_MASK3 0x0d5 +#define EE_AUDIO_TDMIN_B_STAT 0x0d6 +#define EE_AUDIO_TDMIN_B_MUTE_VAL 0x0d7 +#define EE_AUDIO_TDMIN_B_MUTE0 0x0d8 +#define EE_AUDIO_TDMIN_B_MUTE1 0x0d9 +#define EE_AUDIO_TDMIN_B_MUTE2 0x0da +#define EE_AUDIO_TDMIN_B_MUTE3 0x0db +#define EE_AUDIO_TDMIN_C_CTRL 0x0e0 +#define EE_AUDIO_TDMIN_C_SWAP 0x0e1 +#define EE_AUDIO_TDMIN_C_MASK0 0x0e2 +#define EE_AUDIO_TDMIN_C_MASK1 0x0e3 +#define EE_AUDIO_TDMIN_C_MASK2 0x0e4 +#define EE_AUDIO_TDMIN_C_MASK3 0x0e5 +#define EE_AUDIO_TDMIN_C_STAT 0x0e6 +#define EE_AUDIO_TDMIN_C_MUTE_VAL 0x0e7 +#define EE_AUDIO_TDMIN_C_MUTE0 0x0e8 +#define EE_AUDIO_TDMIN_C_MUTE1 0x0e9 +#define EE_AUDIO_TDMIN_C_MUTE2 0x0ea +#define EE_AUDIO_TDMIN_C_MUTE3 0x0eb +#define EE_AUDIO_TDMIN_LB_CTRL 0x0f0 +#define EE_AUDIO_TDMIN_LB_SWAP 0x0f1 +#define EE_AUDIO_TDMIN_LB_MASK0 0x0f2 +#define EE_AUDIO_TDMIN_LB_MASK1 0x0f3 +#define EE_AUDIO_TDMIN_LB_MASK2 0x0f4 +#define EE_AUDIO_TDMIN_LB_MASK3 0x0f5 +#define EE_AUDIO_TDMIN_LB_STAT 0x0f6 +#define EE_AUDIO_TDMIN_LB_MUTE_VAL 0x0f7 +#define EE_AUDIO_TDMIN_LB_MUTE0 0x0f8 +#define EE_AUDIO_TDMIN_LB_MUTE1 0x0f9 +#define EE_AUDIO_TDMIN_LB_MUTE2 0x0fa +#define EE_AUDIO_TDMIN_LB_MUTE3 0x0fb +#define EE_AUDIO_SPDIFIN_CTRL0 0x100 +#define EE_AUDIO_SPDIFIN_CTRL1 0x101 +#define EE_AUDIO_SPDIFIN_CTRL2 0x102 +#define EE_AUDIO_SPDIFIN_CTRL3 0x103 +#define EE_AUDIO_SPDIFIN_CTRL4 0x104 +#define EE_AUDIO_SPDIFIN_CTRL5 0x105 +#define EE_AUDIO_SPDIFIN_CTRL6 0x106 +#define EE_AUDIO_SPDIFIN_STAT0 0x107 +#define EE_AUDIO_SPDIFIN_STAT1 0x108 +#define EE_AUDIO_SPDIFIN_STAT2 0x109 +#define EE_AUDIO_SPDIFIN_MUTE_VAL 0x10a +#define EE_AUDIO_RESAMPLE_CTRL0 0x110 +#define EE_AUDIO_RESAMPLE_CTRL1 0x111 +#define EE_AUDIO_RESAMPLE_CTRL2 0x112 +#define EE_AUDIO_RESAMPLE_CTRL3 0x113 +#define EE_AUDIO_RESAMPLE_COEF0 0x114 +#define EE_AUDIO_RESAMPLE_COEF1 0x115 +#define EE_AUDIO_RESAMPLE_COEF2 0x116 +#define EE_AUDIO_RESAMPLE_COEF3 0x117 +#define EE_AUDIO_RESAMPLE_COEF4 0x118 +#define EE_AUDIO_RESAMPLE_STATUS1 0x119 +#define EE_AUDIO_SPDIFOUT_STAT 0x120 +#define EE_AUDIO_SPDIFOUT_GAIN0 0x121 +#define EE_AUDIO_SPDIFOUT_GAIN1 0x122 +#define EE_AUDIO_SPDIFOUT_CTRL0 0x123 +#define EE_AUDIO_SPDIFOUT_CTRL1 0x124 +#define EE_AUDIO_SPDIFOUT_PREAMB 0x125 +#define EE_AUDIO_SPDIFOUT_SWAP 0x126 +#define EE_AUDIO_SPDIFOUT_CHSTS0 0x127 +#define EE_AUDIO_SPDIFOUT_CHSTS1 0x128 +#define EE_AUDIO_SPDIFOUT_CHSTS2 0x129 +#define EE_AUDIO_SPDIFOUT_CHSTS3 0x12a +#define EE_AUDIO_SPDIFOUT_CHSTS4 0x12b +#define EE_AUDIO_SPDIFOUT_CHSTS5 0x12c +#define EE_AUDIO_SPDIFOUT_CHSTS6 0x12d +#define EE_AUDIO_SPDIFOUT_CHSTS7 0x12e +#define EE_AUDIO_SPDIFOUT_CHSTS8 0x12f +#define EE_AUDIO_SPDIFOUT_CHSTS9 0x130 +#define EE_AUDIO_SPDIFOUT_CHSTSA 0x131 +#define EE_AUDIO_SPDIFOUT_CHSTSB 0x132 +#define EE_AUDIO_SPDIFOUT_MUTE_VAL 0x133 +#define EE_AUDIO_TDMOUT_A_CTRL0 0x140 +#define EE_AUDIO_TDMOUT_A_CTRL1 0x141 +#define EE_AUDIO_TDMOUT_A_SWAP 0x142 +#define EE_AUDIO_TDMOUT_A_MASK0 0x143 +#define EE_AUDIO_TDMOUT_A_MASK1 0x144 +#define EE_AUDIO_TDMOUT_A_MASK2 0x145 +#define EE_AUDIO_TDMOUT_A_MASK3 0x146 +#define EE_AUDIO_TDMOUT_A_STAT 0x147 +#define EE_AUDIO_TDMOUT_A_GAIN0 0x148 +#define EE_AUDIO_TDMOUT_A_GAIN1 0x149 +#define EE_AUDIO_TDMOUT_A_MUTE_VAL 0x14a +#define EE_AUDIO_TDMOUT_A_MUTE0 0x14b +#define EE_AUDIO_TDMOUT_A_MUTE1 0x14c +#define EE_AUDIO_TDMOUT_A_MUTE2 0x14d +#define EE_AUDIO_TDMOUT_A_MUTE3 0x14e +#define EE_AUDIO_TDMOUT_A_MASK_VAL 0x14f +#define EE_AUDIO_TDMOUT_B_CTRL0 0x150 +#define EE_AUDIO_TDMOUT_B_CTRL1 0x151 +#define EE_AUDIO_TDMOUT_B_SWAP 0x152 +#define EE_AUDIO_TDMOUT_B_MASK0 0x153 +#define EE_AUDIO_TDMOUT_B_MASK1 0x154 +#define EE_AUDIO_TDMOUT_B_MASK2 0x155 +#define EE_AUDIO_TDMOUT_B_MASK3 0x156 +#define EE_AUDIO_TDMOUT_B_STAT 0x157 +#define EE_AUDIO_TDMOUT_B_GAIN0 0x158 +#define EE_AUDIO_TDMOUT_B_GAIN1 0x159 +#define EE_AUDIO_TDMOUT_B_MUTE_VAL 0x15a +#define EE_AUDIO_TDMOUT_B_MUTE0 0x15b +#define EE_AUDIO_TDMOUT_B_MUTE1 0x15c +#define EE_AUDIO_TDMOUT_B_MUTE2 0x15d +#define EE_AUDIO_TDMOUT_B_MUTE3 0x15e +#define EE_AUDIO_TDMOUT_B_MASK_VAL 0x15f +#define EE_AUDIO_TDMOUT_C_CTRL0 0x160 +#define EE_AUDIO_TDMOUT_C_CTRL1 0x161 +#define EE_AUDIO_TDMOUT_C_SWAP 0x162 +#define EE_AUDIO_TDMOUT_C_MASK0 0x163 +#define EE_AUDIO_TDMOUT_C_MASK1 0x164 +#define EE_AUDIO_TDMOUT_C_MASK2 0x165 +#define EE_AUDIO_TDMOUT_C_MASK3 0x166 +#define EE_AUDIO_TDMOUT_C_STAT 0x167 +#define EE_AUDIO_TDMOUT_C_GAIN0 0x168 +#define EE_AUDIO_TDMOUT_C_GAIN1 0x169 +#define EE_AUDIO_TDMOUT_C_MUTE_VAL 0x16a +#define EE_AUDIO_TDMOUT_C_MUTE0 0x16b +#define EE_AUDIO_TDMOUT_C_MUTE1 0x16c +#define EE_AUDIO_TDMOUT_C_MUTE2 0x16d +#define EE_AUDIO_TDMOUT_C_MUTE3 0x16e +#define EE_AUDIO_TDMOUT_C_MASK_VAL 0x16f +#define EE_AUDIO_POW_DET_CTRL0 0x180 +#define EE_AUDIO_POW_DET_TH_HI 0x181 +#define EE_AUDIO_POW_DET_TH_LO 0x182 +#define EE_AUDIO_POW_DET_VALUE 0x183 +#define EE_AUDIO_SECURITY_CTRL 0x193 + +#define AUD_ADDR_OFFSET(addr) ((addr) << 2) + +enum clk_sel { + MASTER_A, + MASTER_B, + MASTER_C, + MASTER_D, + MASTER_E, + MASTER_F, + SLAVE_A, + SLAVE_B, + SLAVE_C, + SLAVE_D, + SLAVE_E, + SLAVE_F, + SLAVE_G, + SLAVE_H, + SLAVE_I, + SLAVE_J +}; + +#endif diff --git a/sound/soc/amlogic/auge/spdif.c b/sound/soc/amlogic/auge/spdif.c new file mode 100644 index 000000000000..4d6f4b3b37a8 --- /dev/null +++ b/sound/soc/amlogic/auge/spdif.c @@ -0,0 +1,722 @@ +/* + * sound/soc/amlogic/auge/spdif.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "ddr_mngr.h" +#include "spdif_hw.h" + + +#define DRV_NAME "aml_spdif" + +struct aml_spdif { + struct pinctrl *pin_ctl; + struct aml_audio_controller *actrl; + struct device *dev; + struct clk *gate_spdifin; + struct clk *gate_spdifout; + struct clk *sysclk; + struct clk *fixed_clk; + struct clk *clk_spdifin; + struct clk *clk_spdifout; + unsigned int sysclk_freq; + /* bclk src selection */ + int irq_toddr; + int irq_frddr; + int irq_spdifin; + unsigned int from_ddr_num; + unsigned int to_ddr_num; + struct toddr *tddr; + struct frddr *fddr; +}; + +static const struct snd_pcm_hardware aml_spdif_hardware = { + .info = + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE, + .formats = + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + + .period_bytes_min = 64, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 256 * 1024, + + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 32, +}; + +static irqreturn_t aml_spdifout_isr(int irq, void *devid) +{ + struct snd_pcm_substream *substream = + (struct snd_pcm_substream *)devid; + + snd_pcm_period_elapsed(substream); + + return IRQ_HANDLED; +} + +static irqreturn_t aml_spdifin_isr(int irq, void *devid) +{ + struct snd_pcm_substream *substream = + (struct snd_pcm_substream *)devid; + + snd_pcm_period_elapsed(substream); + + return IRQ_HANDLED; +} + +/* detect PCM/RAW and sample changes by the source */ +static irqreturn_t aml_spdifin_status_isr(int irq, void *devid) +{ + struct aml_spdif *p_spdif = (struct aml_spdif *)devid; + + aml_spdifin_status_check(p_spdif->actrl); + + return IRQ_HANDLED; +} + +static int aml_spdif_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; + struct aml_spdif *p_spdif; + int ret = 0; + + pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + + p_spdif = (struct aml_spdif *)dev_get_drvdata(dev); + + snd_soc_set_runtime_hwparams(substream, &aml_spdif_hardware); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + p_spdif->fddr = aml_audio_register_frddr(dev, + p_spdif->actrl, + p_spdif->from_ddr_num); + if (p_spdif->fddr == NULL) { + dev_err(dev, "failed to claim from ddr %u\n", + p_spdif->from_ddr_num); + return -ENXIO; + } + + ret = request_irq(p_spdif->irq_frddr, + aml_spdifout_isr, 0, "spdifout", substream); + if (ret) { + aml_audio_unregister_frddr(dev, p_spdif->from_ddr_num); + dev_err(p_spdif->dev, "failed to claim irq %u\n", + p_spdif->irq_frddr); + return ret; + } + } else { + p_spdif->tddr = aml_audio_register_toddr(dev, + p_spdif->actrl, + p_spdif->to_ddr_num); + if (p_spdif->tddr == NULL) { + dev_err(dev, "failed to claim to ddr %u\n", + p_spdif->to_ddr_num); + return -ENXIO; + } + + ret = request_irq(p_spdif->irq_toddr, + aml_spdifin_isr, 0, "spdifin", substream); + if (ret) { + aml_audio_unregister_toddr(dev, p_spdif->to_ddr_num); + dev_err(p_spdif->dev, "failed to claim irq %u\n", + p_spdif->irq_toddr); + return ret; + } + } + + runtime->private_data = p_spdif; + + return 0; +} + + +static int aml_spdif_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_spdif *p_spdif = runtime->private_data; + + pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aml_audio_unregister_frddr(p_spdif->dev, p_spdif->from_ddr_num); + free_irq(p_spdif->irq_frddr, substream); + } else { + aml_audio_unregister_toddr(p_spdif->dev, p_spdif->to_ddr_num); + free_irq(p_spdif->irq_toddr, substream); + } + + runtime->private_data = NULL; + + return 0; +} + + +static int aml_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int aml_spdif_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + + return 0; +} + +static int aml_spdif_trigger(struct snd_pcm_substream *substream, int cmd) +{ + return 0; +} + +static int aml_spdif_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_spdif *p_spdif = runtime->private_data; + unsigned int start_addr, end_addr, int_addr; + + start_addr = runtime->dma_addr; + end_addr = start_addr + runtime->dma_bytes - 8; + int_addr = frames_to_bytes(runtime, runtime->period_size) / 8; + + pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct frddr *fr = p_spdif->fddr; + + aml_frddr_set_buf(fr, start_addr, end_addr); + aml_frddr_set_intrpt(fr, int_addr); + } else { + struct toddr *to = p_spdif->tddr; + + aml_toddr_set_buf(to, start_addr, end_addr); + aml_toddr_set_intrpt(to, int_addr); + } + + return 0; +} + +static snd_pcm_uframes_t aml_spdif_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_spdif *p_spdif = runtime->private_data; + unsigned int addr, start_addr; + + start_addr = runtime->dma_addr; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + addr = aml_frddr_get_position(p_spdif->fddr); + else + addr = aml_toddr_get_position(p_spdif->tddr); + + return bytes_to_frames(runtime, addr - start_addr); +} + +int aml_spdif_silence(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + char *ppos; + int n; + + n = frames_to_bytes(runtime, count); + ppos = runtime->dma_area + frames_to_bytes(runtime, pos); + memset(ppos, 0, n); + + return 0; +} + +static int aml_spdif_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return snd_pcm_lib_default_mmap(substream, vma); +} + +static struct snd_pcm_ops aml_spdif_ops = { + .open = aml_spdif_open, + .close = aml_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = aml_spdif_hw_params, + .hw_free = aml_spdif_hw_free, + .prepare = aml_spdif_prepare, + .trigger = aml_spdif_trigger, + .pointer = aml_spdif_pointer, + .silence = aml_spdif_silence, + .mmap = aml_spdif_mmap, +}; + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (256 * 1024) +static int aml_spdif_new(struct snd_soc_pcm_runtime *rtd) +{ + return snd_pcm_lib_preallocate_pages_for_all( + rtd->pcm, SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); +} + +struct snd_soc_platform_driver aml_spdif_platform = { + .ops = &aml_spdif_ops, + .pcm_new = aml_spdif_new, +}; + +static int aml_dai_spdif_probe(struct snd_soc_dai *cpu_dai) +{ + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = p_spdif->dev; + int ret; + + /* gate on */ + clk_prepare_enable(p_spdif->gate_spdifin); + clk_prepare_enable(p_spdif->gate_spdifout); + + ret = clk_set_parent(p_spdif->clk_spdifin, p_spdif->fixed_clk); + if (ret) { + dev_err(dev, + "Can't set clk_spdifin parent clock\n"); + ret = PTR_ERR(p_spdif->clk_spdifin); + return ret; + } + + clk_set_rate(p_spdif->clk_spdifin, 250000000); + ret = clk_prepare_enable(p_spdif->clk_spdifin); + if (ret) { + dev_err(dev, + "Can't enable pcm clk_spdifin clock: %d\n", ret); + return ret; + } + + ret = clk_set_parent(p_spdif->clk_spdifout, p_spdif->sysclk); + if (ret) { + dev_err(dev, + "Can't set clk_spdifout parent clock\n"); + ret = PTR_ERR(p_spdif->clk_spdifout); + return ret; + } + + /* enable clock */ + ret = clk_prepare_enable(p_spdif->clk_spdifout); + if (ret) { + dev_err(dev, + "Can't enable pcm clk_spdifout clock: %d\n", ret); + return ret; + } + + ret = request_irq(p_spdif->irq_spdifin, + aml_spdifin_status_isr, 0, "irq_spdifin", p_spdif); + if (ret) { + dev_err(p_spdif->dev, "failed to claim irq_spdifin %u\n", + p_spdif->irq_spdifin); + return ret; + } + + return 0; +} + +static int aml_dai_spdif_remove(struct snd_soc_dai *cpu_dai) +{ + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + + pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + + free_irq(p_spdif->irq_spdifin, p_spdif); + free_irq(p_spdif->irq_frddr, p_spdif); + free_irq(p_spdif->irq_toddr, p_spdif); + + clk_disable_unprepare(p_spdif->clk_spdifin); + clk_disable_unprepare(p_spdif->clk_spdifout); + clk_disable_unprepare(p_spdif->sysclk); + clk_disable_unprepare(p_spdif->fixed_clk); + clk_disable_unprepare(p_spdif->gate_spdifin); + clk_disable_unprepare(p_spdif->gate_spdifout); + + return 0; +} + +static int aml_dai_spdif_startup( + struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + + pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + + aml_spdif_fifo_reset(p_spdif->actrl, substream->stream); + + return 0; +} + +static void aml_dai_spdif_shutdown( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +} + +static int aml_dai_spdif_prepare( + struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int bit_depth = 0; + + pr_info("%s stream:%d\n", __func__, substream->stream); + + bit_depth = snd_pcm_format_width(runtime->format); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct frddr *fr = p_spdif->fddr; + + aml_frddr_select_dst(fr, SPDIFOUT); + aml_frddr_set_fifos(fr, 0x40, 0x20); + } else { + struct toddr *to = p_spdif->tddr; + unsigned int lsb, toddr_type; + + switch (bit_depth) { + case 8: + toddr_type = 0; + break; + case 16: + toddr_type = 1; + break; + case 24: + toddr_type = 4; + break; + case 32: + toddr_type = 3; + break; + default: + dev_err(p_spdif->dev, + "runtime format invalid bit_depth: %d\n", + bit_depth); + return -EINVAL; + } + + if (bit_depth <= 24) + lsb = 28 - bit_depth; + else + lsb = 4; + + // to ddr spdifin + aml_toddr_select_src(to, SPDIFIN); + aml_toddr_set_format(to, toddr_type, 27, lsb); + aml_toddr_set_fifos(to, 0x40); + } + + aml_spdif_fifo_ctrl(p_spdif->actrl, bit_depth, substream->stream); + + return 0; +} + +static int aml_dai_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev_info(substream->pcm->card->dev, "spdif playback enable\n"); + aml_frddr_enable(p_spdif->fddr, 1); + } else { + dev_info(substream->pcm->card->dev, "spdif capture enable\n"); + aml_toddr_enable(p_spdif->tddr, 1); + } + aml_spdif_enable(p_spdif->actrl, + substream->stream, true); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev_info(substream->pcm->card->dev, "spdif playback disable\n"); + aml_frddr_enable(p_spdif->fddr, 0); + } else { + dev_info(substream->pcm->card->dev, "spdif capture disable\n"); + aml_toddr_enable(p_spdif->tddr, 0); + } + aml_spdif_enable(p_spdif->actrl, + substream->stream, false); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int aml_dai_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + unsigned int rate = params_rate(params); + int ret = 0; + + pr_info("%s\n", __func__); + rate *= 128; + snd_soc_dai_set_sysclk(cpu_dai, + 0, rate, SND_SOC_CLOCK_OUT); + + return ret; +} + +static int aml_dai_set_spdif_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + + pr_info("asoc aml_dai_set_spdif_fmt, %#x, %p\n", fmt, p_spdif); + + return 0; +} + +static void aml_set_spdifclk(struct aml_spdif *p_spdif) +{ + unsigned int mpll_freq = 0; + + pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + if (p_spdif->sysclk_freq) { + unsigned int mul = 4; + + mpll_freq = p_spdif->sysclk_freq * mul; + clk_set_rate(p_spdif->sysclk, mpll_freq); + clk_set_rate(p_spdif->clk_spdifout, + p_spdif->sysclk_freq); + } +} + +static int aml_dai_set_spdif_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + + p_spdif->sysclk_freq = freq; + pr_info("aml_dai_set_spdif_sysclk, %d, %d, %d\n", + clk_id, freq, dir); + aml_set_spdifclk(p_spdif); + + return 0; +} + +static struct snd_soc_dai_ops aml_dai_spdif_ops = { + .startup = aml_dai_spdif_startup, + .shutdown = aml_dai_spdif_shutdown, + .prepare = aml_dai_spdif_prepare, + .trigger = aml_dai_spdif_trigger, + .hw_params = aml_dai_spdif_hw_params, + .set_fmt = aml_dai_set_spdif_fmt, + .set_sysclk = aml_dai_set_spdif_sysclk, +}; + +#define AML_DAI_SPDIF_RATES (SNDRV_PCM_RATE_8000_192000) +#define AML_DAI_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver aml_spdif_dai = { + .name = "SPDIF", + .id = 0, + .probe = aml_dai_spdif_probe, + .remove = aml_dai_spdif_remove, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = AML_DAI_SPDIF_RATES, + .formats = AML_DAI_SPDIF_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = AML_DAI_SPDIF_RATES, + .formats = AML_DAI_SPDIF_FORMATS, + }, + .ops = &aml_dai_spdif_ops, +}; + +static const struct snd_soc_component_driver aml_spdif_component = { + .name = DRV_NAME, +}; + +static int aml_spdif_clks_parse_of(struct aml_spdif *p_spdif) +{ + struct device *dev = p_spdif->dev; + + /* clock gate */ + p_spdif->gate_spdifin = devm_clk_get(dev, "gate_spdifin"); + if (IS_ERR(p_spdif->gate_spdifin)) { + dev_err(dev, "Can't get spdifin gate\n"); + return PTR_ERR(p_spdif->gate_spdifin); + } + + p_spdif->gate_spdifout = devm_clk_get(dev, "gate_spdifout"); + if (IS_ERR(p_spdif->gate_spdifout)) { + dev_err(dev, "Can't get spdifout gate\n"); + return PTR_ERR(p_spdif->gate_spdifout); + } + + p_spdif->sysclk = devm_clk_get(dev, "sysclk"); + if (IS_ERR(p_spdif->sysclk)) { + dev_err(dev, "Can't retrieve sysclk clock\n"); + return PTR_ERR(p_spdif->sysclk); + } + + p_spdif->fixed_clk = devm_clk_get(dev, "fixed_clk"); + if (IS_ERR(p_spdif->fixed_clk)) { + dev_err(dev, "Can't retrieve fixed_clk\n"); + return PTR_ERR(p_spdif->fixed_clk); + } + + p_spdif->clk_spdifin = devm_clk_get(dev, "clk_spdifin"); + if (IS_ERR(p_spdif->clk_spdifin)) { + dev_err(dev, "Can't retrieve spdifin clock\n"); + return PTR_ERR(p_spdif->clk_spdifin); + } + + p_spdif->clk_spdifout = devm_clk_get(dev, "clk_spdifout"); + if (IS_ERR(p_spdif->clk_spdifout)) { + dev_err(dev, "Can't retrieve spdifout clock\n"); + return PTR_ERR(p_spdif->clk_spdifout); + } + + return 0; +} + +static int aml_spdif_platform_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device_node *node_prt = NULL; + struct platform_device *pdev_parent; + struct device *dev = &pdev->dev; + struct aml_audio_controller *actrl = NULL; + struct aml_spdif *aml_spdif = NULL; + int ret = 0; + + aml_spdif = devm_kzalloc(dev, sizeof(struct aml_spdif), GFP_KERNEL); + if (!aml_spdif) + return -ENOMEM; + + aml_spdif->dev = dev; + dev_set_drvdata(dev, aml_spdif); + + /* get audio controller */ + node_prt = of_get_parent(node); + if (node_prt == NULL) + return -ENXIO; + + pdev_parent = of_find_device_by_node(node_prt); + of_node_put(node_prt); + actrl = (struct aml_audio_controller *) + platform_get_drvdata(pdev_parent); + aml_spdif->actrl = actrl; + + /* parse DTS configured ddr */ + ret = of_property_read_u32(node, "spdif_from_ddr", + &aml_spdif->from_ddr_num); + if (ret < 0) { + dev_err(&pdev->dev, "Can't retrieve spdif_from_ddr\n"); + return -ENXIO; + } + + ret = of_property_read_u32(node, "spdif_to_ddr", + &aml_spdif->to_ddr_num); + if (ret < 0) { + dev_err(&pdev->dev, "Can't retrieve spdif_to_ddr\n"); + return -ENXIO; + } + + ret = aml_spdif_clks_parse_of(aml_spdif); + if (ret) + return -EINVAL; + + /* irqs */ + aml_spdif->irq_spdifin = platform_get_irq_byname(pdev, "irq_spdifin"); + if (aml_spdif->irq_spdifin < 0) + dev_err(dev, "platform_get_irq_byname failed\n"); + + aml_spdif->irq_toddr = platform_get_irq_byname(pdev, "irq_toddr"); + if (aml_spdif->irq_toddr < 0) + dev_err(dev, "platform_get_irq_byname failed\n"); + + aml_spdif->irq_frddr = platform_get_irq_byname(pdev, "irq_frddr"); + if (aml_spdif->irq_frddr < 0) + dev_err(dev, "platform_get_irq_byname failed\n"); + + /* spdif pinmux */ + aml_spdif->pin_ctl = devm_pinctrl_get_select(dev, "spdif_pins"); + if (IS_ERR(aml_spdif->pin_ctl)) { + dev_info(dev, "aml_spdif_get_pins error!\n"); + return PTR_ERR(aml_spdif->pin_ctl); + } + + ret = devm_snd_soc_register_component(dev, &aml_spdif_component, + &aml_spdif_dai, 1); + if (ret) { + dev_err(dev, "devm_snd_soc_register_component failed\n"); + return ret; + } + + pr_info("%s, register soc platform\n", __func__); + + return devm_snd_soc_register_platform(dev, &aml_spdif_platform); +} + +static const struct of_device_id aml_spdif_device_id[] = { + { .compatible = "amlogic, snd-spdif" }, + {}, +}; +MODULE_DEVICE_TABLE(of, aml_spdif_device_id); + +struct platform_driver aml_spdif_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = aml_spdif_device_id, + }, + .probe = aml_spdif_platform_probe, +}; +module_platform_driver(aml_spdif_driver); + +MODULE_AUTHOR("Amlogic, Inc."); +MODULE_DESCRIPTION("Amlogic SPDIF ASoc driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, aml_spdif_device_id); diff --git a/sound/soc/amlogic/auge/spdif_hw.c b/sound/soc/amlogic/auge/spdif_hw.c new file mode 100644 index 000000000000..cb8719bc5201 --- /dev/null +++ b/sound/soc/amlogic/auge/spdif_hw.c @@ -0,0 +1,177 @@ +/* + * sound/soc/amlogic/auge/spdif_hw.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include + +#include "spdif_hw.h" + +void aml_spdif_enable( + struct aml_audio_controller *actrl, + int stream, + bool is_enable) +{ + pr_info("spdif stream :%d is_enable:%d\n", stream, is_enable); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFOUT_CTRL0, 1<<31, is_enable<<31); + } else { + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFIN_CTRL0, 1<<31, is_enable<<31); + } +} + +void aml_spdif_arb_config(struct aml_audio_controller *actrl) +{ + /* config ddr arb */ + aml_audiobus_write(actrl, EE_AUDIO_ARB_CTRL, 1<<31|0xff<<0); +} + +void aml_spdifin_status_check(struct aml_audio_controller *actrl) +{ + unsigned int val; + + val = aml_audiobus_read(actrl, + EE_AUDIO_SPDIFIN_STAT0); + + /* pr_info("\t--- spdif handles status0 %#x\n", val); */ + + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFIN_CTRL0, + 1<<26, + 1<<26); + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFIN_CTRL0, + 1<<26, + 0); +} + +void aml_spdif_fifo_reset( + struct aml_audio_controller *actrl, + int stream) +{ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* reset afifo */ + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFOUT_CTRL0, 3<<28, 0); + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFOUT_CTRL0, 1<<29, 1<<29); + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFOUT_CTRL0, 1<<28, 1<<28); + } else { + /* reset afifo */ + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFIN_CTRL0, 3<<28, 0); + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFIN_CTRL0, 1<<29, 1<<29); + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFIN_CTRL0, 1<<28, 1<<28); + } +} + +void aml_spdif_fifo_ctrl( + struct aml_audio_controller *actrl, + int bitwidth, + int stream) +{ + unsigned int frddr_type, toddr_type; + + switch (bitwidth) { + case 8: + frddr_type = 0; + toddr_type = 0; + break; + case 16: + frddr_type = 1; + toddr_type = 1; + break; + case 24: + frddr_type = 4; + toddr_type = 4; + break; + case 32: + frddr_type = 3; + toddr_type = 3; + break; + default: + pr_err("runtime format invalid bitwidth: %d\n", + bitwidth); + return; + } + + pr_info("%s, bit depth:%d, frddr type:%d, toddr:type:%d\n", + __func__, bitwidth, frddr_type, toddr_type); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + // mask lane 0 L/R channels + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFOUT_CTRL0, + 0x1<<29|0x1<<28|0x1<<20|0x1<<19|0xff<<4, + 1<<29|1<<28|0<<20|0<<19|0x3<<4); + + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFOUT_CTRL1, + 0x3 << 24 | 0x1f << 8 | 0x7 << 4, + 0 << 24 | (bitwidth - 1) << 8 | frddr_type<<4); + + aml_audiobus_write(actrl, + EE_AUDIO_SPDIFOUT_SWAP, + 1<<4); + } else { + unsigned int lsb; + + if (bitwidth <= 24) + lsb = 28 - bitwidth; + else + lsb = 4; + + // 250M + aml_audiobus_write(actrl, + EE_AUDIO_SPDIFIN_CTRL1, + 0xff << 20 | 25000 << 0); + + aml_audiobus_write(actrl, + EE_AUDIO_SPDIFIN_CTRL2, + 140 << 20 | 100 << 10 | 86 << 0); + + aml_audiobus_write(actrl, + EE_AUDIO_SPDIFIN_CTRL3, + 83 << 20 | 60 << 10 | 30 << 0); + + aml_audiobus_write(actrl, + EE_AUDIO_SPDIFIN_CTRL4, + (81<<24) | /* reg_sample_mode0_timer */ + (61<<16) | /* reg_sample_mode1_timer */ + (44<<8) | /* reg_sample_mode2_timer*/ + (42<<0) + ); + + aml_audiobus_write(actrl, + EE_AUDIO_SPDIFIN_CTRL5, + (40<<24) | /* reg_sample_mode4_timer = 5[31:24]; */ + (20<<16) | /* reg_sample_mode5_timer = 5[23:16]; */ + (9<<8) | /* reg_sample_mode6_timer = 5[15:8]; */ + (0<<0) /* reg_sample_mode7_timer = 5[7:0]; */ + ); + + aml_audiobus_update_bits(actrl, + EE_AUDIO_SPDIFIN_CTRL0, + 0x3<<24|1<<12, + 3<<24|1<<12); + } + +} diff --git a/sound/soc/amlogic/auge/spdif_hw.h b/sound/soc/amlogic/auge/spdif_hw.h new file mode 100644 index 000000000000..696483f7473f --- /dev/null +++ b/sound/soc/amlogic/auge/spdif_hw.h @@ -0,0 +1,41 @@ +/* + * sound/soc/amlogic/auge/spdif_hw.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_SPDIF_HW_H__ +#define __AML_SPDIF_HW_H__ +#include "audio_io.h" +#include "regs.h" + +extern void aml_spdif_enable( + struct aml_audio_controller *actrl, + int stream, + bool is_enable); + +extern void aml_spdif_arb_config(struct aml_audio_controller *actrl); + +extern void aml_spdifin_status_check( + struct aml_audio_controller *actrl); + +extern void aml_spdif_fifo_reset( + struct aml_audio_controller *actrl, + int stream); + +extern void aml_spdif_fifo_ctrl( + struct aml_audio_controller *actrl, + int bitwidth, + int stream); +#endif diff --git a/sound/soc/amlogic/auge/tdm.c b/sound/soc/amlogic/auge/tdm.c new file mode 100644 index 000000000000..a4df89830cc1 --- /dev/null +++ b/sound/soc/amlogic/auge/tdm.c @@ -0,0 +1,954 @@ +/* + * sound/soc/amlogic/auge/tdm.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ddr_mngr.h" +#include "tdm_hw.h" + +#define DRV_NAME "aml_tdm" + +#define TDM_A 0 +#define TDM_B 1 +#define TDM_C 2 +#define LANE_MAX 4 + +static void dump_pcm_setting(struct pcm_setting *setting) +{ + if (setting == NULL) + return; + + pr_info("dump_pcm_setting(%p)\n", setting); + pr_info("\tpcm_mode(%d)\n", setting->pcm_mode); + pr_info("\tsysclk(%d)\n", setting->sysclk); + pr_info("\tsysclk_bclk_ratio(%d)\n", setting->sysclk_bclk_ratio); + pr_info("\tbclk(%d)\n", setting->bclk); + pr_info("\tbclk_lrclk_ratio(%d)\n", setting->bclk_lrclk_ratio); + pr_info("\tlrclk(%d)\n", setting->lrclk); + pr_info("\ttx_mask(%#x)\n", setting->tx_mask); + pr_info("\trx_mask(%#x)\n", setting->rx_mask); + pr_info("\tslots(%d)\n", setting->slots); + pr_info("\tslot_width(%d)\n", setting->slot_width); + pr_info("\tlane_mask_in(%#x)\n", setting->lane_mask_in); + pr_info("\tlane_mask_out(%#x)\n", setting->lane_mask_out); +} + +struct aml_tdm { + struct pcm_setting setting; + struct pinctrl *pin_ctl; + struct aml_audio_controller *actrl; + struct device *dev; + struct clk *clk; + struct clk *clk_gate; + struct clk *mclk; + unsigned int id; + /* bclk src selection */ + unsigned int clk_sel; + int irq_tdmout; + int irq_tdmin; + unsigned int from_ddr_num; + unsigned int to_ddr_num; + struct toddr *tddr; + struct frddr *fddr; +}; + +static const struct snd_pcm_hardware aml_tdm_hardware = { + .info = + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE, + .formats = + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + + .period_bytes_min = 64, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 256 * 1024, + + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 32, +}; + +static irqreturn_t aml_tdmout_isr(int irq, void *devid) +{ + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)devid; + + snd_pcm_period_elapsed(substream); + + return IRQ_HANDLED; +} + +static irqreturn_t aml_tdmin_isr(int irq, void *devid) +{ + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)devid; + + snd_pcm_period_elapsed(substream); + + return IRQ_HANDLED; +} + +/* get counts of '1's in val */ +static unsigned int pop_count(unsigned int val) +{ + unsigned int count = 0; + + while (val) { + count++; + val = val & (val - 1); + } + + return count; +} + +static int snd_soc_of_get_slot_mask(struct device_node *np, + const char *prop_name, + unsigned int *mask) +{ + u32 val; + const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); + int i; + + if (!of_slot_mask) + return 0; + + val /= sizeof(u32); + for (i = 0; i < val; i++) + if (be32_to_cpup(&of_slot_mask[i])) + *mask |= (1 << i); + + return val; +} + +static int of_parse_tdm_lane_slot_in(struct device_node *np, + unsigned int *lane_mask) +{ + if (lane_mask) + return snd_soc_of_get_slot_mask(np, + "dai-tdm-lane-slot-mask-in", lane_mask); + + return -EINVAL; +} + +static int of_parse_tdm_lane_slot_out(struct device_node *np, + unsigned int *lane_mask) +{ + if (lane_mask) + return snd_soc_of_get_slot_mask(np, + "dai-tdm-lane-slot-mask-out", lane_mask); + + return -EINVAL; +} + +static int aml_tdm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct device *dev = rtd->platform->dev; + struct aml_tdm *p_tdm; + int ret = 0; + + p_tdm = (struct aml_tdm *)dev_get_drvdata(dev); + + snd_soc_set_runtime_hwparams(substream, &aml_tdm_hardware); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + p_tdm->fddr = aml_audio_register_frddr(dev, + p_tdm->actrl, p_tdm->from_ddr_num); + if (p_tdm->fddr == NULL) { + dev_err(dev, "failed to claim from ddr %u\n", + p_tdm->from_ddr_num); + return -ENXIO; + } + + ret = request_irq(p_tdm->irq_tdmout, + aml_tdmout_isr, 0, "tdmout", substream); + if (ret) { + aml_audio_unregister_frddr(dev, + p_tdm->from_ddr_num); + dev_err(p_tdm->dev, "failed to claim irq %u\n", + p_tdm->irq_tdmout); + return ret; + } + } else { + p_tdm->tddr = aml_audio_register_toddr(dev, + p_tdm->actrl, p_tdm->to_ddr_num); + if (p_tdm->tddr == NULL) { + dev_err(dev, "failed to claim to ddr %u\n", + p_tdm->to_ddr_num); + return -ENXIO; + } + + ret = request_irq(p_tdm->irq_tdmin, + aml_tdmin_isr, 0, "tdmin", substream); + if (ret) { + aml_audio_unregister_toddr(dev, p_tdm->to_ddr_num); + dev_err(p_tdm->dev, "failed to claim irq %u\n", + p_tdm->irq_tdmin); + return ret; + } + } + + runtime->private_data = p_tdm; + return 0; +} + +static int aml_tdm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_tdm *p_tdm = runtime->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aml_audio_unregister_frddr(p_tdm->dev, + p_tdm->from_ddr_num); + free_irq(p_tdm->irq_tdmout, substream); + } else { + aml_audio_unregister_toddr(p_tdm->dev, + p_tdm->to_ddr_num); + + free_irq(p_tdm->irq_tdmin, substream); + } + + return 0; +} + +static int aml_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int aml_tdm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int aml_tdm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_tdm *p_tdm = runtime->private_data; + unsigned int start_addr, end_addr, int_addr; + + start_addr = runtime->dma_addr; + end_addr = start_addr + runtime->dma_bytes - 8; + int_addr = frames_to_bytes(runtime, runtime->period_size)/8; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct frddr *fr = p_tdm->fddr; + + aml_frddr_set_buf(fr, start_addr, end_addr); + aml_frddr_set_intrpt(fr, int_addr); + } else { + struct toddr *to = p_tdm->tddr; + + aml_toddr_set_buf(to, start_addr, end_addr); + aml_toddr_set_intrpt(to, int_addr); + } + + return 0; +} + +static snd_pcm_uframes_t aml_tdm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_tdm *p_tdm = runtime->private_data; + unsigned int addr, start_addr; + + start_addr = runtime->dma_addr; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + addr = aml_frddr_get_position(p_tdm->fddr); + else + addr = aml_toddr_get_position(p_tdm->tddr); + + return bytes_to_frames(runtime, addr - start_addr); +} + +static int aml_tdm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return snd_pcm_lib_default_mmap(substream, vma); +} + +static struct snd_pcm_ops aml_tdm_ops = { + .open = aml_tdm_open, + .close = aml_tdm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = aml_tdm_hw_params, + .hw_free = aml_tdm_hw_free, + .prepare = aml_tdm_prepare, + .pointer = aml_tdm_pointer, + .mmap = aml_tdm_mmap, +}; + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (256 * 1024) +static int aml_tdm_new(struct snd_soc_pcm_runtime *rtd) +{ + return snd_pcm_lib_preallocate_pages_for_all( + rtd->pcm, SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); +} + +struct snd_soc_platform_driver aml_tdm_platform = { + .ops = &aml_tdm_ops, + .pcm_new = aml_tdm_new, +}; + +static int aml_dai_tdm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + + aml_tdm_fifo_reset(p_tdm->actrl, substream->stream, p_tdm->id); + + return 0; +} + +static void aml_dai_tdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +} + +static int aml_dai_tdm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + int bit_depth; + + bit_depth = snd_pcm_format_width(runtime->format); + + aml_tdm_fifo_ctrl(p_tdm->actrl, + bit_depth, + substream->stream, + p_tdm->id); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct frddr *fr = p_tdm->fddr; + enum frddr_dest dst; + + switch (p_tdm->id) { + case 0: + dst = TDMOUT_A; + break; + case 1: + dst = TDMOUT_B; + break; + case 2: + dst = TDMOUT_C; + break; + default: + dev_err(p_tdm->dev, "invalid id: %d\n", + p_tdm->id); + return -EINVAL; + } + aml_frddr_select_dst(fr, dst); + aml_frddr_set_fifos(fr, 0x40, 0x20); + } else { + struct toddr *to = p_tdm->tddr; + enum toddr_src src; + unsigned int lsb = 32 - bit_depth; + unsigned int toddr_type; + + switch (bit_depth) { + case 8: + toddr_type = 0; + break; + case 16: + toddr_type = 2; + break; + case 24: + case 32: + toddr_type = 4; + break; + default: + dev_err(p_tdm->dev, "invalid bit_depth: %d\n", + bit_depth); + return -EINVAL; + } + + dev_info(substream->pcm->card->dev, "tdm prepare----capture\n"); + switch (p_tdm->id) { + case 0: + src = TDMIN_A; + break; + case 1: + src = TDMIN_B; + break; + case 2: + src = TDMIN_C; + break; + default: + dev_err(p_tdm->dev, "invalid id: %d\n", + p_tdm->id); + return -EINVAL; + } + + aml_toddr_select_src(to, src); + aml_toddr_set_format(to, toddr_type, 31, lsb); + aml_toddr_set_fifos(to, 0x40); + } + + return 0; +} + +static int aml_dai_tdm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + aml_tdm_enable(p_tdm->actrl, + substream->stream, p_tdm->id, true); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev_info(substream->pcm->card->dev, "tdm playback enable\n"); + aml_frddr_enable(p_tdm->fddr, 1); + } else { + dev_info(substream->pcm->card->dev, "tdm capture enable\n"); + aml_toddr_enable(p_tdm->tddr, 1); + } + + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + aml_tdm_enable(p_tdm->actrl, + substream->stream, p_tdm->id, false); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev_info(substream->pcm->card->dev, "tdm playback enable\n"); + aml_frddr_enable(p_tdm->fddr, 0); + } else { + dev_info(substream->pcm->card->dev, "tdm capture enable\n"); + aml_toddr_enable(p_tdm->tddr, 0); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int pcm_setting_init(struct pcm_setting *setting, unsigned int rate, + unsigned int channels) +{ + + setting->lrclk = rate; + setting->bclk_lrclk_ratio = setting->slots * setting->slot_width; + setting->bclk = setting->lrclk * setting->bclk_lrclk_ratio; + + /* calculate mclk */ + setting->sysclk_bclk_ratio = 4; + setting->sysclk = 4 * setting->bclk; + + return 0; +} +static int aml_tdm_set_lanes(struct aml_tdm *p_tdm, + unsigned int channels, int stream) +{ + struct pcm_setting *setting = &p_tdm->setting; + unsigned int lanes, swap_val; + unsigned int i; + + pr_info("asoc debug: %d-%d\n", channels, setting->slots); + + swap_val = 0; + // assume mask channels one lane + lanes = (channels - 1) / setting->slots + 1; + + pr_info("asoc debug: lanes_ddr = %d\n", lanes); + + // set channels swap + for (i = 0; i < channels; i++) + swap_val |= i << (i * 4); + aml_tdm_set_lane_channel_swap(p_tdm->actrl, + stream, p_tdm->id, swap_val); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + aml_tdm_set_channel_mask(p_tdm->actrl, + stream, p_tdm->id, lanes, setting->tx_mask); + + if (pop_count(setting->tx_mask) > 2) + swap_val = 1 << 4; + + aml_tdm_set_lane_channel_swap(p_tdm->actrl, + stream, p_tdm->id, swap_val); + } else { + aml_tdm_set_channel_mask(p_tdm->actrl, + stream, p_tdm->id, lanes, setting->rx_mask); + + aml_tdm_set_lane_channel_swap(p_tdm->actrl, + stream, p_tdm->id, swap_val); + } + + return 0; +} + +static int aml_dai_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + struct pcm_setting *setting = &p_tdm->setting; + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + int ret; + + + ret = pcm_setting_init(setting, rate, channels); + if (ret) + return ret; + + dump_pcm_setting(setting); + + /* set pcm dai hw params */ + // TODO: add clk_id + snd_soc_dai_set_sysclk(cpu_dai, + 0, setting->sysclk, SND_SOC_CLOCK_OUT); + ret = snd_soc_dai_set_clkdiv(cpu_dai, 0, setting->sysclk_bclk_ratio); + if (ret) + return ret; + + ret = snd_soc_dai_set_bclk_ratio(cpu_dai, setting->bclk_lrclk_ratio); + if (ret) + return ret; + + ret = aml_tdm_set_lanes(p_tdm, channels, substream->stream); + if (ret) + return ret; + + return 0; +} + +static int aml_dai_tdm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + + aml_tdm_set_channel_mask(p_tdm->actrl, + substream->stream, p_tdm->id, 4, 0); + + return 0; +} + +static int aml_dai_set_tdm_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + + pr_info("asoc aml_dai_set_tdm_fmt, %#x, %p, id(%d), clksel(%d)\n", + fmt, p_tdm, p_tdm->id, p_tdm->clk_sel); + + aml_tdm_set_format(p_tdm->actrl, + &(p_tdm->setting), p_tdm->clk_sel, p_tdm->id, fmt); + + return 0; +} + +static void aml_tdm_set_mclk(struct aml_tdm *p_tdm) +{ + struct aml_audio_controller *actrl = p_tdm->actrl; + unsigned int clk_id, offset; + unsigned int mpll_freq = 0; + + offset = p_tdm->clk_sel; + + /* slave mode */ + if (offset > MASTER_F) + return; + + clk_id = p_tdm->id; + + if (p_tdm->setting.sysclk) { + unsigned int mul = 4; + + mpll_freq = p_tdm->setting.sysclk * mul; + clk_set_rate(p_tdm->clk, mpll_freq); + aml_audiobus_write(actrl, EE_AUDIO_MCLK_A_CTRL + offset, + 1 << 31 | //clk enable + clk_id << 24 | // clk src + (mul - 1)); //clk_div mclk + + if (offset == 2) { + //enable another mclka also; + offset = 0; + aml_audiobus_write(actrl, EE_AUDIO_MCLK_A_CTRL + offset, + 1 << 31 | //clk enable + clk_id << 24 | // clk src + (mul - 1)); //clk_div mclk + } + } +} + +static int aml_dai_set_tdm_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + + p_tdm->setting.sysclk = freq; + pr_info("aml_dai_set_tdm_sysclk, %d, %d, %d\n", + clk_id, freq, dir); + aml_tdm_set_mclk(p_tdm); + + return 0; +} + +static int aml_dai_set_bclk_ratio(struct snd_soc_dai *cpu_dai, + unsigned int ratio) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int bclk_ratio, lrclk_hi; + + p_tdm->setting.bclk_lrclk_ratio = ratio; + bclk_ratio = ratio - 1; + lrclk_hi = 0; + + if (p_tdm->setting.pcm_mode == SND_SOC_DAIFMT_I2S || + p_tdm->setting.pcm_mode == SND_SOC_DAIFMT_LEFT_J) { + pr_info("aml_dai_set_bclk_ratio, select I2S mode\n"); + lrclk_hi = bclk_ratio / 2; + } else { + pr_info("aml_dai_set_bclk_ratio, select TDM mode\n"); + } + aml_tdm_set_bclk_ratio(p_tdm->actrl, + p_tdm->clk_sel, lrclk_hi, bclk_ratio); + + return 0; +} + +static int aml_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int mclk_ratio; + + pr_info("aml_dai_set_clkdiv, div %d, clksel(%d)\n", + div, p_tdm->clk_sel); + + p_tdm->setting.sysclk_bclk_ratio = div; + mclk_ratio = div - 1; + aml_tdm_set_lrclkdiv(p_tdm->actrl, p_tdm->clk_sel, mclk_ratio); + + return 0; +} + +static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + + pr_info("aml_dai_set_tdm_slot, %x, %x, %d, %d\n", + tx_mask, rx_mask, slots, slot_width); + p_tdm->setting.tx_mask = tx_mask; + p_tdm->setting.rx_mask = rx_mask; + p_tdm->setting.slots = slots; + p_tdm->setting.slot_width = slot_width; + + aml_tdm_set_slot(p_tdm->actrl, slots, slot_width, p_tdm->id); + + return 0; +} + +static int aml_dai_tdm_probe(struct snd_soc_dai *cpu_dai) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = p_tdm->dev; + int ret; + + /* config ddr arb */ + aml_tdm_arb_config(p_tdm->actrl); + + ret = clk_prepare_enable(p_tdm->clk); + if (ret) { + dev_err(dev, "Can't enable mpll clock: %d\n", ret); + return ret; + } + + return 0; +} + +static int aml_dai_tdm_remove(struct snd_soc_dai *cpu_dai) +{ + struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai); + + clk_disable_unprepare(p_tdm->clk); + return 0; +} + +static struct snd_soc_dai_ops aml_dai_tdm_ops = { + .startup = aml_dai_tdm_startup, + .shutdown = aml_dai_tdm_shutdown, + .prepare = aml_dai_tdm_prepare, + .trigger = aml_dai_tdm_trigger, + .hw_params = aml_dai_tdm_hw_params, + .hw_free = aml_dai_tdm_hw_free, + .set_fmt = aml_dai_set_tdm_fmt, + .set_sysclk = aml_dai_set_tdm_sysclk, + .set_bclk_ratio = aml_dai_set_bclk_ratio, + .set_clkdiv = aml_dai_set_clkdiv, + .set_tdm_slot = aml_dai_set_tdm_slot, +}; + +#define AML_DAI_TDM_RATES (SNDRV_PCM_RATE_8000_192000) +#define AML_DAI_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver aml_tdm_dai[] = { + { + .name = "TDM-A", + .id = 1, + .probe = aml_dai_tdm_probe, + .remove = aml_dai_tdm_remove, + .playback = { + .channels_min = 1, + .channels_max = 32, + .rates = AML_DAI_TDM_RATES, + .formats = AML_DAI_TDM_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 32, + .rates = AML_DAI_TDM_RATES, + .formats = AML_DAI_TDM_FORMATS, + }, + .ops = &aml_dai_tdm_ops, + .symmetric_rates = 1, + }, + { + .name = "TDM-B", + .id = 2, + .probe = aml_dai_tdm_probe, + .remove = aml_dai_tdm_remove, + .playback = { + .channels_min = 1, + .channels_max = 8, + .rates = AML_DAI_TDM_RATES, + .formats = AML_DAI_TDM_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + .rates = AML_DAI_TDM_RATES, + .formats = AML_DAI_TDM_FORMATS, + }, + .ops = &aml_dai_tdm_ops, + .symmetric_rates = 1, + }, + { + .name = "TDM-C", + .id = 3, + .probe = aml_dai_tdm_probe, + .remove = aml_dai_tdm_remove, + .playback = { + .channels_min = 1, + .channels_max = 8, + .rates = AML_DAI_TDM_RATES, + .formats = AML_DAI_TDM_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + .rates = AML_DAI_TDM_RATES, + .formats = AML_DAI_TDM_FORMATS, + }, + .ops = &aml_dai_tdm_ops, + .symmetric_rates = 1, + }, +}; + +static const struct snd_soc_component_driver aml_tdm_component = { + .name = DRV_NAME, +}; + +static const struct of_device_id aml_tdm_device_id[] = { + { .compatible = "amlogic, snd-tdma", .data = (void *)TDM_A}, + { .compatible = "amlogic, snd-tdmb", .data = (void *)TDM_B}, + { .compatible = "amlogic, snd-tdmc", .data = (void *)TDM_C}, + {}, +}; +MODULE_DEVICE_TABLE(of, aml_tdm_device_id); + +static int aml_tdm_platform_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device_node *node_prt = NULL; + struct platform_device *pdev_parent; + struct device *dev = &pdev->dev; + struct aml_audio_controller *actrl = NULL; + struct aml_tdm *p_tdm = NULL; + const struct of_device_id *id; + unsigned long iddata = 0; + int ret = 0; + + p_tdm = devm_kzalloc(dev, sizeof(struct aml_tdm), GFP_KERNEL); + if (!p_tdm) + return -ENOMEM; + + + /* get tdm device id */ + id = of_match_device(of_match_ptr(aml_tdm_device_id), dev); + pr_info("id = %lu\n", (unsigned long)id->data); + iddata = (unsigned long)id->data; + p_tdm->id = (unsigned int)iddata; + + /* get audio controller */ + node_prt = of_get_parent(node); + if (node_prt == NULL) + return -ENXIO; + + pdev_parent = of_find_device_by_node(node_prt); + of_node_put(node_prt); + actrl = (struct aml_audio_controller *) + platform_get_drvdata(pdev_parent); + p_tdm->actrl = actrl; + + /* get tdm mclk sel configs */ + ret = of_property_read_u32(node, "dai-tdm-clk-sel", + &p_tdm->clk_sel); + if (ret < 0) { + dev_err(&pdev->dev, "Can't retrieve dai-tdm-clk-sel\n"); + return -ENXIO; + } + + /* get tdm lanes info. if not, set to default 1 */ + ret = of_parse_tdm_lane_slot_in(node, + &p_tdm->setting.lane_mask_in); + if (ret < 0) + p_tdm->setting.lane_mask_in = 0x1; + + ret = of_parse_tdm_lane_slot_out(node, + &p_tdm->setting.lane_mask_out); + if (ret < 0) + p_tdm->setting.lane_mask_out = 0x1; + + /* get sysclk source */ + if (iddata == TDM_A) { + p_tdm->clk = devm_clk_get(&pdev->dev, "mpll0"); + if (IS_ERR(p_tdm->clk)) { + dev_err(&pdev->dev, "Can't retrieve mpll0 clock\n"); + return PTR_ERR(p_tdm->clk); + } +#if 0 + p_tdm->clk_gate = devm_clk_get(&pdev->dev, "gate"); + if (IS_ERR(p_tdm->clk_gate)) { + dev_err(&pdev->dev, "Can't retrieve clockgate\n"); + return PTR_ERR(p_tdm->clk_gate); + } + + p_tdm->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(p_tdm->mclk)) { + dev_err(&pdev->dev, "Can't retrieve mclk\n"); + return PTR_ERR(p_tdm->mclk); + } +#endif + } else if (iddata == TDM_B) { + p_tdm->clk = devm_clk_get(&pdev->dev, "mpll1"); + if (IS_ERR(p_tdm->clk)) { + dev_err(&pdev->dev, "Can't retrieve mpll1 clock\n"); + return PTR_ERR(p_tdm->clk); + } + } else if (iddata == TDM_C) { + p_tdm->clk = devm_clk_get(&pdev->dev, "mpll2"); + if (IS_ERR(p_tdm->clk)) { + dev_err(&pdev->dev, "Can't retrieve mpll2 clock\n"); + return PTR_ERR(p_tdm->clk); + } + } else { + return -EINVAL; + } + + /* parse DTS configured ddr */ + ret = of_property_read_u32(node, "tdm_from_ddr", + &p_tdm->from_ddr_num); + if (ret < 0) { + dev_err(&pdev->dev, "Can't retrieve tdm_from_ddr\n"); + return -ENXIO; + } + + ret = of_property_read_u32(node, "tdm_to_ddr", + &p_tdm->to_ddr_num); + if (ret < 0) { + dev_err(&pdev->dev, "Can't retrieve tdm_to_ddr\n"); + return -ENXIO; + } + + /* irqs */ + p_tdm->irq_tdmout = platform_get_irq_byname(pdev, "tdmout"); + if (p_tdm->irq_tdmout < 0) + dev_err(dev, "platform_get_irq_byname failed\n"); + + p_tdm->irq_tdmin = platform_get_irq_byname(pdev, "tdmin"); + if (p_tdm->irq_tdmin < 0) + dev_err(dev, "platform_get_irq_byname failed\n"); + + p_tdm->pin_ctl = devm_pinctrl_get_select(dev, "tdm_pins"); + if (IS_ERR(p_tdm->pin_ctl)) { + dev_info(dev, "aml_tdm_get_pins error!\n"); + /*return PTR_ERR(p_tdm->pin_ctl);*/ + } + + p_tdm->dev = dev; + dev_set_drvdata(dev, p_tdm); + + ret = devm_snd_soc_register_component(dev, &aml_tdm_component, + &aml_tdm_dai[iddata], 1); + if (ret) { + dev_err(dev, "devm_snd_soc_register_component failed\n"); + return ret; + } + + pr_info("%s, try register soc platform\n", __func__); + + return devm_snd_soc_register_platform(dev, &aml_tdm_platform); +} + +struct platform_driver aml_tdm_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = aml_tdm_device_id, + }, + .probe = aml_tdm_platform_probe, +}; +module_platform_driver(aml_tdm_driver); + +MODULE_AUTHOR("Amlogic, Inc."); +MODULE_DESCRIPTION("Amlogic TDM ASoc driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, aml_tdm_device_id); diff --git a/sound/soc/amlogic/auge/tdm_hw.c b/sound/soc/amlogic/auge/tdm_hw.c new file mode 100644 index 000000000000..1372dffbc08c --- /dev/null +++ b/sound/soc/amlogic/auge/tdm_hw.c @@ -0,0 +1,348 @@ +/* + * sound/soc/amlogic/auge/tdm_hw.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ +#include + +#include "tdm_hw.h" + +void aml_tdm_enable( + struct aml_audio_controller *actrl, + int stream, int index, + bool is_enable) +{ + unsigned int offset, reg; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("tdm playback enable\n"); + + offset = EE_AUDIO_TDMOUT_B_CTRL0 + - EE_AUDIO_TDMOUT_A_CTRL0; + reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index; + aml_audiobus_update_bits(actrl, reg, 1<<31, is_enable<<31); + } else { + pr_info("tdm capture enable\n"); + + offset = EE_AUDIO_TDMIN_B_CTRL + - EE_AUDIO_TDMIN_A_CTRL; + reg = EE_AUDIO_TDMIN_A_CTRL + offset * index; + aml_audiobus_update_bits(actrl, reg, 1<<31, is_enable<<31); + } + +} + +void aml_tdm_arb_config(struct aml_audio_controller *actrl) +{ + /* config ddr arb */ + aml_audiobus_write(actrl, EE_AUDIO_ARB_CTRL, 1<<31|0xff<<0); +} + +void aml_tdm_fifo_reset( + struct aml_audio_controller *actrl, + int stream, int index) +{ + unsigned int reg, offset; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + offset = EE_AUDIO_TDMOUT_B_CTRL0 + - EE_AUDIO_TDMOUT_A_CTRL0; + reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index; + /* reset afifo */ + aml_audiobus_update_bits(actrl, reg, 3<<28, 0); + aml_audiobus_update_bits(actrl, reg, 1<<29, 1<<29); + aml_audiobus_update_bits(actrl, reg, 1<<28, 1<<28); + } else { + offset = EE_AUDIO_TDMIN_B_CTRL + - EE_AUDIO_TDMIN_A_CTRL; + reg = EE_AUDIO_TDMIN_A_CTRL + offset * index; + /* reset afifo */ + aml_audiobus_update_bits(actrl, reg, 3<<28, 0); + aml_audiobus_update_bits(actrl, reg, 1<<29, 1<<29); + aml_audiobus_update_bits(actrl, reg, 1<<28, 1<<28); + } + +} + +void aml_tdm_fifo_ctrl( + struct aml_audio_controller *actrl, + int bitwidth, int stream, + int index) +{ + unsigned int frddr_type, toddr_type; + unsigned int reg, offset; + + switch (bitwidth) { + case 8: + frddr_type = 0; + toddr_type = 0; + break; + case 16: + frddr_type = 2; + toddr_type = 2; + break; + case 24: + case 32: + frddr_type = 4; + toddr_type = 4; + break; + default: + pr_err("invalid bit_depth: %d\n", + bitwidth); + return; + } + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("tdm prepare----playback\n"); + // from ddr, 63bit split into 2 samples + offset = EE_AUDIO_TDMOUT_B_CTRL1 + - EE_AUDIO_TDMOUT_A_CTRL1; + reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * index; + aml_audiobus_update_bits(actrl, reg, + 0x3<<24|0x1f<<8|0x7<<4, + index<<24|(bitwidth-1)<<8|frddr_type<<4); + } else { + pr_info("tdm prepare----capture\n"); + } + +} + +void aml_tdm_set_format( + struct aml_audio_controller *actrl, + struct pcm_setting *p_config, + unsigned int clk_sel, + unsigned int index, + unsigned int fmt) +{ + unsigned int binv, finv, id; + unsigned int valb, valf; + unsigned int reg_in, reg_out, off_set; + int bclkin_skew, bclkout_skew; + int master_mode; + + id = index; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + valb = SLAVE_A + id; + valf = SLAVE_A; + master_mode = 0; + break; + case SND_SOC_DAIFMT_CBS_CFS: + valb = MASTER_A + clk_sel; + valf = MASTER_A + clk_sel; + master_mode = 1; + break; + default: + return; + } + + //TODO: clk tree + reg_out = EE_AUDIO_CLK_TDMOUT_A_CTRL + id; + reg_in = EE_AUDIO_CLK_TDMIN_A_CTRL + id; + aml_audiobus_update_bits(actrl, + reg_out, + 0xff<<20, + valb<<24|valf<<20); + aml_audiobus_update_bits(actrl, + reg_in, + 0xff<<20, + valb<<24|valf<<20); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if (master_mode) { + bclkout_skew = 1; + bclkin_skew = 5; + } else { + bclkout_skew = 2; + bclkin_skew = 3; + } + break; + case SND_SOC_DAIFMT_DSP_A: + if (master_mode) { + bclkout_skew = 1; + bclkin_skew = 4; + } else { + bclkout_skew = 2; + bclkin_skew = 3; + } + break; + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_DSP_B: + //TODO: need test + bclkout_skew = 2; + bclkin_skew = 2; + break; + default: + return; + } + + p_config->pcm_mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* set lrclk/bclk invertion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + /* Invert both clocks */ + binv = 1; + finv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + /* Invert bit clock */ + binv = 1; + finv = 0; + break; + case SND_SOC_DAIFMT_NB_IF: + /* Invert frame clock */ + binv = 0; + finv = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + /* normal cases */ + binv = 0; + finv = 0; + break; + default: + return; + } + + pr_info("master_mode(%d), binv(%d), finv(%d) out_skew(%d), in_skew(%d)\n", + master_mode, binv, finv, bclkout_skew, bclkin_skew); + + /* TDM out */ + reg_out = EE_AUDIO_CLK_TDMOUT_A_CTRL + id; + aml_audiobus_update_bits(actrl, reg_out, + 0x3<<30|0x1<<29, 0x3<<30/*|binv<<29*/); + // sclk_ph0 (pad) invert + off_set = EE_AUDIO_MST_B_SCLK_CTRL1 - EE_AUDIO_MST_A_SCLK_CTRL1; + reg_out = EE_AUDIO_MST_A_SCLK_CTRL1 + off_set * id; + aml_audiobus_update_bits(actrl, reg_out, 0x3f, binv); + + off_set = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0; + reg_out = EE_AUDIO_TDMOUT_A_CTRL0 + off_set * id; + aml_audiobus_update_bits(actrl, reg_out, 0x1f<<15, bclkout_skew<<15); + + off_set = EE_AUDIO_TDMOUT_B_CTRL1 - EE_AUDIO_TDMOUT_A_CTRL1; + reg_out = EE_AUDIO_TDMOUT_A_CTRL1 + off_set * id; + aml_audiobus_update_bits(actrl, reg_out, 0x1<<28, finv<<28); + + /* TDM in */ + reg_in = EE_AUDIO_CLK_TDMIN_A_CTRL + id; + aml_audiobus_update_bits(actrl, reg_in, + 0x3<<30|0x1<<29, 0x3<<30|binv<<29); + + off_set = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL; + reg_in = EE_AUDIO_TDMIN_A_CTRL + off_set * id; + if (p_config->pcm_mode == SND_SOC_DAIFMT_I2S) + aml_audiobus_update_bits(actrl, reg_in, + 1<<30|3<<26|0x1<<25|0x7<<16, + 1<<30|3<<26|1<<25|bclkin_skew<<16); + else + aml_audiobus_update_bits(actrl, reg_in, + 3<<26|0x7<<16, 3<<26|bclkin_skew<<16); + +} + +void aml_tdm_set_slot( + struct aml_audio_controller *actrl, + int slots, int slot_width, int index) +{ + unsigned int reg, offset; + + offset = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0; + reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index; + aml_audiobus_update_bits(actrl, reg, + 0x3ff, ((slots - 1) << 5) | (slot_width - 1)); + + offset = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL; + reg = EE_AUDIO_TDMIN_A_CTRL + offset * index; + aml_audiobus_update_bits(actrl, reg, + 0xf<<20|0x7<<16|0x1f, index<<20|(slot_width-1)); +} + +void aml_tdm_set_channel_mask( + struct aml_audio_controller *actrl, + int stream, int index, int lanes, int mask) +{ + unsigned int offset, reg; + int i; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + offset = EE_AUDIO_TDMOUT_B_MASK0 - EE_AUDIO_TDMOUT_A_MASK0; + reg = EE_AUDIO_TDMOUT_A_MASK0 + offset * index; + } else { + offset = EE_AUDIO_TDMIN_B_MASK0 - EE_AUDIO_TDMIN_A_MASK0; + reg = EE_AUDIO_TDMIN_A_MASK0 + offset * index; + } + + /* mask 0~3 */ + for (i = 0; i < lanes; i++) + aml_audiobus_write(actrl, reg + i, mask); + +} + +void aml_tdm_set_lane_channel_swap( + struct aml_audio_controller *actrl, + int stream, int index, int swap) +{ + unsigned int offset, reg; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + // set lanes mask acordingly + offset = EE_AUDIO_TDMOUT_B_MASK0 - EE_AUDIO_TDMOUT_A_MASK0; + reg = EE_AUDIO_TDMOUT_A_MASK0 + offset * index; + + pr_info("\ttdmout swap val = %#x\n", swap); + offset = EE_AUDIO_TDMOUT_B_SWAP - EE_AUDIO_TDMOUT_A_SWAP; + reg = EE_AUDIO_TDMOUT_A_SWAP + offset * index; + aml_audiobus_write(actrl, reg, swap); + } else { + offset = EE_AUDIO_TDMIN_B_MASK0 - EE_AUDIO_TDMIN_A_MASK0; + reg = EE_AUDIO_TDMIN_A_MASK0 + offset * index; + + pr_info("\ttdmin swap val = %#x\n", swap); + offset = EE_AUDIO_TDMIN_B_SWAP - EE_AUDIO_TDMIN_A_SWAP; + reg = EE_AUDIO_TDMIN_A_SWAP + offset * index; + aml_audiobus_write(actrl, reg, swap); + } +} + +void aml_tdm_set_bclk_ratio( + struct aml_audio_controller *actrl, + int clk_sel, int lrclk_hi, int bclk_ratio) +{ + unsigned int reg, reg_step = 2; + + reg = EE_AUDIO_MST_A_SCLK_CTRL0 + reg_step * clk_sel; + + aml_audiobus_update_bits(actrl, reg, + (3 << 30)|0x3ff<<10|0x3ff, + (3 << 30)|lrclk_hi<<10|bclk_ratio); +} + +void aml_tdm_set_lrclkdiv( + struct aml_audio_controller *actrl, + int clk_sel, int ratio) +{ + unsigned int reg, reg_step = 2; + + pr_info("aml_dai_set_clkdiv, clksel(%d), ratio(%d)\n", + clk_sel, ratio); + + reg = EE_AUDIO_MST_A_SCLK_CTRL0 + reg_step * clk_sel; + + aml_audiobus_update_bits(actrl, reg, + (3 << 30)|(0x3ff << 20), + (3 << 30)|(ratio << 20)); +} diff --git a/sound/soc/amlogic/auge/tdm_hw.h b/sound/soc/amlogic/auge/tdm_hw.h new file mode 100644 index 000000000000..b093b6df829d --- /dev/null +++ b/sound/soc/amlogic/auge/tdm_hw.h @@ -0,0 +1,84 @@ +/* + * sound/soc/amlogic/auge/tdm_hw.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#ifndef __AML_TDM_HW_H__ +#define __AML_TDM_HW_H__ + +#include "audio_io.h" +#include "regs.h" + +struct pcm_setting { + unsigned int pcm_mode; + unsigned int sysclk; + unsigned int sysclk_bclk_ratio; + unsigned int bclk; + unsigned int bclk_lrclk_ratio; + unsigned int lrclk; + unsigned int tx_mask; + unsigned int rx_mask; + unsigned int slots; + unsigned int slot_width; + unsigned int pcm_width; + unsigned int lane_mask_out; + unsigned int lane_mask_in; +}; + +extern void aml_tdm_enable( + struct aml_audio_controller *actrl, + int stream, int index, + bool is_enable); + +extern void aml_tdm_arb_config( + struct aml_audio_controller *actrl); + +extern void aml_tdm_fifo_reset( + struct aml_audio_controller *actrl, + int stream, int index); + +extern void aml_tdm_fifo_ctrl( + struct aml_audio_controller *actrl, + int bitwidth, int stream, + int index); + +extern void aml_tdm_set_format( + struct aml_audio_controller *actrl, + struct pcm_setting *p_config, + unsigned int clk_sel, + unsigned int index, + unsigned int fmt); + +extern void aml_tdm_set_slot( + struct aml_audio_controller *actrl, + int slots, int slot_width, int index); + +extern void aml_tdm_set_channel_mask( + struct aml_audio_controller *actrl, + int stream, int index, int lanes, int mask); + +extern void aml_tdm_set_lane_channel_swap( + struct aml_audio_controller *actrl, + int stream, int index, int swap); + +extern void aml_tdm_set_bclk_ratio( + struct aml_audio_controller *actrl, + int clk_sel, int lrclk_hi, int bclk_ratio); + +extern void aml_tdm_set_lrclkdiv( + struct aml_audio_controller *actrl, + int clk_sel, int ratio); + +#endif diff --git a/sound/soc/amlogic/meson/Kconfig b/sound/soc/amlogic/meson/Kconfig new file mode 100644 index 000000000000..2a028e2e5794 --- /dev/null +++ b/sound/soc/amlogic/meson/Kconfig @@ -0,0 +1,30 @@ +menuconfig AMLOGIC_SND_SOC_MESON + bool "Amlogic Meson ASoC" + default n + help + Say Y or M if you want to add support for codecs attached to + the Amlogic Asoc interface. You will also need + to select the audio interfaces to support below. + +if AMLOGIC_SND_SOC_MESON + +config AMLOGIC_SND_SPLIT_MODE + bool "AIU split/interleave mode" + depends on AMLOGIC_SND_SOC_MESON + default n + help + Say Y to enable AIU split mode. If not, it is normal mode. + Say Y to enable AIU split mode. If not, it is normal mode. + Say Y to enable AIU split mode. If not, it is normal mode. + Say Y to enable AIU split mode. If not, it is normal mode. + +config AMLOGIC_SND_SPLIT_MODE_MMAP + bool "AIU split mode, mmap" + depends on AMLOGIC_SND_SPLIT_MODE + depends on AMLOGIC_SND_SOC_MESON + default n + help + Say Y or N to enable/disable AIU split mmap + +endif # AMLOGIC_SND_SOC_MESON + diff --git a/sound/soc/amlogic/meson/Makefile b/sound/soc/amlogic/meson/Makefile new file mode 100644 index 000000000000..467ebeff2db0 --- /dev/null +++ b/sound/soc/amlogic/meson/Makefile @@ -0,0 +1,31 @@ +# AML Platform Support +snd-soc-pcm-objs := pcm.o +snd-soc-i2s-objs := i2s.o +snd-soc-i2s-dai-objs := i2s_dai.o +snd-soc-pcm-dai-objs := pcm_dai.o +snd-soc-spdif-dai-objs := spdif_dai.o +snd-soc-hw-objs := audio_hw.o +snd-soc-hw-pcm2bt-objs := audio_hw_pcm.o +snd-soc-dmic-objs := dmic.o + +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-pcm.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-i2s.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-i2s-dai.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-pcm-dai.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-hw.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += notify.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-hw-pcm2bt.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-spdif-dai.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-dmic.o + +# AML spdif codec support +snd-soc-spdif-codec-objs := spdif_codec.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-spdif-codec.o + +#AML M8 Machine support +snd-soc-meson-objs := meson.o +obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-meson.o + +#AML G9TV Machine support +snd-soc-tv-objs := tv.o +#obj-$(CONFIG_AMLOGIC_SND_SOC_MESON) += snd-soc-tv.o diff --git a/sound/soc/amlogic/aml_audio_hw.c b/sound/soc/amlogic/meson/audio_hw.c similarity index 99% rename from sound/soc/amlogic/aml_audio_hw.c rename to sound/soc/amlogic/meson/audio_hw.c index 17e289ebb58c..03095588d3be 100644 --- a/sound/soc/amlogic/aml_audio_hw.c +++ b/sound/soc/amlogic/meson/audio_hw.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_audio_hw.c + * sound/soc/amlogic/meson/audio_hw.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -28,7 +28,7 @@ #include #include #include -#include "aml_audio_hw.h" +#include "audio_hw.h" /* i2s mode 0: master 1: slave */ /* source: 0: linein; 1: ATV; 2: HDMI-in */ diff --git a/sound/soc/amlogic/aml_audio_hw.h b/sound/soc/amlogic/meson/audio_hw.h similarity index 99% rename from sound/soc/amlogic/aml_audio_hw.h rename to sound/soc/amlogic/meson/audio_hw.h index c8ddea4116b2..dc0397c834e2 100644 --- a/sound/soc/amlogic/aml_audio_hw.h +++ b/sound/soc/amlogic/meson/audio_hw.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_audio_hw.h + * sound/soc/amlogic/meson/audio_hw.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_audio_hw_pcm.c b/sound/soc/amlogic/meson/audio_hw_pcm.c similarity index 99% rename from sound/soc/amlogic/aml_audio_hw_pcm.c rename to sound/soc/amlogic/meson/audio_hw_pcm.c index e3bba87d8d64..c7eae7d49c9b 100644 --- a/sound/soc/amlogic/aml_audio_hw_pcm.c +++ b/sound/soc/amlogic/meson/audio_hw_pcm.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_audio_hw_pcm.c + * sound/soc/amlogic/meson/audio_hw_pcm.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -26,7 +26,7 @@ #include #include -#include "aml_audio_hw_pcm.h" +#include "audio_hw_pcm.h" #include diff --git a/sound/soc/amlogic/aml_audio_hw_pcm.h b/sound/soc/amlogic/meson/audio_hw_pcm.h similarity index 95% rename from sound/soc/amlogic/aml_audio_hw_pcm.h rename to sound/soc/amlogic/meson/audio_hw_pcm.h index 304eaf36ae2a..58a3a49631ea 100644 --- a/sound/soc/amlogic/aml_audio_hw_pcm.h +++ b/sound/soc/amlogic/meson/audio_hw_pcm.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_audio_hw_pcm.h + * sound/soc/amlogic/meson/audio_hw_pcm.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -21,7 +21,7 @@ #include "sound/asound.h" #include -#include "aml_audio_hw.h" +#include "audio_hw.h" void aml_set_pcm_format(int pcm_mode); void pcm_in_enable(struct snd_pcm_substream *substream, int flag); diff --git a/sound/soc/amlogic/aml_dmic.c b/sound/soc/amlogic/meson/dmic.c similarity index 99% rename from sound/soc/amlogic/aml_dmic.c rename to sound/soc/amlogic/meson/dmic.c index dfb2f63a98dd..3a7b7dc36c58 100644 --- a/sound/soc/amlogic/aml_dmic.c +++ b/sound/soc/amlogic/meson/dmic.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_dmic.c + * sound/soc/amlogic/meson/dmic.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_i2s.c b/sound/soc/amlogic/meson/i2s.c similarity index 99% rename from sound/soc/amlogic/aml_i2s.c rename to sound/soc/amlogic/meson/i2s.c index a0cdf2b887d3..a4f8e56a2803 100644 --- a/sound/soc/amlogic/aml_i2s.c +++ b/sound/soc/amlogic/meson/i2s.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_i2s.c + * sound/soc/amlogic/meson/i2s.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -39,9 +39,9 @@ /* Amlogic headers */ #include -#include "aml_i2s.h" -#include "aml_spdif_dai.h" -#include "aml_audio_hw.h" +#include "i2s.h" +#include "spdif_dai.h" +#include "audio_hw.h" #include #include diff --git a/sound/soc/amlogic/aml_i2s.h b/sound/soc/amlogic/meson/i2s.h similarity index 98% rename from sound/soc/amlogic/aml_i2s.h rename to sound/soc/amlogic/meson/i2s.h index 32e2ded00cbc..b032cb3d6b9b 100644 --- a/sound/soc/amlogic/aml_i2s.h +++ b/sound/soc/amlogic/meson/i2s.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_i2s.h + * sound/soc/amlogic/meson/i2s.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_i2s_dai.c b/sound/soc/amlogic/meson/i2s_dai.c similarity index 98% rename from sound/soc/amlogic/aml_i2s_dai.c rename to sound/soc/amlogic/meson/i2s_dai.c index 97feeeb90a4f..306f72a2d8dd 100644 --- a/sound/soc/amlogic/aml_i2s_dai.c +++ b/sound/soc/amlogic/meson/i2s_dai.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_i2s_dai.c + * sound/soc/amlogic/meson/i2s_dai.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -38,12 +38,12 @@ #include #include #include -#include "aml_i2s_dai.h" -#include "aml_pcm.h" -#include "aml_i2s.h" -#include "aml_audio_hw.h" +#include "i2s_dai.h" +#include "pcm.h" +#include "i2s.h" +#include "audio_hw.h" #include -#include "aml_spdif_dai.h" +#include "spdif_dai.h" struct aml_dai_info dai_info[3] = { {0} }; diff --git a/sound/soc/amlogic/aml_i2s_dai.h b/sound/soc/amlogic/meson/i2s_dai.h similarity index 95% rename from sound/soc/amlogic/aml_i2s_dai.h rename to sound/soc/amlogic/meson/i2s_dai.h index 0e2c94d70fab..2c1d2e589a8f 100644 --- a/sound/soc/amlogic/aml_i2s_dai.h +++ b/sound/soc/amlogic/meson/i2s_dai.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_i2s_dai.h + * sound/soc/amlogic/meson/i2s_dai.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_meson.c b/sound/soc/amlogic/meson/meson.c similarity index 99% rename from sound/soc/amlogic/aml_meson.c rename to sound/soc/amlogic/meson/meson.c index cf712845e24f..11d46ca1ca87 100644 --- a/sound/soc/amlogic/aml_meson.c +++ b/sound/soc/amlogic/meson/meson.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_meson.c + * sound/soc/amlogic/meson/meson.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -41,9 +41,9 @@ /* #include */ #include -#include "aml_i2s.h" -#include "aml_meson.h" -#include "aml_audio_hw.h" +#include "i2s.h" +#include "meson.h" +#include "audio_hw.h" #include #include #include diff --git a/sound/soc/amlogic/aml_meson.h b/sound/soc/amlogic/meson/meson.h similarity index 98% rename from sound/soc/amlogic/aml_meson.h rename to sound/soc/amlogic/meson/meson.h index 11beaee56524..3d15328aa915 100644 --- a/sound/soc/amlogic/aml_meson.h +++ b/sound/soc/amlogic/meson/meson.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_meson.h + * sound/soc/amlogic/meson/meson.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_notify.c b/sound/soc/amlogic/meson/notify.c similarity index 97% rename from sound/soc/amlogic/aml_notify.c rename to sound/soc/amlogic/meson/notify.c index 4a87930993d4..8dd9ab8eb65f 100644 --- a/sound/soc/amlogic/aml_notify.c +++ b/sound/soc/amlogic/meson/notify.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_notify.c + * sound/soc/amlogic/meson/notify.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_pcm.c b/sound/soc/amlogic/meson/pcm.c similarity index 99% rename from sound/soc/amlogic/aml_pcm.c rename to sound/soc/amlogic/meson/pcm.c index 1a546c4a7d5f..7b61cbf0fc19 100644 --- a/sound/soc/amlogic/aml_pcm.c +++ b/sound/soc/amlogic/meson/pcm.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_pcm.c + * sound/soc/amlogic/meson/pcm.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -38,8 +38,8 @@ #include #include -#include "aml_pcm.h" -#include "aml_audio_hw_pcm.h" +#include "pcm.h" +#include "audio_hw_pcm.h" /*-------------------------------------------------------------------------- * Hardware definition diff --git a/sound/soc/amlogic/aml_pcm.h b/sound/soc/amlogic/meson/pcm.h similarity index 96% rename from sound/soc/amlogic/aml_pcm.h rename to sound/soc/amlogic/meson/pcm.h index 8eb9dabd2e4f..365dd2643f6c 100644 --- a/sound/soc/amlogic/aml_pcm.h +++ b/sound/soc/amlogic/meson/pcm.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_pcm.h + * sound/soc/amlogic/meson/pcm.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_pcm_dai.c b/sound/soc/amlogic/meson/pcm_dai.c similarity index 98% rename from sound/soc/amlogic/aml_pcm_dai.c rename to sound/soc/amlogic/meson/pcm_dai.c index b58c8ce8df4c..8d07aa1c257f 100644 --- a/sound/soc/amlogic/aml_pcm_dai.c +++ b/sound/soc/amlogic/meson/pcm_dai.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_pcm_dai.c + * sound/soc/amlogic/meson/pcm_dai.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -31,10 +31,10 @@ #include #include -#include "aml_pcm_dai.h" -#include "aml_pcm.h" -#include "aml_i2s.h" -#include "aml_audio_hw_pcm.h" +#include "pcm_dai.h" +#include "pcm.h" +#include "i2s.h" +#include "audio_hw_pcm.h" #include diff --git a/sound/soc/amlogic/aml_pcm_dai.h b/sound/soc/amlogic/meson/pcm_dai.h similarity index 95% rename from sound/soc/amlogic/aml_pcm_dai.h rename to sound/soc/amlogic/meson/pcm_dai.h index 4f6b0474c48c..e86655d2154d 100644 --- a/sound/soc/amlogic/aml_pcm_dai.h +++ b/sound/soc/amlogic/meson/pcm_dai.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_pcm_dai.h + * sound/soc/amlogic/meson/pcm_dai.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_spdif_codec.c b/sound/soc/amlogic/meson/spdif_codec.c similarity index 99% rename from sound/soc/amlogic/aml_spdif_codec.c rename to sound/soc/amlogic/meson/spdif_codec.c index 15f06c2b53e4..8bb69c9b0a53 100644 --- a/sound/soc/amlogic/aml_spdif_codec.c +++ b/sound/soc/amlogic/meson/spdif_codec.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_spdif_codec.c + * sound/soc/amlogic/meson/spdif_codec.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_spdif_dai.c b/sound/soc/amlogic/meson/spdif_dai.c similarity index 99% rename from sound/soc/amlogic/aml_spdif_dai.c rename to sound/soc/amlogic/meson/spdif_dai.c index 22b6a88073a2..080eea3e8f37 100644 --- a/sound/soc/amlogic/aml_spdif_dai.c +++ b/sound/soc/amlogic/meson/spdif_dai.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_spdif_dai.c + * sound/soc/amlogic/meson/spdif_dai.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -41,9 +41,9 @@ #include #include -#include "aml_audio_hw.h" -#include "aml_spdif_dai.h" -#include "aml_i2s.h" +#include "audio_hw.h" +#include "spdif_dai.h" +#include "i2s.h" #include #include #include diff --git a/sound/soc/amlogic/aml_spdif_dai.h b/sound/soc/amlogic/meson/spdif_dai.h similarity index 98% rename from sound/soc/amlogic/aml_spdif_dai.h rename to sound/soc/amlogic/meson/spdif_dai.h index 98b5b06978d6..d6e7fdaf315b 100644 --- a/sound/soc/amlogic/aml_spdif_dai.h +++ b/sound/soc/amlogic/meson/spdif_dai.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_spdif_dai.h + * sound/soc/amlogic/meson/spdif_dai.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/amlogic/aml_tv.c b/sound/soc/amlogic/meson/tv.c similarity index 99% rename from sound/soc/amlogic/aml_tv.c rename to sound/soc/amlogic/meson/tv.c index d5ec348b8ba4..6e72fc7dffa0 100644 --- a/sound/soc/amlogic/aml_tv.c +++ b/sound/soc/amlogic/meson/tv.c @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_tv.c + * sound/soc/amlogic/meson/tv.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * @@ -47,9 +47,9 @@ #include #endif -#include "aml_i2s.h" -#include "aml_audio_hw.h" -#include "aml_tv.h" +#include "i2s.h" +#include "audio_hw.h" +#include "tv.h" #define DRV_NAME "aml_snd_card_tv" diff --git a/sound/soc/amlogic/aml_tv.h b/sound/soc/amlogic/meson/tv.h similarity index 97% rename from sound/soc/amlogic/aml_tv.h rename to sound/soc/amlogic/meson/tv.h index 260c76665537..2dad6338717d 100644 --- a/sound/soc/amlogic/aml_tv.h +++ b/sound/soc/amlogic/meson/tv.h @@ -1,5 +1,5 @@ /* - * sound/soc/amlogic/aml_tv.h + * sound/soc/amlogic/aml_meson/tv.h * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/sound/soc/codecs/amlogic/Kconfig b/sound/soc/codecs/amlogic/Kconfig index d94ce2fb58c8..7273c7f3e2df 100644 --- a/sound/soc/codecs/amlogic/Kconfig +++ b/sound/soc/codecs/amlogic/Kconfig @@ -11,7 +11,7 @@ menuconfig AMLOGIC_SND_SOC_CODECS config AMLOGIC_SND_CODEC_DUMMY_CODEC bool "Amlogic Audio dummy codec" depends on AMLOGIC_SND_SOC_CODECS - default n + default n help Amlogic Audio codec, dummy codec, @@ -21,17 +21,27 @@ config AMLOGIC_SND_CODEC_DUMMY_CODEC config AMLOGIC_SND_CODEC_PCM2BT bool "Amlogic Audio pcm2bt codec" depends on AMLOGIC_SND_SOC_CODECS - default n + default n help Amlogic Audio codec, pcm2bt codec, pcm2bt codec, this codec is internal +config AMLOGIC_SND_CODEC_PDM_DUMMY_CODEC + bool "Amlogic Audio pdm dummy codec" + depends on AMLOGIC_SND_SOC_CODECS + default n + help + Amlogic Audio pdm codec, + pdm dummy codec, + pdm dummy codec, + this codec is internal + config AMLOGIC_SND_CODEC_AMLT9015 bool "Amlogic Audio AMLT9015 codec" depends on AMLOGIC_SND_SOC_CODECS - default n + default n help Amlogic Audio codec, AMLT9015 codec, @@ -39,16 +49,16 @@ config AMLOGIC_SND_CODEC_AMLT9015 this codec is internal config AMLOGIC_SND_CODEC_PMU3 - bool "Amlogic Audio AML PMU3 codec" - depends on AMLOGIC_SND_SOC_CODECS + bool "Amlogic Audio AML PMU3 codec" + depends on AMLOGIC_SND_SOC_CODECS default n - help - Amlogic Audio codec, - AML PMU3 codec, - AML PMU3 codec, - this codec is internal - -# Amlogic add Third part codec + help + Amlogic Audio codec, + AML PMU3 codec, + AML PMU3 codec, + this codec is internal +#Third part codecs +# Amlogic add codecs config AMLOGIC_SND_SOC_TAS5707 bool "Texas Instruments TAS5707 amplifier" depends on AMLOGIC_SND_SOC_CODECS @@ -60,4 +70,13 @@ config AMLOGIC_SND_SOC_TAS5707 Enable support for Texas Instruments TAS5707 CODEC. Select this if your TAS5707 is connected via an I2C bus. +config AMLOGIC_SND_SOC_TLV320ADC3101 + bool "Texas Instruments TLV320ADC3101" + depends on AMLOGIC_SND_SOC_CODECS + depends on I2C + default n + help + Enable Support for Texas INstruments TLV320ADC3101 CODEC. + Select this if your TLV320ADC3101 is connected via an I2C bus. + #endif #AMLOGIC_SND_SOC_CODECS diff --git a/sound/soc/codecs/amlogic/Makefile b/sound/soc/codecs/amlogic/Makefile index e27915df9bd1..d38d347d7356 100644 --- a/sound/soc/codecs/amlogic/Makefile +++ b/sound/soc/codecs/amlogic/Makefile @@ -1,17 +1,21 @@ #Amlogic snd-soc-dummy_codec-objs := dummy_codec.o +snd-soc-pdm-dummy-objs := pdm_dummy.o snd-soc-pcm2bt-objs := pcm2bt.o snd-soc-aml_t9015-objs := aml_codec_t9015.o snd-soc-pmu3-objs := aml_pmu3.o #Third part codecs snd-soc-tas5707-objs := tas5707.o +snd-soc-tlv320adc3101-objs := tlv320adc3101.o # Amlogic -obj-$(CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC) += snd-soc-dummy_codec.o -obj-$(CONFIG_AMLOGIC_SND_CODEC_PCM2BT) += snd-soc-pcm2bt.o -obj-$(CONFIG_AMLOGIC_SND_CODEC_AMLT9015) += snd-soc-aml_t9015.o +obj-$(CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC) += snd-soc-dummy_codec.o +obj-$(CONFIG_AMLOGIC_SND_CODEC_PDM_DUMMY_CODEC) += snd-soc-pdm-dummy.o +obj-$(CONFIG_AMLOGIC_SND_CODEC_PCM2BT) += snd-soc-pcm2bt.o +obj-$(CONFIG_AMLOGIC_SND_CODEC_AMLT9015) += snd-soc-aml_t9015.o obj-$(CONFIG_AMLOGIC_SND_CODEC_PMU3) += snd-soc-pmu3.o #Third part codecs -obj-$(CONFIG_AMLOGIC_SND_SOC_TAS5707) += snd-soc-tas5707.o \ No newline at end of file +obj-$(CONFIG_AMLOGIC_SND_SOC_TAS5707) += snd-soc-tas5707.o +obj-$(CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101) += snd-soc-tlv320adc3101.o \ No newline at end of file diff --git a/sound/soc/codecs/amlogic/pdm_dummy.c b/sound/soc/codecs/amlogic/pdm_dummy.c new file mode 100644 index 000000000000..e47ac80278fa --- /dev/null +++ b/sound/soc/codecs/amlogic/pdm_dummy.c @@ -0,0 +1,129 @@ +/* + * sound/soc/codecs/amlogic/pdm_dummy.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 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. + * + */ + +#include +#include +#include +#include + +#define DUMMY_RATES SNDRV_PCM_RATE_8000_48000 +#define DUMMY_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static int pdm_dummy_set_fmt( + struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + return 0; +} + +static int pdm_dummy_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int pdm_dummy_prepare( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + + +static struct snd_soc_dai_ops pdm_dummy_ops = { + .set_fmt = pdm_dummy_set_fmt, + .hw_params = pdm_dummy_hw_params, + .prepare = pdm_dummy_prepare, +}; + +struct snd_soc_dai_driver pdm_dummy_dai = { + .name = "pdm", + .capture = { + .stream_name = "PDM Capture", + .channels_min = 1, + .channels_max = 8, + .rates = DUMMY_RATES, + .formats = DUMMY_FORMATS, + }, + .ops = &pdm_dummy_ops, +}; + +static int pdm_dummy_probe(struct snd_soc_codec *codec) +{ + return 0; +} + +static int pdm_dummy_remove(struct snd_soc_codec *codec) +{ + return 0; +}; + +struct snd_soc_codec_driver soc_codec_dev_pdm_dummy = { + .probe = pdm_dummy_probe, + .remove = pdm_dummy_remove, +}; + + +static const struct of_device_id pdm_dummy_codec_device_id[] = { + { .compatible = "amlogic, pdm_dummy_codec", }, + {}, +}; + +static int pdm_dummy_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_pdm_dummy, + &pdm_dummy_dai, 1); +} + +static int pdm_dummy_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver pdm_dummy_platform_driver = { + .driver = { + .name = "pdm", + .owner = THIS_MODULE, + .of_match_table = pdm_dummy_codec_device_id, + }, + .probe = pdm_dummy_platform_probe, + .remove = pdm_dummy_platform_remove, +}; + +static int __init pdm_dummy_init(void) +{ + return platform_driver_register(&pdm_dummy_platform_driver); +} + +static void __exit pdm_dummy_exit(void) +{ + platform_driver_unregister(&pdm_dummy_platform_driver); +} + +module_init(pdm_dummy_init); +module_exit(pdm_dummy_exit); + +MODULE_AUTHOR("AMLogic, Inc."); +MODULE_DESCRIPTION("ASoC pdm_dummy codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/amlogic/tas5707.c b/sound/soc/codecs/amlogic/tas5707.c index 37adcee5e8a8..0c42513765dc 100644 --- a/sound/soc/codecs/amlogic/tas5707.c +++ b/sound/soc/codecs/amlogic/tas5707.c @@ -200,7 +200,7 @@ static int tas5707_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) case SND_SOC_DAIFMT_CBS_CFS: break; default: - return -EINVAL; + return 0;//-EINVAL; } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -209,7 +209,7 @@ static int tas5707_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) case SND_SOC_DAIFMT_LEFT_J: break; default: - return -EINVAL; + return 0;//-EINVAL; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -218,7 +218,7 @@ static int tas5707_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) case SND_SOC_DAIFMT_NB_IF: break; default: - return -EINVAL; + return 0;//-EINVAL; } return 0; diff --git a/sound/soc/codecs/amlogic/tlv320adc3101.c b/sound/soc/codecs/amlogic/tlv320adc3101.c new file mode 100644 index 000000000000..67830f2cc5c4 --- /dev/null +++ b/sound/soc/codecs/amlogic/tlv320adc3101.c @@ -0,0 +1,865 @@ +/* + * linux/sound/soc/codecs/tlv320adc3101.c + * + * Copyright 2011 Amlogic + * + * Author: Alex Deng + * + * Based on sound/soc/codecs/tlv320aic320x and TI driver for kernel 2.6.27. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320adc3101.h" + +struct adc3101_rate_divs { + u32 mclk; + u32 rate; + u8 p_val; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; + u8 blck_N; +}; + +struct adc3101_priv { + struct regmap *regmap; + u32 sysclk; + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; + int rstn_gpio; + struct snd_soc_codec *codec; + /* for more control */ + int codec_cnt; + int codec_mask; + struct i2c_client *client[4]; + u8 page_no; +}; + +enum{ + MASK_1 = 1 << 0, + MASK_2 = 1 << 1, + MASK_3 = 1 << 2, + MASK_4 = 1 << 3 +}; + +struct adc3101_priv *g_adc3101_priv; +static int lr_gain = 0x20; +module_param(lr_gain, int, 0664); +MODULE_PARM_DESC(lr_gain, "PGA Level Volume"); + +/* 0dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0); +/* 0dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0); + +static const struct snd_kcontrol_new adc3101_snd_controls[] = { + SOC_DOUBLE_R_TLV("PCM Playback Volume", ADC3101_LDACVOL, + ADC3101_RDACVOL, 0, 0x30, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("HP Driver Gain Volume", ADC3101_HPLGAIN, + ADC3101_HPRGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R_TLV("LO Driver Gain Volume", ADC3101_LOLGAIN, + ADC3101_LORGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R("HP DAC Playback Switch", ADC3101_HPLGAIN, + ADC3101_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", ADC3101_LOLGAIN, + ADC3101_LORGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("Mic PGA Switch", ADC3101_LMICPGAVOL, + ADC3101_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", ADC3101_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", ADC3101_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_TLV("ADC Level Volume", ADC3101_LADCVOL, + ADC3101_RADCVOL, 0, 0x28, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("PGA Level Volume", ADC3101_LMICPGAVOL, + ADC3101_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", ADC3101_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", ADC3101_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", ADC3101_RAGC1, 7, 1, 0), + SOC_DOUBLE_R("AGC Target Level", ADC3101_LAGC1, ADC3101_RAGC1, + 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", ADC3101_LAGC1, ADC3101_RAGC1, + 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", ADC3101_LAGC2, ADC3101_RAGC2, + 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", ADC3101_LAGC2, ADC3101_RAGC2, + 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", ADC3101_LAGC3, ADC3101_RAGC3, + 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", ADC3101_LAGC4, ADC3101_RAGC4, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", ADC3101_LAGC5, ADC3101_RAGC5, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", ADC3101_LAGC6, ADC3101_RAGC6, + 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", ADC3101_LAGC7, ADC3101_RAGC7, + 0, 0x0F, 0), +}; + +static const struct adc3101_rate_divs adc3101_divs[] = { + /* mclk rate p j d dosr ndac mdac aosr nadc madc blk_N */ + + /* 8k rate */ + {ADC3101_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, + {ADC3101_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, + {ADC3101_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, + /* 11.025k rate */ + {ADC3101_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, + {ADC3101_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + /* 16k rate */ + {ADC3101_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, + {ADC3101_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, + {ADC3101_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, + /* 22.05k rate */ + {ADC3101_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, + {ADC3101_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, + {ADC3101_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, + /* 32k rate */ + {ADC3101_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, + {ADC3101_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + /* 44.1k rate */ + {ADC3101_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, + {ADC3101_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, + {ADC3101_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, + /* 48k rate */ + {ADC3101_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, + {ADC3101_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, + {ADC3101_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}, + + {ADC3101_FREQ_2048000, 8000, 1, 4, 0, 128, 1, 1, 128, 1, 2, 1}, + {ADC3101_FREQ_4096000, 1, 4, 0, 128, 1, 1, 128, 1, 2, 1}, + {ADC3101_FREQ_8192000, 32000, 1, 4, 0, 128, 1, 1, 128, 1, 2, 1}, + {ADC3101_FREQ_11289600, 44100, 1, 4, 0, 128, 1, 1, 128, 1, 2, 1}, + {ADC3101_FREQ_12288000, 48000, 1, 4, 0, 128, 2, 1, 128, 2, 1, 1}, +}; + +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", ADC3101_HPLROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_L Switch", ADC3101_HPLROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", ADC3101_HPRROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_R Switch", ADC3101_HPRROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", ADC3101_LOLROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", ADC3101_LORROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_L P Switch", ADC3101_LMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_L P Switch", ADC3101_LMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_L P Switch", ADC3101_LMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_R P Switch", ADC3101_RMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_R P Switch", ADC3101_RMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_R P Switch", ADC3101_RMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget adc3101_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ADC3101_ADCSETUP, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ADC3101_ADCSETUP, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias", ADC3101_MICBIAS, 6, 0), + + SND_SOC_DAPM_INPUT("IN1_L"), + SND_SOC_DAPM_INPUT("IN1_R"), + SND_SOC_DAPM_INPUT("IN2_L"), + SND_SOC_DAPM_INPUT("IN2_R"), + SND_SOC_DAPM_INPUT("IN3_L"), + SND_SOC_DAPM_INPUT("IN3_R"), +}; + +static const struct snd_soc_dapm_route adc3101_dapm_routes[] = { + /* Left input */ + {"Left Input Mixer", "IN1_L P Switch", "IN1_L"}, + {"Left Input Mixer", "IN2_L P Switch", "IN2_L"}, + {"Left Input Mixer", "IN3_L P Switch", "IN3_L"}, + + {"Left ADC", NULL, "Left Input Mixer"}, + + /* Right Input */ + {"Right Input Mixer", "IN1_R P Switch", "IN1_R"}, + {"Right Input Mixer", "IN2_R P Switch", "IN2_R"}, + {"Right Input Mixer", "IN3_R P Switch", "IN3_R"}, + + {"Right ADC", NULL, "Right Input Mixer"}, +}; + +static const struct regmap_range_cfg adc3101_regmap_pages[] = { + { + .range_min = 0, + .range_max = ADC3101_RMICPGAVOL, + .selector_reg = ADC3101_PSEL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config adc3101_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .ranges = adc3101_regmap_pages, + .num_ranges = ARRAY_SIZE(adc3101_regmap_pages), + .max_register = ADC3101_RMICPGAVOL, +}; + +static inline int adc3101_get_divs(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adc3101_divs); i++) { + if ((adc3101_divs[i].rate == rate) + && (adc3101_divs[i].mclk == mclk)) { + return i; + } + } + + pr_err("adc3101: master clock and sample rate is not supported\n"); + return -EINVAL; +} + +static int adc3101_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct adc3101_priv *adc3101 = snd_soc_codec_get_drvdata(codec); + + pr_info("%s freq:%d\n", __func__, freq); + switch (freq) { + case ADC3101_FREQ_12000000: + case ADC3101_FREQ_24000000: + case ADC3101_FREQ_25000000: + case ADC3101_FREQ_11289600: + case ADC3101_FREQ_12288000: + case ADC3101_FREQ_2048000: + case ADC3101_FREQ_4096000: + case ADC3101_FREQ_8192000: + adc3101->sysclk = freq; + return 0; + } + + pr_err("adc3101: invalid frequency to set DAI system clock\n"); + return -1; +} + +static int adc3101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u8 iface_reg_1; + u8 dsp_a_val; + u8 iface_reg_2; + + pr_info("%s ...\n", __func__); + + iface_reg_1 = snd_soc_read(codec, ADC3101_IFACE1); + iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2); + dsp_a_val = snd_soc_read(codec, ADC3101_DATASLOTOFFSETCTL); + dsp_a_val = 0; + iface_reg_2 = snd_soc_read(codec, ADC3101_IFACE3); + iface_reg_2 = iface_reg_2 & ~(1 << 3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface_reg_1 |= ADC3101_BCLKMASTER | ADC3101_WCLKMASTER; + pr_info("%s DAIFMT_CBM_CFM\n", __func__); + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + pr_err("adc3101: invalid DAI master/slave interface\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg_1 |= (ADC3101_DSP_MODE << ADC3101_PLLJ_SHIFT); + iface_reg_2 |= (1 << 3); /* invert bit clock */ + dsp_a_val = 0x01; /* add offset 1 */ + break; + case SND_SOC_DAIFMT_DSP_B: + iface_reg_1 |= (ADC3101_DSP_MODE << ADC3101_PLLJ_SHIFT); + iface_reg_2 |= (1 << 3); /* invert bit clock */ + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg_1 |= + (ADC3101_RIGHT_JUSTIFIED_MODE << ADC3101_PLLJ_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg_1 |= + (ADC3101_LEFT_JUSTIFIED_MODE << ADC3101_PLLJ_SHIFT); + break; + default: + pr_err("adc3101: invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_write(codec, ADC3101_IFACE1, iface_reg_1); + snd_soc_write(codec, ADC3101_DATASLOTOFFSETCTL, dsp_a_val); + snd_soc_write(codec, ADC3101_IFACE3, iface_reg_2); + + return 0; +} + +static int __maybe_unused adc3101_hw_high_pass_filter( + struct snd_soc_codec *codec) +{ + /*page 4*/ + char datas[4][31] = { + /* data 1*/ + { + 0x0E, + 0x7F, 0xBE, 0x80, 0x42, + 0x7F, 0xBE, 0x7F, 0xBE, + 0x80, 0x84, 0x7F, 0xFF, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x7F, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }, + /* data 2 */ + { + 0x2c, + 0x7F, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xFF, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }, + /* data 3 */ + { + 0x4E, + 0x7F, 0xBE, 0x80, + 0x42, 0x7F, 0xBE, 0x7F, + 0xBE, 0x80, 0x84, 0x7F, + 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + }, + /* data 4 */ + { + 0x6C, + 0x7F, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xFF, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + } + }; + int i = 0, j = 0, c = 0; + int ret = 0; + int nums[] = {31, 21, 31, 21}; + struct adc3101_priv *adc3101 = snd_soc_codec_get_drvdata(codec); + + pr_info("%s ...\n", __func__); + + snd_soc_write(codec, 0x3D, 2); + snd_soc_write(codec, 0x51, 0); + /* page 4 */ + snd_soc_write(codec, ADC3101_PSEL, 0x4); + + for (i = 0; i < adc3101->codec_cnt; i++) { + for (c = 0; c < 4; c++) { + for (j = 0; j < nums[c]; j++) { + ret = i2c_smbus_write_byte( + adc3101->client[i], + datas[c][j]); + if (ret < 0) + pr_err("%x write error\n", + adc3101->client[i]->addr); + } + } + } + + snd_soc_write(codec, 0x51, 0xC0); + snd_soc_write(codec, 0x52, 0x00); + pr_info("%s done\n", __func__); + + return 0; +} + + +static int adc3101_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct adc3101_priv *adc3101 = snd_soc_codec_get_drvdata(codec); + u8 data; + int i; + + pr_info("%s ...\n", __func__); + + i = adc3101_get_divs(adc3101->sysclk, params_rate(params)); + if (i < 0) { + pr_err("adc3101: sampling rate not supported\n"); + return i; + } + + /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */ + snd_soc_write(codec, ADC3101_CLKMUX, 0); + + /* We will fix R value to 1 and will make P & J=K.D as varialble */ + data = snd_soc_read(codec, ADC3101_PLLPR); + data &= ~(7 << 4); + + snd_soc_write(codec, ADC3101_PLLPR, + (data | (adc3101_divs[i].p_val << 4) | 0x01)); + + snd_soc_write(codec, ADC3101_PLLJ, adc3101_divs[i].pll_j); + + snd_soc_write(codec, ADC3101_PLLDMSB, (adc3101_divs[i].pll_d >> 8)); + snd_soc_write(codec, ADC3101_PLLDLSB, + (adc3101_divs[i].pll_d & 0xff)); + /* NADC divider value */ + data = snd_soc_read(codec, ADC3101_NADC); + data &= ~(0x7f); + data |= 0x80; + snd_soc_write(codec, ADC3101_NADC, data | adc3101_divs[i].nadc); + + pr_info("%s NADC = 0x%02x\n", + __func__, + snd_soc_read(codec, ADC3101_NADC)); + + /* MADC divider value */ + data = snd_soc_read(codec, ADC3101_MADC); + data &= ~(0x7f); + data |= 0x80; + snd_soc_write(codec, ADC3101_MADC, data | adc3101_divs[i].madc); + pr_info("%s MADC = 0x%02x\n", + __func__, + snd_soc_read(codec, ADC3101_MADC)); + + /* AOSR value */ + snd_soc_write(codec, ADC3101_AOSR, adc3101_divs[i].aosr); + pr_info("%s AOSR=%02x\n", + __func__, + snd_soc_read(codec, ADC3101_AOSR)); + + data = snd_soc_read(codec, ADC3101_IFACE1); + data = data & ~(3 << 4); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (ADC3101_WORD_LEN_20BITS << ADC3101_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S24_3LE: + data |= (ADC3101_WORD_LEN_24BITS << ADC3101_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (ADC3101_WORD_LEN_32BITS << ADC3101_DOSRMSB_SHIFT); + break; + } + snd_soc_write(codec, ADC3101_IFACE1, data); + pr_info("%s iface1 = %02x\n", __func__, data); + + snd_soc_write(codec, ADC3101_MICBIAS, 0x50); + /* 0x3f: differential, 0xFC:single input */ + snd_soc_write(codec, ADC3101_LMICPGANIN, 0x3F); + /* 0x3f: differential input */ + snd_soc_write(codec, ADC3101_RMICPGANIN, 0x3F); + /* 0x3f: differential, 0xFC:single input */ + snd_soc_write(codec, ADC3101_LMICPGAPIN, 0x3F); + /* 0x3f: differential input */ + snd_soc_write(codec, ADC3101_RMICPGAPIN, 0x3F); + snd_soc_write(codec, ADC3101_LMICPGAVOL, lr_gain); + snd_soc_write(codec, ADC3101_RMICPGAVOL, lr_gain); + snd_soc_write(codec, ADC3101_ADCSETUP, 0xc2); + snd_soc_write(codec, ADC3101_ADCFGA, 0); + + pr_info("%s ADCSETUP = %02x\n", __func__, + snd_soc_read(codec, ADC3101_ADCSETUP)); + pr_info("%s DOUTCTL=%02x\n", __func__, + snd_soc_read(codec, ADC3101_DOUTCTL)); + pr_info("%s MICBIAS=%02x\n", __func__, + snd_soc_read(codec, ADC3101_MICBIAS)); + + return 0; +} + +static int adc3101_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dac_reg; + + dac_reg = snd_soc_read(codec, ADC3101_DACMUTE) & ~ADC3101_MUTEON; + if (mute) + snd_soc_write(codec, ADC3101_DACMUTE, dac_reg | ADC3101_MUTEON); + else + snd_soc_write(codec, ADC3101_DACMUTE, dac_reg); + + return 0; +} + +static int adc3101_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + pr_info("%s ..\n", __func__); + switch (level) { + case SND_SOC_BIAS_ON: + /* Switch on PLL */ + snd_soc_update_bits(codec, ADC3101_PLLPR, + ADC3101_PLLEN, ADC3101_PLLEN); + + /* Switch on NDAC Divider */ + snd_soc_update_bits(codec, ADC3101_NDAC, + ADC3101_NDACEN, ADC3101_NDACEN); + + /* Switch on MDAC Divider */ + snd_soc_update_bits(codec, ADC3101_MDAC, + ADC3101_MDACEN, ADC3101_MDACEN); + + /* Switch on NADC Divider */ + snd_soc_update_bits(codec, ADC3101_NADC, + ADC3101_NADCEN, ADC3101_NADCEN); + + /* Switch on MADC Divider */ + snd_soc_update_bits(codec, ADC3101_MADC, + ADC3101_MADCEN, ADC3101_MADCEN); + + /* Switch on BCLK_N Divider */ + snd_soc_update_bits(codec, ADC3101_BCLKN, + ADC3101_BCLKEN, ADC3101_BCLKEN); + + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* Switch off PLL */ + snd_soc_update_bits(codec, ADC3101_PLLPR, + ADC3101_PLLEN, 0); + + /* Switch off NDAC Divider */ + snd_soc_update_bits(codec, ADC3101_NDAC, + ADC3101_NDACEN, 0); + + /* Switch off MDAC Divider */ + snd_soc_update_bits(codec, ADC3101_MDAC, + ADC3101_MDACEN, 0); + + /* Switch off NADC Divider */ + snd_soc_update_bits(codec, ADC3101_NADC, + ADC3101_NADCEN, 0); + + /* Switch off MADC Divider */ + snd_soc_update_bits(codec, ADC3101_MADC, + ADC3101_MADCEN, 0); + + /* Switch off BCLK_N Divider */ + snd_soc_update_bits(codec, ADC3101_BCLKN, + ADC3101_BCLKEN, 0); + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->component.dapm.bias_level = level; + snd_soc_codec_init_bias_level(codec, level); + + return 0; +} + +#define ADC3101_RATES SNDRV_PCM_RATE_8000_96000 +#define ADC3101_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops adc3101_ops = { + .hw_params = adc3101_hw_params, + .digital_mute = adc3101_mute, + .set_fmt = adc3101_set_dai_fmt, + .set_sysclk = adc3101_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver adc3101_dai[] = { + { + .name = "tlv320adc3101-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ADC3101_RATES, + .formats = ADC3101_FORMATS,}, + + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8,//2, + .rates = ADC3101_RATES, + .formats = ADC3101_FORMATS,}, + .ops = &adc3101_ops, + .symmetric_rates = 1, + }, + { + .name = "tlv320adc3101-hifi@19", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ADC3101_RATES, + .formats = ADC3101_FORMATS,}, + + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8,//2, + .rates = ADC3101_RATES, + .formats = ADC3101_FORMATS,}, + .ops = &adc3101_ops, + .symmetric_rates = 1, + }, + { + .name = "tlv320adc3101-hifi@1a", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ADC3101_RATES, + .formats = ADC3101_FORMATS,}, + + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8,//2, + .rates = ADC3101_RATES, + .formats = ADC3101_FORMATS,}, + .ops = &adc3101_ops, + .symmetric_rates = 1, + }, + { + .name = "tlv320adc3101-hifi@1b", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ADC3101_RATES, + .formats = ADC3101_FORMATS,}, + + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .rates = ADC3101_RATES, + .formats = ADC3101_FORMATS,}, + .ops = &adc3101_ops, + .symmetric_rates = 1, + }, +}; + +static int adc3101_suspend(struct snd_soc_codec *codec) +{ + adc3101_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int adc3101_resume(struct snd_soc_codec *codec) +{ + adc3101_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static int adc3101_codec_probe(struct snd_soc_codec *codec) +{ + u32 tmp_reg; + + pr_info("%s ...\n", __func__); + + snd_soc_write(codec, ADC3101_RESET, 0x01); + + /* + * Workaround: for an unknown reason, the ADC needs to be powered up + * and down for the first capture to work properly. It seems related to + * a HW BUG or some kind of behavior not documented in the datasheet. + */ + tmp_reg = snd_soc_read(codec, ADC3101_ADCSETUP); + snd_soc_write(codec, ADC3101_ADCSETUP, tmp_reg | + ADC3101_LADC_EN | ADC3101_RADC_EN); + snd_soc_write(codec, ADC3101_ADCSETUP, tmp_reg); + + pr_info("%s done...\n", __func__); + + return 0; +} + +static int adc3101_remove(struct snd_soc_codec *codec) +{ + adc3101_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static struct snd_soc_codec_driver __maybe_unused soc_codec_dev_adc3101_2 = { + .probe = adc3101_codec_probe, + .remove = adc3101_remove, + .suspend = adc3101_suspend, + .resume = adc3101_resume, + .set_bias_level = adc3101_set_bias_level, + +}; +static struct snd_soc_codec_driver soc_codec_dev_adc3101 = { + .probe = adc3101_codec_probe, + .remove = adc3101_remove, + .suspend = adc3101_suspend, + .resume = adc3101_resume, + .set_bias_level = adc3101_set_bias_level, + + .component_driver = { + .controls = adc3101_snd_controls, + .num_controls = ARRAY_SIZE(adc3101_snd_controls), + .dapm_widgets = adc3101_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adc3101_dapm_widgets), + .dapm_routes = adc3101_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adc3101_dapm_routes), + } +}; + +static int adc3101_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct adc3101_priv *adc3101 = NULL; + int ret = 0; + + pr_err("tlv320 %s ...\n", __func__); + + adc3101 = devm_kzalloc(&i2c->dev, sizeof(struct adc3101_priv), + GFP_KERNEL); + if (adc3101 == NULL) + return -ENOMEM; + adc3101->codec_cnt = 0; + + adc3101->regmap = devm_regmap_init_i2c(i2c, &adc3101_i2c_regmap); + if (IS_ERR(adc3101->regmap)) { + pr_info("%s failed devm_regmap_init_i2c\n", __func__); + return PTR_ERR(adc3101->regmap); + } + i2c_set_clientdata(i2c, adc3101); + + adc3101->power_cfg = 0; + adc3101->swapdacs = false; + adc3101->micpga_routing = 0; + adc3101->rstn_gpio = -1; + + ret = of_get_named_gpio(i2c->dev.of_node, "gpio-reset", 0); + if (ret > 0) + adc3101->rstn_gpio = ret; + + if (adc3101->rstn_gpio > 0) { + ret = devm_gpio_request_one(&i2c->dev, + adc3101->rstn_gpio, + GPIOF_OUT_INIT_HIGH, + "adc3101-reset-pin"); + if (ret < 0) { + dev_err(&i2c->dev, "not able to acquire gpio\n"); + return ret; + } + } + pr_info("%s i2c:%p\n", __func__, i2c); + if (g_adc3101_priv == NULL) { + g_adc3101_priv = adc3101; + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_adc3101, adc3101_dai, 1); + } else { + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_adc3101_2, + adc3101_dai + g_adc3101_priv->codec_cnt, + 1); + } + + pr_info("%s %x done\n", __func__, i2c->addr); + + return ret; +} + +static int adc3101_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + + return 0; +} + +static const struct of_device_id tlv320adc3101_of_match[] = { + {.compatible = "ti,tlv320adc3101"}, + {}, +}; +MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match); + +static const struct i2c_device_id adc3101_i2c_id[] = { + { "tlv320adc3101", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adc3101_i2c_id); + +static struct i2c_driver adc3101_i2c_driver = { + .driver = { + .name = "tlv320adc3101", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tlv320adc3101_of_match), + }, + .probe = adc3101_i2c_probe, + .remove = adc3101_i2c_remove, + .id_table = adc3101_i2c_id, +}; + +module_i2c_driver(adc3101_i2c_driver); + +MODULE_DESCRIPTION("ASoC tlv320adc3101 codec driver"); +MODULE_AUTHOR("alex.deng "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/amlogic/tlv320adc3101.h b/sound/soc/codecs/amlogic/tlv320adc3101.h new file mode 100644 index 000000000000..8b7b9422ec55 --- /dev/null +++ b/sound/soc/codecs/amlogic/tlv320adc3101.h @@ -0,0 +1,174 @@ +/* + * tlv320aic32x4.h + * + * 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. + */ + + +#ifndef _TLV320ADC3101_H +#define _TLV320ADC3101_H + +/* tlv320aic32x4 register space (in decimal to match datasheet) */ + +#define ADC3101_REG(page, reg) ((page * 128) + reg) + +#define ADC3101_PAGE1 128 + +#define ADC3101_PSEL ADC3101_REG(0, 0) +#define ADC3101_RESET ADC3101_REG(0, 1) +#define ADC3101_CLKMUX ADC3101_REG(0, 4) +#define ADC3101_PLLPR ADC3101_REG(0, 5) +#define ADC3101_PLLJ ADC3101_REG(0, 6) +#define ADC3101_PLLDMSB ADC3101_REG(0, 7) +#define ADC3101_PLLDLSB ADC3101_REG(0, 8) +#define ADC3101_NDAC ADC3101_REG(0, 11) +#define ADC3101_MDAC ADC3101_REG(0, 12) +#define ADC3101_DOSRMSB ADC3101_REG(0, 13) +#define ADC3101_DOSRLSB ADC3101_REG(0, 14) +#define ADC3101_NADC ADC3101_REG(0, 18) +#define ADC3101_MADC ADC3101_REG(0, 19) +#define ADC3101_AOSR ADC3101_REG(0, 20) +#define ADC3101_CLKMUX2 ADC3101_REG(0, 25) +#define ADC3101_CLKOUTM ADC3101_REG(0, 26) +#define ADC3101_IFACE1 ADC3101_REG(0, 27) +#define ADC3101_DATASLOTOFFSETCTL ADC3101_REG(0, 28) +#define ADC3101_IFACE3 ADC3101_REG(0, 29) +#define ADC3101_BCLKN ADC3101_REG(0, 30) +#define ADC3101_IFACE4 ADC3101_REG(0, 31) +#define ADC3101_IFACE5 ADC3101_REG(0, 32) +#define ADC3101_IFACE6 ADC3101_REG(0, 33) +#define ADC3101_I2SSYNC ADC3101_REG(0, 34) + +#define ADC3101_ADCFLAG ADC3101_REG(0, 36) +#define ADC3101_DATASLOTOFFSETCTL2 ADC3101_REG(0, 37) +#define ADC3101_TDMCTL ADC3101_REG(0, 38) + +#define ADC3101_INTRADCFLAG ADC3101_REG(0, 42) +#define ADC3101_INTRADCFLAG2 ADC3101_REG(0, 43) + +#define ADC3101_DMCLKCTL ADC3101_REG(0, 51) +#define ADC3101_DMDINCTL ADC3101_REG(0, 52) + +#define ADC3101_DOUTCTL ADC3101_REG(0, 53) +#define ADC3101_DINCTL ADC3101_REG(0, 54) + +#define ADC3101_DACSPB ADC3101_REG(0, 60) +#define ADC3101_ADCSPB ADC3101_REG(0, 61) +#define ADC3101_DACSETUP ADC3101_REG(0, 63) +#define ADC3101_DACMUTE ADC3101_REG(0, 64) +#define ADC3101_LDACVOL ADC3101_REG(0, 65) +#define ADC3101_RDACVOL ADC3101_REG(0, 66) +#define ADC3101_ADCSETUP ADC3101_REG(0, 81) +#define ADC3101_ADCFGA ADC3101_REG(0, 82) +#define ADC3101_LADCVOL ADC3101_REG(0, 83) +#define ADC3101_RADCVOL ADC3101_REG(0, 84) +#define ADC3101_LAGC1 ADC3101_REG(0, 86) +#define ADC3101_LAGC2 ADC3101_REG(0, 87) +#define ADC3101_LAGC3 ADC3101_REG(0, 88) +#define ADC3101_LAGC4 ADC3101_REG(0, 89) +#define ADC3101_LAGC5 ADC3101_REG(0, 90) +#define ADC3101_LAGC6 ADC3101_REG(0, 91) +#define ADC3101_LAGC7 ADC3101_REG(0, 92) +#define ADC3101_RAGC1 ADC3101_REG(0, 94) +#define ADC3101_RAGC2 ADC3101_REG(0, 95) +#define ADC3101_RAGC3 ADC3101_REG(0, 96) +#define ADC3101_RAGC4 ADC3101_REG(0, 97) +#define ADC3101_RAGC5 ADC3101_REG(0, 98) +#define ADC3101_RAGC6 ADC3101_REG(0, 99) +#define ADC3101_RAGC7 ADC3101_REG(0, 100) +#define ADC3101_PWRCFG ADC3101_REG(1, 1) +#define ADC3101_LDOCTL ADC3101_REG(1, 2) +#define ADC3101_OUTPWRCTL ADC3101_REG(1, 9) +#define ADC3101_CMMODE ADC3101_REG(1, 10) +#define ADC3101_HPLROUTE ADC3101_REG(1, 12) +#define ADC3101_HPRROUTE ADC3101_REG(1, 13) +#define ADC3101_LOLROUTE ADC3101_REG(1, 14) +#define ADC3101_LORROUTE ADC3101_REG(1, 15) +#define ADC3101_HPLGAIN ADC3101_REG(1, 16) +#define ADC3101_HPRGAIN ADC3101_REG(1, 17) +#define ADC3101_LOLGAIN ADC3101_REG(1, 18) +#define ADC3101_LORGAIN ADC3101_REG(1, 19) +#define ADC3101_HEADSTART ADC3101_REG(1, 20) +#define ADC3101_MICBIAS ADC3101_REG(1, 51) +#define ADC3101_LMICPGAPIN ADC3101_REG(1, 52) +#define ADC3101_LMICPGANIN ADC3101_REG(1, 54) +#define ADC3101_RMICPGAPIN ADC3101_REG(1, 55) +#define ADC3101_RMICPGANIN ADC3101_REG(1, 57) +#define ADC3101_FLOATINGINPUT ADC3101_REG(1, 58) +#define ADC3101_LMICPGAVOL ADC3101_REG(1, 59) +#define ADC3101_RMICPGAVOL ADC3101_REG(1, 60) + +#define ADC3101_FREQ_2048000 2048000 +#define ADC3101_FREQ_4096000 4096000 +#define ADC3101_FREQ_8192000 8192000 +#define ADC3101_FREQ_11289600 11289600 +#define ADC3101_FREQ_12288000 12288000 +#define ADC3101_FREQ_12000000 12000000 +#define ADC3101_FREQ_24000000 24000000 +#define ADC3101_FREQ_44100000 44100000 +#define ADC3101_FREQ_48000000 48000000 +#define ADC3101_FREQ_96000000 96000000 +#define ADC3101_FREQ_25000000 25000000 + +#define ADC3101_WORD_LEN_16BITS 0x00 +#define ADC3101_WORD_LEN_20BITS 0x01 +#define ADC3101_WORD_LEN_24BITS 0x02 +#define ADC3101_WORD_LEN_32BITS 0x03 + +#define ADC3101_LADC_EN (1 << 7) +#define ADC3101_RADC_EN (1 << 6) + +#define ADC3101_I2S_MODE 0x00 +#define ADC3101_DSP_MODE 0x01 +#define ADC3101_RIGHT_JUSTIFIED_MODE 0x02 +#define ADC3101_LEFT_JUSTIFIED_MODE 0x03 + +#define ADC3101_AVDDWEAKDISABLE 0x08 +#define ADC3101_LDOCTLEN 0x01 + +#define ADC3101_LDOIN_18_36 0x01 +#define ADC3101_LDOIN2HP 0x02 + +#define ADC3101_DACSPBLOCK_MASK 0x1f +#define ADC3101_ADCSPBLOCK_MASK 0x1f + +#define ADC3101_PLLJ_SHIFT 6 +#define ADC3101_DOSRMSB_SHIFT 4 + +#define ADC3101_PLLCLKIN 0x03 +#define ADC3101_BCLKIN 0x05 +#define ADC3101_BCLKIN_PLLCLKIN 0x07 + +#define ADC3101_MICBIAS_LDOIN 0x08 +#define ADC3101_MICBIAS_2075V 0x60 + +#define ADC3101_LMICPGANIN_IN2R_10K 0x10 +#define ADC3101_LMICPGANIN_CM1L_10K 0x40 +#define ADC3101_RMICPGANIN_IN1L_10K 0x10 +#define ADC3101_RMICPGANIN_CM1R_10K 0x40 + +#define ADC3101_LMICPGAVOL_NOGAIN 0x80 +#define ADC3101_RMICPGAVOL_NOGAIN 0x80 + +#define ADC3101_BCLKMASTER 0x08 +#define ADC3101_WCLKMASTER 0x04 +#define ADC3101_PLLEN (0x01 << 7) +#define ADC3101_NDACEN (0x01 << 7) +#define ADC3101_MDACEN (0x01 << 7) +#define ADC3101_NADCEN (0x01 << 7) +#define ADC3101_MADCEN (0x01 << 7) +#define ADC3101_BCLKEN (0x01 << 7) +#define ADC3101_DACEN (0x03 << 6) +#define ADC3101_RDAC2LCHN (0x02 << 2) +#define ADC3101_LDAC2RCHN (0x02 << 4) +#define ADC3101_LDAC2LCHN (0x01 << 4) +#define ADC3101_RDAC2RCHN (0x01 << 2) +#define ADC3101_DAC_CHAN_MASK 0x3c + +#define ADC3101_SSTEP2WCLK 0x01 +#define ADC3101_MUTEON 0x0C +#define ADC3101_DACMOD2BCLK 0x01 + +#endif /* _TLV320ADC3101_H */