diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/mt8173-max98090.txt | 13 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt | 13 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt | 45 | ||||
-rw-r--r-- | sound/soc/Kconfig | 1 | ||||
-rw-r--r-- | sound/soc/Makefile | 1 | ||||
-rw-r--r-- | sound/soc/codecs/ml26124.c | 58 | ||||
-rw-r--r-- | sound/soc/mediatek/Kconfig | 30 | ||||
-rw-r--r-- | sound/soc/mediatek/Makefile | 5 | ||||
-rw-r--r-- | sound/soc/mediatek/mt8173-max98090.c | 213 | ||||
-rw-r--r-- | sound/soc/mediatek/mt8173-rt5650-rt5676.c | 278 | ||||
-rw-r--r-- | sound/soc/mediatek/mtk-afe-common.h | 109 | ||||
-rw-r--r-- | sound/soc/mediatek/mtk-afe-pcm.c | 1233 | ||||
-rw-r--r-- | sound/soc/omap/rx51.c | 10 |
13 files changed, 1955 insertions, 54 deletions
diff --git a/Documentation/devicetree/bindings/sound/mt8173-max98090.txt b/Documentation/devicetree/bindings/sound/mt8173-max98090.txt new file mode 100644 index 000000000000..829bd26d17f8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt8173-max98090.txt | |||
@@ -0,0 +1,13 @@ | |||
1 | MT8173 with MAX98090 CODEC | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "mediatek,mt8173-max98090" | ||
5 | - mediatek,audio-codec: the phandle of the MAX98090 audio codec | ||
6 | |||
7 | Example: | ||
8 | |||
9 | sound { | ||
10 | compatible = "mediatek,mt8173-max98090"; | ||
11 | mediatek,audio-codec = <&max98090>; | ||
12 | }; | ||
13 | |||
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt new file mode 100644 index 000000000000..61e98c976bd4 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt | |||
@@ -0,0 +1,13 @@ | |||
1 | MT8173 with RT5650 RT5676 CODECS | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "mediatek,mt8173-rt5650-rt5676" | ||
5 | - mediatek,audio-codec: the phandles of rt5650 and rt5676 codecs | ||
6 | |||
7 | Example: | ||
8 | |||
9 | sound { | ||
10 | compatible = "mediatek,mt8173-rt5650-rt5676"; | ||
11 | mediatek,audio-codec = <&rt5650 &rt5676>; | ||
12 | }; | ||
13 | |||
diff --git a/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt new file mode 100644 index 000000000000..e302c7f43b95 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt | |||
@@ -0,0 +1,45 @@ | |||
1 | Mediatek AFE PCM controller | ||
2 | |||
3 | Required properties: | ||
4 | - compatible = "mediatek,mt8173-afe-pcm"; | ||
5 | - reg: register location and size | ||
6 | - interrupts: Should contain AFE interrupt | ||
7 | - clock-names: should have these clock names: | ||
8 | "infra_sys_audio_clk", | ||
9 | "top_pdn_audio", | ||
10 | "top_pdn_aud_intbus", | ||
11 | "bck0", | ||
12 | "bck1", | ||
13 | "i2s0_m", | ||
14 | "i2s1_m", | ||
15 | "i2s2_m", | ||
16 | "i2s3_m", | ||
17 | "i2s3_b"; | ||
18 | |||
19 | Example: | ||
20 | |||
21 | afe: mt8173-afe-pcm@11220000 { | ||
22 | compatible = "mediatek,mt8173-afe-pcm"; | ||
23 | reg = <0 0x11220000 0 0x1000>; | ||
24 | interrupts = <GIC_SPI 134 IRQ_TYPE_EDGE_FALLING>; | ||
25 | clocks = <&infracfg INFRA_AUDIO>, | ||
26 | <&topckgen TOP_AUDIO_SEL>, | ||
27 | <&topckgen TOP_AUD_INTBUS_SEL>, | ||
28 | <&topckgen TOP_APLL1_DIV0>, | ||
29 | <&topckgen TOP_APLL2_DIV0>, | ||
30 | <&topckgen TOP_I2S0_M_CK_SEL>, | ||
31 | <&topckgen TOP_I2S1_M_CK_SEL>, | ||
32 | <&topckgen TOP_I2S2_M_CK_SEL>, | ||
33 | <&topckgen TOP_I2S3_M_CK_SEL>, | ||
34 | <&topckgen TOP_I2S3_B_CK_SEL>; | ||
35 | clock-names = "infra_sys_audio_clk", | ||
36 | "top_pdn_audio", | ||
37 | "top_pdn_aud_intbus", | ||
38 | "bck0", | ||
39 | "bck1", | ||
40 | "i2s0_m", | ||
41 | "i2s1_m", | ||
42 | "i2s2_m", | ||
43 | "i2s3_m", | ||
44 | "i2s3_b"; | ||
45 | }; | ||
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index e2828e101433..2ae9619443d1 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
@@ -45,6 +45,7 @@ source "sound/soc/nuc900/Kconfig" | |||
45 | source "sound/soc/omap/Kconfig" | 45 | source "sound/soc/omap/Kconfig" |
46 | source "sound/soc/kirkwood/Kconfig" | 46 | source "sound/soc/kirkwood/Kconfig" |
47 | source "sound/soc/intel/Kconfig" | 47 | source "sound/soc/intel/Kconfig" |
48 | source "sound/soc/mediatek/Kconfig" | ||
48 | source "sound/soc/mxs/Kconfig" | 49 | source "sound/soc/mxs/Kconfig" |
49 | source "sound/soc/pxa/Kconfig" | 50 | source "sound/soc/pxa/Kconfig" |
50 | source "sound/soc/qcom/Kconfig" | 51 | source "sound/soc/qcom/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index a0e1ee6b507d..e189903fabf4 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
@@ -24,6 +24,7 @@ obj-$(CONFIG_SND_SOC) += dwc/ | |||
24 | obj-$(CONFIG_SND_SOC) += fsl/ | 24 | obj-$(CONFIG_SND_SOC) += fsl/ |
25 | obj-$(CONFIG_SND_SOC) += jz4740/ | 25 | obj-$(CONFIG_SND_SOC) += jz4740/ |
26 | obj-$(CONFIG_SND_SOC) += intel/ | 26 | obj-$(CONFIG_SND_SOC) += intel/ |
27 | obj-$(CONFIG_SND_SOC) += mediatek/ | ||
27 | obj-$(CONFIG_SND_SOC) += mxs/ | 28 | obj-$(CONFIG_SND_SOC) += mxs/ |
28 | obj-$(CONFIG_SND_SOC) += nuc900/ | 29 | obj-$(CONFIG_SND_SOC) += nuc900/ |
29 | obj-$(CONFIG_SND_SOC) += omap/ | 30 | obj-$(CONFIG_SND_SOC) += omap/ |
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 62dda2488f14..b74118e019fb 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c | |||
@@ -341,6 +341,7 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream, | |||
341 | struct snd_soc_codec *codec = dai->codec; | 341 | struct snd_soc_codec *codec = dai->codec; |
342 | struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); | 342 | struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); |
343 | int i = get_coeff(priv->mclk, params_rate(hw_params)); | 343 | int i = get_coeff(priv->mclk, params_rate(hw_params)); |
344 | int srate; | ||
344 | 345 | ||
345 | if (i < 0) | 346 | if (i < 0) |
346 | return i; | 347 | return i; |
@@ -370,53 +371,16 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream, | |||
370 | BIT(0) | BIT(1), 0); | 371 | BIT(0) | BIT(1), 0); |
371 | } | 372 | } |
372 | 373 | ||
373 | switch (params_rate(hw_params)) { | 374 | srate = get_srate(params_rate(hw_params)); |
374 | case 16000: | 375 | if (srate < 0) |
375 | snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, | 376 | return srate; |
376 | get_srate(params_rate(hw_params))); | 377 | |
377 | snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, | 378 | snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, srate); |
378 | coeff_div[i].pllnl); | 379 | snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, coeff_div[i].pllnl); |
379 | snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, | 380 | snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, coeff_div[i].pllnh); |
380 | coeff_div[i].pllnh); | 381 | snd_soc_update_bits(codec, ML26124_PLLML, 0xff, coeff_div[i].pllml); |
381 | snd_soc_update_bits(codec, ML26124_PLLML, 0xff, | 382 | snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh); |
382 | coeff_div[i].pllml); | 383 | snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv); |
383 | snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, | ||
384 | coeff_div[i].pllmh); | ||
385 | snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, | ||
386 | coeff_div[i].plldiv); | ||
387 | break; | ||
388 | case 32000: | ||
389 | snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, | ||
390 | get_srate(params_rate(hw_params))); | ||
391 | snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, | ||
392 | coeff_div[i].pllnl); | ||
393 | snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, | ||
394 | coeff_div[i].pllnh); | ||
395 | snd_soc_update_bits(codec, ML26124_PLLML, 0xff, | ||
396 | coeff_div[i].pllml); | ||
397 | snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, | ||
398 | coeff_div[i].pllmh); | ||
399 | snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, | ||
400 | coeff_div[i].plldiv); | ||
401 | break; | ||
402 | case 48000: | ||
403 | snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, | ||
404 | get_srate(params_rate(hw_params))); | ||
405 | snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, | ||
406 | coeff_div[i].pllnl); | ||
407 | snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, | ||
408 | coeff_div[i].pllnh); | ||
409 | snd_soc_update_bits(codec, ML26124_PLLML, 0xff, | ||
410 | coeff_div[i].pllml); | ||
411 | snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, | ||
412 | coeff_div[i].pllmh); | ||
413 | snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, | ||
414 | coeff_div[i].plldiv); | ||
415 | break; | ||
416 | default: | ||
417 | pr_err("%s:this rate is no support for ml26124\n", __func__); | ||
418 | return -EINVAL; | ||
419 | } | ||
420 | 384 | ||
421 | return 0; | 385 | return 0; |
422 | } | 386 | } |
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig new file mode 100644 index 000000000000..15c04e2eae34 --- /dev/null +++ b/sound/soc/mediatek/Kconfig | |||
@@ -0,0 +1,30 @@ | |||
1 | config SND_SOC_MEDIATEK | ||
2 | tristate "ASoC support for Mediatek chip" | ||
3 | depends on ARCH_MEDIATEK | ||
4 | help | ||
5 | This adds ASoC platform driver support for Mediatek chip | ||
6 | that can be used with other codecs. | ||
7 | Select Y if you have such device. | ||
8 | Ex: MT8173 | ||
9 | |||
10 | config SND_SOC_MT8173_MAX98090 | ||
11 | tristate "ASoC Audio driver for MT8173 with MAX98090 codec" | ||
12 | depends on SND_SOC_MEDIATEK | ||
13 | select SND_SOC_MAX98090 | ||
14 | help | ||
15 | This adds ASoC driver for Mediatek MT8173 boards | ||
16 | with the MAX98090 audio codec. | ||
17 | Select Y if you have such device. | ||
18 | If unsure select "N". | ||
19 | |||
20 | config SND_SOC_MT8173_RT5650_RT5676 | ||
21 | tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs" | ||
22 | depends on SND_SOC_MEDIATEK | ||
23 | select SND_SOC_RT5645 | ||
24 | select SND_SOC_RT5677 | ||
25 | help | ||
26 | This adds ASoC driver for Mediatek MT8173 boards | ||
27 | with the RT5650 and RT5676 codecs. | ||
28 | Select Y if you have such device. | ||
29 | If unsure select "N". | ||
30 | |||
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile new file mode 100644 index 000000000000..75effbec438d --- /dev/null +++ b/sound/soc/mediatek/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # MTK Platform Support | ||
2 | obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o | ||
3 | # Machine support | ||
4 | obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o | ||
5 | 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-max98090.c new file mode 100644 index 000000000000..4d44b5803e55 --- /dev/null +++ b/sound/soc/mediatek/mt8173-max98090.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver | ||
3 | * | ||
4 | * Copyright (c) 2015 MediaTek Inc. | ||
5 | * Author: Koro Chen <koro.chen@mediatek.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 and | ||
9 | * only version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <sound/soc.h> | ||
19 | #include <sound/jack.h> | ||
20 | #include <linux/gpio.h> | ||
21 | #include "../codecs/max98090.h" | ||
22 | |||
23 | static struct snd_soc_jack mt8173_max98090_jack; | ||
24 | |||
25 | static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { | ||
26 | { | ||
27 | .pin = "Headphone", | ||
28 | .mask = SND_JACK_HEADPHONE, | ||
29 | }, | ||
30 | { | ||
31 | .pin = "Headset Mic", | ||
32 | .mask = SND_JACK_MICROPHONE, | ||
33 | }, | ||
34 | }; | ||
35 | |||
36 | static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { | ||
37 | SND_SOC_DAPM_SPK("Speaker", NULL), | ||
38 | SND_SOC_DAPM_MIC("Int Mic", NULL), | ||
39 | SND_SOC_DAPM_HP("Headphone", NULL), | ||
40 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||
41 | }; | ||
42 | |||
43 | static const struct snd_soc_dapm_route mt8173_max98090_routes[] = { | ||
44 | {"Speaker", NULL, "SPKL"}, | ||
45 | {"Speaker", NULL, "SPKR"}, | ||
46 | {"DMICL", NULL, "Int Mic"}, | ||
47 | {"Headphone", NULL, "HPL"}, | ||
48 | {"Headphone", NULL, "HPR"}, | ||
49 | {"Headset Mic", NULL, "MICBIAS"}, | ||
50 | {"IN34", NULL, "Headset Mic"}, | ||
51 | }; | ||
52 | |||
53 | static const struct snd_kcontrol_new mt8173_max98090_controls[] = { | ||
54 | SOC_DAPM_PIN_SWITCH("Speaker"), | ||
55 | SOC_DAPM_PIN_SWITCH("Int Mic"), | ||
56 | SOC_DAPM_PIN_SWITCH("Headphone"), | ||
57 | SOC_DAPM_PIN_SWITCH("Headset Mic"), | ||
58 | }; | ||
59 | |||
60 | static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream, | ||
61 | struct snd_pcm_hw_params *params) | ||
62 | { | ||
63 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
64 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
65 | |||
66 | return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, | ||
67 | SND_SOC_CLOCK_IN); | ||
68 | } | ||
69 | |||
70 | static struct snd_soc_ops mt8173_max98090_ops = { | ||
71 | .hw_params = mt8173_max98090_hw_params, | ||
72 | }; | ||
73 | |||
74 | static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) | ||
75 | { | ||
76 | int ret; | ||
77 | struct snd_soc_card *card = runtime->card; | ||
78 | struct snd_soc_codec *codec = runtime->codec; | ||
79 | |||
80 | /* enable jack detection */ | ||
81 | ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, | ||
82 | &mt8173_max98090_jack, NULL, 0); | ||
83 | if (ret) { | ||
84 | dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret); | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | ret = snd_soc_jack_add_pins(&mt8173_max98090_jack, | ||
89 | ARRAY_SIZE(mt8173_max98090_jack_pins), | ||
90 | mt8173_max98090_jack_pins); | ||
91 | if (ret) { | ||
92 | dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret); | ||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | return max98090_mic_detect(codec, &mt8173_max98090_jack); | ||
97 | } | ||
98 | |||
99 | /* Digital audio interface glue - connects codec <---> CPU */ | ||
100 | static struct snd_soc_dai_link mt8173_max98090_dais[] = { | ||
101 | /* Front End DAI links */ | ||
102 | { | ||
103 | .name = "MAX98090 Playback", | ||
104 | .stream_name = "MAX98090 Playback", | ||
105 | .cpu_dai_name = "DL1", | ||
106 | .platform_name = "11220000.mt8173-afe-pcm", | ||
107 | .codec_name = "snd-soc-dummy", | ||
108 | .codec_dai_name = "snd-soc-dummy-dai", | ||
109 | .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, | ||
110 | .dynamic = 1, | ||
111 | .dpcm_playback = 1, | ||
112 | }, | ||
113 | { | ||
114 | .name = "MAX98090 Capture", | ||
115 | .stream_name = "MAX98090 Capture", | ||
116 | .cpu_dai_name = "VUL", | ||
117 | .platform_name = "11220000.mt8173-afe-pcm", | ||
118 | .codec_name = "snd-soc-dummy", | ||
119 | .codec_dai_name = "snd-soc-dummy-dai", | ||
120 | .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, | ||
121 | .dynamic = 1, | ||
122 | .dpcm_capture = 1, | ||
123 | }, | ||
124 | /* Back End DAI links */ | ||
125 | { | ||
126 | .name = "Codec", | ||
127 | .cpu_dai_name = "I2S", | ||
128 | .platform_name = "11220000.mt8173-afe-pcm", | ||
129 | .no_pcm = 1, | ||
130 | .codec_dai_name = "HiFi", | ||
131 | .init = mt8173_max98090_init, | ||
132 | .ops = &mt8173_max98090_ops, | ||
133 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
134 | SND_SOC_DAIFMT_CBS_CFS, | ||
135 | .dpcm_playback = 1, | ||
136 | .dpcm_capture = 1, | ||
137 | }, | ||
138 | }; | ||
139 | |||
140 | static struct snd_soc_card mt8173_max98090_card = { | ||
141 | .name = "mt8173-max98090", | ||
142 | .dai_link = mt8173_max98090_dais, | ||
143 | .num_links = ARRAY_SIZE(mt8173_max98090_dais), | ||
144 | .controls = mt8173_max98090_controls, | ||
145 | .num_controls = ARRAY_SIZE(mt8173_max98090_controls), | ||
146 | .dapm_widgets = mt8173_max98090_widgets, | ||
147 | .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), | ||
148 | .dapm_routes = mt8173_max98090_routes, | ||
149 | .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), | ||
150 | }; | ||
151 | |||
152 | static int mt8173_max98090_dev_probe(struct platform_device *pdev) | ||
153 | { | ||
154 | struct snd_soc_card *card = &mt8173_max98090_card; | ||
155 | struct device_node *codec_node; | ||
156 | int ret, i; | ||
157 | |||
158 | codec_node = of_parse_phandle(pdev->dev.of_node, | ||
159 | "mediatek,audio-codec", 0); | ||
160 | if (!codec_node) { | ||
161 | dev_err(&pdev->dev, | ||
162 | "Property 'audio-codec' missing or invalid\n"); | ||
163 | return -EINVAL; | ||
164 | } | ||
165 | for (i = 0; i < card->num_links; i++) { | ||
166 | if (mt8173_max98090_dais[i].codec_name) | ||
167 | continue; | ||
168 | mt8173_max98090_dais[i].codec_of_node = codec_node; | ||
169 | } | ||
170 | card->dev = &pdev->dev; | ||
171 | |||
172 | ret = snd_soc_register_card(card); | ||
173 | if (ret) | ||
174 | dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", | ||
175 | __func__, ret); | ||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | static int mt8173_max98090_dev_remove(struct platform_device *pdev) | ||
180 | { | ||
181 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
182 | |||
183 | snd_soc_unregister_card(card); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static const struct of_device_id mt8173_max98090_dt_match[] = { | ||
188 | { .compatible = "mediatek,mt8173-max98090", }, | ||
189 | { } | ||
190 | }; | ||
191 | MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); | ||
192 | |||
193 | static struct platform_driver mt8173_max98090_driver = { | ||
194 | .driver = { | ||
195 | .name = "mt8173-max98090", | ||
196 | .owner = THIS_MODULE, | ||
197 | .of_match_table = mt8173_max98090_dt_match, | ||
198 | #ifdef CONFIG_PM | ||
199 | .pm = &snd_soc_pm_ops, | ||
200 | #endif | ||
201 | }, | ||
202 | .probe = mt8173_max98090_dev_probe, | ||
203 | .remove = mt8173_max98090_dev_remove, | ||
204 | }; | ||
205 | |||
206 | module_platform_driver(mt8173_max98090_driver); | ||
207 | |||
208 | /* Module information */ | ||
209 | MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); | ||
210 | MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); | ||
211 | MODULE_LICENSE("GPL v2"); | ||
212 | MODULE_ALIAS("platform:mt8173-max98090"); | ||
213 | |||
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c new file mode 100644 index 000000000000..094055323059 --- /dev/null +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c | |||
@@ -0,0 +1,278 @@ | |||
1 | /* | ||
2 | * mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs | ||
3 | * | ||
4 | * Copyright (c) 2015 MediaTek Inc. | ||
5 | * Author: Koro Chen <koro.chen@mediatek.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 and | ||
9 | * only version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/of_gpio.h> | ||
20 | #include <sound/soc.h> | ||
21 | #include <sound/jack.h> | ||
22 | #include "../codecs/rt5645.h" | ||
23 | #include "../codecs/rt5677.h" | ||
24 | |||
25 | #define MCLK_FOR_CODECS 12288000 | ||
26 | |||
27 | static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = { | ||
28 | SND_SOC_DAPM_SPK("Speaker", NULL), | ||
29 | SND_SOC_DAPM_MIC("Int Mic", NULL), | ||
30 | SND_SOC_DAPM_HP("Headphone", NULL), | ||
31 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||
32 | }; | ||
33 | |||
34 | static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = { | ||
35 | {"Speaker", NULL, "SPOL"}, | ||
36 | {"Speaker", NULL, "SPOR"}, | ||
37 | {"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ | ||
38 | {"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */ | ||
39 | {"Sub DMIC R1", NULL, "Int Mic"}, | ||
40 | {"Headphone", NULL, "HPOL"}, | ||
41 | {"Headphone", NULL, "HPOR"}, | ||
42 | {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ | ||
43 | {"Headset Mic", NULL, "micbias1"}, | ||
44 | {"Headset Mic", NULL, "micbias2"}, | ||
45 | {"IN1P", NULL, "Headset Mic"}, | ||
46 | {"IN1N", NULL, "Headset Mic"}, | ||
47 | {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */ | ||
48 | }; | ||
49 | |||
50 | static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = { | ||
51 | SOC_DAPM_PIN_SWITCH("Speaker"), | ||
52 | SOC_DAPM_PIN_SWITCH("Int Mic"), | ||
53 | SOC_DAPM_PIN_SWITCH("Headphone"), | ||
54 | SOC_DAPM_PIN_SWITCH("Headset Mic"), | ||
55 | }; | ||
56 | |||
57 | static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, | ||
58 | struct snd_pcm_hw_params *params) | ||
59 | { | ||
60 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
61 | int i, ret; | ||
62 | |||
63 | for (i = 0; i < rtd->num_codecs; i++) { | ||
64 | struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; | ||
65 | |||
66 | /* pll from mclk 12.288M */ | ||
67 | ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, | ||
68 | params_rate(params) * 512); | ||
69 | if (ret) | ||
70 | return ret; | ||
71 | |||
72 | /* sysclk from pll */ | ||
73 | ret = snd_soc_dai_set_sysclk(codec_dai, 1, | ||
74 | params_rate(params) * 512, | ||
75 | SND_SOC_CLOCK_IN); | ||
76 | if (ret) | ||
77 | return ret; | ||
78 | } | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static struct snd_soc_ops mt8173_rt5650_rt5676_ops = { | ||
83 | .hw_params = mt8173_rt5650_rt5676_hw_params, | ||
84 | }; | ||
85 | |||
86 | static struct snd_soc_jack mt8173_rt5650_rt5676_jack; | ||
87 | |||
88 | static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) | ||
89 | { | ||
90 | struct snd_soc_card *card = runtime->card; | ||
91 | struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; | ||
92 | struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec; | ||
93 | int ret; | ||
94 | |||
95 | rt5645_sel_asrc_clk_src(codec, | ||
96 | RT5645_DA_STEREO_FILTER | | ||
97 | RT5645_AD_STEREO_FILTER, | ||
98 | RT5645_CLK_SEL_I2S1_ASRC); | ||
99 | rt5677_sel_asrc_clk_src(codec_sub, | ||
100 | RT5677_DA_STEREO_FILTER | | ||
101 | RT5677_AD_STEREO1_FILTER, | ||
102 | RT5677_CLK_SEL_I2S1_ASRC); | ||
103 | rt5677_sel_asrc_clk_src(codec_sub, | ||
104 | RT5677_AD_STEREO2_FILTER | | ||
105 | RT5677_I2S2_SOURCE, | ||
106 | RT5677_CLK_SEL_I2S2_ASRC); | ||
107 | |||
108 | /* enable jack detection */ | ||
109 | ret = snd_soc_card_jack_new(card, "Headset Jack", | ||
110 | SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | | ||
111 | SND_JACK_BTN_0 | SND_JACK_BTN_1 | | ||
112 | SND_JACK_BTN_2 | SND_JACK_BTN_3, | ||
113 | &mt8173_rt5650_rt5676_jack, NULL, 0); | ||
114 | if (ret) { | ||
115 | dev_err(card->dev, "Can't new Headset Jack %d\n", ret); | ||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | return rt5645_set_jack_detect(codec, | ||
120 | &mt8173_rt5650_rt5676_jack, | ||
121 | &mt8173_rt5650_rt5676_jack, | ||
122 | &mt8173_rt5650_rt5676_jack); | ||
123 | } | ||
124 | |||
125 | static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { | ||
126 | { | ||
127 | .dai_name = "rt5645-aif1", | ||
128 | }, | ||
129 | { | ||
130 | .dai_name = "rt5677-aif1", | ||
131 | }, | ||
132 | }; | ||
133 | |||
134 | /* Digital audio interface glue - connects codec <---> CPU */ | ||
135 | static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { | ||
136 | /* Front End DAI links */ | ||
137 | { | ||
138 | .name = "rt5650_rt5676 Playback", | ||
139 | .stream_name = "rt5650_rt5676 Playback", | ||
140 | .cpu_dai_name = "DL1", | ||
141 | .platform_name = "11220000.mt8173-afe-pcm", | ||
142 | .codec_name = "snd-soc-dummy", | ||
143 | .codec_dai_name = "snd-soc-dummy-dai", | ||
144 | .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, | ||
145 | .dynamic = 1, | ||
146 | .dpcm_playback = 1, | ||
147 | }, | ||
148 | { | ||
149 | .name = "rt5650_rt5676 Capture", | ||
150 | .stream_name = "rt5650_rt5676 Capture", | ||
151 | .cpu_dai_name = "VUL", | ||
152 | .platform_name = "11220000.mt8173-afe-pcm", | ||
153 | .codec_name = "snd-soc-dummy", | ||
154 | .codec_dai_name = "snd-soc-dummy-dai", | ||
155 | .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, | ||
156 | .dynamic = 1, | ||
157 | .dpcm_capture = 1, | ||
158 | }, | ||
159 | |||
160 | /* Back End DAI links */ | ||
161 | { | ||
162 | .name = "Codec", | ||
163 | .cpu_dai_name = "I2S", | ||
164 | .platform_name = "11220000.mt8173-afe-pcm", | ||
165 | .no_pcm = 1, | ||
166 | .codecs = mt8173_rt5650_rt5676_codecs, | ||
167 | .num_codecs = 2, | ||
168 | .init = mt8173_rt5650_rt5676_init, | ||
169 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
170 | SND_SOC_DAIFMT_CBS_CFS, | ||
171 | .ops = &mt8173_rt5650_rt5676_ops, | ||
172 | .ignore_pmdown_time = 1, | ||
173 | .dpcm_playback = 1, | ||
174 | .dpcm_capture = 1, | ||
175 | }, | ||
176 | { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ | ||
177 | .name = "rt5650_rt5676 intercodec", | ||
178 | .stream_name = "rt5650_rt5676 intercodec", | ||
179 | .cpu_dai_name = "snd-soc-dummy-dai", | ||
180 | .platform_name = "snd-soc-dummy", | ||
181 | .no_pcm = 1, | ||
182 | .codec_dai_name = "rt5677-aif2", | ||
183 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
184 | SND_SOC_DAIFMT_CBM_CFM, | ||
185 | }, | ||
186 | |||
187 | }; | ||
188 | |||
189 | static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = { | ||
190 | { | ||
191 | .name_prefix = "Sub", | ||
192 | }, | ||
193 | }; | ||
194 | |||
195 | static struct snd_soc_card mt8173_rt5650_rt5676_card = { | ||
196 | .name = "mtk-rt5650-rt5676", | ||
197 | .dai_link = mt8173_rt5650_rt5676_dais, | ||
198 | .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais), | ||
199 | .codec_conf = mt8173_rt5650_rt5676_codec_conf, | ||
200 | .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf), | ||
201 | .controls = mt8173_rt5650_rt5676_controls, | ||
202 | .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls), | ||
203 | .dapm_widgets = mt8173_rt5650_rt5676_widgets, | ||
204 | .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets), | ||
205 | .dapm_routes = mt8173_rt5650_rt5676_routes, | ||
206 | .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes), | ||
207 | }; | ||
208 | |||
209 | static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) | ||
210 | { | ||
211 | struct snd_soc_card *card = &mt8173_rt5650_rt5676_card; | ||
212 | int ret; | ||
213 | |||
214 | mt8173_rt5650_rt5676_codecs[0].of_node = | ||
215 | of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); | ||
216 | if (!mt8173_rt5650_rt5676_codecs[0].of_node) { | ||
217 | dev_err(&pdev->dev, | ||
218 | "Property 'audio-codec' missing or invalid\n"); | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | mt8173_rt5650_rt5676_codecs[1].of_node = | ||
222 | of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); | ||
223 | if (!mt8173_rt5650_rt5676_codecs[1].of_node) { | ||
224 | dev_err(&pdev->dev, | ||
225 | "Property 'audio-codec' missing or invalid\n"); | ||
226 | return -EINVAL; | ||
227 | } | ||
228 | mt8173_rt5650_rt5676_codec_conf[0].of_node = | ||
229 | mt8173_rt5650_rt5676_codecs[1].of_node; | ||
230 | |||
231 | mt8173_rt5650_rt5676_dais[3].codec_of_node = | ||
232 | mt8173_rt5650_rt5676_codecs[1].of_node; | ||
233 | |||
234 | card->dev = &pdev->dev; | ||
235 | platform_set_drvdata(pdev, card); | ||
236 | |||
237 | ret = snd_soc_register_card(card); | ||
238 | if (ret) | ||
239 | dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", | ||
240 | __func__, ret); | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev) | ||
245 | { | ||
246 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
247 | |||
248 | snd_soc_unregister_card(card); | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = { | ||
253 | { .compatible = "mediatek,mt8173-rt5650-rt5676", }, | ||
254 | { } | ||
255 | }; | ||
256 | MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match); | ||
257 | |||
258 | static struct platform_driver mt8173_rt5650_rt5676_driver = { | ||
259 | .driver = { | ||
260 | .name = "mtk-rt5650-rt5676", | ||
261 | .owner = THIS_MODULE, | ||
262 | .of_match_table = mt8173_rt5650_rt5676_dt_match, | ||
263 | #ifdef CONFIG_PM | ||
264 | .pm = &snd_soc_pm_ops, | ||
265 | #endif | ||
266 | }, | ||
267 | .probe = mt8173_rt5650_rt5676_dev_probe, | ||
268 | .remove = mt8173_rt5650_rt5676_dev_remove, | ||
269 | }; | ||
270 | |||
271 | module_platform_driver(mt8173_rt5650_rt5676_driver); | ||
272 | |||
273 | /* Module information */ | ||
274 | MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver"); | ||
275 | MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); | ||
276 | MODULE_LICENSE("GPL v2"); | ||
277 | MODULE_ALIAS("platform:mtk-rt5650-rt5676"); | ||
278 | |||
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h new file mode 100644 index 000000000000..a88b17511fdf --- /dev/null +++ b/sound/soc/mediatek/mtk-afe-common.h | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * mtk_afe_common.h -- Mediatek audio driver common definitions | ||
3 | * | ||
4 | * Copyright (c) 2015 MediaTek Inc. | ||
5 | * Author: Koro Chen <koro.chen@mediatek.com> | ||
6 | * Sascha Hauer <s.hauer@pengutronix.de> | ||
7 | * Hidalgo Huang <hidalgo.huang@mediatek.com> | ||
8 | * Ir Lian <ir.lian@mediatek.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 and | ||
12 | * only version 2 as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MTK_AFE_COMMON_H_ | ||
21 | #define _MTK_AFE_COMMON_H_ | ||
22 | |||
23 | #include <linux/clk.h> | ||
24 | #include <linux/regmap.h> | ||
25 | |||
26 | enum { | ||
27 | MTK_AFE_MEMIF_DL1, | ||
28 | MTK_AFE_MEMIF_DL2, | ||
29 | MTK_AFE_MEMIF_VUL, | ||
30 | MTK_AFE_MEMIF_DAI, | ||
31 | MTK_AFE_MEMIF_AWB, | ||
32 | MTK_AFE_MEMIF_MOD_DAI, | ||
33 | MTK_AFE_MEMIF_HDMI, | ||
34 | MTK_AFE_MEMIF_NUM, | ||
35 | MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM, | ||
36 | MTK_AFE_IO_MOD_PCM2, | ||
37 | MTK_AFE_IO_PMIC, | ||
38 | MTK_AFE_IO_I2S, | ||
39 | MTK_AFE_IO_2ND_I2S, | ||
40 | MTK_AFE_IO_HW_GAIN1, | ||
41 | MTK_AFE_IO_HW_GAIN2, | ||
42 | MTK_AFE_IO_MRG_O, | ||
43 | MTK_AFE_IO_MRG_I, | ||
44 | MTK_AFE_IO_DAIBT, | ||
45 | MTK_AFE_IO_HDMI, | ||
46 | }; | ||
47 | |||
48 | enum { | ||
49 | MTK_AFE_IRQ_1, | ||
50 | MTK_AFE_IRQ_2, | ||
51 | MTK_AFE_IRQ_3, | ||
52 | MTK_AFE_IRQ_4, | ||
53 | MTK_AFE_IRQ_5, | ||
54 | MTK_AFE_IRQ_6, | ||
55 | MTK_AFE_IRQ_7, | ||
56 | MTK_AFE_IRQ_8, | ||
57 | MTK_AFE_IRQ_NUM, | ||
58 | }; | ||
59 | |||
60 | enum { | ||
61 | MTK_CLK_INFRASYS_AUD, | ||
62 | MTK_CLK_TOP_PDN_AUD, | ||
63 | MTK_CLK_TOP_PDN_AUD_BUS, | ||
64 | MTK_CLK_I2S0_M, | ||
65 | MTK_CLK_I2S1_M, | ||
66 | MTK_CLK_I2S2_M, | ||
67 | MTK_CLK_I2S3_M, | ||
68 | MTK_CLK_I2S3_B, | ||
69 | MTK_CLK_BCK0, | ||
70 | MTK_CLK_BCK1, | ||
71 | MTK_CLK_NUM | ||
72 | }; | ||
73 | |||
74 | struct mtk_afe; | ||
75 | struct snd_pcm_substream; | ||
76 | |||
77 | struct mtk_afe_memif_data { | ||
78 | int id; | ||
79 | const char *name; | ||
80 | int reg_ofs_base; | ||
81 | int reg_ofs_cur; | ||
82 | int fs_shift; | ||
83 | int mono_shift; | ||
84 | int enable_shift; | ||
85 | int irq_reg_cnt; | ||
86 | int irq_cnt_shift; | ||
87 | int irq_en_shift; | ||
88 | int irq_fs_shift; | ||
89 | int irq_clr_shift; | ||
90 | }; | ||
91 | |||
92 | struct mtk_afe_memif { | ||
93 | unsigned int phys_buf_addr; | ||
94 | int buffer_size; | ||
95 | unsigned int hw_ptr; /* Previous IRQ's HW ptr */ | ||
96 | struct snd_pcm_substream *substream; | ||
97 | const struct mtk_afe_memif_data *data; | ||
98 | const struct mtk_afe_irq_data *irqdata; | ||
99 | }; | ||
100 | |||
101 | struct mtk_afe { | ||
102 | /* address for ioremap audio hardware register */ | ||
103 | void __iomem *base_addr; | ||
104 | struct device *dev; | ||
105 | struct regmap *regmap; | ||
106 | struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM]; | ||
107 | struct clk *clocks[MTK_CLK_NUM]; | ||
108 | }; | ||
109 | #endif | ||
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c new file mode 100644 index 000000000000..cc228db5fb76 --- /dev/null +++ b/sound/soc/mediatek/mtk-afe-pcm.c | |||
@@ -0,0 +1,1233 @@ | |||
1 | /* | ||
2 | * Mediatek ALSA SoC AFE platform driver | ||
3 | * | ||
4 | * Copyright (c) 2015 MediaTek Inc. | ||
5 | * Author: Koro Chen <koro.chen@mediatek.com> | ||
6 | * Sascha Hauer <s.hauer@pengutronix.de> | ||
7 | * Hidalgo Huang <hidalgo.huang@mediatek.com> | ||
8 | * Ir Lian <ir.lian@mediatek.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 and | ||
12 | * only version 2 as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #include <linux/delay.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/of.h> | ||
23 | #include <linux/of_address.h> | ||
24 | #include <linux/pm_runtime.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include "mtk-afe-common.h" | ||
27 | |||
28 | /***************************************************************************** | ||
29 | * R E G I S T E R D E F I N I T I O N | ||
30 | *****************************************************************************/ | ||
31 | #define AUDIO_TOP_CON0 0x0000 | ||
32 | #define AUDIO_TOP_CON1 0x0004 | ||
33 | #define AFE_DAC_CON0 0x0010 | ||
34 | #define AFE_DAC_CON1 0x0014 | ||
35 | #define AFE_I2S_CON1 0x0034 | ||
36 | #define AFE_I2S_CON2 0x0038 | ||
37 | #define AFE_CONN_24BIT 0x006c | ||
38 | |||
39 | #define AFE_CONN1 0x0024 | ||
40 | #define AFE_CONN2 0x0028 | ||
41 | #define AFE_CONN7 0x0460 | ||
42 | #define AFE_CONN8 0x0464 | ||
43 | #define AFE_HDMI_CONN0 0x0390 | ||
44 | |||
45 | /* Memory interface */ | ||
46 | #define AFE_DL1_BASE 0x0040 | ||
47 | #define AFE_DL1_CUR 0x0044 | ||
48 | #define AFE_DL2_BASE 0x0050 | ||
49 | #define AFE_DL2_CUR 0x0054 | ||
50 | #define AFE_AWB_BASE 0x0070 | ||
51 | #define AFE_AWB_CUR 0x007c | ||
52 | #define AFE_VUL_BASE 0x0080 | ||
53 | #define AFE_VUL_CUR 0x008c | ||
54 | #define AFE_DAI_BASE 0x0090 | ||
55 | #define AFE_DAI_CUR 0x009c | ||
56 | #define AFE_MOD_PCM_BASE 0x0330 | ||
57 | #define AFE_MOD_PCM_CUR 0x033c | ||
58 | #define AFE_HDMI_OUT_BASE 0x0374 | ||
59 | #define AFE_HDMI_OUT_CUR 0x0378 | ||
60 | |||
61 | #define AFE_ADDA2_TOP_CON0 0x0600 | ||
62 | |||
63 | #define AFE_HDMI_OUT_CON0 0x0370 | ||
64 | |||
65 | #define AFE_IRQ_MCU_CON 0x03a0 | ||
66 | #define AFE_IRQ_STATUS 0x03a4 | ||
67 | #define AFE_IRQ_CLR 0x03a8 | ||
68 | #define AFE_IRQ_CNT1 0x03ac | ||
69 | #define AFE_IRQ_CNT2 0x03b0 | ||
70 | #define AFE_IRQ_MCU_EN 0x03b4 | ||
71 | #define AFE_IRQ_CNT5 0x03bc | ||
72 | #define AFE_IRQ_CNT7 0x03dc | ||
73 | |||
74 | #define AFE_TDM_CON1 0x0548 | ||
75 | #define AFE_TDM_CON2 0x054c | ||
76 | |||
77 | #define AFE_BASE_END_OFFSET 8 | ||
78 | #define AFE_IRQ_STATUS_BITS 0xff | ||
79 | |||
80 | /* AUDIO_TOP_CON0 (0x0000) */ | ||
81 | #define AUD_TCON0_PDN_SPDF (0x1 << 21) | ||
82 | #define AUD_TCON0_PDN_HDMI (0x1 << 20) | ||
83 | #define AUD_TCON0_PDN_24M (0x1 << 9) | ||
84 | #define AUD_TCON0_PDN_22M (0x1 << 8) | ||
85 | #define AUD_TCON0_PDN_AFE (0x1 << 2) | ||
86 | |||
87 | /* AFE_I2S_CON1 (0x0034) */ | ||
88 | #define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12) | ||
89 | #define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8) | ||
90 | #define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3) | ||
91 | #define AFE_I2S_CON1_EN (0x1 << 0) | ||
92 | |||
93 | /* AFE_I2S_CON2 (0x0038) */ | ||
94 | #define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12) | ||
95 | #define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8) | ||
96 | #define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3) | ||
97 | #define AFE_I2S_CON2_EN (0x1 << 0) | ||
98 | |||
99 | /* AFE_CONN_24BIT (0x006c) */ | ||
100 | #define AFE_CONN_24BIT_O04 (0x1 << 4) | ||
101 | #define AFE_CONN_24BIT_O03 (0x1 << 3) | ||
102 | |||
103 | /* AFE_HDMI_CONN0 (0x0390) */ | ||
104 | #define AFE_HDMI_CONN0_O37_I37 (0x7 << 21) | ||
105 | #define AFE_HDMI_CONN0_O36_I36 (0x6 << 18) | ||
106 | #define AFE_HDMI_CONN0_O35_I33 (0x3 << 15) | ||
107 | #define AFE_HDMI_CONN0_O34_I32 (0x2 << 12) | ||
108 | #define AFE_HDMI_CONN0_O33_I35 (0x5 << 9) | ||
109 | #define AFE_HDMI_CONN0_O32_I34 (0x4 << 6) | ||
110 | #define AFE_HDMI_CONN0_O31_I31 (0x1 << 3) | ||
111 | #define AFE_HDMI_CONN0_O30_I30 (0x0 << 0) | ||
112 | |||
113 | /* AFE_TDM_CON1 (0x0548) */ | ||
114 | #define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24) | ||
115 | #define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12) | ||
116 | #define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8) | ||
117 | #define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4) | ||
118 | #define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3) | ||
119 | #define AFE_TDM_CON1_BCK_INV (0x1 << 1) | ||
120 | #define AFE_TDM_CON1_EN (0x1 << 0) | ||
121 | |||
122 | enum afe_tdm_ch_start { | ||
123 | AFE_TDM_CH_START_O30_O31 = 0, | ||
124 | AFE_TDM_CH_START_O32_O33, | ||
125 | AFE_TDM_CH_START_O34_O35, | ||
126 | AFE_TDM_CH_START_O36_O37, | ||
127 | AFE_TDM_CH_ZERO, | ||
128 | }; | ||
129 | |||
130 | static const struct snd_pcm_hardware mtk_afe_hardware = { | ||
131 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
132 | SNDRV_PCM_INFO_MMAP_VALID), | ||
133 | .buffer_bytes_max = 256 * 1024, | ||
134 | .period_bytes_min = 512, | ||
135 | .period_bytes_max = 128 * 1024, | ||
136 | .periods_min = 2, | ||
137 | .periods_max = 256, | ||
138 | .fifo_size = 0, | ||
139 | }; | ||
140 | |||
141 | static snd_pcm_uframes_t mtk_afe_pcm_pointer | ||
142 | (struct snd_pcm_substream *substream) | ||
143 | { | ||
144 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
145 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
146 | struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; | ||
147 | |||
148 | return bytes_to_frames(substream->runtime, memif->hw_ptr); | ||
149 | } | ||
150 | |||
151 | static const struct snd_pcm_ops mtk_afe_pcm_ops = { | ||
152 | .ioctl = snd_pcm_lib_ioctl, | ||
153 | .pointer = mtk_afe_pcm_pointer, | ||
154 | }; | ||
155 | |||
156 | static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) | ||
157 | { | ||
158 | size_t size; | ||
159 | struct snd_card *card = rtd->card->snd_card; | ||
160 | struct snd_pcm *pcm = rtd->pcm; | ||
161 | |||
162 | size = mtk_afe_hardware.buffer_bytes_max; | ||
163 | |||
164 | return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
165 | card->dev, size, size); | ||
166 | } | ||
167 | |||
168 | static void mtk_afe_pcm_free(struct snd_pcm *pcm) | ||
169 | { | ||
170 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
171 | } | ||
172 | |||
173 | static const struct snd_soc_platform_driver mtk_afe_pcm_platform = { | ||
174 | .ops = &mtk_afe_pcm_ops, | ||
175 | .pcm_new = mtk_afe_pcm_new, | ||
176 | .pcm_free = mtk_afe_pcm_free, | ||
177 | }; | ||
178 | |||
179 | struct mtk_afe_rate { | ||
180 | unsigned int rate; | ||
181 | unsigned int regvalue; | ||
182 | }; | ||
183 | |||
184 | static const struct mtk_afe_rate mtk_afe_i2s_rates[] = { | ||
185 | { .rate = 8000, .regvalue = 0 }, | ||
186 | { .rate = 11025, .regvalue = 1 }, | ||
187 | { .rate = 12000, .regvalue = 2 }, | ||
188 | { .rate = 16000, .regvalue = 4 }, | ||
189 | { .rate = 22050, .regvalue = 5 }, | ||
190 | { .rate = 24000, .regvalue = 6 }, | ||
191 | { .rate = 32000, .regvalue = 8 }, | ||
192 | { .rate = 44100, .regvalue = 9 }, | ||
193 | { .rate = 48000, .regvalue = 10 }, | ||
194 | { .rate = 88000, .regvalue = 11 }, | ||
195 | { .rate = 96000, .regvalue = 12 }, | ||
196 | { .rate = 174000, .regvalue = 13 }, | ||
197 | { .rate = 192000, .regvalue = 14 }, | ||
198 | }; | ||
199 | |||
200 | static int mtk_afe_i2s_fs(unsigned int sample_rate) | ||
201 | { | ||
202 | int i; | ||
203 | |||
204 | for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++) | ||
205 | if (mtk_afe_i2s_rates[i].rate == sample_rate) | ||
206 | return mtk_afe_i2s_rates[i].regvalue; | ||
207 | |||
208 | return -EINVAL; | ||
209 | } | ||
210 | |||
211 | static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate) | ||
212 | { | ||
213 | unsigned int val; | ||
214 | int fs = mtk_afe_i2s_fs(rate); | ||
215 | |||
216 | if (fs < 0) | ||
217 | return -EINVAL; | ||
218 | |||
219 | /* from external ADC */ | ||
220 | regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1); | ||
221 | |||
222 | /* set input */ | ||
223 | val = AFE_I2S_CON2_LOW_JITTER_CLK | | ||
224 | AFE_I2S_CON2_RATE(fs) | | ||
225 | AFE_I2S_CON2_FORMAT_I2S; | ||
226 | |||
227 | regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val); | ||
228 | |||
229 | /* set output */ | ||
230 | val = AFE_I2S_CON1_LOW_JITTER_CLK | | ||
231 | AFE_I2S_CON1_RATE(fs) | | ||
232 | AFE_I2S_CON1_FORMAT_I2S; | ||
233 | |||
234 | regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable) | ||
239 | { | ||
240 | unsigned int val; | ||
241 | |||
242 | regmap_read(afe->regmap, AFE_I2S_CON2, &val); | ||
243 | if (!!(val & AFE_I2S_CON2_EN) == enable) | ||
244 | return; /* must skip soft reset */ | ||
245 | |||
246 | /* I2S soft reset begin */ | ||
247 | regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4); | ||
248 | |||
249 | /* input */ | ||
250 | regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable); | ||
251 | |||
252 | /* output */ | ||
253 | regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable); | ||
254 | |||
255 | /* I2S soft reset end */ | ||
256 | udelay(1); | ||
257 | regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0); | ||
258 | } | ||
259 | |||
260 | static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, | ||
261 | struct clk *m_ck, struct clk *b_ck) | ||
262 | { | ||
263 | int ret; | ||
264 | |||
265 | if (m_ck) { | ||
266 | ret = clk_prepare_enable(m_ck); | ||
267 | if (ret) { | ||
268 | dev_err(afe->dev, "Failed to enable m_ck\n"); | ||
269 | return ret; | ||
270 | } | ||
271 | regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, | ||
272 | AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); | ||
273 | } | ||
274 | |||
275 | if (b_ck) { | ||
276 | ret = clk_prepare_enable(b_ck); | ||
277 | if (ret) { | ||
278 | dev_err(afe->dev, "Failed to enable b_ck\n"); | ||
279 | return ret; | ||
280 | } | ||
281 | } | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int mtk_afe_dais_set_clks(struct mtk_afe *afe, | ||
286 | struct clk *m_ck, unsigned int mck_rate, | ||
287 | struct clk *b_ck, unsigned int bck_rate) | ||
288 | { | ||
289 | int ret; | ||
290 | |||
291 | if (m_ck) { | ||
292 | ret = clk_set_rate(m_ck, mck_rate); | ||
293 | if (ret) { | ||
294 | dev_err(afe->dev, "Failed to set m_ck rate\n"); | ||
295 | return ret; | ||
296 | } | ||
297 | } | ||
298 | |||
299 | if (b_ck) { | ||
300 | ret = clk_set_rate(b_ck, bck_rate); | ||
301 | if (ret) { | ||
302 | dev_err(afe->dev, "Failed to set b_ck rate\n"); | ||
303 | return ret; | ||
304 | } | ||
305 | } | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static void mtk_afe_dais_disable_clks(struct mtk_afe *afe, | ||
310 | struct clk *m_ck, struct clk *b_ck) | ||
311 | { | ||
312 | if (m_ck) { | ||
313 | regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, | ||
314 | AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, | ||
315 | AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); | ||
316 | clk_disable_unprepare(m_ck); | ||
317 | } | ||
318 | if (b_ck) | ||
319 | clk_disable_unprepare(b_ck); | ||
320 | } | ||
321 | |||
322 | static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream, | ||
323 | struct snd_soc_dai *dai) | ||
324 | { | ||
325 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
326 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
327 | |||
328 | if (dai->active) | ||
329 | return 0; | ||
330 | |||
331 | mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream, | ||
336 | struct snd_soc_dai *dai) | ||
337 | { | ||
338 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
339 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
340 | |||
341 | if (dai->active) | ||
342 | return; | ||
343 | |||
344 | mtk_afe_set_i2s_enable(afe, false); | ||
345 | mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); | ||
346 | |||
347 | /* disable AFE */ | ||
348 | regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); | ||
349 | } | ||
350 | |||
351 | static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream, | ||
352 | struct snd_soc_dai *dai) | ||
353 | { | ||
354 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
355 | struct snd_pcm_runtime * const runtime = substream->runtime; | ||
356 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
357 | int ret; | ||
358 | |||
359 | mtk_afe_dais_set_clks(afe, | ||
360 | afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256, | ||
361 | NULL, 0); | ||
362 | /* config I2S */ | ||
363 | ret = mtk_afe_set_i2s(afe, substream->runtime->rate); | ||
364 | if (ret) | ||
365 | return ret; | ||
366 | |||
367 | mtk_afe_set_i2s_enable(afe, true); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream, | ||
373 | struct snd_soc_dai *dai) | ||
374 | { | ||
375 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
376 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
377 | |||
378 | if (dai->active) | ||
379 | return 0; | ||
380 | |||
381 | mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], | ||
382 | afe->clocks[MTK_CLK_I2S3_B]); | ||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream, | ||
387 | struct snd_soc_dai *dai) | ||
388 | { | ||
389 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
390 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
391 | |||
392 | if (dai->active) | ||
393 | return; | ||
394 | |||
395 | mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], | ||
396 | afe->clocks[MTK_CLK_I2S3_B]); | ||
397 | |||
398 | /* disable AFE */ | ||
399 | regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); | ||
400 | } | ||
401 | |||
402 | static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream, | ||
403 | struct snd_soc_dai *dai) | ||
404 | { | ||
405 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
406 | struct snd_pcm_runtime * const runtime = substream->runtime; | ||
407 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
408 | unsigned int val; | ||
409 | |||
410 | mtk_afe_dais_set_clks(afe, | ||
411 | afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128, | ||
412 | afe->clocks[MTK_CLK_I2S3_B], | ||
413 | runtime->rate * runtime->channels * 32); | ||
414 | |||
415 | val = AFE_TDM_CON1_BCK_INV | | ||
416 | AFE_TDM_CON1_1_BCK_DELAY | | ||
417 | AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */ | ||
418 | AFE_TDM_CON1_WLEN_32BIT | | ||
419 | AFE_TDM_CON1_32_BCK_CYCLES | | ||
420 | AFE_TDM_CON1_LRCK_WIDTH(32); | ||
421 | regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val); | ||
422 | |||
423 | /* set tdm2 config */ | ||
424 | switch (runtime->channels) { | ||
425 | case 1: | ||
426 | case 2: | ||
427 | val = AFE_TDM_CH_START_O30_O31; | ||
428 | val |= (AFE_TDM_CH_ZERO << 4); | ||
429 | val |= (AFE_TDM_CH_ZERO << 8); | ||
430 | val |= (AFE_TDM_CH_ZERO << 12); | ||
431 | break; | ||
432 | case 3: | ||
433 | case 4: | ||
434 | val = AFE_TDM_CH_START_O30_O31; | ||
435 | val |= (AFE_TDM_CH_START_O32_O33 << 4); | ||
436 | val |= (AFE_TDM_CH_ZERO << 8); | ||
437 | val |= (AFE_TDM_CH_ZERO << 12); | ||
438 | break; | ||
439 | case 5: | ||
440 | case 6: | ||
441 | val = AFE_TDM_CH_START_O30_O31; | ||
442 | val |= (AFE_TDM_CH_START_O32_O33 << 4); | ||
443 | val |= (AFE_TDM_CH_START_O34_O35 << 8); | ||
444 | val |= (AFE_TDM_CH_ZERO << 12); | ||
445 | break; | ||
446 | case 7: | ||
447 | case 8: | ||
448 | val = AFE_TDM_CH_START_O30_O31; | ||
449 | val |= (AFE_TDM_CH_START_O32_O33 << 4); | ||
450 | val |= (AFE_TDM_CH_START_O34_O35 << 8); | ||
451 | val |= (AFE_TDM_CH_START_O36_O37 << 12); | ||
452 | break; | ||
453 | default: | ||
454 | val = 0; | ||
455 | } | ||
456 | regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val); | ||
457 | |||
458 | regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, | ||
459 | 0x000000f0, runtime->channels << 4); | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd, | ||
464 | struct snd_soc_dai *dai) | ||
465 | { | ||
466 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
467 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
468 | |||
469 | dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name); | ||
470 | |||
471 | switch (cmd) { | ||
472 | case SNDRV_PCM_TRIGGER_START: | ||
473 | case SNDRV_PCM_TRIGGER_RESUME: | ||
474 | regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, | ||
475 | AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0); | ||
476 | |||
477 | /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */ | ||
478 | regmap_write(afe->regmap, AFE_HDMI_CONN0, | ||
479 | AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 | | ||
480 | AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 | | ||
481 | AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 | | ||
482 | AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37); | ||
483 | |||
484 | /* enable Out control */ | ||
485 | regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1); | ||
486 | |||
487 | /* enable tdm */ | ||
488 | regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1); | ||
489 | |||
490 | return 0; | ||
491 | case SNDRV_PCM_TRIGGER_STOP: | ||
492 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
493 | /* disable tdm */ | ||
494 | regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0); | ||
495 | |||
496 | /* disable Out control */ | ||
497 | regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0); | ||
498 | |||
499 | regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, | ||
500 | AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, | ||
501 | AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF); | ||
502 | |||
503 | return 0; | ||
504 | default: | ||
505 | return -EINVAL; | ||
506 | } | ||
507 | } | ||
508 | |||
509 | static int mtk_afe_dais_startup(struct snd_pcm_substream *substream, | ||
510 | struct snd_soc_dai *dai) | ||
511 | { | ||
512 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
513 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
514 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
515 | struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; | ||
516 | int ret; | ||
517 | |||
518 | memif->substream = substream; | ||
519 | |||
520 | snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware); | ||
521 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
522 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
523 | if (ret < 0) | ||
524 | dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n"); | ||
525 | return ret; | ||
526 | } | ||
527 | |||
528 | static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream, | ||
529 | struct snd_soc_dai *dai) | ||
530 | { | ||
531 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
532 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
533 | struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; | ||
534 | |||
535 | memif->substream = NULL; | ||
536 | } | ||
537 | |||
538 | static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, | ||
539 | struct snd_pcm_hw_params *params, | ||
540 | struct snd_soc_dai *dai) | ||
541 | { | ||
542 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
543 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
544 | struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; | ||
545 | int ret; | ||
546 | |||
547 | dev_dbg(afe->dev, | ||
548 | "%s period = %u, rate= %u, channels=%u\n", | ||
549 | __func__, params_period_size(params), params_rate(params), | ||
550 | params_channels(params)); | ||
551 | |||
552 | ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | ||
553 | if (ret < 0) | ||
554 | return ret; | ||
555 | |||
556 | memif->phys_buf_addr = substream->runtime->dma_addr; | ||
557 | memif->buffer_size = substream->runtime->dma_bytes; | ||
558 | memif->hw_ptr = 0; | ||
559 | |||
560 | /* start */ | ||
561 | regmap_write(afe->regmap, | ||
562 | memif->data->reg_ofs_base, memif->phys_buf_addr); | ||
563 | /* end */ | ||
564 | regmap_write(afe->regmap, | ||
565 | memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, | ||
566 | memif->phys_buf_addr + memif->buffer_size - 1); | ||
567 | |||
568 | /* set channel */ | ||
569 | if (memif->data->mono_shift >= 0) { | ||
570 | unsigned int mono = (params_channels(params) == 1) ? 1 : 0; | ||
571 | |||
572 | regmap_update_bits(afe->regmap, AFE_DAC_CON1, | ||
573 | 1 << memif->data->mono_shift, | ||
574 | mono << memif->data->mono_shift); | ||
575 | } | ||
576 | |||
577 | /* set rate */ | ||
578 | if (memif->data->fs_shift < 0) | ||
579 | return 0; | ||
580 | if (memif->data->id == MTK_AFE_MEMIF_DAI || | ||
581 | memif->data->id == MTK_AFE_MEMIF_MOD_DAI) { | ||
582 | unsigned int val; | ||
583 | |||
584 | switch (params_rate(params)) { | ||
585 | case 8000: | ||
586 | val = 0; | ||
587 | break; | ||
588 | case 16000: | ||
589 | val = 1; | ||
590 | break; | ||
591 | case 32000: | ||
592 | val = 2; | ||
593 | break; | ||
594 | default: | ||
595 | return -EINVAL; | ||
596 | } | ||
597 | |||
598 | if (memif->data->id == MTK_AFE_MEMIF_DAI) | ||
599 | regmap_update_bits(afe->regmap, AFE_DAC_CON0, | ||
600 | 0x3 << memif->data->fs_shift, | ||
601 | val << memif->data->fs_shift); | ||
602 | else | ||
603 | regmap_update_bits(afe->regmap, AFE_DAC_CON1, | ||
604 | 0x3 << memif->data->fs_shift, | ||
605 | val << memif->data->fs_shift); | ||
606 | |||
607 | } else { | ||
608 | int fs = mtk_afe_i2s_fs(params_rate(params)); | ||
609 | |||
610 | if (fs < 0) | ||
611 | return -EINVAL; | ||
612 | |||
613 | regmap_update_bits(afe->regmap, AFE_DAC_CON1, | ||
614 | 0xf << memif->data->fs_shift, | ||
615 | fs << memif->data->fs_shift); | ||
616 | } | ||
617 | |||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream, | ||
622 | struct snd_soc_dai *dai) | ||
623 | { | ||
624 | return snd_pcm_lib_free_pages(substream); | ||
625 | } | ||
626 | |||
627 | static int mtk_afe_dais_prepare(struct snd_pcm_substream *substream, | ||
628 | struct snd_soc_dai *dai) | ||
629 | { | ||
630 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
631 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
632 | |||
633 | /* enable AFE */ | ||
634 | regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); | ||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd, | ||
639 | struct snd_soc_dai *dai) | ||
640 | { | ||
641 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
642 | struct snd_pcm_runtime * const runtime = substream->runtime; | ||
643 | struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); | ||
644 | struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; | ||
645 | unsigned int counter = runtime->period_size; | ||
646 | |||
647 | dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd); | ||
648 | |||
649 | switch (cmd) { | ||
650 | case SNDRV_PCM_TRIGGER_START: | ||
651 | case SNDRV_PCM_TRIGGER_RESUME: | ||
652 | if (memif->data->enable_shift >= 0) | ||
653 | regmap_update_bits(afe->regmap, AFE_DAC_CON0, | ||
654 | 1 << memif->data->enable_shift, | ||
655 | 1 << memif->data->enable_shift); | ||
656 | |||
657 | /* set irq counter */ | ||
658 | regmap_update_bits(afe->regmap, | ||
659 | memif->data->irq_reg_cnt, | ||
660 | 0x3ffff << memif->data->irq_cnt_shift, | ||
661 | counter << memif->data->irq_cnt_shift); | ||
662 | |||
663 | /* set irq fs */ | ||
664 | if (memif->data->irq_fs_shift >= 0) { | ||
665 | int fs = mtk_afe_i2s_fs(runtime->rate); | ||
666 | |||
667 | if (fs < 0) | ||
668 | return -EINVAL; | ||
669 | |||
670 | regmap_update_bits(afe->regmap, | ||
671 | AFE_IRQ_MCU_CON, | ||
672 | 0xf << memif->data->irq_fs_shift, | ||
673 | fs << memif->data->irq_fs_shift); | ||
674 | } | ||
675 | /* enable interrupt */ | ||
676 | regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, | ||
677 | 1 << memif->data->irq_en_shift, | ||
678 | 1 << memif->data->irq_en_shift); | ||
679 | |||
680 | return 0; | ||
681 | case SNDRV_PCM_TRIGGER_STOP: | ||
682 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
683 | if (memif->data->enable_shift >= 0) | ||
684 | regmap_update_bits(afe->regmap, AFE_DAC_CON0, | ||
685 | 1 << memif->data->enable_shift, 0); | ||
686 | /* disable interrupt */ | ||
687 | regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, | ||
688 | 1 << memif->data->irq_en_shift, | ||
689 | 0 << memif->data->irq_en_shift); | ||
690 | /* and clear pending IRQ */ | ||
691 | regmap_write(afe->regmap, AFE_IRQ_CLR, | ||
692 | 1 << memif->data->irq_clr_shift); | ||
693 | memif->hw_ptr = 0; | ||
694 | return 0; | ||
695 | default: | ||
696 | return -EINVAL; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | /* FE DAIs */ | ||
701 | static const struct snd_soc_dai_ops mtk_afe_dai_ops = { | ||
702 | .startup = mtk_afe_dais_startup, | ||
703 | .shutdown = mtk_afe_dais_shutdown, | ||
704 | .hw_params = mtk_afe_dais_hw_params, | ||
705 | .hw_free = mtk_afe_dais_hw_free, | ||
706 | .prepare = mtk_afe_dais_prepare, | ||
707 | .trigger = mtk_afe_dais_trigger, | ||
708 | }; | ||
709 | |||
710 | /* BE DAIs */ | ||
711 | static const struct snd_soc_dai_ops mtk_afe_i2s_ops = { | ||
712 | .startup = mtk_afe_i2s_startup, | ||
713 | .shutdown = mtk_afe_i2s_shutdown, | ||
714 | .prepare = mtk_afe_i2s_prepare, | ||
715 | }; | ||
716 | |||
717 | static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = { | ||
718 | .startup = mtk_afe_hdmi_startup, | ||
719 | .shutdown = mtk_afe_hdmi_shutdown, | ||
720 | .prepare = mtk_afe_hdmi_prepare, | ||
721 | .trigger = mtk_afe_hdmi_trigger, | ||
722 | |||
723 | }; | ||
724 | |||
725 | static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = { | ||
726 | /* FE DAIs: memory intefaces to CPU */ | ||
727 | { | ||
728 | .name = "DL1", /* downlink 1 */ | ||
729 | .id = MTK_AFE_MEMIF_DL1, | ||
730 | .playback = { | ||
731 | .stream_name = "DL1", | ||
732 | .channels_min = 1, | ||
733 | .channels_max = 2, | ||
734 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
735 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
736 | }, | ||
737 | .ops = &mtk_afe_dai_ops, | ||
738 | }, { | ||
739 | .name = "VUL", /* voice uplink */ | ||
740 | .id = MTK_AFE_MEMIF_VUL, | ||
741 | .capture = { | ||
742 | .stream_name = "VUL", | ||
743 | .channels_min = 1, | ||
744 | .channels_max = 2, | ||
745 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
746 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
747 | }, | ||
748 | .ops = &mtk_afe_dai_ops, | ||
749 | }, { | ||
750 | /* BE DAIs */ | ||
751 | .name = "I2S", | ||
752 | .id = MTK_AFE_IO_I2S, | ||
753 | .playback = { | ||
754 | .stream_name = "I2S Playback", | ||
755 | .channels_min = 1, | ||
756 | .channels_max = 2, | ||
757 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
758 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
759 | }, | ||
760 | .capture = { | ||
761 | .stream_name = "I2S Capture", | ||
762 | .channels_min = 1, | ||
763 | .channels_max = 2, | ||
764 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
765 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
766 | }, | ||
767 | .ops = &mtk_afe_i2s_ops, | ||
768 | .symmetric_rates = 1, | ||
769 | }, | ||
770 | }; | ||
771 | |||
772 | static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = { | ||
773 | /* FE DAIs */ | ||
774 | { | ||
775 | .name = "HDMI", | ||
776 | .id = MTK_AFE_MEMIF_HDMI, | ||
777 | .playback = { | ||
778 | .stream_name = "HDMI", | ||
779 | .channels_min = 2, | ||
780 | .channels_max = 8, | ||
781 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | | ||
782 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | | ||
783 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | | ||
784 | SNDRV_PCM_RATE_192000, | ||
785 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
786 | }, | ||
787 | .ops = &mtk_afe_dai_ops, | ||
788 | }, { | ||
789 | /* BE DAIs */ | ||
790 | .name = "HDMIO", | ||
791 | .id = MTK_AFE_IO_HDMI, | ||
792 | .playback = { | ||
793 | .stream_name = "HDMIO Playback", | ||
794 | .channels_min = 2, | ||
795 | .channels_max = 8, | ||
796 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | | ||
797 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | | ||
798 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | | ||
799 | SNDRV_PCM_RATE_192000, | ||
800 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
801 | }, | ||
802 | .ops = &mtk_afe_hdmi_ops, | ||
803 | }, | ||
804 | }; | ||
805 | |||
806 | static const struct snd_kcontrol_new mtk_afe_o03_mix[] = { | ||
807 | SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0), | ||
808 | }; | ||
809 | |||
810 | static const struct snd_kcontrol_new mtk_afe_o04_mix[] = { | ||
811 | SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0), | ||
812 | }; | ||
813 | |||
814 | static const struct snd_kcontrol_new mtk_afe_o09_mix[] = { | ||
815 | SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0), | ||
816 | }; | ||
817 | |||
818 | static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { | ||
819 | SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0), | ||
820 | }; | ||
821 | |||
822 | static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { | ||
823 | /* Backend DAIs */ | ||
824 | SND_SOC_DAPM_AIF_IN("I2S Capture", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
825 | SND_SOC_DAPM_AIF_OUT("I2S Playback", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
826 | |||
827 | /* inter-connections */ | ||
828 | SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
829 | SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
830 | SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
831 | SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
832 | |||
833 | SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, | ||
834 | mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)), | ||
835 | SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0, | ||
836 | mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)), | ||
837 | SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0, | ||
838 | mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)), | ||
839 | SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0, | ||
840 | mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)), | ||
841 | }; | ||
842 | |||
843 | static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { | ||
844 | {"I05", NULL, "DL1"}, | ||
845 | {"I06", NULL, "DL1"}, | ||
846 | {"I2S Playback", NULL, "O03"}, | ||
847 | {"I2S Playback", NULL, "O04"}, | ||
848 | {"VUL", NULL, "O09"}, | ||
849 | {"VUL", NULL, "O10"}, | ||
850 | {"I17", NULL, "I2S Capture"}, | ||
851 | {"I18", NULL, "I2S Capture"}, | ||
852 | { "O03", "I05 Switch", "I05" }, | ||
853 | { "O04", "I06 Switch", "I06" }, | ||
854 | { "O09", "I17 Switch", "I17" }, | ||
855 | { "O10", "I18 Switch", "I18" }, | ||
856 | }; | ||
857 | |||
858 | static const struct snd_soc_dapm_widget mtk_afe_hdmi_widgets[] = { | ||
859 | /* Backend DAIs */ | ||
860 | SND_SOC_DAPM_AIF_OUT("HDMIO Playback", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
861 | }; | ||
862 | |||
863 | static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { | ||
864 | {"HDMIO Playback", NULL, "HDMI"}, | ||
865 | }; | ||
866 | |||
867 | static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = { | ||
868 | .name = "mtk-afe-pcm-dai", | ||
869 | .dapm_widgets = mtk_afe_pcm_widgets, | ||
870 | .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets), | ||
871 | .dapm_routes = mtk_afe_pcm_routes, | ||
872 | .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes), | ||
873 | }; | ||
874 | |||
875 | static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = { | ||
876 | .name = "mtk-afe-hdmi-dai", | ||
877 | .dapm_widgets = mtk_afe_hdmi_widgets, | ||
878 | .num_dapm_widgets = ARRAY_SIZE(mtk_afe_hdmi_widgets), | ||
879 | .dapm_routes = mtk_afe_hdmi_routes, | ||
880 | .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes), | ||
881 | }; | ||
882 | |||
883 | static const char *aud_clks[MTK_CLK_NUM] = { | ||
884 | [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk", | ||
885 | [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio", | ||
886 | [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus", | ||
887 | [MTK_CLK_I2S0_M] = "i2s0_m", | ||
888 | [MTK_CLK_I2S1_M] = "i2s1_m", | ||
889 | [MTK_CLK_I2S2_M] = "i2s2_m", | ||
890 | [MTK_CLK_I2S3_M] = "i2s3_m", | ||
891 | [MTK_CLK_I2S3_B] = "i2s3_b", | ||
892 | [MTK_CLK_BCK0] = "bck0", | ||
893 | [MTK_CLK_BCK1] = "bck1", | ||
894 | }; | ||
895 | |||
896 | static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { | ||
897 | { | ||
898 | .name = "DL1", | ||
899 | .id = MTK_AFE_MEMIF_DL1, | ||
900 | .reg_ofs_base = AFE_DL1_BASE, | ||
901 | .reg_ofs_cur = AFE_DL1_CUR, | ||
902 | .fs_shift = 0, | ||
903 | .mono_shift = 21, | ||
904 | .enable_shift = 1, | ||
905 | .irq_reg_cnt = AFE_IRQ_CNT1, | ||
906 | .irq_cnt_shift = 0, | ||
907 | .irq_en_shift = 0, | ||
908 | .irq_fs_shift = 4, | ||
909 | .irq_clr_shift = 0, | ||
910 | }, { | ||
911 | .name = "DL2", | ||
912 | .id = MTK_AFE_MEMIF_DL2, | ||
913 | .reg_ofs_base = AFE_DL2_BASE, | ||
914 | .reg_ofs_cur = AFE_DL2_CUR, | ||
915 | .fs_shift = 4, | ||
916 | .mono_shift = 22, | ||
917 | .enable_shift = 2, | ||
918 | .irq_reg_cnt = AFE_IRQ_CNT1, | ||
919 | .irq_cnt_shift = 20, | ||
920 | .irq_en_shift = 2, | ||
921 | .irq_fs_shift = 16, | ||
922 | .irq_clr_shift = 2, | ||
923 | }, { | ||
924 | .name = "VUL", | ||
925 | .id = MTK_AFE_MEMIF_VUL, | ||
926 | .reg_ofs_base = AFE_VUL_BASE, | ||
927 | .reg_ofs_cur = AFE_VUL_CUR, | ||
928 | .fs_shift = 16, | ||
929 | .mono_shift = 27, | ||
930 | .enable_shift = 3, | ||
931 | .irq_reg_cnt = AFE_IRQ_CNT2, | ||
932 | .irq_cnt_shift = 0, | ||
933 | .irq_en_shift = 1, | ||
934 | .irq_fs_shift = 8, | ||
935 | .irq_clr_shift = 1, | ||
936 | }, { | ||
937 | .name = "DAI", | ||
938 | .id = MTK_AFE_MEMIF_DAI, | ||
939 | .reg_ofs_base = AFE_DAI_BASE, | ||
940 | .reg_ofs_cur = AFE_DAI_CUR, | ||
941 | .fs_shift = 24, | ||
942 | .mono_shift = -1, | ||
943 | .enable_shift = 4, | ||
944 | .irq_reg_cnt = AFE_IRQ_CNT2, | ||
945 | .irq_cnt_shift = 20, | ||
946 | .irq_en_shift = 3, | ||
947 | .irq_fs_shift = 20, | ||
948 | .irq_clr_shift = 3, | ||
949 | }, { | ||
950 | .name = "AWB", | ||
951 | .id = MTK_AFE_MEMIF_AWB, | ||
952 | .reg_ofs_base = AFE_AWB_BASE, | ||
953 | .reg_ofs_cur = AFE_AWB_CUR, | ||
954 | .fs_shift = 12, | ||
955 | .mono_shift = 24, | ||
956 | .enable_shift = 6, | ||
957 | .irq_reg_cnt = AFE_IRQ_CNT7, | ||
958 | .irq_cnt_shift = 0, | ||
959 | .irq_en_shift = 14, | ||
960 | .irq_fs_shift = 24, | ||
961 | .irq_clr_shift = 6, | ||
962 | }, { | ||
963 | .name = "MOD_DAI", | ||
964 | .id = MTK_AFE_MEMIF_MOD_DAI, | ||
965 | .reg_ofs_base = AFE_MOD_PCM_BASE, | ||
966 | .reg_ofs_cur = AFE_MOD_PCM_CUR, | ||
967 | .fs_shift = 30, | ||
968 | .mono_shift = 30, | ||
969 | .enable_shift = 7, | ||
970 | .irq_reg_cnt = AFE_IRQ_CNT2, | ||
971 | .irq_cnt_shift = 20, | ||
972 | .irq_en_shift = 3, | ||
973 | .irq_fs_shift = 20, | ||
974 | .irq_clr_shift = 3, | ||
975 | }, { | ||
976 | .name = "HDMI", | ||
977 | .id = MTK_AFE_MEMIF_HDMI, | ||
978 | .reg_ofs_base = AFE_HDMI_OUT_BASE, | ||
979 | .reg_ofs_cur = AFE_HDMI_OUT_CUR, | ||
980 | .fs_shift = -1, | ||
981 | .mono_shift = -1, | ||
982 | .enable_shift = -1, | ||
983 | .irq_reg_cnt = AFE_IRQ_CNT5, | ||
984 | .irq_cnt_shift = 0, | ||
985 | .irq_en_shift = 12, | ||
986 | .irq_fs_shift = -1, | ||
987 | .irq_clr_shift = 4, | ||
988 | }, | ||
989 | }; | ||
990 | |||
991 | static const struct regmap_config mtk_afe_regmap_config = { | ||
992 | .reg_bits = 32, | ||
993 | .reg_stride = 4, | ||
994 | .val_bits = 32, | ||
995 | .max_register = AFE_ADDA2_TOP_CON0, | ||
996 | .cache_type = REGCACHE_NONE, | ||
997 | }; | ||
998 | |||
999 | static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id) | ||
1000 | { | ||
1001 | struct mtk_afe *afe = dev_id; | ||
1002 | unsigned int reg_value, hw_ptr; | ||
1003 | int i, ret; | ||
1004 | |||
1005 | ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value); | ||
1006 | if (ret) { | ||
1007 | dev_err(afe->dev, "%s irq status err\n", __func__); | ||
1008 | reg_value = AFE_IRQ_STATUS_BITS; | ||
1009 | goto err_irq; | ||
1010 | } | ||
1011 | |||
1012 | for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) { | ||
1013 | struct mtk_afe_memif *memif = &afe->memif[i]; | ||
1014 | |||
1015 | if (!(reg_value & (1 << memif->data->irq_clr_shift))) | ||
1016 | continue; | ||
1017 | |||
1018 | ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, | ||
1019 | &hw_ptr); | ||
1020 | if (ret || hw_ptr == 0) { | ||
1021 | dev_err(afe->dev, "%s hw_ptr err\n", __func__); | ||
1022 | hw_ptr = memif->phys_buf_addr; | ||
1023 | } | ||
1024 | memif->hw_ptr = hw_ptr - memif->phys_buf_addr; | ||
1025 | snd_pcm_period_elapsed(memif->substream); | ||
1026 | } | ||
1027 | |||
1028 | err_irq: | ||
1029 | /* clear irq */ | ||
1030 | regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS); | ||
1031 | |||
1032 | return IRQ_HANDLED; | ||
1033 | } | ||
1034 | |||
1035 | static int mtk_afe_runtime_suspend(struct device *dev) | ||
1036 | { | ||
1037 | struct mtk_afe *afe = dev_get_drvdata(dev); | ||
1038 | |||
1039 | /* disable AFE clk */ | ||
1040 | regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, | ||
1041 | AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE); | ||
1042 | |||
1043 | clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); | ||
1044 | clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]); | ||
1045 | clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); | ||
1046 | clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); | ||
1047 | clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); | ||
1048 | return 0; | ||
1049 | } | ||
1050 | |||
1051 | static int mtk_afe_runtime_resume(struct device *dev) | ||
1052 | { | ||
1053 | struct mtk_afe *afe = dev_get_drvdata(dev); | ||
1054 | int ret; | ||
1055 | |||
1056 | ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]); | ||
1057 | if (ret) | ||
1058 | return ret; | ||
1059 | |||
1060 | ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); | ||
1061 | if (ret) | ||
1062 | goto err_infra; | ||
1063 | |||
1064 | ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]); | ||
1065 | if (ret) | ||
1066 | goto err_top_aud_bus; | ||
1067 | |||
1068 | ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]); | ||
1069 | if (ret) | ||
1070 | goto err_top_aud; | ||
1071 | |||
1072 | ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]); | ||
1073 | if (ret) | ||
1074 | goto err_bck0; | ||
1075 | |||
1076 | /* enable AFE clk */ | ||
1077 | regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0); | ||
1078 | |||
1079 | /* set O3/O4 16bits */ | ||
1080 | regmap_update_bits(afe->regmap, AFE_CONN_24BIT, | ||
1081 | AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0); | ||
1082 | |||
1083 | /* unmask all IRQs */ | ||
1084 | regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff); | ||
1085 | return 0; | ||
1086 | |||
1087 | err_bck0: | ||
1088 | clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); | ||
1089 | err_top_aud: | ||
1090 | clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); | ||
1091 | err_top_aud_bus: | ||
1092 | clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); | ||
1093 | err_infra: | ||
1094 | clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); | ||
1095 | return ret; | ||
1096 | } | ||
1097 | |||
1098 | static int mtk_afe_init_audio_clk(struct mtk_afe *afe) | ||
1099 | { | ||
1100 | size_t i; | ||
1101 | |||
1102 | for (i = 0; i < ARRAY_SIZE(aud_clks); i++) { | ||
1103 | afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]); | ||
1104 | if (IS_ERR(afe->clocks[i])) { | ||
1105 | dev_err(afe->dev, "%s devm_clk_get %s fail\n", | ||
1106 | __func__, aud_clks[i]); | ||
1107 | return PTR_ERR(afe->clocks[i]); | ||
1108 | } | ||
1109 | } | ||
1110 | clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */ | ||
1111 | clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */ | ||
1112 | return 0; | ||
1113 | } | ||
1114 | |||
1115 | static int mtk_afe_pcm_dev_probe(struct platform_device *pdev) | ||
1116 | { | ||
1117 | int ret, i; | ||
1118 | unsigned int irq_id; | ||
1119 | struct mtk_afe *afe; | ||
1120 | struct resource *res; | ||
1121 | |||
1122 | afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); | ||
1123 | if (!afe) | ||
1124 | return -ENOMEM; | ||
1125 | |||
1126 | afe->dev = &pdev->dev; | ||
1127 | |||
1128 | irq_id = platform_get_irq(pdev, 0); | ||
1129 | if (!irq_id) { | ||
1130 | dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); | ||
1131 | return -ENXIO; | ||
1132 | } | ||
1133 | ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler, | ||
1134 | 0, "Afe_ISR_Handle", (void *)afe); | ||
1135 | if (ret) { | ||
1136 | dev_err(afe->dev, "could not request_irq\n"); | ||
1137 | return ret; | ||
1138 | } | ||
1139 | |||
1140 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1141 | afe->base_addr = devm_ioremap_resource(&pdev->dev, res); | ||
1142 | if (IS_ERR(afe->base_addr)) | ||
1143 | return PTR_ERR(afe->base_addr); | ||
1144 | |||
1145 | afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, | ||
1146 | &mtk_afe_regmap_config); | ||
1147 | if (IS_ERR(afe->regmap)) | ||
1148 | return PTR_ERR(afe->regmap); | ||
1149 | |||
1150 | /* initial audio related clock */ | ||
1151 | ret = mtk_afe_init_audio_clk(afe); | ||
1152 | if (ret) { | ||
1153 | dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n"); | ||
1154 | return ret; | ||
1155 | } | ||
1156 | |||
1157 | for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) | ||
1158 | afe->memif[i].data = &memif_data[i]; | ||
1159 | |||
1160 | platform_set_drvdata(pdev, afe); | ||
1161 | |||
1162 | pm_runtime_enable(&pdev->dev); | ||
1163 | if (!pm_runtime_enabled(&pdev->dev)) { | ||
1164 | ret = mtk_afe_runtime_resume(&pdev->dev); | ||
1165 | if (ret) | ||
1166 | goto err_pm_disable; | ||
1167 | } | ||
1168 | |||
1169 | ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform); | ||
1170 | if (ret) | ||
1171 | goto err_pm_disable; | ||
1172 | |||
1173 | ret = snd_soc_register_component(&pdev->dev, | ||
1174 | &mtk_afe_pcm_dai_component, | ||
1175 | mtk_afe_pcm_dais, | ||
1176 | ARRAY_SIZE(mtk_afe_pcm_dais)); | ||
1177 | if (ret) | ||
1178 | goto err_platform; | ||
1179 | |||
1180 | ret = snd_soc_register_component(&pdev->dev, | ||
1181 | &mtk_afe_hdmi_dai_component, | ||
1182 | mtk_afe_hdmi_dais, | ||
1183 | ARRAY_SIZE(mtk_afe_hdmi_dais)); | ||
1184 | if (ret) | ||
1185 | goto err_comp; | ||
1186 | |||
1187 | dev_info(&pdev->dev, "MTK AFE driver initialized.\n"); | ||
1188 | return 0; | ||
1189 | |||
1190 | err_comp: | ||
1191 | snd_soc_unregister_component(&pdev->dev); | ||
1192 | err_platform: | ||
1193 | snd_soc_unregister_platform(&pdev->dev); | ||
1194 | err_pm_disable: | ||
1195 | pm_runtime_disable(&pdev->dev); | ||
1196 | return ret; | ||
1197 | } | ||
1198 | |||
1199 | static int mtk_afe_pcm_dev_remove(struct platform_device *pdev) | ||
1200 | { | ||
1201 | pm_runtime_disable(&pdev->dev); | ||
1202 | snd_soc_unregister_component(&pdev->dev); | ||
1203 | snd_soc_unregister_platform(&pdev->dev); | ||
1204 | return 0; | ||
1205 | } | ||
1206 | |||
1207 | static const struct of_device_id mtk_afe_pcm_dt_match[] = { | ||
1208 | { .compatible = "mediatek,mt8173-afe-pcm", }, | ||
1209 | { } | ||
1210 | }; | ||
1211 | MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match); | ||
1212 | |||
1213 | static const struct dev_pm_ops mtk_afe_pm_ops = { | ||
1214 | SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume, | ||
1215 | NULL) | ||
1216 | }; | ||
1217 | |||
1218 | static struct platform_driver mtk_afe_pcm_driver = { | ||
1219 | .driver = { | ||
1220 | .name = "mtk-afe-pcm", | ||
1221 | .owner = THIS_MODULE, | ||
1222 | .of_match_table = mtk_afe_pcm_dt_match, | ||
1223 | .pm = &mtk_afe_pm_ops, | ||
1224 | }, | ||
1225 | .probe = mtk_afe_pcm_dev_probe, | ||
1226 | .remove = mtk_afe_pcm_dev_remove, | ||
1227 | }; | ||
1228 | |||
1229 | module_platform_driver(mtk_afe_pcm_driver); | ||
1230 | |||
1231 | MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver"); | ||
1232 | MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); | ||
1233 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index fded99362d39..3bebfb1d3a6f 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c | |||
@@ -245,6 +245,8 @@ static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { | |||
245 | static const struct snd_soc_dapm_route audio_map[] = { | 245 | static const struct snd_soc_dapm_route audio_map[] = { |
246 | {"Ext Spk", NULL, "HPLOUT"}, | 246 | {"Ext Spk", NULL, "HPLOUT"}, |
247 | {"Ext Spk", NULL, "HPROUT"}, | 247 | {"Ext Spk", NULL, "HPROUT"}, |
248 | {"Ext Spk", NULL, "HPLCOM"}, | ||
249 | {"Ext Spk", NULL, "HPRCOM"}, | ||
248 | {"Headphone Jack", NULL, "LLOUT"}, | 250 | {"Headphone Jack", NULL, "LLOUT"}, |
249 | {"Headphone Jack", NULL, "RLOUT"}, | 251 | {"Headphone Jack", NULL, "RLOUT"}, |
250 | {"FM Transmitter", NULL, "LLOUT"}, | 252 | {"FM Transmitter", NULL, "LLOUT"}, |
@@ -288,15 +290,8 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) | |||
288 | struct snd_soc_codec *codec = rtd->codec; | 290 | struct snd_soc_codec *codec = rtd->codec; |
289 | struct snd_soc_card *card = rtd->card; | 291 | struct snd_soc_card *card = rtd->card; |
290 | struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); | 292 | struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); |
291 | |||
292 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
293 | int err; | 293 | int err; |
294 | 294 | ||
295 | /* Set up NC codec pins */ | ||
296 | snd_soc_dapm_nc_pin(dapm, "MIC3L"); | ||
297 | snd_soc_dapm_nc_pin(dapm, "MIC3R"); | ||
298 | snd_soc_dapm_nc_pin(dapm, "LINE1R"); | ||
299 | |||
300 | err = tpa6130a2_add_controls(codec); | 295 | err = tpa6130a2_add_controls(codec); |
301 | if (err < 0) { | 296 | if (err < 0) { |
302 | dev_err(card->dev, "Failed to add TPA6130A2 controls\n"); | 297 | dev_err(card->dev, "Failed to add TPA6130A2 controls\n"); |
@@ -383,6 +378,7 @@ static struct snd_soc_card rx51_sound_card = { | |||
383 | .num_aux_devs = ARRAY_SIZE(rx51_aux_dev), | 378 | .num_aux_devs = ARRAY_SIZE(rx51_aux_dev), |
384 | .codec_conf = rx51_codec_conf, | 379 | .codec_conf = rx51_codec_conf, |
385 | .num_configs = ARRAY_SIZE(rx51_codec_conf), | 380 | .num_configs = ARRAY_SIZE(rx51_codec_conf), |
381 | .fully_routed = true, | ||
386 | 382 | ||
387 | .controls = aic34_rx51_controls, | 383 | .controls = aic34_rx51_controls, |
388 | .num_controls = ARRAY_SIZE(aic34_rx51_controls), | 384 | .num_controls = ARRAY_SIZE(aic34_rx51_controls), |