[alsa-devel] [RFC PATCH v2 0/5] ASoC: Mediatek: Add support for MT2701 SOC
Refactor and go v2 because of the 60KB limitation
This patch adds basic support for Mediatek AFE for MT2701 SoC. The patch is based on broonie tree "for-next" branch.
The interface to connect with BT chip is not finished yet, but as an upstream rookie I really need some comments. Any comments will be greatly appreciated.
Garlic Tseng (5): ASoC: mediatek: Refine mt8173 driver ASoC: mediatek: add documents for mt2701 ASoC: mediatek: add clock and irq control for 2701 platform driver ASoC: mediatek: add mt2701 platform driver implementation. ASoC: mediatek: Add mt2701-cs42448 driver and config option.
.../devicetree/bindings/sound/mt2701-cs42448.txt | 39 + .../devicetree/bindings/sound/mtk2701-afe-pcm.txt | 150 ++ sound/soc/mediatek/Kconfig | 21 + sound/soc/mediatek/Makefile | 15 +- sound/soc/mediatek/mt2701/Makefile | 20 + sound/soc/mediatek/mt2701/mt2701-cs42448.c | 376 +++++ sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c | 259 +++ sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h | 28 + sound/soc/mediatek/mt2701/mtk2701-afe-common.h | 217 +++ sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c | 1752 ++++++++++++++++++++ sound/soc/mediatek/mt2701/mtk2701-irq.c | 109 ++ sound/soc/mediatek/mt2701/mtk2701-irq.h | 30 + sound/soc/mediatek/mt2701/mtk2701-reg.h | 165 ++ sound/soc/mediatek/mt8173/Makefile | 7 + sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c | 2 +- .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c | 2 +- .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c | 4 +- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c | 2 +- sound/soc/mediatek/{ => mt8173}/mtk-afe-common.h | 0 sound/soc/mediatek/{ => mt8173}/mtk-afe-pcm.c | 0 20 files changed, 3186 insertions(+), 12 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt create mode 100644 Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt create mode 100644 sound/soc/mediatek/mt2701/Makefile create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-common.h create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c create mode 100644 sound/soc/mediatek/mt2701/mtk2701-irq.c create mode 100644 sound/soc/mediatek/mt2701/mtk2701-irq.h create mode 100644 sound/soc/mediatek/mt2701/mtk2701-reg.h create mode 100644 sound/soc/mediatek/mt8173/Makefile rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%) rename sound/soc/mediatek/{ => mt8173}/mtk-afe-common.h (100%) rename sound/soc/mediatek/{ => mt8173}/mtk-afe-pcm.c (100%)
Move mt8173 driver to another folder.
The software control sequence of mt2701 is very different from that of mt8173, so this patch moves mt8173 code to another folder.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com --- sound/soc/mediatek/Makefile | 12 +++++------- sound/soc/mediatek/mt8173/Makefile | 7 +++++++ sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c | 2 +- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c | 2 +- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c | 4 ++-- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c | 2 +- sound/soc/mediatek/{ => mt8173}/mtk-afe-common.h | 0 sound/soc/mediatek/{ => mt8173}/mtk-afe-pcm.c | 0 8 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 sound/soc/mediatek/mt8173/Makefile rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%) rename sound/soc/mediatek/{ => mt8173}/mtk-afe-common.h (100%) rename sound/soc/mediatek/{ => mt8173}/mtk-afe-pcm.c (100%)
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index d486860..5048165 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -1,7 +1,5 @@ -# MTK Platform Support -obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o -# Machine support -obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o +# 8173 Machine support +obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173/ diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile new file mode 100644 index 0000000..d486860 --- /dev/null +++ b/sound/soc/mediatek/mt8173/Makefile @@ -0,0 +1,7 @@ +# MTK Platform Support +obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o +# Machine support +obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c similarity index 99% rename from sound/soc/mediatek/mt8173-max98090.c rename to sound/soc/mediatek/mt8173/mt8173-max98090.c index 71a1a35..5524a2c 100644 --- a/sound/soc/mediatek/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -18,7 +18,7 @@ #include <sound/soc.h> #include <sound/jack.h> #include <linux/gpio.h> -#include "../codecs/max98090.h" +#include "../../codecs/max98090.h"
static struct snd_soc_jack mt8173_max98090_jack;
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c similarity index 99% rename from sound/soc/mediatek/mt8173-rt5650-rt5514.c rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 58e0836..467f704 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -19,7 +19,7 @@ #include <linux/of_gpio.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/rt5645.h" +#include "../../codecs/rt5645.h"
#define MCLK_FOR_CODECS 12288000
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c similarity index 99% rename from sound/soc/mediatek/mt8173-rt5650-rt5676.c rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 5c4c58c..cfa23d9 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -19,8 +19,8 @@ #include <linux/of_gpio.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/rt5645.h" -#include "../codecs/rt5677.h" +#include "../../codecs/rt5645.h" +#include "../../codecs/rt5677.h"
#define MCLK_FOR_CODECS 12288000
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c similarity index 99% rename from sound/soc/mediatek/mt8173-rt5650.c rename to sound/soc/mediatek/mt8173/mt8173-rt5650.c index bb09bb1..20b3b7d 100644 --- a/sound/soc/mediatek/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -19,7 +19,7 @@ #include <linux/of_gpio.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/rt5645.h" +#include "../../codecs/rt5645.h"
#define MCLK_FOR_CODECS 12288000
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mt8173/mtk-afe-common.h similarity index 100% rename from sound/soc/mediatek/mtk-afe-common.h rename to sound/soc/mediatek/mt8173/mtk-afe-common.h diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mt8173/mtk-afe-pcm.c similarity index 100% rename from sound/soc/mediatek/mtk-afe-pcm.c rename to sound/soc/mediatek/mt8173/mtk-afe-pcm.c
On Thu, 2016-04-14 at 20:14 +0800, Garlic Tseng wrote:
Move mt8173 driver to another folder.
The software control sequence of mt2701 is very different from that of mt8173, so this patch moves mt8173 code to another folder.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com
sound/soc/mediatek/Makefile | 12 +++++------- sound/soc/mediatek/mt8173/Makefile | 7 +++++++ sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c | 2 +- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c | 2 +- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c | 4 ++-- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c | 2 +- sound/soc/mediatek/{ => mt8173}/mtk-afe-common.h | 0 sound/soc/mediatek/{ => mt8173}/mtk-afe-pcm.c | 0 8 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 sound/soc/mediatek/mt8173/Makefile rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%) rename sound/soc/mediatek/{ => mt8173}/mtk-afe-common.h (100%) rename sound/soc/mediatek/{ => mt8173}/mtk-afe-pcm.c (100%)
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index d486860..5048165 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -1,7 +1,5 @@ -# MTK Platform Support -obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o -# Machine support -obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o +# 8173 Machine support +obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173/
Since all CONFIG_SND_SOC_MT8173_* depends on CONFIG_SND_SOC_MEDIATEK, you could just do:
obj-$(CONFIG_SND_SOC_MEDIATEK) += mt8173/
This way we don't need to modify 2 makefiles when adding new ASoC driver.
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile new file mode 100644 index 0000000..d486860 --- /dev/null +++ b/sound/soc/mediatek/mt8173/Makefile @@ -0,0 +1,7 @@ +# MTK Platform Support +obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
Maybe we should also rename this to mt8173-afe-pcm.o?
Joe.C
On Thu, 2016-04-14 at 20:44 +0800, Yingjoe Chen wrote:
On Thu, 2016-04-14 at 20:14 +0800, Garlic Tseng wrote:
Move mt8173 driver to another folder.
The software control sequence of mt2701 is very different from that of mt8173, so this patch moves mt8173 code to another folder.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com
sound/soc/mediatek/Makefile | 12 +++++------- sound/soc/mediatek/mt8173/Makefile | 7 +++++++ sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c | 2 +- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c | 2 +- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c | 4 ++-- sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c | 2 +- sound/soc/mediatek/{ => mt8173}/mtk-afe-common.h | 0 sound/soc/mediatek/{ => mt8173}/mtk-afe-pcm.c | 0 8 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 sound/soc/mediatek/mt8173/Makefile rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%) rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%) rename sound/soc/mediatek/{ => mt8173}/mtk-afe-common.h (100%) rename sound/soc/mediatek/{ => mt8173}/mtk-afe-pcm.c (100%)
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index d486860..5048165 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -1,7 +1,5 @@ -# MTK Platform Support -obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o -# Machine support -obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o -obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o +# 8173 Machine support +obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173/ +obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173/
Since all CONFIG_SND_SOC_MT8173_* depends on CONFIG_SND_SOC_MEDIATEK, you could just do:
obj-$(CONFIG_SND_SOC_MEDIATEK) += mt8173/
This way we don't need to modify 2 makefiles when adding new ASoC driver.
Yes you're right. I'll modify it.
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile new file mode 100644 index 0000000..d486860 --- /dev/null +++ b/sound/soc/mediatek/mt8173/Makefile @@ -0,0 +1,7 @@ +# MTK Platform Support +obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
Maybe we should also rename this to mt8173-afe-pcm.o?
Well I agree with you. Maybe I have to check all the files and modify them with prefix "mt8173" and "mt2701". (or "mtk8173" and "mtk2701"? I'm not so sure here)
Joe.C
Thanks for your comments.
Add documents for mt2701-cs42448 machine driver and mt2701 platform driver.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com --- .../devicetree/bindings/sound/mt2701-cs42448.txt | 39 ++++++ .../devicetree/bindings/sound/mtk2701-afe-pcm.txt | 150 +++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt create mode 100644 Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt
diff --git a/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt new file mode 100644 index 0000000..be5d9b9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt @@ -0,0 +1,39 @@ +MT2701 with CS42448 CODEC + +Required properties: +- compatible : "mediatek,mt2701-cs42448-machin" +- mediatek,platform: the phandle of MT8173 ASoC platform +- audio-routing: a list of the connections between audio +- pinctrl-names: Should contain only one value - "default" +- pinctrl-0: Should specify pin control groups used for this controller. +- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to + control I2S1-in mux. + +Example: + + sound:sound { + compatible = "mediatek,mt2701-cs42448-machine"; + mediatek,platform = <&afe>; + /* CS42448 Machine name */ + audio-routing = + "Line Out Jack", "AOUT1L", + "Line Out Jack", "AOUT1R", + "Line Out Jack", "AOUT2L", + "Line Out Jack", "AOUT2R", + "Line Out Jack", "AOUT3L", + "Line Out Jack", "AOUT3R", + "Line Out Jack", "AOUT4L", + "Line Out Jack", "AOUT4R", + "AIN1L", "AMIC", + "AIN1R", "AMIC", + "AIN2L", "Tuner In", + "AIN2R", "Tuner In", + "AIN3L", "Satellite Tuner In", + "AIN3R", "Satellite Tuner In", + "AIN3L", "AUX In", + "AIN3R", "AUX In"; + pinctrl-names = "default"; + pinctrl-0 = <&aud_pins_default>; + i2s1-in-sel-gpio1 = <&pio 53 0>; + i2s1-in-sel-gpio2 = <&pio 54 0>; + }; \ No newline at end of file diff --git a/Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt new file mode 100644 index 0000000..02232ca0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt @@ -0,0 +1,150 @@ +Mediatek AFE PCM controller for mt2701 + +Required properties: +- compatible = "mediatek,mt2701-audio"; +- reg: register location and size +- interrupts: Should contain AFE interrupt +- clock-names: should have these clock names: + "infra_sys_audio_clk", + "top_audio_mux1_sel", + "top_audio_mux2_sel", + "top_audio_mux1_div", + "top_audio_mux2_div", + "top_audio_48k_timing", + "top_audio_44k_timing", + "top_audpll_mux_sel", + "top_apll_sel", + "top_aud1_pll_98M", + "top_aud2_pll_90M", + "top_hadds2_pll_98M", + "top_hadds2_pll_294M", + "top_audpll", + "top_audpll_d4", + "top_audpll_d8", + "top_audpll_d16", + "top_audpll_d24", + "top_audintbus_sel", + "clk_26m", + "top_syspll1_d4", + "top_aud_k1_src_sel", + "top_aud_k2_src_sel", + "top_aud_k3_src_sel", + "top_aud_k4_src_sel", + "top_aud_k5_src_sel", + "top_aud_k6_src_sel", + "top_aud_k1_src_div", + "top_aud_k2_src_div", + "top_aud_k3_src_div", + "top_aud_k4_src_div", + "top_aud_k5_src_div", + "top_aud_k6_src_div", + "top_aud_i2s1_mclk", + "top_aud_i2s2_mclk", + "top_aud_i2s3_mclk", + "top_aud_i2s4_mclk", + "top_aud_i2s5_mclk", + "top_aud_i2s6_mclk", + "top_asm_m_sel", + "top_asm_h_sel", + "top_univpll2_d4", + "top_univpll2_d2", + "top_syspll_d5"; + +Example: + + afe: mt8173-afe-pcm@11220000 { + compatible = "mediatek,mt2701-audio"; + reg = <0 0x11220000 0 0x2000>, + <0 0x112A0000 0 0x20000>; + interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>, + <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>; + clocks = <&infracfg CLK_INFRA_AUDIO>, + <&topckgen CLK_TOP_AUD_MUX1_SEL>, + <&topckgen CLK_TOP_AUD_MUX2_SEL>, + <&topckgen CLK_TOP_AUD_MUX1_DIV>, + <&topckgen CLK_TOP_AUD_MUX2_DIV>, + <&topckgen CLK_TOP_AUD_48K_TIMING>, + <&topckgen CLK_TOP_AUD_44K_TIMING>, + <&topckgen CLK_TOP_AUDPLL_MUX_SEL>, + <&topckgen CLK_TOP_APLL_SEL>, + <&topckgen CLK_TOP_AUD1PLL_98M>, + <&topckgen CLK_TOP_AUD2PLL_90M>, + <&topckgen CLK_TOP_HADDS2PLL_98M>, + <&topckgen CLK_TOP_HADDS2PLL_294M>, + <&topckgen CLK_TOP_AUDPLL>, + <&topckgen CLK_TOP_AUDPLL_D4>, + <&topckgen CLK_TOP_AUDPLL_D8>, + <&topckgen CLK_TOP_AUDPLL_D16>, + <&topckgen CLK_TOP_AUDPLL_D24>, + <&topckgen CLK_TOP_AUDINTBUS_SEL>, + <&clk26m>, + <&topckgen CLK_TOP_SYSPLL1_D4>, + <&topckgen CLK_TOP_AUD_K1_SRC_SEL>, + <&topckgen CLK_TOP_AUD_K2_SRC_SEL>, + <&topckgen CLK_TOP_AUD_K3_SRC_SEL>, + <&topckgen CLK_TOP_AUD_K4_SRC_SEL>, + <&topckgen CLK_TOP_AUD_K5_SRC_SEL>, + <&topckgen CLK_TOP_AUD_K6_SRC_SEL>, + <&topckgen CLK_TOP_AUD_K1_SRC_DIV>, + <&topckgen CLK_TOP_AUD_K2_SRC_DIV>, + <&topckgen CLK_TOP_AUD_K3_SRC_DIV>, + <&topckgen CLK_TOP_AUD_K4_SRC_DIV>, + <&topckgen CLK_TOP_AUD_K5_SRC_DIV>, + <&topckgen CLK_TOP_AUD_K6_SRC_DIV>, + <&topckgen CLK_TOP_AUD_I2S1_MCLK>, + <&topckgen CLK_TOP_AUD_I2S2_MCLK>, + <&topckgen CLK_TOP_AUD_I2S3_MCLK>, + <&topckgen CLK_TOP_AUD_I2S4_MCLK>, + <&topckgen CLK_TOP_AUD_I2S5_MCLK>, + <&topckgen CLK_TOP_AUD_I2S6_MCLK>, + <&topckgen CLK_TOP_ASM_M_SEL>, + <&topckgen CLK_TOP_ASM_H_SEL>, + <&topckgen CLK_TOP_UNIVPLL2_D4>, + <&topckgen CLK_TOP_UNIVPLL2_D2>, + <&topckgen CLK_TOP_SYSPLL_D5>; + + clock-names = "infra_sys_audio_clk", + "top_audio_mux1_sel", + "top_audio_mux2_sel", + "top_audio_mux1_div", + "top_audio_mux2_div", + "top_audio_48k_timing", + "top_audio_44k_timing", + "top_audpll_mux_sel", + "top_apll_sel", + "top_aud1_pll_98M", + "top_aud2_pll_90M", + "top_hadds2_pll_98M", + "top_hadds2_pll_294M", + "top_audpll", + "top_audpll_d4", + "top_audpll_d8", + "top_audpll_d16", + "top_audpll_d24", + "top_audintbus_sel", + "clk_26m", + "top_syspll1_d4", + "top_aud_k1_src_sel", + "top_aud_k2_src_sel", + "top_aud_k3_src_sel", + "top_aud_k4_src_sel", + "top_aud_k5_src_sel", + "top_aud_k6_src_sel", + "top_aud_k1_src_div", + "top_aud_k2_src_div", + "top_aud_k3_src_div", + "top_aud_k4_src_div", + "top_aud_k5_src_div", + "top_aud_k6_src_div", + "top_aud_i2s1_mclk", + "top_aud_i2s2_mclk", + "top_aud_i2s3_mclk", + "top_aud_i2s4_mclk", + "top_aud_i2s5_mclk", + "top_aud_i2s6_mclk", + "top_asm_m_sel", + "top_asm_h_sel", + "top_univpll2_d4", + "top_univpll2_d2", + "top_syspll_d5"; + };
On 14/04/16 14:14, Garlic Tseng wrote: [...]
diff --git a/Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt new file mode 100644
Nit: I would prefer to name it mt2701-afe-pcm.txt as all the other SoC specific files.
Regards, Matthias
On Wed, 2016-04-20 at 12:36 +0200, Matthias Brugger wrote:
On 14/04/16 14:14, Garlic Tseng wrote: [...]
diff --git a/Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt new file mode 100644
Nit: I would prefer to name it mt2701-afe-pcm.txt as all the other SoC specific files.
OK, I'll change it in the next version. Thanks for you comment.
Regards, Matthias
Clock and irq controls for 2701 platform driver. There are some BLOCK_COMMENT_STYLE errors from checkpatch and they will be fixed after BT functions finished.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com --- sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c | 259 +++++++++++++++++++++ sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h | 28 +++ sound/soc/mediatek/mt2701/mtk2701-afe-common.h | 217 +++++++++++++++++ sound/soc/mediatek/mt2701/mtk2701-irq.c | 109 +++++++++ sound/soc/mediatek/mt2701/mtk2701-irq.h | 30 +++ sound/soc/mediatek/mt2701/mtk2701-reg.h | 165 +++++++++++++ 6 files changed, 808 insertions(+) create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-common.h create mode 100644 sound/soc/mediatek/mt2701/mtk2701-irq.c create mode 100644 sound/soc/mediatek/mt2701/mtk2701-irq.h create mode 100644 sound/soc/mediatek/mt2701/mtk2701-reg.h
diff --git a/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c new file mode 100644 index 0000000..eb1334a --- /dev/null +++ b/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c @@ -0,0 +1,259 @@ +/* + * mtk2701-afe-clock-ctrl.c -- Mediatek 2701 afe clock ctrl + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Garlic Tseng garlic.tseng@mediatek.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <sound/soc.h> +#include <linux/regmap.h> +#include <linux/pm_runtime.h> + +#include "mtk2701-reg.h" +#include "mtk2701-afe-common.h" +#include "mtk2701-afe-clock-ctrl.h" + +void mtk2701_afe_enable_clock(struct mtk_afe *afe, int en) +{ + if (en) { + mtk2701_turn_on_a1sys_clock(afe); + mtk2701_turn_on_a2sys_clock(afe); + mtk2701_turn_on_afe_clock(afe); + regmap_update_bits(afe->regmap, ASYS_TOP_CON, + AUDIO_TOP_CON0_A1SYS_A2SYS_ON, + AUDIO_TOP_CON0_A1SYS_A2SYS_ON); + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + AFE_DAC_CON0_AFE_ON, AFE_DAC_CON0_AFE_ON); + regmap_write(afe->regmap, PWR2_TOP_CON, PWR2_TOP_CON_INIT_VAL); + regmap_write(afe->regmap, PWR1_ASM_CON1, + PWR1_ASM_CON1_INIT_VAL); + regmap_write(afe->regmap, PWR2_ASM_CON1, + PWR2_ASM_CON1_INIT_VAL); + } else { + mtk2701_turn_off_afe_clock(afe); + mtk2701_turn_off_a1sys_clock(afe); + mtk2701_turn_off_a2sys_clock(afe); + regmap_update_bits(afe->regmap, ASYS_TOP_CON, + AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0); + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + AFE_DAC_CON0_AFE_ON, 0); + } +} + +void mtk2701_turn_on_a1sys_clock(struct mtk_afe *afe) +{ + int ret = 0; + struct audio_clock_attr *aud_clks = afe->aud_clks; + + /* Set Mux */ + ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, + aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock_data->name, + ret); + + ret = clk_set_parent(aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock, + aud_clks[AUDCLK_TOP_AUD1PLL_98M].clock); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, + aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock_data->name, + aud_clks[AUDCLK_TOP_AUD1PLL_98M].clock_data->name, ret); + + /* Set Divider */ + ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, + aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock_data->name, + ret); + ret = clk_set_rate(aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock, + 98304000 / 2); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__, + aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock_data->name, + 98304000 / 2, ret); + + /* Enable clock gate */ + ret = clk_enable(aud_clks[AUDCLK_TOP_AUD_48K_TIMING].clock); + if (ret) + dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__, + aud_clks[AUDCLK_TOP_AUD_48K_TIMING].clock_data->name, + ret); + /* Enable infra audio */ + ret = clk_enable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock); + if (ret) + dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__, + aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock_data->name, ret); +} + +void mtk2701_turn_off_a1sys_clock(struct mtk_afe *afe) +{ + struct audio_clock_attr *aud_clks = afe->aud_clks; + + clk_disable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock); + clk_disable(aud_clks[AUDCLK_TOP_AUD_48K_TIMING].clock); + clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock); + clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock); +} + +void mtk2701_turn_on_a2sys_clock(struct mtk_afe *afe) +{ + int ret = 0; + struct audio_clock_attr *aud_clks = afe->aud_clks; + /* Set Mux */ + ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, + aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock_data->name, + ret); + ret = clk_set_parent(aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock, + aud_clks[AUDCLK_TOP_AUD2PLL_90M].clock); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, + aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock_data->name, + aud_clks[AUDCLK_TOP_AUD2PLL_90M].clock_data->name, ret); + /* Set Divider */ + ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, + aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock_data->name, + ret); + ret = clk_set_rate(aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock, + 90316800 / 2); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__, + aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock_data->name, + 90316800 / 2, ret); + + /* Enable clock gate */ + ret = clk_enable(aud_clks[AUDCLK_TOP_AUD_44K_TIMING].clock); + if (ret) + dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__, + aud_clks[AUDCLK_TOP_AUD_44K_TIMING].clock_data->name, + ret); + /* Enable infra audio */ + ret = clk_enable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock); + if (ret) + dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__, + aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock_data->name, ret); +} + +void mtk2701_turn_off_a2sys_clock(struct mtk_afe *afe) +{ + struct audio_clock_attr *aud_clks = afe->aud_clks; + + clk_disable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock); + clk_disable(aud_clks[AUDCLK_TOP_AUD_44K_TIMING].clock); + clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock); + clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock); +} + +void mtk2701_turn_on_afe_clock(struct mtk_afe *afe) +{ + int ret; + struct audio_clock_attr *aud_clks = afe->aud_clks; + + /*MT_CG_INFRA_AUDIO, INFRA_PDN_STA[5]*/ + ret = clk_enable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock); + if (ret) + dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__, + aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock_data->name, ret); + + /* Set AUDCLK_TOP_AUDINTBUS to AUDCLK_TOP_SYSPLL1_D4 */ + ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUDINTBUS].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, + aud_clks[AUDCLK_TOP_AUDINTBUS].clock_data->name, ret); + + ret = clk_set_parent(aud_clks[AUDCLK_TOP_AUDINTBUS].clock, + aud_clks[AUDCLK_TOP_SYSPLL1_D4].clock); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, + aud_clks[AUDCLK_TOP_AUDINTBUS].clock_data->name, + aud_clks[AUDCLK_TOP_SYSPLL1_D4].clock_data->name, ret); + + /* Set AUDCLK_TOP_ASM_H_SEL to AUDCLK_TOP_UNIVPLL2_D2*/ + ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_ASM_H_SEL].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, + aud_clks[AUDCLK_TOP_ASM_H_SEL].clock_data->name, ret); + + ret = clk_set_parent(aud_clks[AUDCLK_TOP_ASM_H_SEL].clock, + aud_clks[AUDCLK_TOP_UNIVPLL2_D2].clock); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, + aud_clks[AUDCLK_TOP_ASM_H_SEL].clock_data->name, + aud_clks[AUDCLK_TOP_UNIVPLL2_D2].clock_data->name, ret); + + if (ret) + dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__, + aud_clks[AUDCLK_TOP_ASM_H_SEL].clock_data->name, ret); + + /* Set AUDCLK_TOP_ASM_M_SEL to AUDCLK_TOP_UNIVPLL2_D4*/ + ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_ASM_M_SEL].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, + aud_clks[AUDCLK_TOP_ASM_M_SEL].clock_data->name, ret); + + ret = clk_set_parent(aud_clks[AUDCLK_TOP_ASM_M_SEL].clock, + aud_clks[AUDCLK_TOP_UNIVPLL2_D4].clock); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__, + aud_clks[AUDCLK_TOP_ASM_M_SEL].clock_data->name, + aud_clks[AUDCLK_TOP_UNIVPLL2_D4].clock_data->name, ret); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUDIO_TOP_CON0_PDN_AFE, 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUDIO_TOP_CON0_PDN_APLL_CK, 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, + AUDIO_TOP_CON4_PDN_A1SYS, 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, + AUDIO_TOP_CON4_PDN_A2SYS, 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, + AUDIO_TOP_CON4_PDN_AFE_CONN, 0); +} + +void mtk2701_turn_off_afe_clock(struct mtk_afe *afe) +{ + struct audio_clock_attr *aud_clks = afe->aud_clks; + + /*MT_CG_INFRA_AUDIO,*/ + clk_disable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock); + + clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUDINTBUS].clock); + clk_disable_unprepare(aud_clks[AUDCLK_TOP_ASM_H_SEL].clock); + clk_disable_unprepare(aud_clks[AUDCLK_TOP_ASM_M_SEL].clock); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUDIO_TOP_CON0_PDN_APLL_CK, + AUDIO_TOP_CON0_PDN_APLL_CK); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, + AUDIO_TOP_CON4_PDN_A1SYS, AUDIO_TOP_CON4_PDN_A1SYS); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, + AUDIO_TOP_CON4_PDN_A2SYS, AUDIO_TOP_CON4_PDN_A2SYS); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, + AUDIO_TOP_CON4_PDN_AFE_CONN, + AUDIO_TOP_CON4_PDN_AFE_CONN); +} + +MODULE_DESCRIPTION("MTK2701 afe clock control"); +MODULE_AUTHOR("Garlic Tseng garlic.tseng@mediatek.com"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h new file mode 100644 index 0000000..832b1a9 --- /dev/null +++ b/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h @@ -0,0 +1,28 @@ +/* + * mtk2701-afe-clock-ctrl.h -- Mediatek 2701 afe clock ctrl definition + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Garlic Tseng garlic.tseng@mediatek.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MTK2701_AFE_CLOCK_CTRL_H_ +#define _MTK2701_AFE_CLOCK_CTRL_H_ + +void mtk2701_afe_enable_clock(struct mtk_afe *afe, int en); +void mtk2701_turn_on_a1sys_clock(struct mtk_afe *afe); +void mtk2701_turn_off_a1sys_clock(struct mtk_afe *afe); +void mtk2701_turn_on_a2sys_clock(struct mtk_afe *afe); +void mtk2701_turn_off_a2sys_clock(struct mtk_afe *afe); +void mtk2701_turn_on_afe_clock(struct mtk_afe *afe); +void mtk2701_turn_off_afe_clock(struct mtk_afe *afe); + +#endif diff --git a/sound/soc/mediatek/mt2701/mtk2701-afe-common.h b/sound/soc/mediatek/mt2701/mtk2701-afe-common.h new file mode 100644 index 0000000..daf40dd --- /dev/null +++ b/sound/soc/mediatek/mt2701/mtk2701-afe-common.h @@ -0,0 +1,217 @@ +/* + * mtk2701-afe-common.h -- Mediatek 2701 audio driver definitions + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Garlic Tseng garlic.tseng@mediatek.com + * Koro Chen koro.chen@mediatek.com + * Sascha Hauer s.hauer@pengutronix.de + * Hidalgo Huang hidalgo.huang@mediatek.com + * Ir Lian ir.lian@mediatek.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MTK_AFE_COMMON_H_ +#define _MTK_AFE_COMMON_H_ +#include <sound/soc.h> +#include <linux/clk.h> +#include <linux/regmap.h> + +#define MTK_MEMIF_STREAM_NUM (SNDRV_PCM_STREAM_LAST + 1) + +enum { + MTK_AFE_I2S_1, + MTK_AFE_I2S_2, + MTK_AFE_I2S_3, + MTK_AFE_I2S_4, + MTK_I2S_NUM, +}; + +enum { + MTK_AFE_MEMIF_1, + MTK_AFE_MEMIF_2, + MTK_AFE_MEMIF_3, + MTK_AFE_MEMIF_4, + MTK_AFE_MEMIF_5, + MTK_AFE_MEMIF_SINGLE_NUM, + MTK_AFE_MEMIF_M = MTK_AFE_MEMIF_SINGLE_NUM, + MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_I2S = MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_2ND_I2S, + MTK_AFE_IO_3RD_I2S, + MTK_AFE_IO_4TH_I2S, + MTK_AFE_IO_5TH_I2S, + MTK_AFE_IO_6TH_I2S, + MTK_AFE_IO_MRG_O, + MTK_AFE_IO_MRG_I, +}; + +enum { + /*need for DAIBT, will implement before review*/ +/* + IRQ_AFE_IRQ1, + IRQ_AFE_IRQ2, +*/ + IRQ_ASYS_START, + IRQ_ASYS_IRQ1 = IRQ_ASYS_START, + IRQ_ASYS_IRQ2, + IRQ_ASYS_IRQ3, + IRQ_ASYS_END, + IRQ_NUM = IRQ_ASYS_END, +}; + +enum { + DIV_ID_MCLK_TO_BCK, + DIV_ID_BCK_TO_LRCK, +}; + +/*2701 clock def*/ +enum audio_system_clock_type { + AUDCLK_INFRA_SYS_AUDIO, + AUDCLK_TOP_AUD_MUX1_SEL, + AUDCLK_TOP_AUD_MUX2_SEL, + AUDCLK_TOP_AUD_MUX1_DIV, + AUDCLK_TOP_AUD_MUX2_DIV, + AUDCLK_TOP_AUD_48K_TIMING, + AUDCLK_TOP_AUD_44K_TIMING, + AUDCLK_TOP_AUDPLL_MUX_SEL, + AUDCLK_TOP_APLL_SEL, + AUDCLK_TOP_AUD1PLL_98M, + AUDCLK_TOP_AUD2PLL_90M, + AUDCLK_TOP_HADDS2PLL_98M, + AUDCLK_TOP_HADDS2PLL_294M, + AUDCLK_TOP_AUDPLL, + AUDCLK_TOP_AUDPLL_D4, + AUDCLK_TOP_AUDPLL_D8, + AUDCLK_TOP_AUDPLL_D16, + AUDCLK_TOP_AUDPLL_D24, + AUDCLK_TOP_AUDINTBUS, + AUDCLK_CLK_26M, + AUDCLK_TOP_SYSPLL1_D4, + AUDCLK_TOP_AUD_K1_SRC_SEL, + AUDCLK_TOP_AUD_K2_SRC_SEL, + AUDCLK_TOP_AUD_K3_SRC_SEL, + AUDCLK_TOP_AUD_K4_SRC_SEL, + AUDCLK_TOP_AUD_K5_SRC_SEL, + AUDCLK_TOP_AUD_K6_SRC_SEL, + AUDCLK_TOP_AUD_K1_SRC_DIV, + AUDCLK_TOP_AUD_K2_SRC_DIV, + AUDCLK_TOP_AUD_K3_SRC_DIV, + AUDCLK_TOP_AUD_K4_SRC_DIV, + AUDCLK_TOP_AUD_K5_SRC_DIV, + AUDCLK_TOP_AUD_K6_SRC_DIV, + AUDCLK_TOP_AUD_I2S1_MCLK, + AUDCLK_TOP_AUD_I2S2_MCLK, + AUDCLK_TOP_AUD_I2S3_MCLK, + AUDCLK_TOP_AUD_I2S4_MCLK, + AUDCLK_TOP_AUD_I2S5_MCLK, + AUDCLK_TOP_AUD_I2S6_MCLK, + AUDCLK_TOP_ASM_M_SEL, + AUDCLK_TOP_ASM_H_SEL, + AUDCLK_TOP_UNIVPLL2_D4, + AUDCLK_TOP_UNIVPLL2_D2, + AUDCLK_TOP_SYSPLL_D5, + CLOCK_NUM +}; + +struct audio_clock_attr_data { + const char *name; + const bool prepare_once; +}; + +struct audio_clock_attr { + struct audio_clock_attr_data *clock_data; + bool is_prepared; + struct clk *clock; +}; + +struct mtk_afe; +struct snd_pcm_substream; + +struct mtk_afe_memif_data { + int id; + const char *name; + int reg_ofs_base; + int reg_ofs_cur; + int fs_reg; + int fs_shift; + int mono_reg; + int mono_shift; + int enable_shift; + int hd_reg; + int hd_shift; + int agent_disable_shift; +}; + +struct mtk_afe_irq_data { + int irq_id; + int irq_cnt_reg; + int irq_cnt_shift; + int irq_cnt_maskbit; + int irq_fs_reg; + int irq_fs_shift; + int irq_fs_maskbit; + int irq_en_reg; + int irq_en_shift; + int irq_occupy; +}; + +struct mtk_afe_irq { + const struct mtk_afe_irq_data *irq_data; + int irq_occupyed; + struct mtk_afe_memif *memif; + void (*isr)(struct mtk_afe *afe, struct mtk_afe_memif *memif); +}; + +struct mtk_afe_memif { + unsigned int phys_buf_addr; + int buffer_size; + unsigned int hw_ptr; + struct snd_pcm_substream *substream; + const struct mtk_afe_memif_data *data; + struct mtk_afe_irq *irq; +}; + +struct mtk_i2s_data { + int i2s_ctrl_reg; + int i2s_pwn_shift; + int i2s_asrc_fs_shift; + int i2s_asrc_fs_mask; +}; + +enum mtk_i2s_dir { + I2S_OUT, + I2S_IN, + I2S_DIR_NUM, +}; + +struct mtk_i2s_path { + int dai_id; + int mclk_rate; + int div_mclk_to_bck; + int div_bck_to_lrck; + int format; + snd_pcm_format_t stream_fmt; + int on[I2S_DIR_NUM]; + int occupied[I2S_DIR_NUM]; + const struct mtk_i2s_data *i2s_data[2]; +}; + +struct mtk_afe { + void __iomem *base_addr; + struct device *dev; + struct regmap *regmap; + struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM][MTK_MEMIF_STREAM_NUM]; + struct audio_clock_attr aud_clks[CLOCK_NUM]; + struct mtk_afe_irq irqs[IRQ_NUM]; + struct mtk_i2s_path i2s_path[MTK_I2S_NUM]; +}; + +#endif diff --git a/sound/soc/mediatek/mt2701/mtk2701-irq.c b/sound/soc/mediatek/mt2701/mtk2701-irq.c new file mode 100644 index 0000000..a319ae5 --- /dev/null +++ b/sound/soc/mediatek/mt2701/mtk2701-irq.c @@ -0,0 +1,109 @@ +/* + * mtk2701-irq.c -- Mediatek 2701 audio driver irq function + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Garlic Tseng garlic.tseng@mediatek.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "mtk2701-reg.h" +#include "mtk2701-afe-common.h" +#include "mtk2701-irq.h" + +u32 mtk2701_asys_irq_status(struct mtk_afe *afe) +{ + u32 status = 0; + + regmap_read(afe->regmap, ASYS_IRQ_STATUS, &status); + return status; +} + +void mtk2701_asys_irq_clear(struct mtk_afe *afe, u32 status) +{ + regmap_write(afe->regmap, ASYS_IRQ_CLR, status); +} + +void mtk2701_memif_isr(struct mtk_afe *afe, struct mtk_afe_memif *memif) +{ + if (memif) { + u32 base, cur; + + mtk2701_afe_memif_base(afe, memif, &base); + mtk2701_afe_memif_pointer(afe, memif, &cur); + memif->hw_ptr = cur - base; + snd_pcm_period_elapsed(memif->substream); + } +} + +int mtk2701_afe_memif_base(struct mtk_afe *afe, struct mtk_afe_memif *memif, + u32 *base) +{ + if (!memif || !memif->data) { + dev_err(afe->dev, "%s() error: invalid memif %p\n", + __func__, memif); + return -EINVAL; + } + if (!base) + return -ENOMEM; + regmap_read(afe->regmap, memif->data->reg_ofs_base, base); + return 0; +} + +int mtk2701_afe_memif_pointer(struct mtk_afe *afe, struct mtk_afe_memif *memif, + u32 *cur_ptr) +{ + if (!memif || !memif->data) { + dev_err(afe->dev, "%s() error: invalid memif %p\n", + __func__, memif); + return -EINVAL; + } + if (!cur_ptr) + return -ENOMEM; + regmap_read(afe->regmap, memif->data->reg_ofs_cur, cur_ptr); + return 0; +} + +static DEFINE_MUTEX(asys_irqs_lock); +int mtk2701_asys_irq_acquire(struct mtk_afe *afe) +{ + int i; + + mutex_lock(&asys_irqs_lock); + for (i = IRQ_ASYS_START; i < IRQ_ASYS_END; ++i) { + if (afe->irqs[i].irq_occupyed == 0) { + afe->irqs[i].irq_occupyed = 1; + mutex_unlock(&asys_irqs_lock); + return i; + } + } + mutex_unlock(&asys_irqs_lock); + return IRQ_NUM; +} + +int mtk2701_asys_irq_release(struct mtk_afe *afe, int irq_id) +{ + mutex_lock(&asys_irqs_lock); + if (irq_id >= IRQ_ASYS_START && irq_id < IRQ_ASYS_END) { + afe->irqs[irq_id].irq_occupyed = 0; + mutex_unlock(&asys_irqs_lock); + return 0; + } + mutex_unlock(&asys_irqs_lock); + return -EINVAL; +} + +MODULE_DESCRIPTION("MTK2701 irq control"); +MODULE_AUTHOR("Garlic Tseng garlic.tseng@mediatek.com"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/mediatek/mt2701/mtk2701-irq.h b/sound/soc/mediatek/mt2701/mtk2701-irq.h new file mode 100644 index 0000000..637d304 --- /dev/null +++ b/sound/soc/mediatek/mt2701/mtk2701-irq.h @@ -0,0 +1,30 @@ +/* + * mtk2701-irq.h -- Mediatek 2701 audio driver irq function definition + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Garlic Tseng garlic.tseng@mediatek.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MTK_2701_IRQ_H_ +#define _MTK_2701_IRQ_H_ + +u32 mtk2701_asys_irq_status(struct mtk_afe *afe); +void mtk2701_asys_irq_clear(struct mtk_afe *afe, u32 status); +void mtk2701_memif_isr(struct mtk_afe *afe, struct mtk_afe_memif *memif); +int mtk2701_afe_memif_base(struct mtk_afe *afe, struct mtk_afe_memif *memif, + u32 *base); +int mtk2701_afe_memif_pointer(struct mtk_afe *afe, struct mtk_afe_memif *memif, + u32 *cur_ptr); +int mtk2701_asys_irq_acquire(struct mtk_afe *afe); +int mtk2701_asys_irq_release(struct mtk_afe *afe, int irq_id); + +#endif diff --git a/sound/soc/mediatek/mt2701/mtk2701-reg.h b/sound/soc/mediatek/mt2701/mtk2701-reg.h new file mode 100644 index 0000000..4f83dcc --- /dev/null +++ b/sound/soc/mediatek/mt2701/mtk2701-reg.h @@ -0,0 +1,165 @@ +/* + * mtk2701-reg.h -- Mediatek 2701 audio driver reg definition + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Garlic Tseng garlic.tseng@mediatek.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MTK2701_REG_H_ +#define _MTK2701_REG_H_ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> +#include <sound/soc.h> +#include "mtk2701-afe-common.h" + +/***************************************************************************** + * R E G I S T E R D E F I N I T I O N + *****************************************************************************/ +#define AUDIO_TOP_CON0 0x0000 +#define AUDIO_TOP_CON4 0x0010 +#define AUDIO_TOP_CON5 0x0014 +#define ASMI_TIMING_CON1 0x0100 +#define ASMO_TIMING_CON1 0x0104 +#define PWR1_ASM_CON1 0x0108 +#define ASYS_TOP_CON 0x0600 +#define ASYS_I2SIN1_CON 0x0604 +#define ASYS_I2SIN2_CON 0x0608 +#define ASYS_I2SIN3_CON 0x060C +#define ASYS_I2SIN4_CON 0x0610 +#define ASYS_I2SIN5_CON 0x0614 +#define ASYS_I2SO1_CON 0x061C +#define ASYS_I2SO2_CON 0x0620 +#define ASYS_I2SO3_CON 0x0624 +#define ASYS_I2SO4_CON 0x0628 +#define ASYS_I2SO5_CON 0x062C +#define PWR2_TOP_CON 0x0634 +#define AFE_CONN0 0x06C0 +#define AFE_CONN1 0x06C4 +#define AFE_CONN2 0x06C8 +#define AFE_CONN3 0x06CC +#define AFE_CONN15 0x06FC +#define AFE_CONN16 0x0700 +#define AFE_CONN17 0x0704 +#define AFE_CONN18 0x0708 +#define AFE_CONN19 0x070C +#define AFE_CONN20 0x0710 +#define AFE_CONN21 0x0714 +#define AFE_CONN22 0x0718 +#define AFE_CONN23 0x071C +#define AFE_CONN24 0x0720 +#define ASYS_IRQ1_CON 0x0780 +#define ASYS_IRQ2_CON 0x0784 +#define ASYS_IRQ3_CON 0x0788 +#define ASYS_IRQ_CLR 0x07C0 +#define ASYS_IRQ_STATUS 0x07C4 +#define PWR2_ASM_CON1 0x1070 +#define AFE_DAC_CON0 0x1200 +#define AFE_DAC_CON1 0x1204 +#define AFE_DAC_CON2 0x1208 +#define AFE_DAC_CON3 0x120C +#define AFE_DAC_CON4 0x1210 +#define AFE_MEMIF_HD_CON1 0x121C +#define AFE_MEMIF_PBUF_SIZE 0x1238 +#define AFE_MEMIF_HD_CON0 0x123C +#define AFE_DL1_BASE 0x1240 +#define AFE_DL1_CUR 0x1244 +#define AFE_DL2_BASE 0x1250 +#define AFE_DL2_CUR 0x1254 +#define AFE_DL3_BASE 0x1260 +#define AFE_DL3_CUR 0x1264 +#define AFE_DL4_BASE 0x1270 +#define AFE_DL4_CUR 0x1274 +#define AFE_DL5_BASE 0x1280 +#define AFE_DL5_CUR 0x1284 +#define AFE_DLMCH_BASE 0x12A0 +#define AFE_DLMCH_CUR 0x12A4 +#define AFE_VUL_BASE 0x1300 +#define AFE_VUL_CUR 0x130C +#define AFE_UL2_BASE 0x1310 +#define AFE_UL2_END 0x1318 +#define AFE_UL2_CUR 0x131C +#define AFE_UL3_BASE 0x1320 +#define AFE_UL3_END 0x1328 +#define AFE_UL3_CUR 0x132C +#define AFE_UL4_BASE 0x1330 +#define AFE_UL4_END 0x1338 +#define AFE_UL4_CUR 0x133C +#define AFE_UL5_BASE 0x1340 +#define AFE_UL5_END 0x1348 +#define AFE_UL5_CUR 0x134C + +/*AUDIO_TOP_CON0 (0x0000)*/ +#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON (0x3 << 0) +#define AUDIO_TOP_CON0_PDN_AFE (0x1 << 2) +#define AUDIO_TOP_CON0_PDN_APLL_CK (0x1 << 23) + +/*AUDIO_TOP_CON4 (0x0010)*/ +#define AUDIO_TOP_CON4_I2SO1_PWN (0x1 << 6) +#define AUDIO_TOP_CON4_PDN_A1SYS (0x1 << 21) +#define AUDIO_TOP_CON4_PDN_A2SYS (0x1 << 22) +#define AUDIO_TOP_CON4_PDN_AFE_CONN (0x1 << 23) + +/*PWR1_ASM_CON1 (0x0108)*/ +#define PWR1_ASM_CON1_INIT_VAL (0x492) + +/* ASYS_I2SO1_CON (0x061C)*/ +#define ASYS_I2SO1_CON_FS (0x1f << 8) +#define ASYS_I2SO1_CON_FS_SET(x) ((x) << 8) +#define ASYS_I2SO1_CON_MULTI_CH (0x1 << 16) +#define ASYS_I2SO1_CON_SIDEGEN (0x1 << 30) +#define ASYS_I2SO1_CON_I2S_EN (0x1 << 0) +/*0:EIAJ 1:I2S*/ +#define ASYS_I2SO1_CON_I2S_MODE (0x1 << 3) +#define ASYS_I2SO1_CON_WIDE_MODE (0x1 << 1) +#define ASYS_I2SO1_CON_WIDE_MODE_SET(x) ((x) << 1) + +/*PWR2_TOP_CON (0x0634)*/ +#define PWR2_TOP_CON_INIT_VAL (0xffe1ffff) + +/*ASYS_IRQ_CLR (0x07C0)*/ +#define ASYS_IRQ_CLR_ALL (0xffffffff) + +/*PWR2_ASM_CON1 (0x1070)*/ +#define PWR2_ASM_CON1_INIT_VAL (0x492492) + +/*AFE_DAC_CON0 (0x1200)*/ +#define AFE_DAC_CON0_AFE_ON (0x1 << 0) + +/* AFE_MEMIF_PBUF_SIZE (0x1238)*/ +#define AFE_MEMIF_PBUF_SIZE_DLM_MASK (0x1 << 29) +#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE (0x0 << 29) +#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE (0x1 << 29) +#define DLMCH_BIT_WIDTH_MASK (0x1 << 28) +#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK (0xF << 24) +#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x) ((x) << 24) +#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK (0x3 << 12) +#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES (0x1 << 12) + +/*I2S in/out register bit control*/ +#define ASYS_I2S_CON_FS (0x1f << 8) +#define ASYS_I2S_CON_FS_SET(x) ((x) << 8) +#define ASYS_I2S_CON_MULTI_CH (0x1 << 16) +#define ASYS_I2S_CON_RESET (0x1 << 30) +#define ASYS_I2S_CON_I2S_EN (0x1 << 0) +#define ASYS_I2S_CON_I2S_COUPLE_MODE (0x1 << 17) +/*0:EIAJ 1:I2S*/ +#define ASYS_I2S_CON_I2S_MODE (0x1 << 3) +#define ASYS_I2S_CON_WIDE_MODE (0x1 << 1) +#define ASYS_I2S_CON_WIDE_MODE_SET(x) ((x) << 1) +#define ASYS_I2S_IN_PHASE_FIX (0x1 << 31) + +#define AFE_END_ADDR 0x15e0 +#endif
Add mt2701 platform driver. There are some BLOCK_COMMENT_STYLE errors from checkpatch and they will be fixed after BT functions finished.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com --- sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c | 1752 +++++++++++++++++++++++++++ 1 file changed, 1752 insertions(+) create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c
diff --git a/sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c new file mode 100644 index 0000000..630caec --- /dev/null +++ b/sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c @@ -0,0 +1,1752 @@ +/* + * Mediatek ALSA SoC AFE platform driver for 2701 + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Garlic Tseng garlic.tseng@mediatek.com + * Koro Chen koro.chen@mediatek.com + * Hidalgo Huang hidalgo.huang@mediatek.com + * Ir Lian ir.lian@mediatek.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> +#include <sound/soc.h> + +#include "mtk2701-afe-common.h" + +#include "mtk2701-afe-clock-ctrl.h" +#include "mtk2701-irq.h" +#include "mtk2701-reg.h" + +#define AFE_BASE_END_OFFSET 8 +#define AFE_IRQ_STATUS_BITS 0xff +#define PLL_DOMAIN_0_RATE 98304000 +#define PLL_DOMAIN_1_RATE 90316800 + +static const struct snd_pcm_hardware mtk2701_afe_hardware = { + .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED + | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 1024, + .period_bytes_max = 1024 * 256, + .periods_min = 4, + .periods_max = 1024, + .buffer_bytes_max = 1024 * 1024 * 16, + .fifo_size = 0, +}; + +static snd_pcm_uframes_t mtk2701_afe_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int stream_dir = substream->stream; + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id][stream_dir]; + + return bytes_to_frames(substream->runtime, memif->hw_ptr); +} + +static const struct snd_pcm_ops mtk2701_afe_pcm_ops = { + .ioctl = snd_pcm_lib_ioctl, + .pointer = mtk2701_afe_pcm_pointer, +}; + +static int mtk2701_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + size_t size; + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + + size = mtk2701_afe_hardware.buffer_bytes_max; + return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + card->dev, size, size); +} + +static void mtk2701_afe_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static const struct snd_soc_platform_driver mtk2701_afe_pcm_platform = { + .ops = &mtk2701_afe_pcm_ops, + .pcm_new = mtk2701_afe_pcm_new, + .pcm_free = mtk2701_afe_pcm_free, +}; + +struct mtk2701_afe_rate { + unsigned int rate; + unsigned int regvalue; +}; + +static const struct mtk2701_afe_rate mtk2701_afe_i2s_rates[] = { + { .rate = 8000, .regvalue = 0 }, + { .rate = 12000, .regvalue = 1 }, + { .rate = 16000, .regvalue = 2 }, + { .rate = 24000, .regvalue = 3 }, + { .rate = 32000, .regvalue = 4 }, + { .rate = 48000, .regvalue = 5 }, + { .rate = 96000, .regvalue = 6 }, + { .rate = 192000, .regvalue = 7 }, + { .rate = 384000, .regvalue = 8 }, + { .rate = 7350, .regvalue = 16 }, + { .rate = 11025, .regvalue = 17 }, + { .rate = 14700, .regvalue = 18 }, + { .rate = 22050, .regvalue = 19 }, + { .rate = 29400, .regvalue = 20 }, + { .rate = 44100, .regvalue = 21 }, + { .rate = 88200, .regvalue = 22 }, + { .rate = 176400, .regvalue = 23 }, + { .rate = 352800, .regvalue = 24 }, +}; + +static const struct mtk2701_afe_rate mtk2701_afe_irq_rates[] = { + { .rate = 8000, .regvalue = 0 }, + { .rate = 12000, .regvalue = 1 }, + { .rate = 16000, .regvalue = 2 }, + { .rate = 24000, .regvalue = 3 }, + { .rate = 32000, .regvalue = 4 }, + { .rate = 48000, .regvalue = 5 }, + { .rate = 96000, .regvalue = 6 }, + { .rate = 192000, .regvalue = 7 }, + { .rate = 11025, .regvalue = 9 }, + { .rate = 22050, .regvalue = 0xb }, + { .rate = 44100, .regvalue = 0xd }, + { .rate = 88200, .regvalue = 0xe }, + { .rate = 176400, .regvalue = 0xf }, +}; + +void mtk2701_mclk_configuration(struct mtk_afe *afe, int id, int domain, + int mclk) +{ + int ret; + int aud_src_div_id = AUDCLK_TOP_AUD_K1_SRC_DIV + id; + int aud_src_clk_id = AUDCLK_TOP_AUD_K1_SRC_SEL + id; + struct audio_clock_attr *clks = afe->aud_clks; + + /* Set MCLK Kx_SRC_SEL(domain) */ + ret = clk_prepare_enable(clks[aud_src_clk_id].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, clks[aud_src_clk_id].clock_data->name, ret); + + if (domain == 0) { + ret = clk_set_parent(clks[aud_src_clk_id].clock, + clks[AUDCLK_TOP_AUD_MUX1_SEL].clock); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, clks[aud_src_clk_id].clock_data->name, + clks[AUDCLK_TOP_AUD_MUX1_SEL].clock_data->name, + ret); + } else { + ret = clk_set_parent(clks[aud_src_clk_id].clock, + clks[AUDCLK_TOP_AUD_MUX2_SEL].clock); + if (ret) + dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", + __func__, clks[aud_src_clk_id].clock_data->name, + clks[AUDCLK_TOP_AUD_MUX2_SEL].clock_data->name, + ret); + } + clk_disable_unprepare(clks[aud_src_clk_id].clock); + + /* Set MCLK Kx_SRC_DIV(divider) */ + ret = clk_prepare_enable(clks[aud_src_div_id].clock); + if (ret) + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, clks[aud_src_div_id].clock_data->name, ret); + + ret = clk_set_rate(clks[aud_src_div_id].clock, mclk); + if (ret) + dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__, + clks[aud_src_div_id].clock_data->name, mclk, ret); + clk_disable_unprepare(clks[aud_src_div_id].clock); +} + +int mtk2701_dai_num_to_i2s(struct mtk_afe *afe, int num) +{ + int val = num - MTK_AFE_IO_I2S; + + if (val < 0 || val > MTK_I2S_NUM) { + dev_err(afe->dev, "%s, num not available, num %d, val %d\n", + __func__, num, val); + return -1; + } + return val; +} + +static int mtk2701_afe_i2s_fs(unsigned int sample_rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mtk2701_afe_i2s_rates); i++) + if (mtk2701_afe_i2s_rates[i].rate == sample_rate) + return mtk2701_afe_i2s_rates[i].regvalue; + + return -EINVAL; +} + +/*need for BT, will implement BT before upstream*/ +/* +static int mtk2701_afe_irq_fs(unsigned int sample_rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mtk2701_afe_irq_rates); i++) + if (mtk2701_afe_irq_rates[i].rate == sample_rate) + return mtk2701_afe_irq_rates[i].regvalue; + + return -EINVAL; +} +*/ + +static int mtk2701_afe_i2s_enable_clks(struct mtk_afe *afe, + struct audio_clock_attr *clk_attr) +{ + int ret; + + if (clk_attr->clock_data->prepare_once) + ret = clk_enable(clk_attr->clock); + else + ret = clk_prepare_enable(clk_attr->clock); + if (ret) { + dev_err(afe->dev, "Failed to enable %s\n", + clk_attr->clock_data->name); + return ret; + } + return 0; +} + +static int mtk2701_afe_i2s_disable_clks(struct mtk_afe *afe, + struct audio_clock_attr *clk_attr) +{ + if (clk_attr->clock_data->prepare_once) + clk_disable(clk_attr->clock); + else + clk_disable_unprepare(clk_attr->clock); + return 0; +} + +static int mtk2701_afe_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id); + + /*enable mclk*/ + mtk2701_afe_i2s_enable_clks(afe, + &afe->aud_clks[AUDCLK_TOP_AUD_I2S1_MCLK + i2s_num]); + return 0; +} + +static int mtk2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + int dir_invert) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id); + struct mtk_i2s_path *i2s_path = &afe->i2s_path[i2s_num]; + const struct mtk_i2s_data *i2s_data; + int stream_dir = substream->stream; + + if (dir_invert) { + if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) + stream_dir = SNDRV_PCM_STREAM_CAPTURE; + else + stream_dir = SNDRV_PCM_STREAM_PLAYBACK; + } + i2s_data = i2s_path->i2s_data[stream_dir]; + + i2s_path->on[stream_dir]--; + if (i2s_path->on[stream_dir] < 0) { + dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n", + i2s_path->on[stream_dir], stream_dir); + i2s_path->on[stream_dir] = 0; + } + if (i2s_path->on[stream_dir]) + return 0; + + /*disable i2s*/ + regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, + ASYS_I2S_CON_I2S_EN, 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, + 1 << i2s_data->i2s_pwn_shift, + 1 << i2s_data->i2s_pwn_shift); + return 0; +} + +static void mtk2701_afe_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id); + struct mtk_i2s_path *i2s_path = &afe->i2s_path[i2s_num]; + + if (i2s_path->occupied[substream->stream]) { + i2s_path->occupied[substream->stream] = 0; + } else { + dev_info(afe->dev, + "i2s not occpuied but someone want to shutdown it.\n"); + goto I2S_UNSTART; + } + + mtk2701_afe_i2s_path_shutdown(substream, dai, 0); + + /*need to disable i2s-out path when disable i2s-in*/ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + mtk2701_afe_i2s_path_shutdown(substream, dai, 1); + +I2S_UNSTART: + /*disable mclk*/ + mtk2701_afe_i2s_disable_clks(afe, + &afe->aud_clks[AUDCLK_TOP_AUD_I2S1_MCLK + i2s_num]); +} + +static int mtk2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + int dir_invert) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id); + struct mtk_i2s_path *i2s_path = &afe->i2s_path[i2s_num]; + const struct mtk_i2s_data *i2s_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + int reg, fs, w_len = 1; + int stream_dir = substream->stream; + unsigned int mask = 0, val = 0; + + if (dir_invert) { + if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) + stream_dir = SNDRV_PCM_STREAM_CAPTURE; + else + stream_dir = SNDRV_PCM_STREAM_PLAYBACK; + } + i2s_data = i2s_path->i2s_data[stream_dir]; + + /*no need to enable if already done*/ + i2s_path->on[stream_dir]++; + + if (i2s_path->on[stream_dir] != 1) + return 0; + + fs = mtk2701_afe_i2s_fs(runtime->rate); + + if (i2s_path->div_bck_to_lrck == 32) + w_len = 0; + else if (i2s_path->div_bck_to_lrck == 64) + w_len = 1; + else + dev_warn(dai->dev, "%s() bad bit count %d\n", __func__, + afe->i2s_path[i2s_num].div_bck_to_lrck); + + mask = ASYS_I2S_CON_FS | + ASYS_I2S_CON_MULTI_CH | /*0*/ + ASYS_I2S_CON_I2S_COUPLE_MODE | /*0*/ + ASYS_I2S_CON_I2S_MODE | + ASYS_I2S_CON_WIDE_MODE; + + val = ASYS_I2S_CON_FS_SET(fs) | + ASYS_I2S_CON_I2S_MODE | + ASYS_I2S_CON_WIDE_MODE_SET(w_len); + + if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) { + mask |= ASYS_I2S_IN_PHASE_FIX; + val |= ASYS_I2S_IN_PHASE_FIX; + } + + regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val); + + if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) + reg = ASMO_TIMING_CON1; + else + reg = ASMI_TIMING_CON1; + + regmap_update_bits(afe->regmap, reg, + i2s_data->i2s_asrc_fs_mask << i2s_data->i2s_asrc_fs_shift, + fs << i2s_data->i2s_asrc_fs_shift); + + /*enable i2s*/ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, + 1 << i2s_data->i2s_pwn_shift, + 0 << i2s_data->i2s_pwn_shift); + + /*reset irq hw status before enable*/ + regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, + ASYS_I2S_CON_RESET, ASYS_I2S_CON_RESET); + udelay(1); + regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, + ASYS_I2S_CON_RESET, 0); + udelay(1); + regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, + ASYS_I2S_CON_I2S_EN, ASYS_I2S_CON_I2S_EN); + return 0; +} + +static int mtk2701_afe_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int clk_domain; + + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id); + struct mtk_i2s_path *i2s_path = &afe->i2s_path[i2s_num]; + const int mclk_rate = i2s_path->mclk_rate; + + if (i2s_path->occupied[substream->stream]) + return -EBUSY; + i2s_path->occupied[substream->stream] = 1; + + if (PLL_DOMAIN_0_RATE % mclk_rate == 0) { + clk_domain = 0; + } else if (PLL_DOMAIN_1_RATE % mclk_rate == 0) { + clk_domain = 1; + } else { + dev_err(dai->dev, "%s() bad mclk rate %d\n", + __func__, mclk_rate); + return -EINVAL; + } + mtk2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mtk2701_i2s_path_prepare_enable(substream, dai, 0); + } else { + /*need to enable i2s-out path when enable i2s-in*/ + /*prepare for another direction "out"*/ + mtk2701_i2s_path_prepare_enable(substream, dai, 1); + /*prepare for "in"*/ + mtk2701_i2s_path_prepare_enable(substream, dai, 0); + } + + return 0; +} + +static int mtk2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct mtk_afe *afe = dev_get_drvdata(dai->dev); + int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id); + /* mclk */ + if (dir == SND_SOC_CLOCK_IN) { + dev_warn(dai->dev, + "%s() warning: mt2701 doesn't support mclk input\n", + __func__); + return -EINVAL; + } + afe->i2s_path[i2s_num].mclk_rate = freq; + return 0; +} + +static int mtk2701_afe_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, + int div) +{ + struct mtk_afe *afe = dev_get_drvdata(dai->dev); + int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id); + + switch (div_id) { + case DIV_ID_MCLK_TO_BCK: + afe->i2s_path[i2s_num].div_mclk_to_bck = div; + break; + case DIV_ID_BCK_TO_LRCK: + afe->i2s_path[i2s_num].div_bck_to_lrck = div; + break; + default: + return -EINVAL; + } + return 0; +} + +static int mtk2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct mtk_afe *afe = dev_get_drvdata(dai->dev); + int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id); + + afe = dev_get_drvdata(dai->dev); + afe->i2s_path[i2s_num].format = fmt; + return 0; +} + +static int mtk2701_playback_mem_avail(struct mtk_afe *afe, int memif_num) +{ + struct mtk_afe_memif *memif_tmp; + + if (memif_num >= MTK_AFE_MEMIF_1 && + memif_num < MTK_AFE_MEMIF_SINGLE_NUM) { + memif_tmp = + &afe->memif[MTK_AFE_MEMIF_M][SNDRV_PCM_STREAM_PLAYBACK]; + if (memif_tmp->substream) + return 0; + } else if (memif_num == MTK_AFE_MEMIF_M) { + int i; + + for (i = MTK_AFE_MEMIF_1; i < MTK_AFE_MEMIF_SINGLE_NUM; ++i) { + memif_tmp = &afe->memif[i][SNDRV_PCM_STREAM_PLAYBACK]; + if (memif_tmp->substream) + return 0; + } + } + return 1; +} + +static int mtk2701_afe_dais_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct snd_pcm_runtime *runtime = substream->runtime; + int stream_dir = substream->stream; + int memif_num = rtd->cpu_dai->id; + struct mtk_afe_memif *memif = &afe->memif[memif_num][stream_dir]; + int is_dlm = 0; + int ret, i; + struct mtk_afe_memif *memif_tmp; + + if (memif->substream) { + dev_warn(afe->dev, "%s memif is occupied, stream_dir %d, memif_num = %d\n", + __func__, stream_dir, memif_num); + return -EBUSY; + } + + if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK && + !mtk2701_playback_mem_avail(afe, memif_num)) { + dev_warn(afe->dev, "%s memif is not available, stream_dir %d, memif_num %d\n", + __func__, stream_dir, memif_num); + return -EBUSY; + } + + if (memif_num == MTK_AFE_MEMIF_M && + stream_dir == SNDRV_PCM_STREAM_PLAYBACK) + is_dlm = 1; + + memif->substream = substream; + + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16); + /*enable agent*/ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON5, + 1 << memif->data->agent_disable_shift, + 0 << memif->data->agent_disable_shift); + if (is_dlm) { + for (i = MTK_AFE_MEMIF_1; i < MTK_AFE_MEMIF_SINGLE_NUM; ++i) { + memif_tmp = &afe->memif[i][SNDRV_PCM_STREAM_PLAYBACK]; + regmap_update_bits(afe->regmap, AUDIO_TOP_CON5, + 1 << memif_tmp->data->agent_disable_shift, + 0 << memif_tmp->data->agent_disable_shift); + } + } + + snd_soc_set_runtime_hwparams(substream, &mtk2701_afe_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n"); + + /*require irq resource*/ + if (!memif->irq) { + int irq_id = mtk2701_asys_irq_acquire(afe); + + if (irq_id != IRQ_NUM) { + /* link */ + memif->irq = &afe->irqs[irq_id]; + afe->irqs[irq_id].memif = memif; + afe->irqs[irq_id].isr = mtk2701_memif_isr; + } else { + dev_err(afe->dev, "%s() error: no more asys irq\n", + __func__); + } + } + return ret; +} + +static void mtk2701_afe_dais_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int stream_dir = substream->stream; + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id][stream_dir]; + int irq_id, i; + int is_dlm = 0; + struct mtk_afe_memif *memif_tmp; + + irq_id = memif->irq->irq_data->irq_id; + if (rtd->cpu_dai->id == MTK_AFE_MEMIF_M && + stream_dir == SNDRV_PCM_STREAM_PLAYBACK) + is_dlm = 1; + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON5, + 1 << memif->data->agent_disable_shift, + 1 << memif->data->agent_disable_shift); + if (is_dlm) { + for (i = MTK_AFE_MEMIF_1; i < MTK_AFE_MEMIF_SINGLE_NUM; ++i) { + memif_tmp = &afe->memif[i][SNDRV_PCM_STREAM_PLAYBACK]; + regmap_update_bits(afe->regmap, AUDIO_TOP_CON5, + 1 << memif_tmp->data->agent_disable_shift, + 1 << memif_tmp->data->agent_disable_shift); + } + } + mtk2701_asys_irq_release(afe, irq_id); + memif->irq = NULL; + afe->irqs[irq_id].memif = NULL; + afe->irqs[irq_id].isr = NULL; + memif->substream = NULL; +} + +static int mtk2701_afe_dais_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret; + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + return ret; + return 0; +} + +static int mtk2701_afe_dais_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int mtk2701_afe_dais_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int stream_dir = substream->stream; + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id][stream_dir]; + int is_dlm = 0; + int hd_audio = 0; + int fs; + int channels = runtime->channels; + + if (rtd->cpu_dai->id == MTK_AFE_MEMIF_M && + stream_dir == SNDRV_PCM_STREAM_PLAYBACK) + is_dlm = 1; + + /*set hd mode*/ + switch (substream->runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + hd_audio = 0; + break; + case SNDRV_PCM_FORMAT_S32_LE: + hd_audio = 1; + break; + case SNDRV_PCM_FORMAT_S24_LE: + hd_audio = 1; + break; + default: + dev_err(afe->dev, "%s() error: unsupported format %d\n", + __func__, substream->runtime->format); + break; + } + + regmap_update_bits(afe->regmap, memif->data->hd_reg, + 1 << memif->data->hd_shift, + hd_audio << memif->data->hd_shift); + + memif->phys_buf_addr = substream->runtime->dma_addr; + memif->buffer_size = substream->runtime->dma_bytes; + memif->hw_ptr = 0; + + /* set rate */ + if (memif->data->fs_shift < 0) + return 0; + + fs = mtk2701_afe_i2s_fs(runtime->rate); + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, memif->data->fs_reg, + 0x1f << memif->data->fs_shift, + fs << memif->data->fs_shift); + /* set channel */ + if (memif->data->mono_shift >= 0) { + unsigned int mono = (runtime->channels == 1) ? 1 : 0; + + regmap_update_bits(afe->regmap, memif->data->mono_reg, + 1 << memif->data->mono_shift, + mono << memif->data->mono_shift); + } + /* start */ + regmap_write(afe->regmap, + memif->data->reg_ofs_base, memif->phys_buf_addr); + /* end */ + regmap_write(afe->regmap, + memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, + memif->phys_buf_addr + memif->buffer_size - 1); + + if (is_dlm) { /*setting for multi-ch playback*/ + regmap_update_bits(afe->regmap, + AFE_MEMIF_PBUF_SIZE, + AFE_MEMIF_PBUF_SIZE_DLM_MASK, + AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE); + regmap_update_bits(afe->regmap, + AFE_MEMIF_PBUF_SIZE, + AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK, + AFE_MEMIF_PBUF_SIZE_DLM_32BYTES); + regmap_update_bits(afe->regmap, + AFE_MEMIF_PBUF_SIZE, + AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK, + AFE_MEMIF_PBUF_SIZE_DLM_CH(channels)); + + } else if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(afe->regmap, + AFE_MEMIF_PBUF_SIZE, + AFE_MEMIF_PBUF_SIZE_DLM_MASK, + AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE); + } + return 0; +} + +static int mtk2701_afe_dais_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int stream_dir = substream->stream; + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id][stream_dir]; + struct mtk_afe_memif *memif_tmp; + struct snd_pcm_runtime * const runtime = substream->runtime; + unsigned int counter = runtime->period_size; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + /*memory interface enable*/ + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, + 1 << memif->data->enable_shift); + + /* set irq counter */ + regmap_update_bits(afe->regmap, + memif->irq->irq_data->irq_cnt_reg, + memif->irq->irq_data->irq_cnt_maskbit + << memif->irq->irq_data->irq_cnt_shift, + counter << (memif->irq->irq_data->irq_cnt_shift)); + /* set irq fs */ + if (memif->irq->irq_data->irq_fs_shift >= 0) { + int fs; + /*need for BT, will implement it before upstream*/ + /* + if(memif->irqdata->irq_id == IRQ_AFE_IRQ1 || + memif->irqdata->irq_id == IRQ_AFE_IRQ2 ) + fs = mtk2701_afe_irq_fs(runtime->rate); + else + fs = mtk2701_afe_i2s_fs(runtime->rate); + */ + fs = mtk2701_afe_i2s_fs(runtime->rate); + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, + memif->irq->irq_data->irq_fs_reg, + memif->irq->irq_data->irq_fs_maskbit + << memif->irq->irq_data->irq_fs_shift, + fs << memif->irq->irq_data->irq_fs_shift); + } + + if (rtd->cpu_dai->id == MTK_AFE_MEMIF_M && + stream_dir == SNDRV_PCM_STREAM_PLAYBACK) { + memif_tmp = &afe->memif[MTK_AFE_MEMIF_1][stream_dir]; + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif_tmp->data->enable_shift, + 1 << memif_tmp->data->enable_shift); + } + /* enable interrupt */ + regmap_update_bits(afe->regmap, + memif->irq->irq_data->irq_en_reg, + 1 << memif->irq->irq_data->irq_en_shift, + 1 << memif->irq->irq_data->irq_en_shift); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + /* disable interrupt */ + regmap_update_bits(afe->regmap, + memif->irq->irq_data->irq_en_reg, + 1 << memif->irq->irq_data->irq_en_shift, + 0 << memif->irq->irq_data->irq_en_shift); + memif->hw_ptr = 0; + /*memory interface disable*/ + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, 0); + if (rtd->cpu_dai->id == MTK_AFE_MEMIF_M && + stream_dir == SNDRV_PCM_STREAM_PLAYBACK) { + memif_tmp = &afe->memif[MTK_AFE_MEMIF_1][stream_dir]; + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif_tmp->data->enable_shift, + 0); + } + return 0; + default: + return -EINVAL; + } +} + +/* FE DAIs */ +static const struct snd_soc_dai_ops mtk2701_afe_dai_ops = { + .startup = mtk2701_afe_dais_startup, + .shutdown = mtk2701_afe_dais_shutdown, + .hw_params = mtk2701_afe_dais_hw_params, + .hw_free = mtk2701_afe_dais_hw_free, + .prepare = mtk2701_afe_dais_prepare, + .trigger = mtk2701_afe_dais_trigger, +}; + +/* BE DAIs */ +static const struct snd_soc_dai_ops mtk2701_afe_i2s_ops = { + .startup = mtk2701_afe_i2s_startup, + .shutdown = mtk2701_afe_i2s_shutdown, + .prepare = mtk2701_afe_i2s_prepare, + .set_sysclk = mtk2701_afe_i2s_set_sysclk, + .set_clkdiv = mtk2701_afe_i2s_set_clkdiv, + .set_fmt = mtk2701_afe_i2s_set_fmt, +}; + +static struct snd_soc_dai_driver mtk2701_afe_pcm_dais[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "PCM0", + .id = MTK_AFE_MEMIF_1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + }, + .capture = { + .stream_name = "UL1", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk2701_afe_dai_ops, + }, + { + .name = "PCM_multi", + .id = MTK_AFE_MEMIF_M, + .playback = { + .stream_name = "DLM", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + + }, + .ops = &mtk2701_afe_dai_ops, + }, + { + .name = "PCM1", + .id = MTK_AFE_MEMIF_2, + .capture = { + .stream_name = "UL2", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + + }, + .ops = &mtk2701_afe_dai_ops, + }, + { + /* BE DAIs */ + .name = "I2S0", + .id = MTK_AFE_IO_I2S, + .playback = { + .stream_name = "I2S0 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + + }, + .capture = { + .stream_name = "I2S0 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + + }, + .ops = &mtk2701_afe_i2s_ops, + .symmetric_rates = 1, + }, + { + .name = "I2S1", + .id = MTK_AFE_IO_2ND_I2S, + .playback = { + .stream_name = "I2S1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + }, + .capture = { + .stream_name = "I2S1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + }, + .ops = &mtk2701_afe_i2s_ops, + .symmetric_rates = 1, + }, + { + .name = "I2S2", + .id = MTK_AFE_IO_3RD_I2S, + .playback = { + .stream_name = "I2S2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + }, + .capture = { + .stream_name = "I2S2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + }, + .ops = &mtk2701_afe_i2s_ops, + .symmetric_rates = 1, + }, + { + .name = "I2S3", + .id = MTK_AFE_IO_4TH_I2S, + .playback = { + .stream_name = "I2S3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + }, + .capture = { + .stream_name = "I2S3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE) + }, + .ops = &mtk2701_afe_i2s_ops, + .symmetric_rates = 1, + }, +}; + +static const struct snd_kcontrol_new mtk2701_afe_o00_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN0, 0, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o01_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN1, 1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o02_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_CONN2, 2, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o03_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 3, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o15_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I12 Switch", AFE_CONN15, 12, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o16_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I13 Switch", AFE_CONN16, 13, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o17_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o18_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o19_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o20_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o21_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o22_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o23_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_o24_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s0[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s0", ASYS_I2SO1_CON, 26, 1, + 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s1[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s1", ASYS_I2SO2_CON, 26, 1, + 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s2[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s2", PWR2_TOP_CON, 17, 1, + 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s3[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s3", PWR2_TOP_CON, 18, 1, + 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s4[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s4", PWR2_TOP_CON, 19, 1, + 0), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc0[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out0", AUDIO_TOP_CON4, 14, 1, + 1), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc1[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out1", AUDIO_TOP_CON4, 15, 1, + 1), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc2[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out2", PWR2_TOP_CON, 6, 1, + 1), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc3[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out3", PWR2_TOP_CON, 7, 1, + 1), +}; + +static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc4[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out4", PWR2_TOP_CON, 8, 1, + 1), +}; + +static const struct snd_soc_dapm_widget mtk2701_afe_pcm_widgets[] = { + /* inter-connections */ + SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I02", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0, mtk2701_afe_o00_mix, + ARRAY_SIZE(mtk2701_afe_o00_mix)), + SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0, mtk2701_afe_o01_mix, + ARRAY_SIZE(mtk2701_afe_o01_mix)), + SND_SOC_DAPM_MIXER("O02", SND_SOC_NOPM, 0, 0, mtk2701_afe_o02_mix, + ARRAY_SIZE(mtk2701_afe_o02_mix)), + SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, mtk2701_afe_o03_mix, + ARRAY_SIZE(mtk2701_afe_o03_mix)), + SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0, mtk2701_afe_o15_mix, + ARRAY_SIZE(mtk2701_afe_o15_mix)), + SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0, mtk2701_afe_o16_mix, + ARRAY_SIZE(mtk2701_afe_o16_mix)), + SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0, mtk2701_afe_o17_mix, + ARRAY_SIZE(mtk2701_afe_o17_mix)), + SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0, mtk2701_afe_o18_mix, + ARRAY_SIZE(mtk2701_afe_o18_mix)), + SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0, mtk2701_afe_o19_mix, + ARRAY_SIZE(mtk2701_afe_o19_mix)), + SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0, mtk2701_afe_o20_mix, + ARRAY_SIZE(mtk2701_afe_o20_mix)), + SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0, mtk2701_afe_o21_mix, + ARRAY_SIZE(mtk2701_afe_o21_mix)), + SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0, mtk2701_afe_o22_mix, + ARRAY_SIZE(mtk2701_afe_o22_mix)), + SND_SOC_DAPM_MIXER("I12I13", SND_SOC_NOPM, 0, 0, + mtk2701_afe_multi_ch_out_i2s0, + ARRAY_SIZE(mtk2701_afe_multi_ch_out_i2s0)), + SND_SOC_DAPM_MIXER("I14I15", SND_SOC_NOPM, 0, 0, + mtk2701_afe_multi_ch_out_i2s1, + ARRAY_SIZE(mtk2701_afe_multi_ch_out_i2s1)), + SND_SOC_DAPM_MIXER("I16I17", SND_SOC_NOPM, 0, 0, + mtk2701_afe_multi_ch_out_i2s2, + ARRAY_SIZE(mtk2701_afe_multi_ch_out_i2s2)), + SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0, + mtk2701_afe_multi_ch_out_i2s3, + ARRAY_SIZE(mtk2701_afe_multi_ch_out_i2s3)), + + SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0, + mtk2701_afe_multi_ch_out_asrc0, + ARRAY_SIZE(mtk2701_afe_multi_ch_out_asrc0)), + SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0, + mtk2701_afe_multi_ch_out_asrc1, + ARRAY_SIZE(mtk2701_afe_multi_ch_out_asrc1)), + SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0, + mtk2701_afe_multi_ch_out_asrc2, + ARRAY_SIZE(mtk2701_afe_multi_ch_out_asrc2)), + SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0, + mtk2701_afe_multi_ch_out_asrc3, + ARRAY_SIZE(mtk2701_afe_multi_ch_out_asrc3)), +}; + +static const struct snd_soc_dapm_route mtk2701_afe_pcm_routes[] = { + {"I12", NULL, "DL1"}, + {"I13", NULL, "DL1"}, + {"I2S0 Playback", NULL, "O15"}, + {"I2S0 Playback", NULL, "O16"}, + {"I2S1 Playback", NULL, "O17"}, + {"I2S1 Playback", NULL, "O18"}, + {"I2S2 Playback", NULL, "O19"}, + {"I2S2 Playback", NULL, "O20"}, + {"I2S3 Playback", NULL, "O21"}, + {"I2S3 Playback", NULL, "O22"}, + + {"UL1", NULL, "O00"}, + {"UL1", NULL, "O01"}, + {"UL2", NULL, "O02"}, + {"UL2", NULL, "O03"}, + {"I00", NULL, "I2S0 Capture"}, + {"I01", NULL, "I2S0 Capture"}, + + {"I02", NULL, "I2S1 Capture"}, + {"I03", NULL, "I2S1 Capture"}, + /*I02,03 link to UL2, also need to open I2S0*/ + {"I02", NULL, "I2S0 Capture"}, + {"I03", NULL, "I2S0 Capture"}, + + {"ASRC_O0", "Multi ch asrc out0", "DLM"}, + {"ASRC_O1", "Multi ch asrc out1", "DLM"}, + {"ASRC_O2", "Multi ch asrc out2", "DLM"}, + {"ASRC_O3", "Multi ch asrc out3", "DLM"}, + + {"I12I13", "Multi ch Out I2s0", "ASRC_O0"}, + {"I14I15", "Multi ch Out I2s1", "ASRC_O1"}, + {"I16I17", "Multi ch Out I2s2", "ASRC_O2"}, + {"I18I19", "Multi ch Out I2s3", "ASRC_O3"}, + + { "I12", NULL, "I12I13" }, + { "I13", NULL, "I12I13" }, + { "I14", NULL, "I14I15" }, + { "I15", NULL, "I14I15" }, + { "I16", NULL, "I16I17" }, + { "I17", NULL, "I16I17" }, + { "I18", NULL, "I18I19" }, + { "I19", NULL, "I18I19" }, + + { "O00", "I00 Switch", "I00" }, + { "O01", "I01 Switch", "I01" }, + { "O02", "I02 Switch", "I02" }, + { "O03", "I03 Switch", "I03" }, + + { "O15", "I12 Switch", "I12" }, + { "O16", "I13 Switch", "I13" }, + { "O17", "I14 Switch", "I14" }, + { "O18", "I15 Switch", "I15" }, + { "O19", "I16 Switch", "I16" }, + { "O20", "I17 Switch", "I17" }, + { "O21", "I18 Switch", "I18" }, + { "O22", "I19 Switch", "I19" }, +}; + +static const struct snd_soc_component_driver mtk2701_afe_pcm_dai_component = { + .name = "mtk-afe-pcm-dai", + .dapm_widgets = mtk2701_afe_pcm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk2701_afe_pcm_widgets), + .dapm_routes = mtk2701_afe_pcm_routes, + .num_dapm_routes = ARRAY_SIZE(mtk2701_afe_pcm_routes), +}; + +static const struct mtk_afe_memif_data + memif_data[MTK_AFE_MEMIF_NUM][MTK_MEMIF_STREAM_NUM] = { + { + { + .name = "DL1", + .id = MTK_AFE_MEMIF_1, + .reg_ofs_base = AFE_DL1_BASE, + .reg_ofs_cur = AFE_DL1_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 0, + .mono_reg = AFE_DAC_CON3, + .mono_shift = 16, + .enable_shift = 1, + .hd_reg = AFE_MEMIF_HD_CON0, + .hd_shift = 0, + .agent_disable_shift = 6, + }, + { + .name = "UL1", + .id = MTK_AFE_MEMIF_1, + .reg_ofs_base = AFE_VUL_BASE, + .reg_ofs_cur = AFE_VUL_CUR, + .fs_reg = AFE_DAC_CON2, + .fs_shift = 0, + .mono_reg = AFE_DAC_CON4, + .mono_shift = 0, + .enable_shift = 10, + .hd_reg = AFE_MEMIF_HD_CON1, + .hd_shift = 0, + .agent_disable_shift = 0, + } + }, + { + { + .name = "DL2", + .id = MTK_AFE_MEMIF_2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 5, + .mono_reg = AFE_DAC_CON3, + .mono_shift = 17, + .enable_shift = 2, + .hd_reg = AFE_MEMIF_HD_CON0, + .hd_shift = 2, + .agent_disable_shift = 7, + }, + { + .name = "UL2", + .id = MTK_AFE_MEMIF_2, + .reg_ofs_base = AFE_UL2_BASE, + .reg_ofs_cur = AFE_UL2_CUR, + .fs_reg = AFE_DAC_CON2, + .fs_shift = 5, + .mono_reg = AFE_DAC_CON4, + .mono_shift = 2, + .enable_shift = 11, + .hd_reg = AFE_MEMIF_HD_CON1, + .hd_shift = 2, + .agent_disable_shift = 1, + } + }, + { + { + .name = "DL3", + .id = MTK_AFE_MEMIF_3, + .reg_ofs_base = AFE_DL3_BASE, + .reg_ofs_cur = AFE_DL3_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 10, + .mono_reg = AFE_DAC_CON3, + .mono_shift = 18, + .enable_shift = 3, + .hd_reg = AFE_MEMIF_HD_CON0, + .hd_shift = 4, + .agent_disable_shift = 8, + }, + { + .name = "UL3", + .id = MTK_AFE_MEMIF_3, + .reg_ofs_base = AFE_UL3_BASE, + .reg_ofs_cur = AFE_UL3_CUR, + .fs_reg = AFE_DAC_CON2, + .fs_shift = 10, + .mono_reg = AFE_DAC_CON4, + .mono_shift = 4, + .enable_shift = 12, + .hd_reg = AFE_MEMIF_HD_CON0, + .hd_shift = 0, + .agent_disable_shift = 2, + } + }, + { + { + .name = "DL4", + .id = MTK_AFE_MEMIF_4, + .reg_ofs_base = AFE_DL4_BASE, + .reg_ofs_cur = AFE_DL4_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 15, + .mono_reg = AFE_DAC_CON3, + .mono_shift = 19, + .enable_shift = 4, + .hd_reg = AFE_MEMIF_HD_CON0, + .hd_shift = 6, + .agent_disable_shift = 9, + }, + { + .name = "UL4", + .id = MTK_AFE_MEMIF_4, + .reg_ofs_base = AFE_UL4_BASE, + .reg_ofs_cur = AFE_UL4_CUR, + .fs_reg = AFE_DAC_CON2, + .fs_shift = 15, + .mono_reg = AFE_DAC_CON4, + .mono_shift = 6, + .enable_shift = 13, + .hd_reg = AFE_MEMIF_HD_CON0, + .hd_shift = 6, + .agent_disable_shift = 3, + } + }, + { + { + .name = "DL5", + .id = MTK_AFE_MEMIF_5, + .reg_ofs_base = AFE_DL5_BASE, + .reg_ofs_cur = AFE_DL5_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 20, + .mono_reg = AFE_DAC_CON3, + .mono_shift = 20, + .enable_shift = 5, + .hd_reg = AFE_MEMIF_HD_CON0, + .hd_shift = 8, + .agent_disable_shift = 10, + }, + { + .name = "UL5", + .id = MTK_AFE_MEMIF_5, + .reg_ofs_base = AFE_UL5_BASE, + .reg_ofs_cur = AFE_UL5_CUR, + .fs_reg = AFE_DAC_CON2, + .fs_shift = 20, + .mono_reg = AFE_DAC_CON4, + .mono_shift = 8, + .enable_shift = 14, + .hd_reg = AFE_MEMIF_HD_CON0, + .hd_shift = 8, + .agent_disable_shift = 4, + } + }, + { + { + .name = "DLM", + .id = MTK_AFE_MEMIF_M, + .reg_ofs_base = AFE_DLMCH_BASE, + .reg_ofs_cur = AFE_DLMCH_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 0, + .mono_reg = -1, + .mono_shift = -1, + .enable_shift = 7, + .hd_reg = AFE_MEMIF_PBUF_SIZE, + .hd_shift = 28, + .agent_disable_shift = 12, + }, + { + } + } +}; + +static const struct mtk_afe_irq_data irq_data[IRQ_NUM] = { + { + .irq_id = IRQ_ASYS_IRQ1, + .irq_cnt_reg = ASYS_IRQ1_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ1_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1f, + .irq_en_reg = ASYS_IRQ1_CON, + .irq_en_shift = 31, + }, + { + .irq_id = IRQ_ASYS_IRQ2, + .irq_cnt_reg = ASYS_IRQ2_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ2_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1f, + .irq_en_reg = ASYS_IRQ2_CON, + .irq_en_shift = 31, + }, + { + .irq_id = IRQ_ASYS_IRQ3, + .irq_cnt_reg = ASYS_IRQ3_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ3_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1f, + .irq_en_reg = ASYS_IRQ3_CON, + .irq_en_shift = 31, + } +}; + +static const struct mtk_i2s_data mtk2701_i2s_data[MTK_I2S_NUM][2] = { + { + { + .i2s_ctrl_reg = ASYS_I2SO1_CON, + .i2s_pwn_shift = 6, + .i2s_asrc_fs_shift = 0, + .i2s_asrc_fs_mask = 0x1f, + + }, + { + .i2s_ctrl_reg = ASYS_I2SIN1_CON, + .i2s_pwn_shift = 0, + .i2s_asrc_fs_shift = 0, + .i2s_asrc_fs_mask = 0x1f, + + }, + }, + { + { + .i2s_ctrl_reg = ASYS_I2SO2_CON, + .i2s_pwn_shift = 7, + .i2s_asrc_fs_shift = 5, + .i2s_asrc_fs_mask = 0x1f, + + }, + { + .i2s_ctrl_reg = ASYS_I2SIN2_CON, + .i2s_pwn_shift = 1, + .i2s_asrc_fs_shift = 5, + .i2s_asrc_fs_mask = 0x1f, + + }, + }, + { + { + .i2s_ctrl_reg = ASYS_I2SO3_CON, + .i2s_pwn_shift = 8, + .i2s_asrc_fs_shift = 10, + .i2s_asrc_fs_mask = 0x1f, + + }, + { + .i2s_ctrl_reg = ASYS_I2SIN3_CON, + .i2s_pwn_shift = 2, + .i2s_asrc_fs_shift = 10, + .i2s_asrc_fs_mask = 0x1f, + + }, + }, + { + { + .i2s_ctrl_reg = ASYS_I2SO4_CON, + .i2s_pwn_shift = 9, + .i2s_asrc_fs_shift = 15, + .i2s_asrc_fs_mask = 0x1f, + + }, + { + .i2s_ctrl_reg = ASYS_I2SIN4_CON, + .i2s_pwn_shift = 3, + .i2s_asrc_fs_shift = 15, + .i2s_asrc_fs_mask = 0x1f, + + }, + }, +}; + +static struct audio_clock_attr_data aud_clks_data[CLOCK_NUM] = { + [AUDCLK_INFRA_SYS_AUDIO] = {"infra_sys_audio_clk", true}, + [AUDCLK_TOP_AUD_MUX1_SEL] = {"top_audio_mux1_sel", false}, + [AUDCLK_TOP_AUD_MUX2_SEL] = {"top_audio_mux2_sel", false}, + [AUDCLK_TOP_AUD_MUX1_DIV] = {"top_audio_mux1_div", false}, + [AUDCLK_TOP_AUD_MUX2_DIV] = {"top_audio_mux2_div", false}, + [AUDCLK_TOP_AUD_48K_TIMING] = {"top_audio_48k_timing", true}, + [AUDCLK_TOP_AUD_44K_TIMING] = {"top_audio_44k_timing", true}, + [AUDCLK_TOP_AUDPLL_MUX_SEL] = {"top_audpll_mux_sel", false}, + [AUDCLK_TOP_APLL_SEL] = {"top_apll_sel", false}, + [AUDCLK_TOP_AUD1PLL_98M] = {"top_aud1_pll_98M", false}, + [AUDCLK_TOP_AUD2PLL_90M] = {"top_aud2_pll_90M", false}, + [AUDCLK_TOP_HADDS2PLL_98M] = {"top_hadds2_pll_98M", false}, + [AUDCLK_TOP_HADDS2PLL_294M] = {"top_hadds2_pll_294M", false}, + [AUDCLK_TOP_AUDPLL] = {"top_audpll", false}, + [AUDCLK_TOP_AUDPLL_D4] = {"top_audpll_d4", false}, + [AUDCLK_TOP_AUDPLL_D8] = {"top_audpll_d8", false}, + [AUDCLK_TOP_AUDPLL_D16] = {"top_audpll_d16", false}, + [AUDCLK_TOP_AUDPLL_D24] = {"top_audpll_d24", false}, + [AUDCLK_TOP_AUDINTBUS] = {"top_audintbus_sel", false}, + [AUDCLK_CLK_26M] = {"clk_26m", false }, + [AUDCLK_TOP_SYSPLL1_D4] = {"top_syspll1_d4", false}, + [AUDCLK_TOP_AUD_K1_SRC_SEL] = {"top_aud_k1_src_sel", false}, + [AUDCLK_TOP_AUD_K2_SRC_SEL] = {"top_aud_k2_src_sel", false}, + [AUDCLK_TOP_AUD_K3_SRC_SEL] = {"top_aud_k3_src_sel", false}, + [AUDCLK_TOP_AUD_K4_SRC_SEL] = {"top_aud_k4_src_sel", false}, + [AUDCLK_TOP_AUD_K5_SRC_SEL] = {"top_aud_k5_src_sel", false}, + [AUDCLK_TOP_AUD_K6_SRC_SEL] = {"top_aud_k6_src_sel", false}, + [AUDCLK_TOP_AUD_K1_SRC_DIV] = {"top_aud_k1_src_div", false}, + [AUDCLK_TOP_AUD_K2_SRC_DIV] = {"top_aud_k2_src_div", false}, + [AUDCLK_TOP_AUD_K3_SRC_DIV] = {"top_aud_k3_src_div", false}, + [AUDCLK_TOP_AUD_K4_SRC_DIV] = {"top_aud_k4_src_div", false}, + [AUDCLK_TOP_AUD_K5_SRC_DIV] = {"top_aud_k5_src_div", false}, + [AUDCLK_TOP_AUD_K6_SRC_DIV] = {"top_aud_k6_src_div", false}, + [AUDCLK_TOP_AUD_I2S1_MCLK] = {"top_aud_i2s1_mclk", true}, + [AUDCLK_TOP_AUD_I2S2_MCLK] = {"top_aud_i2s2_mclk", true}, + [AUDCLK_TOP_AUD_I2S3_MCLK] = {"top_aud_i2s3_mclk", true}, + [AUDCLK_TOP_AUD_I2S4_MCLK] = {"top_aud_i2s4_mclk", true}, + [AUDCLK_TOP_AUD_I2S5_MCLK] = {"top_aud_i2s5_mclk", true}, + [AUDCLK_TOP_AUD_I2S6_MCLK] = {"top_aud_i2s6_mclk", true}, + [AUDCLK_TOP_ASM_M_SEL] = {"top_asm_m_sel", false}, + [AUDCLK_TOP_ASM_H_SEL] = {"top_asm_h_sel", false}, + [AUDCLK_TOP_UNIVPLL2_D4] = {"top_univpll2_d4", false}, + [AUDCLK_TOP_UNIVPLL2_D2] = {"top_univpll2_d2", false}, + [AUDCLK_TOP_SYSPLL_D5] = {"top_syspll_d5", false}, +}; + +static const struct regmap_config mtk2701_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = AFE_END_ADDR, + .cache_type = REGCACHE_NONE, +}; + +static irqreturn_t mtk2701_asys_isr(int irq_id, void *dev) +{ + int id; + struct mtk_afe *afe = dev; + struct mtk_afe_irq *irq; + u32 status; + + status = mtk2701_asys_irq_status(afe); + mtk2701_asys_irq_clear(afe, status); + + for (id = IRQ_ASYS_START; id < IRQ_ASYS_END; ++id) { + irq = &afe->irqs[id]; + if (status & (0x1 << (id - IRQ_ASYS_START)) && irq->isr) + irq->isr(afe, irq->memif); + } + return IRQ_HANDLED; +} + +static int mtk2701_afe_runtime_suspend(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + + mtk2701_afe_enable_clock(afe, 0); + return 0; +} + +static int mtk2701_afe_runtime_resume(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + + pr_warn("%s\n", __func__); + mtk2701_afe_enable_clock(afe, 1); + return 0; +} + +static int mtk2701_afe_init_audio_clk(struct mtk_afe *afe) +{ + size_t i; + int ret = 0; + struct audio_clock_attr *aud_clks = afe->aud_clks; + + for (i = 0; i < CLOCK_NUM; i++) { + aud_clks[i].clock = devm_clk_get(afe->dev, + aud_clks[i].clock_data->name); + if (IS_ERR(aud_clks[i].clock)) { + dev_err(afe->dev, "%s devm_clk_get %s fail\n", + __func__, aud_clks[i].clock_data->name); + return PTR_ERR(aud_clks[i].clock); + } + } + for (i = 0; i < CLOCK_NUM; i++) { + if (aud_clks[i].clock_data->prepare_once) { + ret = clk_prepare(aud_clks[i].clock); + if (ret) { + dev_err(afe->dev, "%s clk_prepare %s fail %d\n", + __func__, aud_clks[i].clock_data->name, + ret); + break; + } + aud_clks[i].is_prepared = true; + } + } + return ret; +} + +static int mtk2701_afe_pcm_dev_probe(struct platform_device *pdev) +{ + int ret, i, j; + unsigned int irq_id; + struct mtk_afe *afe; + struct resource *res; + + ret = 0; + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + afe->dev = &pdev->dev; +/* need for BT*/ +/* + irq_id = platform_get_irq(pdev, 0); + if (!irq_id) { + dev_err(afe->dev, "np %s no first irq\n", afe->dev->of_node->name); + return -ENXIO; + } + + //TODO, change it to no flag. + //TODO check irq handler + ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler, + IRQF_TRIGGER_LOW, "afe-isr", (void *)afe); + if (ret) { + dev_err(afe->dev, "could not request_irq for afe-isr\n"); + return ret; + } +*/ + irq_id = platform_get_irq(pdev, 1); + if (!irq_id) { + dev_err(afe->dev, "%s no second irq\n", + afe->dev->of_node->name); + return -ENXIO; + } + ret = devm_request_irq(afe->dev, irq_id, mtk2701_asys_isr, + IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); + if (ret) { + dev_err(afe->dev, "could not request_irq for asys-isr\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + afe->base_addr = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mtk2701_afe_regmap_config); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) + for (j = 0; j < MTK_MEMIF_STREAM_NUM; ++j) + afe->memif[i][j].data = &memif_data[i][j]; + for (i = 0; i < IRQ_NUM; i++) + afe->irqs[i].irq_data = &irq_data[i]; + + for (i = 0; i < CLOCK_NUM; i++) + afe->aud_clks[i].clock_data = &aud_clks_data[i]; + + for (i = 0; i < MTK_I2S_NUM; i++) { + afe->i2s_path[i].i2s_data[I2S_OUT] + = &mtk2701_i2s_data[i][I2S_OUT]; + afe->i2s_path[i].i2s_data[I2S_IN] + = &mtk2701_i2s_data[i][I2S_IN]; + } + + /* initial audio related clock */ + ret = mtk2701_afe_init_audio_clk(afe); + platform_set_drvdata(pdev, afe); + + ret = snd_soc_register_platform(&pdev->dev, &mtk2701_afe_pcm_platform); + if (ret) { + dev_warn(afe->dev, "err_platform\n"); + goto err_platform; + } + + ret = snd_soc_register_component(&pdev->dev, + &mtk2701_afe_pcm_dai_component, + mtk2701_afe_pcm_dais, + ARRAY_SIZE(mtk2701_afe_pcm_dais)); + if (ret) { + dev_warn(afe->dev, "err_dai_component\n"); + goto err_dai_component; + } + /*enable afe clock*/ + mtk2701_afe_enable_clock(afe, 1); + return 0; +err_platform: + snd_soc_unregister_platform(&pdev->dev); +err_dai_component: + snd_soc_unregister_component(&pdev->dev); + return ret; +} + +static int mtk2701_afe_pcm_dev_remove(struct platform_device *pdev) +{ + struct mtk_afe *afe = platform_get_drvdata(pdev); + + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + /*disable afe clock*/ + mtk2701_afe_enable_clock(afe, 0); + return 0; +} + +static const struct of_device_id mtk2701_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt2701-audio", } +}; +MODULE_DEVICE_TABLE(of, mtk2701_afe_pcm_dt_match); + +static const struct dev_pm_ops mtk2701_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mtk2701_afe_runtime_suspend, + mtk2701_afe_runtime_resume, NULL) +}; + +static struct platform_driver mtk2701_afe_pcm_driver = { + .driver = { + .name = "mt2701-audio", + .owner = THIS_MODULE, + .of_match_table = mtk2701_afe_pcm_dt_match, +#ifdef CONFIG_PM + .pm = &mtk2701_afe_pm_ops, +#endif + }, + .probe = mtk2701_afe_pcm_dev_probe, + .remove = mtk2701_afe_pcm_dev_remove, +}; + +module_platform_driver(mtk2701_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701"); +MODULE_AUTHOR("Garlic Tseng garlic.tseng@mediatek.com"); +MODULE_LICENSE("GPL v2");
Add machine driver and config option for MT2701.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com --- sound/soc/mediatek/Kconfig | 21 ++ sound/soc/mediatek/Makefile | 3 + sound/soc/mediatek/mt2701/Makefile | 20 ++ sound/soc/mediatek/mt2701/mt2701-cs42448.c | 376 +++++++++++++++++++++++++++++ 4 files changed, 420 insertions(+) create mode 100644 sound/soc/mediatek/mt2701/Makefile create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index f7e789e..b190322 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -48,3 +48,24 @@ config SND_SOC_MT8173_RT5650_RT5676 with the RT5650 and RT5676 codecs. Select Y if you have such device. If unsure select "N". + +config SND_SOC_MT2701 + bool "SND_SOC_MT2701" + depends on SND_SOC_MEDIATEK + select SND_SOC_CS42XX8_I2C + help + This adds ASoC driver for Mediatek MT2701 boards + with the CS42448 codecs. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT2701_CS42448 + bool "SND_SOC_MT2701_CS42448" + depends on SND_SOC_MT2701 + select SND_SOC_CS42XX8_I2C + help + This adds ASoC driver for Mediatek MT2701 boards + with the CS42448 codecs. + Select Y if you have such device. + If unsure select "N". + diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 5048165..60387ca 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -3,3 +3,6 @@ obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173/ + +# 2701 Machine support +obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701/ diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile new file mode 100644 index 0000000..9f27653 --- /dev/null +++ b/sound/soc/mediatek/mt2701/Makefile @@ -0,0 +1,20 @@ +# +# Copyright (C) 2015 MediaTek Inc. +# +# 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. +# +# 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. +# + +# platform driver +obj-$(CONFIG_SND_SOC_MT2701) += mtk2701-afe-pcm.o +obj-$(CONFIG_SND_SOC_MT2701) += mtk2701-irq.o +obj-$(CONFIG_SND_SOC_MT2701) += mtk2701-afe-clock-ctrl.o + +# machine driver +obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c new file mode 100644 index 0000000..426f677 --- /dev/null +++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c @@ -0,0 +1,376 @@ +/* + * mt2701-cs42448.c -- MT2701 CS42448 ALSA SoC machine driver + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Ir Lian ir.lian@mediatek.com + * Garlic Tseng garlic.tseng@mediatek.com + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <sound/soc.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/pinctrl/consumer.h> +#include <linux/of_gpio.h> + +#include "mtk2701-afe-common.h" + +struct mt2701_cs42448_private { + int i2s1_in_mux; + int i2s1_in_mux_gpio_sel_1; + int i2s1_in_mux_gpio_sel_2; +}; + +static const char * const i2sin_mux_switch_text[] = { + "ADC_SDOUT2", + "ADC_SDOUT3", + "I2S_IN_1", + "I2S_IN_2", +}; + +static const struct soc_enum i2sin_mux_enum = + SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text); + +static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card); + + ucontrol->value.integer.value[0] = priv->i2s1_in_mux; + return 0; +} + +static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card); + + if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux) + return 0; + + switch (ucontrol->value.integer.value[0]) { + case 0: + gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0); + gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0); + break; + case 1: + gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1); + gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0); + break; + case 2: + gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0); + gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1); + break; + case 3: + gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1); + gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1); + break; + default: + dev_warn(card->dev, "%s invalid setting\n", __func__); + } + + priv->i2s1_in_mux = ucontrol->value.integer.value[0]; + return 0; +} + +static const struct snd_soc_dapm_widget + mt2701_cs42448_asoc_card_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Line Out Jack", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_LINE("Tuner In", NULL), + SND_SOC_DAPM_LINE("Satellite Tuner In", NULL), + SND_SOC_DAPM_LINE("AUX In", NULL), +}; + +static const struct snd_kcontrol_new mt2701_cs42448_controls[] = { + SOC_DAPM_PIN_SWITCH("Line Out Jack"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("Tuner In"), + SOC_DAPM_PIN_SWITCH("Satellite Tuner In"), + SOC_DAPM_PIN_SWITCH("AUX In"), + SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum, + mt2701_cs42448_i2sin1_mux_get, + mt2701_cs42448_i2sin1_mux_set), +}; + +static const unsigned int mt2701_cs42448_sampling_rates[] = {48000}; + +static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = { + .count = ARRAY_SIZE(mt2701_cs42448_sampling_rates), + .list = mt2701_cs42448_sampling_rates, + .mask = 0, +}; + +static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream) +{ + int err; + + err = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &mt2701_cs42448_constraints_rates); + if (err < 0) { + dev_err(substream->pcm->card->dev, /*better way to get device?*/ + "%s snd_pcm_hw_constraint_list failed: 0x%x\n", + __func__, err); + return err; + } + return 0; +} + +static struct snd_soc_ops mt2701_cs42448_fe_ops = { + .startup = mt2701_cs42448_fe_ops_startup, +}; + +static int mt2701_cs42448_be_ops_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 *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + /* codec slave, mt2701 master */ + unsigned int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_CONT; + unsigned int mclk_rate; + unsigned int rate = params_rate(params); + unsigned int div_mclk_to_bck = rate > 192000 ? 2 : 4; + unsigned int div_bck_to_lrck = 64; + + mclk_rate = rate * div_bck_to_lrck * div_mclk_to_bck; + + /* mt2701 mclk */ + snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT); + /* mt2701 bck */ + snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_MCLK_TO_BCK, div_mclk_to_bck); + /* mt2701 lrck */ + snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_BCK_TO_LRCK, div_bck_to_lrck); + /* mt2701 master */ + snd_soc_dai_set_fmt(cpu_dai, fmt); + + /* codec mclk */ + snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN); + /* codec slave */ + snd_soc_dai_set_fmt(codec_dai, fmt); + return 0; +} + +static struct snd_soc_ops mt2701_cs42448_be_ops = { + .hw_params = mt2701_cs42448_be_ops_hw_params +}; + +static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = { + /*FE*/ + { + .name = "mt2701-cs42448-pcm0", + .stream_name = "mt2701-cs42448-pcm0", + .cpu_dai_name = "PCM0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_GATED, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ops = &mt2701_cs42448_fe_ops, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "mt2701-cs42448-multi-ch-out", + .stream_name = "mt2701-cs42448-multi-ch-out", + .cpu_dai_name = "PCM_multi", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_GATED, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ops = &mt2701_cs42448_fe_ops, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "mt2701-cs42448-pcm1-data-UL", + .stream_name = "mt2701-cs42448-pcm1-data-UL", + .cpu_dai_name = "PCM1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_GATED, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ops = &mt2701_cs42448_fe_ops, + .dynamic = 1, + .dpcm_capture = 1, + }, + /*BE*/ + { + .name = "mt2701-cs42448-I2S0", + .cpu_dai_name = "I2S0", + .no_pcm = 1, + .codec_dai_name = "cs42448", + .codec_name = "cs42xx8.2-0048", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_GATED, + .ops = &mt2701_cs42448_be_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "mt2701-cs42448-I2S1", + .cpu_dai_name = "I2S1", + .no_pcm = 1, + .codec_dai_name = "cs42448", + .codec_name = "cs42xx8.2-0048", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_GATED, + .ops = &mt2701_cs42448_be_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "mt2701-cs42448-I2S2", + .cpu_dai_name = "I2S2", + .no_pcm = 1, + .codec_dai_name = "cs42448", + .codec_name = "cs42xx8.2-0048", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_GATED, + .ops = &mt2701_cs42448_be_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "mt2701-cs42448-I2S3", + .cpu_dai_name = "I2S3", + .no_pcm = 1, + .codec_dai_name = "cs42448", + .codec_name = "cs42xx8.2-0048", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_GATED, + .ops = &mt2701_cs42448_be_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card mt2701_cs42448_soc_card = { + .name = "mt2701-cs42448", + .dai_link = mt2701_cs42448_dai_links, + .num_links = ARRAY_SIZE(mt2701_cs42448_dai_links), + .controls = mt2701_cs42448_controls, + .num_controls = ARRAY_SIZE(mt2701_cs42448_controls), + .dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets), +}; + +static int mt2701_cs42448_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt2701_cs42448_soc_card; + int ret; + int i; + struct device_node *platform_node; + struct mt2701_cs42448_private *priv = + devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private), + GFP_KERNEL); + struct device *dev = &pdev->dev; + + if (!priv) + return -ENOMEM; + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + for (i = 0; i < card->num_links; i++) { + if (mt2701_cs42448_dai_links[i].platform_name) + continue; + mt2701_cs42448_dai_links[i].platform_of_node = platform_node; + } + + card->dev = dev; + + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) { + dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); + return ret; + } + + priv->i2s1_in_mux_gpio_sel_1 = + of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0); + if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) { + ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1, + "i2s1_in_mux_gpio_sel_1"); + if (ret) + dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n", + __func__, ret); + gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0); + } + + priv->i2s1_in_mux_gpio_sel_2 = + of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0); + if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) { + ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2, + "i2s1_in_mux_gpio_sel_2"); + if (ret) + dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n", + __func__, ret); + gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0); + } + snd_soc_card_set_drvdata(card, priv); + + ret = snd_soc_register_card(card); + + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static int mt2701_cs42448_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + devm_kfree(&pdev->dev, snd_soc_card_get_drvdata(card)); + return snd_soc_unregister_card(card); +} + +#ifdef CONFIG_OF +static const struct of_device_id mt2701_cs42448_machine_dt_match[] = { + {.compatible = "mediatek,mt2701-cs42448-machine",}, + {} +}; +#endif + +static struct platform_driver mt2701_cs42448_machine = { + .driver = { + .name = "mt2701-cs42448", + .owner = THIS_MODULE, + #ifdef CONFIG_OF + .of_match_table = mt2701_cs42448_machine_dt_match, + #endif + }, + .probe = mt2701_cs42448_machine_probe, + .remove = mt2701_cs42448_machine_remove +}; + +module_platform_driver(mt2701_cs42448_machine); + +/* Module information */ +MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver"); +MODULE_AUTHOR("Ir Lian ir.lian@mediatek.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("mt2701 cs42448 soc card");
On Thu, 2016-04-14 at 20:14 +0800, Garlic Tseng wrote:
Add machine driver and config option for MT2701.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com
sound/soc/mediatek/Kconfig | 21 ++ sound/soc/mediatek/Makefile | 3 + sound/soc/mediatek/mt2701/Makefile | 20 ++ sound/soc/mediatek/mt2701/mt2701-cs42448.c | 376 +++++++++++++++++++++++++++++ 4 files changed, 420 insertions(+) create mode 100644 sound/soc/mediatek/mt2701/Makefile create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index f7e789e..b190322 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -48,3 +48,24 @@ config SND_SOC_MT8173_RT5650_RT5676 with the RT5650 and RT5676 codecs. Select Y if you have such device. If unsure select "N".
+config SND_SOC_MT2701
It seems we need different driver for different soc. So after this patch, SND_SOC_MEDIATEK only select driver for mt8173. Maybe it should be renamed to SND_SOC_MT8173 to reflect this.
- bool "SND_SOC_MT2701"
Please check other config in this file and have a proper config prompt here.
Can we support module like mt8173?
- depends on SND_SOC_MEDIATEK
- select SND_SOC_CS42XX8_I2C
- help
This adds ASoC driver for Mediatek MT2701 boards
with the CS42448 codecs.
Select Y if you have such device.
If unsure select "N".
Based on the Makefile, this is platform driver for mt2701. This should not depends on codec. Please update the help message and select.
+config SND_SOC_MT2701_CS42448
- bool "SND_SOC_MT2701_CS42448"
- depends on SND_SOC_MT2701
- select SND_SOC_CS42XX8_I2C
- help
This adds ASoC driver for Mediatek MT2701 boards
with the CS42448 codecs.
Select Y if you have such device.
If unsure select "N".
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 5048165..60387ca 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -3,3 +3,6 @@ obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173/
+# 2701 Machine support +obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701/
Sorting, please put mt2701 before mt8173.
Joe.C
On Thu, 2016-04-14 at 20:39 +0800, Yingjoe Chen wrote:
On Thu, 2016-04-14 at 20:14 +0800, Garlic Tseng wrote:
Add machine driver and config option for MT2701.
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com
sound/soc/mediatek/Kconfig | 21 ++ sound/soc/mediatek/Makefile | 3 + sound/soc/mediatek/mt2701/Makefile | 20 ++ sound/soc/mediatek/mt2701/mt2701-cs42448.c | 376 +++++++++++++++++++++++++++++ 4 files changed, 420 insertions(+) create mode 100644 sound/soc/mediatek/mt2701/Makefile create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index f7e789e..b190322 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -48,3 +48,24 @@ config SND_SOC_MT8173_RT5650_RT5676 with the RT5650 and RT5676 codecs. Select Y if you have such device. If unsure select "N".
+config SND_SOC_MT2701
It seems we need different driver for different soc. So after this patch, SND_SOC_MEDIATEK only select driver for mt8173. Maybe it should be renamed to SND_SOC_MT8173 to reflect this.
Yes I think you are right. I'll do it.
- bool "SND_SOC_MT2701"
Please check other config in this file and have a proper config prompt here.
Can we support module like mt8173?
We already support mt8173. I didn't change the config for mt8173 and it works well before.
Next version I'll change SND_SOC_MEDIATEK to SND_SOC_MT8173 and check the config prompt.
- depends on SND_SOC_MEDIATEK
- select SND_SOC_CS42XX8_I2C
- help
This adds ASoC driver for Mediatek MT2701 boards
with the CS42448 codecs.
Select Y if you have such device.
If unsure select "N".
Based on the Makefile, this is platform driver for mt2701. This should not depends on codec. Please update the help message and select.
Yes you are right. I'll do it.
+config SND_SOC_MT2701_CS42448
- bool "SND_SOC_MT2701_CS42448"
- depends on SND_SOC_MT2701
- select SND_SOC_CS42XX8_I2C
- help
This adds ASoC driver for Mediatek MT2701 boards
with the CS42448 codecs.
Select Y if you have such device.
If unsure select "N".
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 5048165..60387ca 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -3,3 +3,6 @@ obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173/ obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173/
+# 2701 Machine support +obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701/
Sorting, please put mt2701 before mt8173.
OK I'll do it next patch.
Joe.C
participants (4)
-
Garlic Tseng
-
Matthias Brugger
-
YiMing (Garlic) Tseng
-
Yingjoe Chen