audio: add asoc auge driver for axg

PD#142470: audio: new Asoc driver
1) tdm module
2) spdif module
3) pdm module
4) audio clock

Change-Id: I064975f4cb036d013a7ca74d781a91c31e7c2436
Signed-off-by: Xing Wang <xing.wang@amlogic.com>
This commit is contained in:
Xing Wang
2017-05-30 21:22:31 +08:00
committed by Victor Wan
parent a9109d5dda
commit 6a466f3fc3
67 changed files with 8974 additions and 108 deletions

View File

@@ -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>;
};

View File

@@ -13916,3 +13916,12 @@ M: Long Yu <long.yu@amlogic.com>
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 <shuai.li@amlogic.com>
M: xing wang <xing.wang@amlogic.com>
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

View File

@@ -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 =
<GIC_SPI 84 IRQ_TYPE_EDGE_RISING
GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 85 IRQ_TYPE_EDGE_RISING
GIC_SPI 89 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 86 IRQ_TYPE_EDGE_RISING
GIC_SPI 90 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 87 IRQ_TYPE_EDGE_RISING
GIC_SPI 84 IRQ_TYPE_EDGE_RISING
GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
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 = <GIC_SPI 86 IRQ_TYPE_EDGE_RISING>;
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>;

View File

@@ -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 =
<GIC_SPI 84 IRQ_TYPE_EDGE_RISING
GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 85 IRQ_TYPE_EDGE_RISING
GIC_SPI 89 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 86 IRQ_TYPE_EDGE_RISING
GIC_SPI 90 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 87 IRQ_TYPE_EDGE_RISING
GIC_SPI 84 IRQ_TYPE_EDGE_RISING
GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
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 = <GIC_SPI 86 IRQ_TYPE_EDGE_RISING>;
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>;

View File

@@ -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 =
<GIC_SPI 84 IRQ_TYPE_EDGE_RISING
GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 85 IRQ_TYPE_EDGE_RISING
GIC_SPI 89 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 86 IRQ_TYPE_EDGE_RISING
GIC_SPI 90 IRQ_TYPE_EDGE_RISING>;
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 =
<GIC_SPI 87 IRQ_TYPE_EDGE_RISING
GIC_SPI 84 IRQ_TYPE_EDGE_RISING
GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
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 = <GIC_SPI 86 IRQ_TYPE_EDGE_RISING>;
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>;

View File

@@ -17,6 +17,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/amlogic,axg-clkc.h>
#include <dt-bindings/clock/amlogic,axg-audio-clk.h>
#include <dt-bindings/gpio/mesonaxg-gpio.h>
#include <dt-bindings/pwm/pwm.h>
#include <dt-bindings/pwm/meson.h>
@@ -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 {

View File

@@ -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

View File

@@ -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"),

View File

@@ -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 */

View File

@@ -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

View File

@@ -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/

View File

@@ -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.

View File

@@ -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

View File

@@ -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 <linux/device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/clk-provider.h>
#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);

View File

@@ -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

View File

@@ -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 <linux/module.h>
#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");

View File

@@ -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

View File

@@ -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 <linux/clk.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#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.");

View File

@@ -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 <sound/soc.h>
#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_ */

View File

@@ -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 <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#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 = <xxx>"
* 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;
}

View File

@@ -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 <sound/soc.h>
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 */

View File

@@ -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 <linux/device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <dt-bindings/clock/amlogic,axg-audio-clk.h>
#include <linux/clk-provider.h>
#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);

View File

@@ -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 <linux/module.h>
#include <linux/slab.h>
#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<<mask_bit, 1<<31|1<<mask_bit);
to->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<<mask_bit, 0<<mask_bit);
/* no ddr active, disable arb switch */
value = aml_audiobus_read(actrl, EE_AUDIO_ARB_CTRL) & 0x77;
if (value == 0)
aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL,
1<<31, 0<<31);
kfree(to);
toddrs[id] = NULL;
pr_info("toddrs[%d] released by device %s\n", id, dev_name(dev));
return 0;
}
struct toddr *aml_audio_register_toddr(struct device *dev,
struct aml_audio_controller *actrl, enum ddr_num id)
{
struct toddr *to = NULL;
mutex_lock(&ddr_mutex);
to = register_toddr_l(dev, actrl, id);
mutex_unlock(&ddr_mutex);
return to;
}
int aml_audio_unregister_toddr(struct device *dev, enum ddr_num id)
{
int ret;
mutex_lock(&ddr_mutex);
ret = unregister_toddr_l(dev, id);
mutex_unlock(&ddr_mutex);
return ret;
}
static inline unsigned int
calc_toddr_address(unsigned int reg, unsigned int base)
{
return base + reg - EE_AUDIO_TODDR_A_CTRL0;
}
int aml_toddr_set_buf(struct toddr *to, unsigned int start,
unsigned int end)
{
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_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<<mask_bit, 1<<31|1<<mask_bit);
from->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<<mask_bit, 0<<mask_bit);
/* no ddr active, disable arb switch */
value = aml_audiobus_read(actrl, EE_AUDIO_ARB_CTRL) & 0x77;
if (value == 0)
aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL,
1<<31, 0<<31);
kfree(from);
frddrs[id] = NULL;
pr_info("frddrs[%d] released by device %s\n", id, dev_name(dev));
return 0;
}
struct frddr *aml_audio_register_frddr(struct device *dev,
struct aml_audio_controller *actrl, enum ddr_num id)
{
struct frddr *fr = NULL;
mutex_lock(&ddr_mutex);
fr = register_frddr_l(dev, actrl, id);
mutex_unlock(&ddr_mutex);
return fr;
}
int aml_audio_unregister_frddr(struct device *dev, enum ddr_num id)
{
int ret;
mutex_lock(&ddr_mutex);
ret = unregister_frddr_l(dev, id);
mutex_unlock(&ddr_mutex);
return ret;
}
static inline unsigned int
calc_frddr_address(unsigned int reg, unsigned int base)
{
return base + reg - EE_AUDIO_FRDDR_A_CTRL0;
}
int aml_frddr_set_buf(struct frddr *fr, unsigned int start,
unsigned int end)
{
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_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");

View File

@@ -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 <linux/device.h>
#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

View File

@@ -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 <linux/of.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#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);

View File

@@ -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

View File

@@ -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 <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#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);

View File

@@ -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 <linux/clk.h>
#include <linux/pinctrl/consumer.h>
#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__*/

View File

@@ -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 <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/string.h>
#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);
}

View File

@@ -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__*/

View File

@@ -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
};

View File

@@ -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

View File

@@ -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 <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#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);

View File

@@ -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 <sound/soc.h>
#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);
}
}

View File

@@ -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

View File

@@ -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 <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#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);

View File

@@ -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 <sound/soc.h>
#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));
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 <linux/amlogic/media/sound/aiu_regs.h>
#include <linux/amlogic/media/sound/audin_regs.h>
#include <linux/amlogic/cpu_version.h>
#include "aml_audio_hw.h"
#include "audio_hw.h"
/* i2s mode 0: master 1: slave */
/* source: 0: linein; 1: ATV; 2: HDMI-in */

View File

@@ -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.
*

View File

@@ -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 <linux/amlogic/iomap.h>
#include <linux/amlogic/media/sound/audin_regs.h>
#include "aml_audio_hw_pcm.h"
#include "audio_hw_pcm.h"
#include <linux/amlogic/media/sound/aiu_regs.h>

View File

@@ -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 <sound/pcm.h>
#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);

View File

@@ -1,5 +1,5 @@
/*
* sound/soc/amlogic/aml_dmic.c
* sound/soc/amlogic/meson/dmic.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*

View File

@@ -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 <linux/amlogic/iomap.h>
#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 <linux/amlogic/media/sound/aiu_regs.h>
#include <linux/amlogic/media/sound/audin_regs.h>

View File

@@ -1,5 +1,5 @@
/*
* sound/soc/amlogic/aml_i2s.h
* sound/soc/amlogic/meson/i2s.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*

View File

@@ -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 <sound/control.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#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 <linux/amlogic/media/sound/aout_notify.h>
#include "aml_spdif_dai.h"
#include "spdif_dai.h"
struct aml_dai_info dai_info[3] = { {0} };

View File

@@ -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.
*

View File

@@ -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 <linux/amlogic/saradc.h> */
#include <linux/amlogic/iomap.h>
#include "aml_i2s.h"
#include "aml_meson.h"
#include "aml_audio_hw.h"
#include "i2s.h"
#include "meson.h"
#include "audio_hw.h"
#include <linux/amlogic/media/sound/audin_regs.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>

View File

@@ -1,5 +1,5 @@
/*
* sound/soc/amlogic/aml_meson.h
* sound/soc/amlogic/meson/meson.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* sound/soc/amlogic/aml_notify.c
* sound/soc/amlogic/meson/notify.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*

View File

@@ -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 <sound/pcm_params.h>
#include <linux/amlogic/media/sound/audin_regs.h>
#include "aml_pcm.h"
#include "aml_audio_hw_pcm.h"
#include "pcm.h"
#include "audio_hw_pcm.h"
/*--------------------------------------------------------------------------
* Hardware definition

View File

@@ -1,5 +1,5 @@
/*
* sound/soc/amlogic/aml_pcm.h
* sound/soc/amlogic/meson/pcm.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*

View File

@@ -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 <sound/initval.h>
#include <sound/soc.h>
#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 <linux/of.h>

View File

@@ -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.
*

View File

@@ -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.
*

View File

@@ -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 <sound/pcm_params.h>
#include <linux/amlogic/iomap.h>
#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 <linux/amlogic/media/sound/aout_notify.h>
#include <linux/amlogic/media/sound/aiu_regs.h>
#include <linux/amlogic/media/sound/audin_regs.h>

View File

@@ -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.
*

View File

@@ -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 <sound/tas57xx.h>
#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"

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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
obj-$(CONFIG_AMLOGIC_SND_SOC_TAS5707) += snd-soc-tas5707.o
obj-$(CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101) += snd-soc-tlv320adc3101.o

View File

@@ -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 <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <sound/soc.h>
#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");

View File

@@ -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;

View File

@@ -0,0 +1,865 @@
/*
* linux/sound/soc/codecs/tlv320adc3101.c
*
* Copyright 2011 Amlogic
*
* Author: Alex Deng <alex.deng@amlogic.com>
*
* 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 <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#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 <alex.deng@amlogic.com>");
MODULE_LICENSE("GPL");

View File

@@ -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 */