[alsa-devel] [RFC PATCH 0/4] ASoC: Mediatek: Add support for MT2701 SOC
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 finish yet, but as an upstream rookie I really need some comments. Any comments will be greatly appreciated.
Garlic Tseng (4): ASoC: mediatek: Refine mt8173 driver ASoC: mediatek: add documents for mt2701 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-max98090.c | 213 --- sound/soc/mediatek/mt8173-rt5650-rt5514.c | 258 --- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 288 ---- sound/soc/mediatek/mt8173-rt5650.c | 236 --- sound/soc/mediatek/mt8173/Makefile | 7 + sound/soc/mediatek/mt8173/mt8173-max98090.c | 213 +++ sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 258 +++ sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c | 288 ++++ sound/soc/mediatek/mt8173/mt8173-rt5650.c | 236 +++ sound/soc/mediatek/mt8173/mtk-afe-common.h | 101 ++ sound/soc/mediatek/mt8173/mtk-afe-pcm.c | 1333 +++++++++++++++ sound/soc/mediatek/mtk-afe-common.h | 101 -- sound/soc/mediatek/mtk-afe-pcm.c | 1333 --------------- 26 files changed, 5610 insertions(+), 2436 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 delete mode 100644 sound/soc/mediatek/mt8173-max98090.c delete mode 100644 sound/soc/mediatek/mt8173-rt5650-rt5514.c delete mode 100644 sound/soc/mediatek/mt8173-rt5650-rt5676.c delete mode 100644 sound/soc/mediatek/mt8173-rt5650.c create mode 100644 sound/soc/mediatek/mt8173/Makefile create mode 100644 sound/soc/mediatek/mt8173/mt8173-max98090.c create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650.c create mode 100644 sound/soc/mediatek/mt8173/mtk-afe-common.h create mode 100644 sound/soc/mediatek/mt8173/mtk-afe-pcm.c delete mode 100644 sound/soc/mediatek/mtk-afe-common.h delete mode 100644 sound/soc/mediatek/mtk-afe-pcm.c
Move mt8173 driver to another folder. The software control sequence of mt2701 are very different from that of mt8173, so this patch move mt8173 code to another folder
Signed-off-by: Garlic Tseng garlic.tseng@mediatek.com --- sound/soc/mediatek/Makefile | 12 +- sound/soc/mediatek/mt8173-max98090.c | 213 ---- sound/soc/mediatek/mt8173-rt5650-rt5514.c | 258 ----- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 288 ----- sound/soc/mediatek/mt8173-rt5650.c | 236 ---- sound/soc/mediatek/mt8173/Makefile | 7 + sound/soc/mediatek/mt8173/mt8173-max98090.c | 213 ++++ sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 258 +++++ sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c | 288 +++++ sound/soc/mediatek/mt8173/mt8173-rt5650.c | 236 ++++ sound/soc/mediatek/mt8173/mtk-afe-common.h | 101 ++ sound/soc/mediatek/mt8173/mtk-afe-pcm.c | 1333 ++++++++++++++++++++++ sound/soc/mediatek/mtk-afe-common.h | 101 -- sound/soc/mediatek/mtk-afe-pcm.c | 1333 ---------------------- 14 files changed, 2441 insertions(+), 2436 deletions(-) delete mode 100644 sound/soc/mediatek/mt8173-max98090.c delete mode 100644 sound/soc/mediatek/mt8173-rt5650-rt5514.c delete mode 100644 sound/soc/mediatek/mt8173-rt5650-rt5676.c delete mode 100644 sound/soc/mediatek/mt8173-rt5650.c create mode 100644 sound/soc/mediatek/mt8173/Makefile create mode 100644 sound/soc/mediatek/mt8173/mt8173-max98090.c create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650.c create mode 100644 sound/soc/mediatek/mt8173/mtk-afe-common.h create mode 100644 sound/soc/mediatek/mt8173/mtk-afe-pcm.c delete mode 100644 sound/soc/mediatek/mtk-afe-common.h delete mode 100644 sound/soc/mediatek/mtk-afe-pcm.c
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-max98090.c b/sound/soc/mediatek/mt8173-max98090.c deleted file mode 100644 index 71a1a35..0000000 --- a/sound/soc/mediatek/mt8173-max98090.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver - * - * Copyright (c) 2015 MediaTek Inc. - * Author: Koro Chen koro.chen@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 <sound/jack.h> -#include <linux/gpio.h> -#include "../codecs/max98090.h" - -static struct snd_soc_jack mt8173_max98090_jack; - -static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { - { - .pin = "Headphone", - .mask = SND_JACK_HEADPHONE, - }, - { - .pin = "Headset Mic", - .mask = SND_JACK_MICROPHONE, - }, -}; - -static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), -}; - -static const struct snd_soc_dapm_route mt8173_max98090_routes[] = { - {"Speaker", NULL, "SPKL"}, - {"Speaker", NULL, "SPKR"}, - {"DMICL", NULL, "Int Mic"}, - {"Headphone", NULL, "HPL"}, - {"Headphone", NULL, "HPR"}, - {"Headset Mic", NULL, "MICBIAS"}, - {"IN34", NULL, "Headset Mic"}, -}; - -static const struct snd_kcontrol_new mt8173_max98090_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), - SOC_DAPM_PIN_SWITCH("Int Mic"), - SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), -}; - -static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, - SND_SOC_CLOCK_IN); -} - -static struct snd_soc_ops mt8173_max98090_ops = { - .hw_params = mt8173_max98090_hw_params, -}; - -static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) -{ - int ret; - struct snd_soc_card *card = runtime->card; - struct snd_soc_codec *codec = runtime->codec; - - /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, - &mt8173_max98090_jack, NULL, 0); - if (ret) { - dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret); - return ret; - } - - ret = snd_soc_jack_add_pins(&mt8173_max98090_jack, - ARRAY_SIZE(mt8173_max98090_jack_pins), - mt8173_max98090_jack_pins); - if (ret) { - dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret); - return ret; - } - - return max98090_mic_detect(codec, &mt8173_max98090_jack); -} - -/* Digital audio interface glue - connects codec <---> CPU */ -static struct snd_soc_dai_link mt8173_max98090_dais[] = { - /* Front End DAI links */ - { - .name = "MAX98090 Playback", - .stream_name = "MAX98090 Playback", - .cpu_dai_name = "DL1", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dynamic = 1, - .dpcm_playback = 1, - }, - { - .name = "MAX98090 Capture", - .stream_name = "MAX98090 Capture", - .cpu_dai_name = "VUL", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dynamic = 1, - .dpcm_capture = 1, - }, - /* Back End DAI links */ - { - .name = "Codec", - .cpu_dai_name = "I2S", - .no_pcm = 1, - .codec_dai_name = "HiFi", - .init = mt8173_max98090_init, - .ops = &mt8173_max98090_ops, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, -}; - -static struct snd_soc_card mt8173_max98090_card = { - .name = "mt8173-max98090", - .owner = THIS_MODULE, - .dai_link = mt8173_max98090_dais, - .num_links = ARRAY_SIZE(mt8173_max98090_dais), - .controls = mt8173_max98090_controls, - .num_controls = ARRAY_SIZE(mt8173_max98090_controls), - .dapm_widgets = mt8173_max98090_widgets, - .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), - .dapm_routes = mt8173_max98090_routes, - .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), -}; - -static int mt8173_max98090_dev_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &mt8173_max98090_card; - struct device_node *codec_node, *platform_node; - int ret, i; - - 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 (mt8173_max98090_dais[i].platform_name) - continue; - mt8173_max98090_dais[i].platform_of_node = platform_node; - } - - codec_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,audio-codec", 0); - if (!codec_node) { - dev_err(&pdev->dev, - "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; - } - for (i = 0; i < card->num_links; i++) { - if (mt8173_max98090_dais[i].codec_name) - continue; - mt8173_max98090_dais[i].codec_of_node = codec_node; - } - card->dev = &pdev->dev; - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); - return ret; -} - -static const struct of_device_id mt8173_max98090_dt_match[] = { - { .compatible = "mediatek,mt8173-max98090", }, - { } -}; -MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); - -static struct platform_driver mt8173_max98090_driver = { - .driver = { - .name = "mt8173-max98090", - .of_match_table = mt8173_max98090_dt_match, -#ifdef CONFIG_PM - .pm = &snd_soc_pm_ops, -#endif - }, - .probe = mt8173_max98090_dev_probe, -}; - -module_platform_driver(mt8173_max98090_driver); - -/* Module information */ -MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); -MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:mt8173-max98090"); - diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173-rt5650-rt5514.c deleted file mode 100644 index 58e0836..0000000 --- a/sound/soc/mediatek/mt8173-rt5650-rt5514.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * mt8173-rt5650-rt5514.c -- MT8173 machine driver with RT5650/5514 codecs - * - * Copyright (c) 2016 MediaTek Inc. - * Author: Koro Chen koro.chen@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/gpio.h> -#include <linux/of_gpio.h> -#include <sound/soc.h> -#include <sound/jack.h> -#include "../codecs/rt5645.h" - -#define MCLK_FOR_CODECS 12288000 - -static const struct snd_soc_dapm_widget mt8173_rt5650_rt5514_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), -}; - -static const struct snd_soc_dapm_route mt8173_rt5650_rt5514_routes[] = { - {"Speaker", NULL, "SPOL"}, - {"Speaker", NULL, "SPOR"}, - {"Sub DMIC1L", NULL, "Int Mic"}, - {"Sub DMIC1R", NULL, "Int Mic"}, - {"Headphone", NULL, "HPOL"}, - {"Headphone", NULL, "HPOR"}, - {"Headset Mic", NULL, "micbias1"}, - {"Headset Mic", NULL, "micbias2"}, - {"IN1P", NULL, "Headset Mic"}, - {"IN1N", NULL, "Headset Mic"}, -}; - -static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), - SOC_DAPM_PIN_SWITCH("Int Mic"), - SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), -}; - -static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - int i, ret; - - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - - /* pll from mclk 12.288M */ - ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, - params_rate(params) * 512); - if (ret) - return ret; - - /* sysclk from pll */ - ret = snd_soc_dai_set_sysclk(codec_dai, 1, - params_rate(params) * 512, - SND_SOC_CLOCK_IN); - if (ret) - return ret; - } - return 0; -} - -static struct snd_soc_ops mt8173_rt5650_rt5514_ops = { - .hw_params = mt8173_rt5650_rt5514_hw_params, -}; - -static struct snd_soc_jack mt8173_rt5650_rt5514_jack; - -static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime) -{ - struct snd_soc_card *card = runtime->card; - struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; - int ret; - - rt5645_sel_asrc_clk_src(codec, - RT5645_DA_STEREO_FILTER | - RT5645_AD_STEREO_FILTER, - RT5645_CLK_SEL_I2S1_ASRC); - - /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_rt5514_jack, NULL, 0); - if (ret) { - dev_err(card->dev, "Can't new Headset Jack %d\n", ret); - return ret; - } - - return rt5645_set_jack_detect(codec, - &mt8173_rt5650_rt5514_jack, - &mt8173_rt5650_rt5514_jack, - &mt8173_rt5650_rt5514_jack); -} - -static struct snd_soc_dai_link_component mt8173_rt5650_rt5514_codecs[] = { - { - .dai_name = "rt5645-aif1", - }, - { - .dai_name = "rt5514-aif1", - }, -}; - -enum { - DAI_LINK_PLAYBACK, - DAI_LINK_CAPTURE, - DAI_LINK_CODEC_I2S, -}; - -/* Digital audio interface glue - connects codec <---> CPU */ -static struct snd_soc_dai_link mt8173_rt5650_rt5514_dais[] = { - /* Front End DAI links */ - [DAI_LINK_PLAYBACK] = { - .name = "rt5650_rt5514 Playback", - .stream_name = "rt5650_rt5514 Playback", - .cpu_dai_name = "DL1", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dynamic = 1, - .dpcm_playback = 1, - }, - [DAI_LINK_CAPTURE] = { - .name = "rt5650_rt5514 Capture", - .stream_name = "rt5650_rt5514 Capture", - .cpu_dai_name = "VUL", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dynamic = 1, - .dpcm_capture = 1, - }, - /* Back End DAI links */ - [DAI_LINK_CODEC_I2S] = { - .name = "Codec", - .cpu_dai_name = "I2S", - .no_pcm = 1, - .codecs = mt8173_rt5650_rt5514_codecs, - .num_codecs = 2, - .init = mt8173_rt5650_rt5514_init, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .ops = &mt8173_rt5650_rt5514_ops, - .ignore_pmdown_time = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, -}; - -static struct snd_soc_codec_conf mt8173_rt5650_rt5514_codec_conf[] = { - { - .name_prefix = "Sub", - }, -}; - -static struct snd_soc_card mt8173_rt5650_rt5514_card = { - .name = "mtk-rt5650-rt5514", - .owner = THIS_MODULE, - .dai_link = mt8173_rt5650_rt5514_dais, - .num_links = ARRAY_SIZE(mt8173_rt5650_rt5514_dais), - .codec_conf = mt8173_rt5650_rt5514_codec_conf, - .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5514_codec_conf), - .controls = mt8173_rt5650_rt5514_controls, - .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5514_controls), - .dapm_widgets = mt8173_rt5650_rt5514_widgets, - .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5514_widgets), - .dapm_routes = mt8173_rt5650_rt5514_routes, - .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5514_routes), -}; - -static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &mt8173_rt5650_rt5514_card; - struct device_node *platform_node; - int i, ret; - - 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 (mt8173_rt5650_rt5514_dais[i].platform_name) - continue; - mt8173_rt5650_rt5514_dais[i].platform_of_node = platform_node; - } - - mt8173_rt5650_rt5514_codecs[0].of_node = - of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); - if (!mt8173_rt5650_rt5514_codecs[0].of_node) { - dev_err(&pdev->dev, - "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; - } - mt8173_rt5650_rt5514_codecs[1].of_node = - of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); - if (!mt8173_rt5650_rt5514_codecs[1].of_node) { - dev_err(&pdev->dev, - "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; - } - mt8173_rt5650_rt5514_codec_conf[0].of_node = - mt8173_rt5650_rt5514_codecs[1].of_node; - - card->dev = &pdev->dev; - platform_set_drvdata(pdev, card); - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); - return ret; -} - -static const struct of_device_id mt8173_rt5650_rt5514_dt_match[] = { - { .compatible = "mediatek,mt8173-rt5650-rt5514", }, - { } -}; -MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5514_dt_match); - -static struct platform_driver mt8173_rt5650_rt5514_driver = { - .driver = { - .name = "mtk-rt5650-rt5514", - .of_match_table = mt8173_rt5650_rt5514_dt_match, -#ifdef CONFIG_PM - .pm = &snd_soc_pm_ops, -#endif - }, - .probe = mt8173_rt5650_rt5514_dev_probe, -}; - -module_platform_driver(mt8173_rt5650_rt5514_driver); - -/* Module information */ -MODULE_DESCRIPTION("MT8173 RT5650 and RT5514 SoC machine driver"); -MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:mtk-rt5650-rt5514"); - diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c deleted file mode 100644 index 5c4c58c..0000000 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs - * - * Copyright (c) 2015 MediaTek Inc. - * Author: Koro Chen koro.chen@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/gpio.h> -#include <linux/of_gpio.h> -#include <sound/soc.h> -#include <sound/jack.h> -#include "../codecs/rt5645.h" -#include "../codecs/rt5677.h" - -#define MCLK_FOR_CODECS 12288000 - -static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), -}; - -static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = { - {"Speaker", NULL, "SPOL"}, - {"Speaker", NULL, "SPOR"}, - {"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ - {"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */ - {"Sub DMIC R1", NULL, "Int Mic"}, - {"Headphone", NULL, "HPOL"}, - {"Headphone", NULL, "HPOR"}, - {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ - {"Headset Mic", NULL, "micbias1"}, - {"Headset Mic", NULL, "micbias2"}, - {"IN1P", NULL, "Headset Mic"}, - {"IN1N", NULL, "Headset Mic"}, - {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */ -}; - -static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), - SOC_DAPM_PIN_SWITCH("Int Mic"), - SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), -}; - -static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - int i, ret; - - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - - /* pll from mclk 12.288M */ - ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, - params_rate(params) * 512); - if (ret) - return ret; - - /* sysclk from pll */ - ret = snd_soc_dai_set_sysclk(codec_dai, 1, - params_rate(params) * 512, - SND_SOC_CLOCK_IN); - if (ret) - return ret; - } - return 0; -} - -static struct snd_soc_ops mt8173_rt5650_rt5676_ops = { - .hw_params = mt8173_rt5650_rt5676_hw_params, -}; - -static struct snd_soc_jack mt8173_rt5650_rt5676_jack; - -static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) -{ - struct snd_soc_card *card = runtime->card; - struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; - struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec; - int ret; - - rt5645_sel_asrc_clk_src(codec, - RT5645_DA_STEREO_FILTER | - RT5645_AD_STEREO_FILTER, - RT5645_CLK_SEL_I2S1_ASRC); - rt5677_sel_asrc_clk_src(codec_sub, - RT5677_DA_STEREO_FILTER | - RT5677_AD_STEREO1_FILTER, - RT5677_CLK_SEL_I2S1_ASRC); - rt5677_sel_asrc_clk_src(codec_sub, - RT5677_AD_STEREO2_FILTER | - RT5677_I2S2_SOURCE, - RT5677_CLK_SEL_I2S2_ASRC); - - /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_rt5676_jack, NULL, 0); - if (ret) { - dev_err(card->dev, "Can't new Headset Jack %d\n", ret); - return ret; - } - - return rt5645_set_jack_detect(codec, - &mt8173_rt5650_rt5676_jack, - &mt8173_rt5650_rt5676_jack, - &mt8173_rt5650_rt5676_jack); -} - -static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { - { - .dai_name = "rt5645-aif1", - }, - { - .dai_name = "rt5677-aif1", - }, -}; - -enum { - DAI_LINK_PLAYBACK, - DAI_LINK_CAPTURE, - DAI_LINK_CODEC_I2S, - DAI_LINK_INTERCODEC -}; - -/* Digital audio interface glue - connects codec <---> CPU */ -static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { - /* Front End DAI links */ - [DAI_LINK_PLAYBACK] = { - .name = "rt5650_rt5676 Playback", - .stream_name = "rt5650_rt5676 Playback", - .cpu_dai_name = "DL1", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dynamic = 1, - .dpcm_playback = 1, - }, - [DAI_LINK_CAPTURE] = { - .name = "rt5650_rt5676 Capture", - .stream_name = "rt5650_rt5676 Capture", - .cpu_dai_name = "VUL", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dynamic = 1, - .dpcm_capture = 1, - }, - - /* Back End DAI links */ - [DAI_LINK_CODEC_I2S] = { - .name = "Codec", - .cpu_dai_name = "I2S", - .no_pcm = 1, - .codecs = mt8173_rt5650_rt5676_codecs, - .num_codecs = 2, - .init = mt8173_rt5650_rt5676_init, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .ops = &mt8173_rt5650_rt5676_ops, - .ignore_pmdown_time = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, - /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ - [DAI_LINK_INTERCODEC] = { - .name = "rt5650_rt5676 intercodec", - .stream_name = "rt5650_rt5676 intercodec", - .cpu_dai_name = "snd-soc-dummy-dai", - .platform_name = "snd-soc-dummy", - .no_pcm = 1, - .codec_dai_name = "rt5677-aif2", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - }, - -}; - -static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = { - { - .name_prefix = "Sub", - }, -}; - -static struct snd_soc_card mt8173_rt5650_rt5676_card = { - .name = "mtk-rt5650-rt5676", - .owner = THIS_MODULE, - .dai_link = mt8173_rt5650_rt5676_dais, - .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais), - .codec_conf = mt8173_rt5650_rt5676_codec_conf, - .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf), - .controls = mt8173_rt5650_rt5676_controls, - .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls), - .dapm_widgets = mt8173_rt5650_rt5676_widgets, - .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets), - .dapm_routes = mt8173_rt5650_rt5676_routes, - .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes), -}; - -static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &mt8173_rt5650_rt5676_card; - struct device_node *platform_node; - int i, ret; - - 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 (mt8173_rt5650_rt5676_dais[i].platform_name) - continue; - mt8173_rt5650_rt5676_dais[i].platform_of_node = platform_node; - } - - mt8173_rt5650_rt5676_codecs[0].of_node = - of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); - if (!mt8173_rt5650_rt5676_codecs[0].of_node) { - dev_err(&pdev->dev, - "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; - } - mt8173_rt5650_rt5676_codecs[1].of_node = - of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); - if (!mt8173_rt5650_rt5676_codecs[1].of_node) { - dev_err(&pdev->dev, - "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; - } - mt8173_rt5650_rt5676_codec_conf[0].of_node = - mt8173_rt5650_rt5676_codecs[1].of_node; - - mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node = - mt8173_rt5650_rt5676_codecs[1].of_node; - - card->dev = &pdev->dev; - platform_set_drvdata(pdev, card); - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); - return ret; -} - -static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = { - { .compatible = "mediatek,mt8173-rt5650-rt5676", }, - { } -}; -MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match); - -static struct platform_driver mt8173_rt5650_rt5676_driver = { - .driver = { - .name = "mtk-rt5650-rt5676", - .of_match_table = mt8173_rt5650_rt5676_dt_match, -#ifdef CONFIG_PM - .pm = &snd_soc_pm_ops, -#endif - }, - .probe = mt8173_rt5650_rt5676_dev_probe, -}; - -module_platform_driver(mt8173_rt5650_rt5676_driver); - -/* Module information */ -MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver"); -MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:mtk-rt5650-rt5676"); - diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c deleted file mode 100644 index bb09bb1..0000000 --- a/sound/soc/mediatek/mt8173-rt5650.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * mt8173-rt5650.c -- MT8173 machine driver with RT5650 codecs - * - * Copyright (c) 2016 MediaTek Inc. - * Author: Koro Chen koro.chen@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/gpio.h> -#include <linux/of_gpio.h> -#include <sound/soc.h> -#include <sound/jack.h> -#include "../codecs/rt5645.h" - -#define MCLK_FOR_CODECS 12288000 - -static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), -}; - -static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { - {"Speaker", NULL, "SPOL"}, - {"Speaker", NULL, "SPOR"}, - {"DMIC L1", NULL, "Int Mic"}, - {"DMIC R1", NULL, "Int Mic"}, - {"Headphone", NULL, "HPOL"}, - {"Headphone", NULL, "HPOR"}, - {"Headset Mic", NULL, "micbias1"}, - {"Headset Mic", NULL, "micbias2"}, - {"IN1P", NULL, "Headset Mic"}, - {"IN1N", NULL, "Headset Mic"}, -}; - -static const struct snd_kcontrol_new mt8173_rt5650_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), - SOC_DAPM_PIN_SWITCH("Int Mic"), - SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), -}; - -static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - int i, ret; - - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - - /* pll from mclk 12.288M */ - ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, - params_rate(params) * 512); - if (ret) - return ret; - - /* sysclk from pll */ - ret = snd_soc_dai_set_sysclk(codec_dai, 1, - params_rate(params) * 512, - SND_SOC_CLOCK_IN); - if (ret) - return ret; - } - return 0; -} - -static struct snd_soc_ops mt8173_rt5650_ops = { - .hw_params = mt8173_rt5650_hw_params, -}; - -static struct snd_soc_jack mt8173_rt5650_jack; - -static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) -{ - struct snd_soc_card *card = runtime->card; - struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; - int ret; - - rt5645_sel_asrc_clk_src(codec, - RT5645_DA_STEREO_FILTER | - RT5645_AD_STEREO_FILTER, - RT5645_CLK_SEL_I2S1_ASRC); - /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_jack, NULL, 0); - if (ret) { - dev_err(card->dev, "Can't new Headset Jack %d\n", ret); - return ret; - } - - return rt5645_set_jack_detect(codec, - &mt8173_rt5650_jack, - &mt8173_rt5650_jack, - &mt8173_rt5650_jack); -} - -static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = { - { - .dai_name = "rt5645-aif1", - }, -}; - -enum { - DAI_LINK_PLAYBACK, - DAI_LINK_CAPTURE, - DAI_LINK_CODEC_I2S, -}; - -/* Digital audio interface glue - connects codec <---> CPU */ -static struct snd_soc_dai_link mt8173_rt5650_dais[] = { - /* Front End DAI links */ - [DAI_LINK_PLAYBACK] = { - .name = "rt5650 Playback", - .stream_name = "rt5650 Playback", - .cpu_dai_name = "DL1", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dynamic = 1, - .dpcm_playback = 1, - }, - [DAI_LINK_CAPTURE] = { - .name = "rt5650 Capture", - .stream_name = "rt5650 Capture", - .cpu_dai_name = "VUL", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, - .dynamic = 1, - .dpcm_capture = 1, - }, - /* Back End DAI links */ - [DAI_LINK_CODEC_I2S] = { - .name = "Codec", - .cpu_dai_name = "I2S", - .no_pcm = 1, - .codecs = mt8173_rt5650_codecs, - .num_codecs = 1, - .init = mt8173_rt5650_init, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .ops = &mt8173_rt5650_ops, - .ignore_pmdown_time = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, -}; - -static struct snd_soc_card mt8173_rt5650_card = { - .name = "mtk-rt5650", - .owner = THIS_MODULE, - .dai_link = mt8173_rt5650_dais, - .num_links = ARRAY_SIZE(mt8173_rt5650_dais), - .controls = mt8173_rt5650_controls, - .num_controls = ARRAY_SIZE(mt8173_rt5650_controls), - .dapm_widgets = mt8173_rt5650_widgets, - .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets), - .dapm_routes = mt8173_rt5650_routes, - .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes), -}; - -static int mt8173_rt5650_dev_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &mt8173_rt5650_card; - struct device_node *platform_node; - int i, ret; - - 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 (mt8173_rt5650_dais[i].platform_name) - continue; - mt8173_rt5650_dais[i].platform_of_node = platform_node; - } - - mt8173_rt5650_codecs[0].of_node = - of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); - if (!mt8173_rt5650_codecs[0].of_node) { - dev_err(&pdev->dev, - "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; - } - card->dev = &pdev->dev; - platform_set_drvdata(pdev, card); - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); - return ret; -} - -static const struct of_device_id mt8173_rt5650_dt_match[] = { - { .compatible = "mediatek,mt8173-rt5650", }, - { } -}; -MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match); - -static struct platform_driver mt8173_rt5650_driver = { - .driver = { - .name = "mtk-rt5650", - .of_match_table = mt8173_rt5650_dt_match, -#ifdef CONFIG_PM - .pm = &snd_soc_pm_ops, -#endif - }, - .probe = mt8173_rt5650_dev_probe, -}; - -module_platform_driver(mt8173_rt5650_driver); - -/* Module information */ -MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver"); -MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:mtk-rt5650"); - 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/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c new file mode 100644 index 0000000..5524a2c --- /dev/null +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -0,0 +1,213 @@ +/* + * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen koro.chen@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 <sound/jack.h> +#include <linux/gpio.h> +#include "../../codecs/max98090.h" + +static struct snd_soc_jack mt8173_max98090_jack; + +static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_max98090_routes[] = { + {"Speaker", NULL, "SPKL"}, + {"Speaker", NULL, "SPKR"}, + {"DMICL", NULL, "Int Mic"}, + {"Headphone", NULL, "HPL"}, + {"Headphone", NULL, "HPR"}, + {"Headset Mic", NULL, "MICBIAS"}, + {"IN34", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, + SND_SOC_CLOCK_IN); +} + +static struct snd_soc_ops mt8173_max98090_ops = { + .hw_params = mt8173_max98090_hw_params, +}; + +static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec; + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, + &mt8173_max98090_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret); + return ret; + } + + ret = snd_soc_jack_add_pins(&mt8173_max98090_jack, + ARRAY_SIZE(mt8173_max98090_jack_pins), + mt8173_max98090_jack_pins); + if (ret) { + dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret); + return ret; + } + + return max98090_mic_detect(codec, &mt8173_max98090_jack); +} + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_max98090_dais[] = { + /* Front End DAI links */ + { + .name = "MAX98090 Playback", + .stream_name = "MAX98090 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "MAX98090 Capture", + .stream_name = "MAX98090 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codec_dai_name = "HiFi", + .init = mt8173_max98090_init, + .ops = &mt8173_max98090_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card mt8173_max98090_card = { + .name = "mt8173-max98090", + .owner = THIS_MODULE, + .dai_link = mt8173_max98090_dais, + .num_links = ARRAY_SIZE(mt8173_max98090_dais), + .controls = mt8173_max98090_controls, + .num_controls = ARRAY_SIZE(mt8173_max98090_controls), + .dapm_widgets = mt8173_max98090_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), + .dapm_routes = mt8173_max98090_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), +}; + +static int mt8173_max98090_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_max98090_card; + struct device_node *codec_node, *platform_node; + int ret, i; + + 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 (mt8173_max98090_dais[i].platform_name) + continue; + mt8173_max98090_dais[i].platform_of_node = platform_node; + } + + codec_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,audio-codec", 0); + if (!codec_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + for (i = 0; i < card->num_links; i++) { + if (mt8173_max98090_dais[i].codec_name) + continue; + mt8173_max98090_dais[i].codec_of_node = codec_node; + } + card->dev = &pdev->dev; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static const struct of_device_id mt8173_max98090_dt_match[] = { + { .compatible = "mediatek,mt8173-max98090", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); + +static struct platform_driver mt8173_max98090_driver = { + .driver = { + .name = "mt8173-max98090", + .of_match_table = mt8173_max98090_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_max98090_dev_probe, +}; + +module_platform_driver(mt8173_max98090_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); +MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mt8173-max98090"); + diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c new file mode 100644 index 0000000..467f704 --- /dev/null +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -0,0 +1,258 @@ +/* + * mt8173-rt5650-rt5514.c -- MT8173 machine driver with RT5650/5514 codecs + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Koro Chen koro.chen@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/gpio.h> +#include <linux/of_gpio.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../../codecs/rt5645.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_rt5514_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_rt5514_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"Sub DMIC1L", NULL, "Int Mic"}, + {"Sub DMIC1R", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_rt5514_ops = { + .hw_params = mt8173_rt5650_rt5514_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_rt5514_jack; + +static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5514_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_rt5514_jack, + &mt8173_rt5650_rt5514_jack, + &mt8173_rt5650_rt5514_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_rt5514_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, + { + .dai_name = "rt5514-aif1", + }, +}; + +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_rt5514_dais[] = { + /* Front End DAI links */ + [DAI_LINK_PLAYBACK] = { + .name = "rt5650_rt5514 Playback", + .stream_name = "rt5650_rt5514 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + [DAI_LINK_CAPTURE] = { + .name = "rt5650_rt5514 Capture", + .stream_name = "rt5650_rt5514 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + [DAI_LINK_CODEC_I2S] = { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codecs = mt8173_rt5650_rt5514_codecs, + .num_codecs = 2, + .init = mt8173_rt5650_rt5514_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_rt5514_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_codec_conf mt8173_rt5650_rt5514_codec_conf[] = { + { + .name_prefix = "Sub", + }, +}; + +static struct snd_soc_card mt8173_rt5650_rt5514_card = { + .name = "mtk-rt5650-rt5514", + .owner = THIS_MODULE, + .dai_link = mt8173_rt5650_rt5514_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_rt5514_dais), + .codec_conf = mt8173_rt5650_rt5514_codec_conf, + .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5514_codec_conf), + .controls = mt8173_rt5650_rt5514_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5514_controls), + .dapm_widgets = mt8173_rt5650_rt5514_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5514_widgets), + .dapm_routes = mt8173_rt5650_rt5514_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5514_routes), +}; + +static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_rt5514_card; + struct device_node *platform_node; + int i, ret; + + 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 (mt8173_rt5650_rt5514_dais[i].platform_name) + continue; + mt8173_rt5650_rt5514_dais[i].platform_of_node = platform_node; + } + + mt8173_rt5650_rt5514_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_rt5514_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5514_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); + if (!mt8173_rt5650_rt5514_codecs[1].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5514_codec_conf[0].of_node = + mt8173_rt5650_rt5514_codecs[1].of_node; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static const struct of_device_id mt8173_rt5650_rt5514_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650-rt5514", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5514_dt_match); + +static struct platform_driver mt8173_rt5650_rt5514_driver = { + .driver = { + .name = "mtk-rt5650-rt5514", + .of_match_table = mt8173_rt5650_rt5514_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_rt5514_dev_probe, +}; + +module_platform_driver(mt8173_rt5650_rt5514_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 and RT5514 SoC machine driver"); +MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650-rt5514"); + diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c new file mode 100644 index 0000000..cfa23d9 --- /dev/null +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -0,0 +1,288 @@ +/* + * mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen koro.chen@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/gpio.h> +#include <linux/of_gpio.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../../codecs/rt5645.h" +#include "../../codecs/rt5677.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ + {"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */ + {"Sub DMIC R1", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */ +}; + +static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_rt5676_ops = { + .hw_params = mt8173_rt5650_rt5676_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_rt5676_jack; + +static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + rt5677_sel_asrc_clk_src(codec_sub, + RT5677_DA_STEREO_FILTER | + RT5677_AD_STEREO1_FILTER, + RT5677_CLK_SEL_I2S1_ASRC); + rt5677_sel_asrc_clk_src(codec_sub, + RT5677_AD_STEREO2_FILTER | + RT5677_I2S2_SOURCE, + RT5677_CLK_SEL_I2S2_ASRC); + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5676_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_rt5676_jack, + &mt8173_rt5650_rt5676_jack, + &mt8173_rt5650_rt5676_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, + { + .dai_name = "rt5677-aif1", + }, +}; + +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, + DAI_LINK_INTERCODEC +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { + /* Front End DAI links */ + [DAI_LINK_PLAYBACK] = { + .name = "rt5650_rt5676 Playback", + .stream_name = "rt5650_rt5676 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + [DAI_LINK_CAPTURE] = { + .name = "rt5650_rt5676 Capture", + .stream_name = "rt5650_rt5676 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + + /* Back End DAI links */ + [DAI_LINK_CODEC_I2S] = { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codecs = mt8173_rt5650_rt5676_codecs, + .num_codecs = 2, + .init = mt8173_rt5650_rt5676_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_rt5676_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + [DAI_LINK_INTERCODEC] = { + .name = "rt5650_rt5676 intercodec", + .stream_name = "rt5650_rt5676 intercodec", + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_dai_name = "rt5677-aif2", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, + +}; + +static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = { + { + .name_prefix = "Sub", + }, +}; + +static struct snd_soc_card mt8173_rt5650_rt5676_card = { + .name = "mtk-rt5650-rt5676", + .owner = THIS_MODULE, + .dai_link = mt8173_rt5650_rt5676_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais), + .codec_conf = mt8173_rt5650_rt5676_codec_conf, + .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf), + .controls = mt8173_rt5650_rt5676_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls), + .dapm_widgets = mt8173_rt5650_rt5676_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets), + .dapm_routes = mt8173_rt5650_rt5676_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes), +}; + +static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_rt5676_card; + struct device_node *platform_node; + int i, ret; + + 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 (mt8173_rt5650_rt5676_dais[i].platform_name) + continue; + mt8173_rt5650_rt5676_dais[i].platform_of_node = platform_node; + } + + mt8173_rt5650_rt5676_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_rt5676_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5676_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); + if (!mt8173_rt5650_rt5676_codecs[1].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5676_codec_conf[0].of_node = + mt8173_rt5650_rt5676_codecs[1].of_node; + + mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node = + mt8173_rt5650_rt5676_codecs[1].of_node; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650-rt5676", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match); + +static struct platform_driver mt8173_rt5650_rt5676_driver = { + .driver = { + .name = "mtk-rt5650-rt5676", + .of_match_table = mt8173_rt5650_rt5676_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_rt5676_dev_probe, +}; + +module_platform_driver(mt8173_rt5650_rt5676_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver"); +MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650-rt5676"); + diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c new file mode 100644 index 0000000..20b3b7d --- /dev/null +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -0,0 +1,236 @@ +/* + * mt8173-rt5650.c -- MT8173 machine driver with RT5650 codecs + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Koro Chen koro.chen@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/gpio.h> +#include <linux/of_gpio.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../../codecs/rt5645.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"DMIC L1", NULL, "Int Mic"}, + {"DMIC R1", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_rt5650_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_ops = { + .hw_params = mt8173_rt5650_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_jack; + +static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_jack, + &mt8173_rt5650_jack, + &mt8173_rt5650_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, +}; + +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_dais[] = { + /* Front End DAI links */ + [DAI_LINK_PLAYBACK] = { + .name = "rt5650 Playback", + .stream_name = "rt5650 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + [DAI_LINK_CAPTURE] = { + .name = "rt5650 Capture", + .stream_name = "rt5650 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + [DAI_LINK_CODEC_I2S] = { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codecs = mt8173_rt5650_codecs, + .num_codecs = 1, + .init = mt8173_rt5650_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card mt8173_rt5650_card = { + .name = "mtk-rt5650", + .owner = THIS_MODULE, + .dai_link = mt8173_rt5650_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_dais), + .controls = mt8173_rt5650_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_controls), + .dapm_widgets = mt8173_rt5650_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets), + .dapm_routes = mt8173_rt5650_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes), +}; + +static int mt8173_rt5650_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_card; + struct device_node *platform_node; + int i, ret; + + 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 (mt8173_rt5650_dais[i].platform_name) + continue; + mt8173_rt5650_dais[i].platform_of_node = platform_node; + } + + mt8173_rt5650_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static const struct of_device_id mt8173_rt5650_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match); + +static struct platform_driver mt8173_rt5650_driver = { + .driver = { + .name = "mtk-rt5650", + .of_match_table = mt8173_rt5650_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_dev_probe, +}; + +module_platform_driver(mt8173_rt5650_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver"); +MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650"); + diff --git a/sound/soc/mediatek/mt8173/mtk-afe-common.h b/sound/soc/mediatek/mt8173/mtk-afe-common.h new file mode 100644 index 0000000..f341f62 --- /dev/null +++ b/sound/soc/mediatek/mt8173/mtk-afe-common.h @@ -0,0 +1,101 @@ +/* + * mtk_afe_common.h -- Mediatek audio driver common definitions + * + * Copyright (c) 2015 MediaTek Inc. + * Author: 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 <linux/clk.h> +#include <linux/regmap.h> + +enum { + MTK_AFE_MEMIF_DL1, + MTK_AFE_MEMIF_DL2, + MTK_AFE_MEMIF_VUL, + MTK_AFE_MEMIF_DAI, + MTK_AFE_MEMIF_AWB, + MTK_AFE_MEMIF_MOD_DAI, + MTK_AFE_MEMIF_HDMI, + MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_MOD_PCM2, + MTK_AFE_IO_PMIC, + MTK_AFE_IO_I2S, + MTK_AFE_IO_2ND_I2S, + MTK_AFE_IO_HW_GAIN1, + MTK_AFE_IO_HW_GAIN2, + MTK_AFE_IO_MRG_O, + MTK_AFE_IO_MRG_I, + MTK_AFE_IO_DAIBT, + MTK_AFE_IO_HDMI, +}; + +enum { + MTK_AFE_IRQ_1, + MTK_AFE_IRQ_2, + MTK_AFE_IRQ_3, + MTK_AFE_IRQ_4, + MTK_AFE_IRQ_5, + MTK_AFE_IRQ_6, + MTK_AFE_IRQ_7, + MTK_AFE_IRQ_8, + MTK_AFE_IRQ_NUM, +}; + +enum { + MTK_CLK_INFRASYS_AUD, + MTK_CLK_TOP_PDN_AUD, + MTK_CLK_TOP_PDN_AUD_BUS, + MTK_CLK_I2S0_M, + MTK_CLK_I2S1_M, + MTK_CLK_I2S2_M, + MTK_CLK_I2S3_M, + MTK_CLK_I2S3_B, + MTK_CLK_BCK0, + MTK_CLK_BCK1, + MTK_CLK_NUM +}; + +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_shift; + int mono_shift; + int enable_shift; + int irq_reg_cnt; + int irq_cnt_shift; + int irq_en_shift; + int irq_fs_shift; + int irq_clr_shift; + int msb_shift; +}; + +struct mtk_afe_memif { + unsigned int phys_buf_addr; + int buffer_size; + struct snd_pcm_substream *substream; + const struct mtk_afe_memif_data *data; + const struct mtk_afe_irq_data *irqdata; +}; + +#endif diff --git a/sound/soc/mediatek/mt8173/mtk-afe-pcm.c b/sound/soc/mediatek/mt8173/mtk-afe-pcm.c new file mode 100644 index 0000000..f1c58a2 --- /dev/null +++ b/sound/soc/mediatek/mt8173/mtk-afe-pcm.c @@ -0,0 +1,1333 @@ +/* + * Mediatek ALSA SoC AFE platform driver + * + * Copyright (c) 2015 MediaTek Inc. + * Author: 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. + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/dma-mapping.h> +#include <linux/pm_runtime.h> +#include <sound/soc.h> +#include "mtk-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_CON1 0x0004 +#define AFE_DAC_CON0 0x0010 +#define AFE_DAC_CON1 0x0014 +#define AFE_I2S_CON1 0x0034 +#define AFE_I2S_CON2 0x0038 +#define AFE_CONN_24BIT 0x006c +#define AFE_MEMIF_MSB 0x00cc + +#define AFE_CONN1 0x0024 +#define AFE_CONN2 0x0028 +#define AFE_CONN3 0x002c +#define AFE_CONN7 0x0460 +#define AFE_CONN8 0x0464 +#define AFE_HDMI_CONN0 0x0390 + +/* Memory interface */ +#define AFE_DL1_BASE 0x0040 +#define AFE_DL1_CUR 0x0044 +#define AFE_DL1_END 0x0048 +#define AFE_DL2_BASE 0x0050 +#define AFE_DL2_CUR 0x0054 +#define AFE_AWB_BASE 0x0070 +#define AFE_AWB_CUR 0x007c +#define AFE_VUL_BASE 0x0080 +#define AFE_VUL_CUR 0x008c +#define AFE_VUL_END 0x0088 +#define AFE_DAI_BASE 0x0090 +#define AFE_DAI_CUR 0x009c +#define AFE_MOD_PCM_BASE 0x0330 +#define AFE_MOD_PCM_CUR 0x033c +#define AFE_HDMI_OUT_BASE 0x0374 +#define AFE_HDMI_OUT_CUR 0x0378 +#define AFE_HDMI_OUT_END 0x037c + +#define AFE_ADDA_TOP_CON0 0x0120 +#define AFE_ADDA2_TOP_CON0 0x0600 + +#define AFE_HDMI_OUT_CON0 0x0370 + +#define AFE_IRQ_MCU_CON 0x03a0 +#define AFE_IRQ_STATUS 0x03a4 +#define AFE_IRQ_CLR 0x03a8 +#define AFE_IRQ_CNT1 0x03ac +#define AFE_IRQ_CNT2 0x03b0 +#define AFE_IRQ_MCU_EN 0x03b4 +#define AFE_IRQ_CNT5 0x03bc +#define AFE_IRQ_CNT7 0x03dc + +#define AFE_TDM_CON1 0x0548 +#define AFE_TDM_CON2 0x054c + +#define AFE_BASE_END_OFFSET 8 +#define AFE_IRQ_STATUS_BITS 0xff + +/* AUDIO_TOP_CON0 (0x0000) */ +#define AUD_TCON0_PDN_SPDF (0x1 << 21) +#define AUD_TCON0_PDN_HDMI (0x1 << 20) +#define AUD_TCON0_PDN_24M (0x1 << 9) +#define AUD_TCON0_PDN_22M (0x1 << 8) +#define AUD_TCON0_PDN_AFE (0x1 << 2) + +/* AFE_I2S_CON1 (0x0034) */ +#define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12) +#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3) +#define AFE_I2S_CON1_EN (0x1 << 0) + +/* AFE_I2S_CON2 (0x0038) */ +#define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12) +#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3) +#define AFE_I2S_CON2_EN (0x1 << 0) + +/* AFE_CONN_24BIT (0x006c) */ +#define AFE_CONN_24BIT_O04 (0x1 << 4) +#define AFE_CONN_24BIT_O03 (0x1 << 3) + +/* AFE_HDMI_CONN0 (0x0390) */ +#define AFE_HDMI_CONN0_O37_I37 (0x7 << 21) +#define AFE_HDMI_CONN0_O36_I36 (0x6 << 18) +#define AFE_HDMI_CONN0_O35_I33 (0x3 << 15) +#define AFE_HDMI_CONN0_O34_I32 (0x2 << 12) +#define AFE_HDMI_CONN0_O33_I35 (0x5 << 9) +#define AFE_HDMI_CONN0_O32_I34 (0x4 << 6) +#define AFE_HDMI_CONN0_O31_I31 (0x1 << 3) +#define AFE_HDMI_CONN0_O30_I30 (0x0 << 0) + +/* AFE_TDM_CON1 (0x0548) */ +#define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24) +#define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12) +#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8) +#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4) +#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3) +#define AFE_TDM_CON1_BCK_INV (0x1 << 1) +#define AFE_TDM_CON1_EN (0x1 << 0) + +enum afe_tdm_ch_start { + AFE_TDM_CH_START_O30_O31 = 0, + AFE_TDM_CH_START_O32_O33, + AFE_TDM_CH_START_O34_O35, + AFE_TDM_CH_START_O36_O37, + AFE_TDM_CH_ZERO, +}; + +static const unsigned int mtk_afe_backup_list[] = { + AUDIO_TOP_CON0, + AFE_CONN1, + AFE_CONN2, + AFE_CONN7, + AFE_CONN8, + AFE_DAC_CON1, + AFE_DL1_BASE, + AFE_DL1_END, + AFE_VUL_BASE, + AFE_VUL_END, + AFE_HDMI_OUT_BASE, + AFE_HDMI_OUT_END, + AFE_HDMI_CONN0, + AFE_DAC_CON0, +}; + +struct mtk_afe { + /* address for ioremap audio hardware register */ + void __iomem *base_addr; + struct device *dev; + struct regmap *regmap; + struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM]; + struct clk *clocks[MTK_CLK_NUM]; + unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)]; + bool suspended; +}; + +static const struct snd_pcm_hardware mtk_afe_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 512, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 256, + .fifo_size = 0, +}; + +static snd_pcm_uframes_t mtk_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); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + unsigned int hw_ptr; + int ret; + + ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr); + if (ret || hw_ptr == 0) { + dev_err(afe->dev, "%s hw_ptr err\n", __func__); + hw_ptr = memif->phys_buf_addr; + } + + return bytes_to_frames(substream->runtime, + hw_ptr - memif->phys_buf_addr); +} + +static const struct snd_pcm_ops mtk_afe_pcm_ops = { + .ioctl = snd_pcm_lib_ioctl, + .pointer = mtk_afe_pcm_pointer, +}; + +static int mtk_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 = mtk_afe_hardware.buffer_bytes_max; + + return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + card->dev, size, size); +} + +static void mtk_afe_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static const struct snd_soc_platform_driver mtk_afe_pcm_platform = { + .ops = &mtk_afe_pcm_ops, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mtk_afe_pcm_free, +}; + +struct mtk_afe_rate { + unsigned int rate; + unsigned int regvalue; +}; + +static const struct mtk_afe_rate mtk_afe_i2s_rates[] = { + { .rate = 8000, .regvalue = 0 }, + { .rate = 11025, .regvalue = 1 }, + { .rate = 12000, .regvalue = 2 }, + { .rate = 16000, .regvalue = 4 }, + { .rate = 22050, .regvalue = 5 }, + { .rate = 24000, .regvalue = 6 }, + { .rate = 32000, .regvalue = 8 }, + { .rate = 44100, .regvalue = 9 }, + { .rate = 48000, .regvalue = 10 }, + { .rate = 88000, .regvalue = 11 }, + { .rate = 96000, .regvalue = 12 }, + { .rate = 174000, .regvalue = 13 }, + { .rate = 192000, .regvalue = 14 }, +}; + +static int mtk_afe_i2s_fs(unsigned int sample_rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++) + if (mtk_afe_i2s_rates[i].rate == sample_rate) + return mtk_afe_i2s_rates[i].regvalue; + + return -EINVAL; +} + +static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate) +{ + unsigned int val; + int fs = mtk_afe_i2s_fs(rate); + + if (fs < 0) + return -EINVAL; + + /* from external ADC */ + regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1); + regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1); + + /* set input */ + val = AFE_I2S_CON2_LOW_JITTER_CLK | + AFE_I2S_CON2_RATE(fs) | + AFE_I2S_CON2_FORMAT_I2S; + + regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val); + + /* set output */ + val = AFE_I2S_CON1_LOW_JITTER_CLK | + AFE_I2S_CON1_RATE(fs) | + AFE_I2S_CON1_FORMAT_I2S; + + regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val); + return 0; +} + +static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable) +{ + unsigned int val; + + regmap_read(afe->regmap, AFE_I2S_CON2, &val); + if (!!(val & AFE_I2S_CON2_EN) == enable) + return; + + /* input */ + regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable); + + /* output */ + regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable); +} + +static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, + struct clk *m_ck, struct clk *b_ck) +{ + int ret; + + if (m_ck) { + ret = clk_prepare_enable(m_ck); + if (ret) { + dev_err(afe->dev, "Failed to enable m_ck\n"); + return ret; + } + } + + if (b_ck) { + ret = clk_prepare_enable(b_ck); + if (ret) { + dev_err(afe->dev, "Failed to enable b_ck\n"); + return ret; + } + } + return 0; +} + +static int mtk_afe_dais_set_clks(struct mtk_afe *afe, + struct clk *m_ck, unsigned int mck_rate, + struct clk *b_ck, unsigned int bck_rate) +{ + int ret; + + if (m_ck) { + ret = clk_set_rate(m_ck, mck_rate); + if (ret) { + dev_err(afe->dev, "Failed to set m_ck rate\n"); + return ret; + } + } + + if (b_ck) { + ret = clk_set_rate(b_ck, bck_rate); + if (ret) { + dev_err(afe->dev, "Failed to set b_ck rate\n"); + return ret; + } + } + return 0; +} + +static void mtk_afe_dais_disable_clks(struct mtk_afe *afe, + struct clk *m_ck, struct clk *b_ck) +{ + if (m_ck) + clk_disable_unprepare(m_ck); + if (b_ck) + clk_disable_unprepare(b_ck); +} + +static int mtk_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); + + if (dai->active) + return 0; + + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); + return 0; +} + +static void mtk_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); + + if (dai->active) + return; + + mtk_afe_set_i2s_enable(afe, false); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL); +} + +static int mtk_afe_i2s_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 ret; + + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256, + NULL, 0); + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256, + NULL, 0); + /* config I2S */ + ret = mtk_afe_set_i2s(afe, substream->runtime->rate); + if (ret) + return ret; + + mtk_afe_set_i2s_enable(afe, true); + + return 0; +} + +static int mtk_afe_hdmi_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); + + if (dai->active) + return 0; + + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], + afe->clocks[MTK_CLK_I2S3_B]); + return 0; +} + +static void mtk_afe_hdmi_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); + + if (dai->active) + return; + + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], + afe->clocks[MTK_CLK_I2S3_B]); +} + +static int mtk_afe_hdmi_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); + unsigned int val; + + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128, + afe->clocks[MTK_CLK_I2S3_B], + runtime->rate * runtime->channels * 32); + + val = AFE_TDM_CON1_BCK_INV | + AFE_TDM_CON1_1_BCK_DELAY | + AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */ + AFE_TDM_CON1_WLEN_32BIT | + AFE_TDM_CON1_32_BCK_CYCLES | + AFE_TDM_CON1_LRCK_WIDTH(32); + regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val); + + /* set tdm2 config */ + switch (runtime->channels) { + case 1: + case 2: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_ZERO << 4); + val |= (AFE_TDM_CH_ZERO << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 3: + case 4: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_ZERO << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 5: + case 6: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_START_O34_O35 << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 7: + case 8: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_START_O34_O35 << 8); + val |= (AFE_TDM_CH_START_O36_O37 << 12); + break; + default: + val = 0; + } + regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val); + + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, + 0x000000f0, runtime->channels << 4); + return 0; +} + +static int mtk_afe_hdmi_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); + + dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0); + + /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */ + regmap_write(afe->regmap, AFE_HDMI_CONN0, + AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 | + AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 | + AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 | + AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37); + + /* enable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1); + + /* enable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1); + + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + /* disable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0); + + /* disable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF); + + return 0; + default: + return -EINVAL; + } +} + +static int mtk_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; + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int ret; + + memif->substream = substream; + + snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware); + + /* + * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be + * smaller than period_size due to AFE's internal buffer. + * This easily leads to overrun when avail_min is period_size. + * One more period can hold the possible unread buffer. + */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIODS, + 3, + mtk_afe_hardware.periods_max); + if (ret < 0) { + dev_err(afe->dev, "hw_constraint_minmax failed\n"); + return ret; + } + } + 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"); + return ret; +} + +static void mtk_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); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + + memif->substream = NULL; +} + +static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + 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 mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int msb_at_bit33 = 0; + int ret; + + dev_dbg(afe->dev, + "%s period = %u, rate= %u, channels=%u\n", + __func__, params_period_size(params), params_rate(params), + params_channels(params)); + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + return ret; + + msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0; + memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr); + memif->buffer_size = substream->runtime->dma_bytes; + + /* 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); + + /* set MSB to 33-bit */ + regmap_update_bits(afe->regmap, AFE_MEMIF_MSB, + 1 << memif->data->msb_shift, + msb_at_bit33 << memif->data->msb_shift); + + /* set channel */ + if (memif->data->mono_shift >= 0) { + unsigned int mono = (params_channels(params) == 1) ? 1 : 0; + + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 1 << memif->data->mono_shift, + mono << memif->data->mono_shift); + } + + /* set rate */ + if (memif->data->fs_shift < 0) + return 0; + if (memif->data->id == MTK_AFE_MEMIF_DAI || + memif->data->id == MTK_AFE_MEMIF_MOD_DAI) { + unsigned int val; + + switch (params_rate(params)) { + case 8000: + val = 0; + break; + case 16000: + val = 1; + break; + case 32000: + val = 2; + break; + default: + return -EINVAL; + } + + if (memif->data->id == MTK_AFE_MEMIF_DAI) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 0x3 << memif->data->fs_shift, + val << memif->data->fs_shift); + else + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 0x3 << memif->data->fs_shift, + val << memif->data->fs_shift); + + } else { + int fs = mtk_afe_i2s_fs(params_rate(params)); + + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 0xf << memif->data->fs_shift, + fs << memif->data->fs_shift); + } + + return 0; +} + +static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int mtk_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 snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + unsigned int counter = runtime->period_size; + + dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + 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->data->irq_reg_cnt, + 0x3ffff << memif->data->irq_cnt_shift, + counter << memif->data->irq_cnt_shift); + + /* set irq fs */ + if (memif->data->irq_fs_shift >= 0) { + int fs = mtk_afe_i2s_fs(runtime->rate); + + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, + AFE_IRQ_MCU_CON, + 0xf << memif->data->irq_fs_shift, + fs << memif->data->irq_fs_shift); + } + /* enable interrupt */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, + 1 << memif->data->irq_en_shift, + 1 << memif->data->irq_en_shift); + + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, 0); + /* disable interrupt */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, + 1 << memif->data->irq_en_shift, + 0 << memif->data->irq_en_shift); + /* and clear pending IRQ */ + regmap_write(afe->regmap, AFE_IRQ_CLR, + 1 << memif->data->irq_clr_shift); + return 0; + default: + return -EINVAL; + } +} + +/* FE DAIs */ +static const struct snd_soc_dai_ops mtk_afe_dai_ops = { + .startup = mtk_afe_dais_startup, + .shutdown = mtk_afe_dais_shutdown, + .hw_params = mtk_afe_dais_hw_params, + .hw_free = mtk_afe_dais_hw_free, + .trigger = mtk_afe_dais_trigger, +}; + +/* BE DAIs */ +static const struct snd_soc_dai_ops mtk_afe_i2s_ops = { + .startup = mtk_afe_i2s_startup, + .shutdown = mtk_afe_i2s_shutdown, + .prepare = mtk_afe_i2s_prepare, +}; + +static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = { + .startup = mtk_afe_hdmi_startup, + .shutdown = mtk_afe_hdmi_shutdown, + .prepare = mtk_afe_hdmi_prepare, + .trigger = mtk_afe_hdmi_trigger, + +}; + +static int mtk_afe_runtime_suspend(struct device *dev); +static int mtk_afe_runtime_resume(struct device *dev); + +static int mtk_afe_dai_suspend(struct snd_soc_dai *dai) +{ + struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai); + int i; + + dev_dbg(afe->dev, "%s\n", __func__); + if (pm_runtime_status_suspended(afe->dev) || afe->suspended) + return 0; + + for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++) + regmap_read(afe->regmap, mtk_afe_backup_list[i], + &afe->backup_regs[i]); + + afe->suspended = true; + mtk_afe_runtime_suspend(afe->dev); + return 0; +} + +static int mtk_afe_dai_resume(struct snd_soc_dai *dai) +{ + struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai); + int i = 0; + + dev_dbg(afe->dev, "%s\n", __func__); + if (pm_runtime_status_suspended(afe->dev) || !afe->suspended) + return 0; + + mtk_afe_runtime_resume(afe->dev); + + for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++) + regmap_write(afe->regmap, mtk_afe_backup_list[i], + afe->backup_regs[i]); + + afe->suspended = false; + return 0; +} + +static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", /* downlink 1 */ + .id = MTK_AFE_MEMIF_DL1, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + .name = "VUL", /* voice uplink */ + .id = MTK_AFE_MEMIF_VUL, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .capture = { + .stream_name = "VUL", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + /* BE DAIs */ + .name = "I2S", + .id = MTK_AFE_IO_I2S, + .playback = { + .stream_name = "I2S Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "I2S Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_i2s_ops, + .symmetric_rates = 1, + }, +}; + +static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = { + /* FE DAIs */ + { + .name = "HDMI", + .id = MTK_AFE_MEMIF_HDMI, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .playback = { + .stream_name = "HDMI", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + /* BE DAIs */ + .name = "HDMIO", + .id = MTK_AFE_IO_HDMI, + .playback = { + .stream_name = "HDMIO Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_hdmi_ops, + }, +}; + +static const struct snd_kcontrol_new mtk_afe_o03_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o04_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o09_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { + /* inter-connections */ + SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I06", 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("O03", SND_SOC_NOPM, 0, 0, + mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)), + SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0, + mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)), + SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0, + mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)), + SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0, + mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)), +}; + +static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { + {"I05", NULL, "DL1"}, + {"I06", NULL, "DL1"}, + {"I2S Playback", NULL, "O03"}, + {"I2S Playback", NULL, "O04"}, + {"VUL", NULL, "O09"}, + {"VUL", NULL, "O10"}, + {"I03", NULL, "I2S Capture"}, + {"I04", NULL, "I2S Capture"}, + {"I17", NULL, "I2S Capture"}, + {"I18", NULL, "I2S Capture"}, + { "O03", "I05 Switch", "I05" }, + { "O04", "I06 Switch", "I06" }, + { "O09", "I17 Switch", "I17" }, + { "O09", "I03 Switch", "I03" }, + { "O10", "I18 Switch", "I18" }, + { "O10", "I04 Switch", "I04" }, +}; + +static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { + {"HDMIO Playback", NULL, "HDMI"}, +}; + +static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = { + .name = "mtk-afe-pcm-dai", + .dapm_widgets = mtk_afe_pcm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets), + .dapm_routes = mtk_afe_pcm_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes), +}; + +static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = { + .name = "mtk-afe-hdmi-dai", + .dapm_routes = mtk_afe_hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes), +}; + +static const char *aud_clks[MTK_CLK_NUM] = { + [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk", + [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio", + [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus", + [MTK_CLK_I2S0_M] = "i2s0_m", + [MTK_CLK_I2S1_M] = "i2s1_m", + [MTK_CLK_I2S2_M] = "i2s2_m", + [MTK_CLK_I2S3_M] = "i2s3_m", + [MTK_CLK_I2S3_B] = "i2s3_b", + [MTK_CLK_BCK0] = "bck0", + [MTK_CLK_BCK1] = "bck1", +}; + +static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { + { + .name = "DL1", + .id = MTK_AFE_MEMIF_DL1, + .reg_ofs_base = AFE_DL1_BASE, + .reg_ofs_cur = AFE_DL1_CUR, + .fs_shift = 0, + .mono_shift = 21, + .enable_shift = 1, + .irq_reg_cnt = AFE_IRQ_CNT1, + .irq_cnt_shift = 0, + .irq_en_shift = 0, + .irq_fs_shift = 4, + .irq_clr_shift = 0, + .msb_shift = 0, + }, { + .name = "DL2", + .id = MTK_AFE_MEMIF_DL2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .fs_shift = 4, + .mono_shift = 22, + .enable_shift = 2, + .irq_reg_cnt = AFE_IRQ_CNT1, + .irq_cnt_shift = 20, + .irq_en_shift = 2, + .irq_fs_shift = 16, + .irq_clr_shift = 2, + .msb_shift = 1, + }, { + .name = "VUL", + .id = MTK_AFE_MEMIF_VUL, + .reg_ofs_base = AFE_VUL_BASE, + .reg_ofs_cur = AFE_VUL_CUR, + .fs_shift = 16, + .mono_shift = 27, + .enable_shift = 3, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 0, + .irq_en_shift = 1, + .irq_fs_shift = 8, + .irq_clr_shift = 1, + .msb_shift = 6, + }, { + .name = "DAI", + .id = MTK_AFE_MEMIF_DAI, + .reg_ofs_base = AFE_DAI_BASE, + .reg_ofs_cur = AFE_DAI_CUR, + .fs_shift = 24, + .mono_shift = -1, + .enable_shift = 4, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 20, + .irq_en_shift = 3, + .irq_fs_shift = 20, + .irq_clr_shift = 3, + .msb_shift = 5, + }, { + .name = "AWB", + .id = MTK_AFE_MEMIF_AWB, + .reg_ofs_base = AFE_AWB_BASE, + .reg_ofs_cur = AFE_AWB_CUR, + .fs_shift = 12, + .mono_shift = 24, + .enable_shift = 6, + .irq_reg_cnt = AFE_IRQ_CNT7, + .irq_cnt_shift = 0, + .irq_en_shift = 14, + .irq_fs_shift = 24, + .irq_clr_shift = 6, + .msb_shift = 3, + }, { + .name = "MOD_DAI", + .id = MTK_AFE_MEMIF_MOD_DAI, + .reg_ofs_base = AFE_MOD_PCM_BASE, + .reg_ofs_cur = AFE_MOD_PCM_CUR, + .fs_shift = 30, + .mono_shift = 30, + .enable_shift = 7, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 20, + .irq_en_shift = 3, + .irq_fs_shift = 20, + .irq_clr_shift = 3, + .msb_shift = 4, + }, { + .name = "HDMI", + .id = MTK_AFE_MEMIF_HDMI, + .reg_ofs_base = AFE_HDMI_OUT_BASE, + .reg_ofs_cur = AFE_HDMI_OUT_CUR, + .fs_shift = -1, + .mono_shift = -1, + .enable_shift = -1, + .irq_reg_cnt = AFE_IRQ_CNT5, + .irq_cnt_shift = 0, + .irq_en_shift = 12, + .irq_fs_shift = -1, + .irq_clr_shift = 4, + .msb_shift = 8, + }, +}; + +static const struct regmap_config mtk_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = AFE_ADDA2_TOP_CON0, + .cache_type = REGCACHE_NONE, +}; + +static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id) +{ + struct mtk_afe *afe = dev_id; + unsigned int reg_value; + int i, ret; + + ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value); + if (ret) { + dev_err(afe->dev, "%s irq status err\n", __func__); + reg_value = AFE_IRQ_STATUS_BITS; + goto err_irq; + } + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) { + struct mtk_afe_memif *memif = &afe->memif[i]; + + if (!(reg_value & (1 << memif->data->irq_clr_shift))) + continue; + + snd_pcm_period_elapsed(memif->substream); + } + +err_irq: + /* clear irq */ + regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS); + + return IRQ_HANDLED; +} + +static int mtk_afe_runtime_suspend(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + + /* disable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); + + /* disable AFE clk */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE); + + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]); + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); + clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); + return 0; +} + +static int mtk_afe_runtime_resume(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]); + if (ret) + return ret; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); + if (ret) + goto err_infra; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]); + if (ret) + goto err_top_aud_bus; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]); + if (ret) + goto err_top_aud; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]); + if (ret) + goto err_bck0; + + /* enable AFE clk */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0); + + /* set O3/O4 16bits */ + regmap_update_bits(afe->regmap, AFE_CONN_24BIT, + AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0); + + /* unmask all IRQs */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff); + + /* enable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); + return 0; + +err_bck0: + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); +err_top_aud: + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); +err_top_aud_bus: + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); +err_infra: + clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); + return ret; +} + +static int mtk_afe_init_audio_clk(struct mtk_afe *afe) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(aud_clks); i++) { + afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]); + if (IS_ERR(afe->clocks[i])) { + dev_err(afe->dev, "%s devm_clk_get %s fail\n", + __func__, aud_clks[i]); + return PTR_ERR(afe->clocks[i]); + } + } + clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */ + clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */ + return 0; +} + +static int mtk_afe_pcm_dev_probe(struct platform_device *pdev) +{ + int ret, i; + unsigned int irq_id; + struct mtk_afe *afe; + struct resource *res; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); + if (ret) + return ret; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + afe->dev = &pdev->dev; + + irq_id = platform_get_irq(pdev, 0); + if (!irq_id) { + dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); + return -ENXIO; + } + ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler, + 0, "Afe_ISR_Handle", (void *)afe); + if (ret) { + dev_err(afe->dev, "could not request_irq\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, + &mtk_afe_regmap_config); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + /* initial audio related clock */ + ret = mtk_afe_init_audio_clk(afe); + if (ret) { + dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n"); + return ret; + } + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) + afe->memif[i].data = &memif_data[i]; + + platform_set_drvdata(pdev, afe); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = mtk_afe_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform); + if (ret) + goto err_pm_disable; + + ret = snd_soc_register_component(&pdev->dev, + &mtk_afe_pcm_dai_component, + mtk_afe_pcm_dais, + ARRAY_SIZE(mtk_afe_pcm_dais)); + if (ret) + goto err_platform; + + ret = snd_soc_register_component(&pdev->dev, + &mtk_afe_hdmi_dai_component, + mtk_afe_hdmi_dais, + ARRAY_SIZE(mtk_afe_hdmi_dais)); + if (ret) + goto err_comp; + + dev_info(&pdev->dev, "MTK AFE driver initialized.\n"); + return 0; + +err_comp: + snd_soc_unregister_component(&pdev->dev); +err_platform: + snd_soc_unregister_platform(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int mtk_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + mtk_afe_runtime_suspend(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id mtk_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt8173-afe-pcm", }, + { } +}; +MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match); + +static const struct dev_pm_ops mtk_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume, + NULL) +}; + +static struct platform_driver mtk_afe_pcm_driver = { + .driver = { + .name = "mtk-afe-pcm", + .of_match_table = mtk_afe_pcm_dt_match, + .pm = &mtk_afe_pm_ops, + }, + .probe = mtk_afe_pcm_dev_probe, + .remove = mtk_afe_pcm_dev_remove, +}; + +module_platform_driver(mtk_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver"); +MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h deleted file mode 100644 index f341f62..0000000 --- a/sound/soc/mediatek/mtk-afe-common.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * mtk_afe_common.h -- Mediatek audio driver common definitions - * - * Copyright (c) 2015 MediaTek Inc. - * Author: 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 <linux/clk.h> -#include <linux/regmap.h> - -enum { - MTK_AFE_MEMIF_DL1, - MTK_AFE_MEMIF_DL2, - MTK_AFE_MEMIF_VUL, - MTK_AFE_MEMIF_DAI, - MTK_AFE_MEMIF_AWB, - MTK_AFE_MEMIF_MOD_DAI, - MTK_AFE_MEMIF_HDMI, - MTK_AFE_MEMIF_NUM, - MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM, - MTK_AFE_IO_MOD_PCM2, - MTK_AFE_IO_PMIC, - MTK_AFE_IO_I2S, - MTK_AFE_IO_2ND_I2S, - MTK_AFE_IO_HW_GAIN1, - MTK_AFE_IO_HW_GAIN2, - MTK_AFE_IO_MRG_O, - MTK_AFE_IO_MRG_I, - MTK_AFE_IO_DAIBT, - MTK_AFE_IO_HDMI, -}; - -enum { - MTK_AFE_IRQ_1, - MTK_AFE_IRQ_2, - MTK_AFE_IRQ_3, - MTK_AFE_IRQ_4, - MTK_AFE_IRQ_5, - MTK_AFE_IRQ_6, - MTK_AFE_IRQ_7, - MTK_AFE_IRQ_8, - MTK_AFE_IRQ_NUM, -}; - -enum { - MTK_CLK_INFRASYS_AUD, - MTK_CLK_TOP_PDN_AUD, - MTK_CLK_TOP_PDN_AUD_BUS, - MTK_CLK_I2S0_M, - MTK_CLK_I2S1_M, - MTK_CLK_I2S2_M, - MTK_CLK_I2S3_M, - MTK_CLK_I2S3_B, - MTK_CLK_BCK0, - MTK_CLK_BCK1, - MTK_CLK_NUM -}; - -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_shift; - int mono_shift; - int enable_shift; - int irq_reg_cnt; - int irq_cnt_shift; - int irq_en_shift; - int irq_fs_shift; - int irq_clr_shift; - int msb_shift; -}; - -struct mtk_afe_memif { - unsigned int phys_buf_addr; - int buffer_size; - struct snd_pcm_substream *substream; - const struct mtk_afe_memif_data *data; - const struct mtk_afe_irq_data *irqdata; -}; - -#endif diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c deleted file mode 100644 index f1c58a2..0000000 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * Mediatek ALSA SoC AFE platform driver - * - * Copyright (c) 2015 MediaTek Inc. - * Author: 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. - */ - -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/dma-mapping.h> -#include <linux/pm_runtime.h> -#include <sound/soc.h> -#include "mtk-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_CON1 0x0004 -#define AFE_DAC_CON0 0x0010 -#define AFE_DAC_CON1 0x0014 -#define AFE_I2S_CON1 0x0034 -#define AFE_I2S_CON2 0x0038 -#define AFE_CONN_24BIT 0x006c -#define AFE_MEMIF_MSB 0x00cc - -#define AFE_CONN1 0x0024 -#define AFE_CONN2 0x0028 -#define AFE_CONN3 0x002c -#define AFE_CONN7 0x0460 -#define AFE_CONN8 0x0464 -#define AFE_HDMI_CONN0 0x0390 - -/* Memory interface */ -#define AFE_DL1_BASE 0x0040 -#define AFE_DL1_CUR 0x0044 -#define AFE_DL1_END 0x0048 -#define AFE_DL2_BASE 0x0050 -#define AFE_DL2_CUR 0x0054 -#define AFE_AWB_BASE 0x0070 -#define AFE_AWB_CUR 0x007c -#define AFE_VUL_BASE 0x0080 -#define AFE_VUL_CUR 0x008c -#define AFE_VUL_END 0x0088 -#define AFE_DAI_BASE 0x0090 -#define AFE_DAI_CUR 0x009c -#define AFE_MOD_PCM_BASE 0x0330 -#define AFE_MOD_PCM_CUR 0x033c -#define AFE_HDMI_OUT_BASE 0x0374 -#define AFE_HDMI_OUT_CUR 0x0378 -#define AFE_HDMI_OUT_END 0x037c - -#define AFE_ADDA_TOP_CON0 0x0120 -#define AFE_ADDA2_TOP_CON0 0x0600 - -#define AFE_HDMI_OUT_CON0 0x0370 - -#define AFE_IRQ_MCU_CON 0x03a0 -#define AFE_IRQ_STATUS 0x03a4 -#define AFE_IRQ_CLR 0x03a8 -#define AFE_IRQ_CNT1 0x03ac -#define AFE_IRQ_CNT2 0x03b0 -#define AFE_IRQ_MCU_EN 0x03b4 -#define AFE_IRQ_CNT5 0x03bc -#define AFE_IRQ_CNT7 0x03dc - -#define AFE_TDM_CON1 0x0548 -#define AFE_TDM_CON2 0x054c - -#define AFE_BASE_END_OFFSET 8 -#define AFE_IRQ_STATUS_BITS 0xff - -/* AUDIO_TOP_CON0 (0x0000) */ -#define AUD_TCON0_PDN_SPDF (0x1 << 21) -#define AUD_TCON0_PDN_HDMI (0x1 << 20) -#define AUD_TCON0_PDN_24M (0x1 << 9) -#define AUD_TCON0_PDN_22M (0x1 << 8) -#define AUD_TCON0_PDN_AFE (0x1 << 2) - -/* AFE_I2S_CON1 (0x0034) */ -#define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12) -#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8) -#define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3) -#define AFE_I2S_CON1_EN (0x1 << 0) - -/* AFE_I2S_CON2 (0x0038) */ -#define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12) -#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8) -#define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3) -#define AFE_I2S_CON2_EN (0x1 << 0) - -/* AFE_CONN_24BIT (0x006c) */ -#define AFE_CONN_24BIT_O04 (0x1 << 4) -#define AFE_CONN_24BIT_O03 (0x1 << 3) - -/* AFE_HDMI_CONN0 (0x0390) */ -#define AFE_HDMI_CONN0_O37_I37 (0x7 << 21) -#define AFE_HDMI_CONN0_O36_I36 (0x6 << 18) -#define AFE_HDMI_CONN0_O35_I33 (0x3 << 15) -#define AFE_HDMI_CONN0_O34_I32 (0x2 << 12) -#define AFE_HDMI_CONN0_O33_I35 (0x5 << 9) -#define AFE_HDMI_CONN0_O32_I34 (0x4 << 6) -#define AFE_HDMI_CONN0_O31_I31 (0x1 << 3) -#define AFE_HDMI_CONN0_O30_I30 (0x0 << 0) - -/* AFE_TDM_CON1 (0x0548) */ -#define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24) -#define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12) -#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8) -#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4) -#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3) -#define AFE_TDM_CON1_BCK_INV (0x1 << 1) -#define AFE_TDM_CON1_EN (0x1 << 0) - -enum afe_tdm_ch_start { - AFE_TDM_CH_START_O30_O31 = 0, - AFE_TDM_CH_START_O32_O33, - AFE_TDM_CH_START_O34_O35, - AFE_TDM_CH_START_O36_O37, - AFE_TDM_CH_ZERO, -}; - -static const unsigned int mtk_afe_backup_list[] = { - AUDIO_TOP_CON0, - AFE_CONN1, - AFE_CONN2, - AFE_CONN7, - AFE_CONN8, - AFE_DAC_CON1, - AFE_DL1_BASE, - AFE_DL1_END, - AFE_VUL_BASE, - AFE_VUL_END, - AFE_HDMI_OUT_BASE, - AFE_HDMI_OUT_END, - AFE_HDMI_CONN0, - AFE_DAC_CON0, -}; - -struct mtk_afe { - /* address for ioremap audio hardware register */ - void __iomem *base_addr; - struct device *dev; - struct regmap *regmap; - struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM]; - struct clk *clocks[MTK_CLK_NUM]; - unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)]; - bool suspended; -}; - -static const struct snd_pcm_hardware mtk_afe_hardware = { - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID), - .buffer_bytes_max = 256 * 1024, - .period_bytes_min = 512, - .period_bytes_max = 128 * 1024, - .periods_min = 2, - .periods_max = 256, - .fifo_size = 0, -}; - -static snd_pcm_uframes_t mtk_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); - struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; - unsigned int hw_ptr; - int ret; - - ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr); - if (ret || hw_ptr == 0) { - dev_err(afe->dev, "%s hw_ptr err\n", __func__); - hw_ptr = memif->phys_buf_addr; - } - - return bytes_to_frames(substream->runtime, - hw_ptr - memif->phys_buf_addr); -} - -static const struct snd_pcm_ops mtk_afe_pcm_ops = { - .ioctl = snd_pcm_lib_ioctl, - .pointer = mtk_afe_pcm_pointer, -}; - -static int mtk_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 = mtk_afe_hardware.buffer_bytes_max; - - return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - card->dev, size, size); -} - -static void mtk_afe_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - -static const struct snd_soc_platform_driver mtk_afe_pcm_platform = { - .ops = &mtk_afe_pcm_ops, - .pcm_new = mtk_afe_pcm_new, - .pcm_free = mtk_afe_pcm_free, -}; - -struct mtk_afe_rate { - unsigned int rate; - unsigned int regvalue; -}; - -static const struct mtk_afe_rate mtk_afe_i2s_rates[] = { - { .rate = 8000, .regvalue = 0 }, - { .rate = 11025, .regvalue = 1 }, - { .rate = 12000, .regvalue = 2 }, - { .rate = 16000, .regvalue = 4 }, - { .rate = 22050, .regvalue = 5 }, - { .rate = 24000, .regvalue = 6 }, - { .rate = 32000, .regvalue = 8 }, - { .rate = 44100, .regvalue = 9 }, - { .rate = 48000, .regvalue = 10 }, - { .rate = 88000, .regvalue = 11 }, - { .rate = 96000, .regvalue = 12 }, - { .rate = 174000, .regvalue = 13 }, - { .rate = 192000, .regvalue = 14 }, -}; - -static int mtk_afe_i2s_fs(unsigned int sample_rate) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++) - if (mtk_afe_i2s_rates[i].rate == sample_rate) - return mtk_afe_i2s_rates[i].regvalue; - - return -EINVAL; -} - -static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate) -{ - unsigned int val; - int fs = mtk_afe_i2s_fs(rate); - - if (fs < 0) - return -EINVAL; - - /* from external ADC */ - regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1); - regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1); - - /* set input */ - val = AFE_I2S_CON2_LOW_JITTER_CLK | - AFE_I2S_CON2_RATE(fs) | - AFE_I2S_CON2_FORMAT_I2S; - - regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val); - - /* set output */ - val = AFE_I2S_CON1_LOW_JITTER_CLK | - AFE_I2S_CON1_RATE(fs) | - AFE_I2S_CON1_FORMAT_I2S; - - regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val); - return 0; -} - -static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable) -{ - unsigned int val; - - regmap_read(afe->regmap, AFE_I2S_CON2, &val); - if (!!(val & AFE_I2S_CON2_EN) == enable) - return; - - /* input */ - regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable); - - /* output */ - regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable); -} - -static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, - struct clk *m_ck, struct clk *b_ck) -{ - int ret; - - if (m_ck) { - ret = clk_prepare_enable(m_ck); - if (ret) { - dev_err(afe->dev, "Failed to enable m_ck\n"); - return ret; - } - } - - if (b_ck) { - ret = clk_prepare_enable(b_ck); - if (ret) { - dev_err(afe->dev, "Failed to enable b_ck\n"); - return ret; - } - } - return 0; -} - -static int mtk_afe_dais_set_clks(struct mtk_afe *afe, - struct clk *m_ck, unsigned int mck_rate, - struct clk *b_ck, unsigned int bck_rate) -{ - int ret; - - if (m_ck) { - ret = clk_set_rate(m_ck, mck_rate); - if (ret) { - dev_err(afe->dev, "Failed to set m_ck rate\n"); - return ret; - } - } - - if (b_ck) { - ret = clk_set_rate(b_ck, bck_rate); - if (ret) { - dev_err(afe->dev, "Failed to set b_ck rate\n"); - return ret; - } - } - return 0; -} - -static void mtk_afe_dais_disable_clks(struct mtk_afe *afe, - struct clk *m_ck, struct clk *b_ck) -{ - if (m_ck) - clk_disable_unprepare(m_ck); - if (b_ck) - clk_disable_unprepare(b_ck); -} - -static int mtk_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); - - if (dai->active) - return 0; - - mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); - mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); - return 0; -} - -static void mtk_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); - - if (dai->active) - return; - - mtk_afe_set_i2s_enable(afe, false); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, - AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); - mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); - mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL); -} - -static int mtk_afe_i2s_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 ret; - - mtk_afe_dais_set_clks(afe, - afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256, - NULL, 0); - mtk_afe_dais_set_clks(afe, - afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256, - NULL, 0); - /* config I2S */ - ret = mtk_afe_set_i2s(afe, substream->runtime->rate); - if (ret) - return ret; - - mtk_afe_set_i2s_enable(afe, true); - - return 0; -} - -static int mtk_afe_hdmi_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); - - if (dai->active) - return 0; - - mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], - afe->clocks[MTK_CLK_I2S3_B]); - return 0; -} - -static void mtk_afe_hdmi_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); - - if (dai->active) - return; - - mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], - afe->clocks[MTK_CLK_I2S3_B]); -} - -static int mtk_afe_hdmi_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); - unsigned int val; - - mtk_afe_dais_set_clks(afe, - afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128, - afe->clocks[MTK_CLK_I2S3_B], - runtime->rate * runtime->channels * 32); - - val = AFE_TDM_CON1_BCK_INV | - AFE_TDM_CON1_1_BCK_DELAY | - AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */ - AFE_TDM_CON1_WLEN_32BIT | - AFE_TDM_CON1_32_BCK_CYCLES | - AFE_TDM_CON1_LRCK_WIDTH(32); - regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val); - - /* set tdm2 config */ - switch (runtime->channels) { - case 1: - case 2: - val = AFE_TDM_CH_START_O30_O31; - val |= (AFE_TDM_CH_ZERO << 4); - val |= (AFE_TDM_CH_ZERO << 8); - val |= (AFE_TDM_CH_ZERO << 12); - break; - case 3: - case 4: - val = AFE_TDM_CH_START_O30_O31; - val |= (AFE_TDM_CH_START_O32_O33 << 4); - val |= (AFE_TDM_CH_ZERO << 8); - val |= (AFE_TDM_CH_ZERO << 12); - break; - case 5: - case 6: - val = AFE_TDM_CH_START_O30_O31; - val |= (AFE_TDM_CH_START_O32_O33 << 4); - val |= (AFE_TDM_CH_START_O34_O35 << 8); - val |= (AFE_TDM_CH_ZERO << 12); - break; - case 7: - case 8: - val = AFE_TDM_CH_START_O30_O31; - val |= (AFE_TDM_CH_START_O32_O33 << 4); - val |= (AFE_TDM_CH_START_O34_O35 << 8); - val |= (AFE_TDM_CH_START_O36_O37 << 12); - break; - default: - val = 0; - } - regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val); - - regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, - 0x000000f0, runtime->channels << 4); - return 0; -} - -static int mtk_afe_hdmi_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); - - dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0); - - /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */ - regmap_write(afe->regmap, AFE_HDMI_CONN0, - AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 | - AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 | - AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 | - AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37); - - /* enable Out control */ - regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1); - - /* enable tdm */ - regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1); - - return 0; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - /* disable tdm */ - regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0); - - /* disable Out control */ - regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0); - - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, - AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF); - - return 0; - default: - return -EINVAL; - } -} - -static int mtk_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; - struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; - int ret; - - memif->substream = substream; - - snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware); - - /* - * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be - * smaller than period_size due to AFE's internal buffer. - * This easily leads to overrun when avail_min is period_size. - * One more period can hold the possible unread buffer. - */ - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - ret = snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIODS, - 3, - mtk_afe_hardware.periods_max); - if (ret < 0) { - dev_err(afe->dev, "hw_constraint_minmax failed\n"); - return ret; - } - } - 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"); - return ret; -} - -static void mtk_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); - struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; - - memif->substream = NULL; -} - -static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - 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 mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; - int msb_at_bit33 = 0; - int ret; - - dev_dbg(afe->dev, - "%s period = %u, rate= %u, channels=%u\n", - __func__, params_period_size(params), params_rate(params), - params_channels(params)); - - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) - return ret; - - msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0; - memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr); - memif->buffer_size = substream->runtime->dma_bytes; - - /* 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); - - /* set MSB to 33-bit */ - regmap_update_bits(afe->regmap, AFE_MEMIF_MSB, - 1 << memif->data->msb_shift, - msb_at_bit33 << memif->data->msb_shift); - - /* set channel */ - if (memif->data->mono_shift >= 0) { - unsigned int mono = (params_channels(params) == 1) ? 1 : 0; - - regmap_update_bits(afe->regmap, AFE_DAC_CON1, - 1 << memif->data->mono_shift, - mono << memif->data->mono_shift); - } - - /* set rate */ - if (memif->data->fs_shift < 0) - return 0; - if (memif->data->id == MTK_AFE_MEMIF_DAI || - memif->data->id == MTK_AFE_MEMIF_MOD_DAI) { - unsigned int val; - - switch (params_rate(params)) { - case 8000: - val = 0; - break; - case 16000: - val = 1; - break; - case 32000: - val = 2; - break; - default: - return -EINVAL; - } - - if (memif->data->id == MTK_AFE_MEMIF_DAI) - regmap_update_bits(afe->regmap, AFE_DAC_CON0, - 0x3 << memif->data->fs_shift, - val << memif->data->fs_shift); - else - regmap_update_bits(afe->regmap, AFE_DAC_CON1, - 0x3 << memif->data->fs_shift, - val << memif->data->fs_shift); - - } else { - int fs = mtk_afe_i2s_fs(params_rate(params)); - - if (fs < 0) - return -EINVAL; - - regmap_update_bits(afe->regmap, AFE_DAC_CON1, - 0xf << memif->data->fs_shift, - fs << memif->data->fs_shift); - } - - return 0; -} - -static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - return snd_pcm_lib_free_pages(substream); -} - -static int mtk_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 snd_pcm_runtime * const runtime = substream->runtime; - struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); - struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; - unsigned int counter = runtime->period_size; - - dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - 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->data->irq_reg_cnt, - 0x3ffff << memif->data->irq_cnt_shift, - counter << memif->data->irq_cnt_shift); - - /* set irq fs */ - if (memif->data->irq_fs_shift >= 0) { - int fs = mtk_afe_i2s_fs(runtime->rate); - - if (fs < 0) - return -EINVAL; - - regmap_update_bits(afe->regmap, - AFE_IRQ_MCU_CON, - 0xf << memif->data->irq_fs_shift, - fs << memif->data->irq_fs_shift); - } - /* enable interrupt */ - regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, - 1 << memif->data->irq_en_shift, - 1 << memif->data->irq_en_shift); - - return 0; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - if (memif->data->enable_shift >= 0) - regmap_update_bits(afe->regmap, AFE_DAC_CON0, - 1 << memif->data->enable_shift, 0); - /* disable interrupt */ - regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, - 1 << memif->data->irq_en_shift, - 0 << memif->data->irq_en_shift); - /* and clear pending IRQ */ - regmap_write(afe->regmap, AFE_IRQ_CLR, - 1 << memif->data->irq_clr_shift); - return 0; - default: - return -EINVAL; - } -} - -/* FE DAIs */ -static const struct snd_soc_dai_ops mtk_afe_dai_ops = { - .startup = mtk_afe_dais_startup, - .shutdown = mtk_afe_dais_shutdown, - .hw_params = mtk_afe_dais_hw_params, - .hw_free = mtk_afe_dais_hw_free, - .trigger = mtk_afe_dais_trigger, -}; - -/* BE DAIs */ -static const struct snd_soc_dai_ops mtk_afe_i2s_ops = { - .startup = mtk_afe_i2s_startup, - .shutdown = mtk_afe_i2s_shutdown, - .prepare = mtk_afe_i2s_prepare, -}; - -static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = { - .startup = mtk_afe_hdmi_startup, - .shutdown = mtk_afe_hdmi_shutdown, - .prepare = mtk_afe_hdmi_prepare, - .trigger = mtk_afe_hdmi_trigger, - -}; - -static int mtk_afe_runtime_suspend(struct device *dev); -static int mtk_afe_runtime_resume(struct device *dev); - -static int mtk_afe_dai_suspend(struct snd_soc_dai *dai) -{ - struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai); - int i; - - dev_dbg(afe->dev, "%s\n", __func__); - if (pm_runtime_status_suspended(afe->dev) || afe->suspended) - return 0; - - for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++) - regmap_read(afe->regmap, mtk_afe_backup_list[i], - &afe->backup_regs[i]); - - afe->suspended = true; - mtk_afe_runtime_suspend(afe->dev); - return 0; -} - -static int mtk_afe_dai_resume(struct snd_soc_dai *dai) -{ - struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai); - int i = 0; - - dev_dbg(afe->dev, "%s\n", __func__); - if (pm_runtime_status_suspended(afe->dev) || !afe->suspended) - return 0; - - mtk_afe_runtime_resume(afe->dev); - - for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++) - regmap_write(afe->regmap, mtk_afe_backup_list[i], - afe->backup_regs[i]); - - afe->suspended = false; - return 0; -} - -static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = { - /* FE DAIs: memory intefaces to CPU */ - { - .name = "DL1", /* downlink 1 */ - .id = MTK_AFE_MEMIF_DL1, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, - .playback = { - .stream_name = "DL1", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &mtk_afe_dai_ops, - }, { - .name = "VUL", /* voice uplink */ - .id = MTK_AFE_MEMIF_VUL, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, - .capture = { - .stream_name = "VUL", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &mtk_afe_dai_ops, - }, { - /* BE DAIs */ - .name = "I2S", - .id = MTK_AFE_IO_I2S, - .playback = { - .stream_name = "I2S Playback", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "I2S Capture", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &mtk_afe_i2s_ops, - .symmetric_rates = 1, - }, -}; - -static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = { - /* FE DAIs */ - { - .name = "HDMI", - .id = MTK_AFE_MEMIF_HDMI, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, - .playback = { - .stream_name = "HDMI", - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &mtk_afe_dai_ops, - }, { - /* BE DAIs */ - .name = "HDMIO", - .id = MTK_AFE_IO_HDMI, - .playback = { - .stream_name = "HDMIO Playback", - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &mtk_afe_hdmi_ops, - }, -}; - -static const struct snd_kcontrol_new mtk_afe_o03_mix[] = { - SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0), -}; - -static const struct snd_kcontrol_new mtk_afe_o04_mix[] = { - SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0), -}; - -static const struct snd_kcontrol_new mtk_afe_o09_mix[] = { - SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0), - SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0), -}; - -static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { - SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0), - SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0), -}; - -static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { - /* inter-connections */ - SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("I06", 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("O03", SND_SOC_NOPM, 0, 0, - mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)), - SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0, - mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)), - SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0, - mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)), - SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0, - mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)), -}; - -static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { - {"I05", NULL, "DL1"}, - {"I06", NULL, "DL1"}, - {"I2S Playback", NULL, "O03"}, - {"I2S Playback", NULL, "O04"}, - {"VUL", NULL, "O09"}, - {"VUL", NULL, "O10"}, - {"I03", NULL, "I2S Capture"}, - {"I04", NULL, "I2S Capture"}, - {"I17", NULL, "I2S Capture"}, - {"I18", NULL, "I2S Capture"}, - { "O03", "I05 Switch", "I05" }, - { "O04", "I06 Switch", "I06" }, - { "O09", "I17 Switch", "I17" }, - { "O09", "I03 Switch", "I03" }, - { "O10", "I18 Switch", "I18" }, - { "O10", "I04 Switch", "I04" }, -}; - -static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { - {"HDMIO Playback", NULL, "HDMI"}, -}; - -static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = { - .name = "mtk-afe-pcm-dai", - .dapm_widgets = mtk_afe_pcm_widgets, - .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets), - .dapm_routes = mtk_afe_pcm_routes, - .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes), -}; - -static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = { - .name = "mtk-afe-hdmi-dai", - .dapm_routes = mtk_afe_hdmi_routes, - .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes), -}; - -static const char *aud_clks[MTK_CLK_NUM] = { - [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk", - [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio", - [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus", - [MTK_CLK_I2S0_M] = "i2s0_m", - [MTK_CLK_I2S1_M] = "i2s1_m", - [MTK_CLK_I2S2_M] = "i2s2_m", - [MTK_CLK_I2S3_M] = "i2s3_m", - [MTK_CLK_I2S3_B] = "i2s3_b", - [MTK_CLK_BCK0] = "bck0", - [MTK_CLK_BCK1] = "bck1", -}; - -static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { - { - .name = "DL1", - .id = MTK_AFE_MEMIF_DL1, - .reg_ofs_base = AFE_DL1_BASE, - .reg_ofs_cur = AFE_DL1_CUR, - .fs_shift = 0, - .mono_shift = 21, - .enable_shift = 1, - .irq_reg_cnt = AFE_IRQ_CNT1, - .irq_cnt_shift = 0, - .irq_en_shift = 0, - .irq_fs_shift = 4, - .irq_clr_shift = 0, - .msb_shift = 0, - }, { - .name = "DL2", - .id = MTK_AFE_MEMIF_DL2, - .reg_ofs_base = AFE_DL2_BASE, - .reg_ofs_cur = AFE_DL2_CUR, - .fs_shift = 4, - .mono_shift = 22, - .enable_shift = 2, - .irq_reg_cnt = AFE_IRQ_CNT1, - .irq_cnt_shift = 20, - .irq_en_shift = 2, - .irq_fs_shift = 16, - .irq_clr_shift = 2, - .msb_shift = 1, - }, { - .name = "VUL", - .id = MTK_AFE_MEMIF_VUL, - .reg_ofs_base = AFE_VUL_BASE, - .reg_ofs_cur = AFE_VUL_CUR, - .fs_shift = 16, - .mono_shift = 27, - .enable_shift = 3, - .irq_reg_cnt = AFE_IRQ_CNT2, - .irq_cnt_shift = 0, - .irq_en_shift = 1, - .irq_fs_shift = 8, - .irq_clr_shift = 1, - .msb_shift = 6, - }, { - .name = "DAI", - .id = MTK_AFE_MEMIF_DAI, - .reg_ofs_base = AFE_DAI_BASE, - .reg_ofs_cur = AFE_DAI_CUR, - .fs_shift = 24, - .mono_shift = -1, - .enable_shift = 4, - .irq_reg_cnt = AFE_IRQ_CNT2, - .irq_cnt_shift = 20, - .irq_en_shift = 3, - .irq_fs_shift = 20, - .irq_clr_shift = 3, - .msb_shift = 5, - }, { - .name = "AWB", - .id = MTK_AFE_MEMIF_AWB, - .reg_ofs_base = AFE_AWB_BASE, - .reg_ofs_cur = AFE_AWB_CUR, - .fs_shift = 12, - .mono_shift = 24, - .enable_shift = 6, - .irq_reg_cnt = AFE_IRQ_CNT7, - .irq_cnt_shift = 0, - .irq_en_shift = 14, - .irq_fs_shift = 24, - .irq_clr_shift = 6, - .msb_shift = 3, - }, { - .name = "MOD_DAI", - .id = MTK_AFE_MEMIF_MOD_DAI, - .reg_ofs_base = AFE_MOD_PCM_BASE, - .reg_ofs_cur = AFE_MOD_PCM_CUR, - .fs_shift = 30, - .mono_shift = 30, - .enable_shift = 7, - .irq_reg_cnt = AFE_IRQ_CNT2, - .irq_cnt_shift = 20, - .irq_en_shift = 3, - .irq_fs_shift = 20, - .irq_clr_shift = 3, - .msb_shift = 4, - }, { - .name = "HDMI", - .id = MTK_AFE_MEMIF_HDMI, - .reg_ofs_base = AFE_HDMI_OUT_BASE, - .reg_ofs_cur = AFE_HDMI_OUT_CUR, - .fs_shift = -1, - .mono_shift = -1, - .enable_shift = -1, - .irq_reg_cnt = AFE_IRQ_CNT5, - .irq_cnt_shift = 0, - .irq_en_shift = 12, - .irq_fs_shift = -1, - .irq_clr_shift = 4, - .msb_shift = 8, - }, -}; - -static const struct regmap_config mtk_afe_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = AFE_ADDA2_TOP_CON0, - .cache_type = REGCACHE_NONE, -}; - -static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id) -{ - struct mtk_afe *afe = dev_id; - unsigned int reg_value; - int i, ret; - - ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value); - if (ret) { - dev_err(afe->dev, "%s irq status err\n", __func__); - reg_value = AFE_IRQ_STATUS_BITS; - goto err_irq; - } - - for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) { - struct mtk_afe_memif *memif = &afe->memif[i]; - - if (!(reg_value & (1 << memif->data->irq_clr_shift))) - continue; - - snd_pcm_period_elapsed(memif->substream); - } - -err_irq: - /* clear irq */ - regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS); - - return IRQ_HANDLED; -} - -static int mtk_afe_runtime_suspend(struct device *dev) -{ - struct mtk_afe *afe = dev_get_drvdata(dev); - - /* disable AFE */ - regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); - - /* disable AFE clk */ - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE); - - clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); - clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]); - clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); - clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); - clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); - return 0; -} - -static int mtk_afe_runtime_resume(struct device *dev) -{ - struct mtk_afe *afe = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]); - if (ret) - return ret; - - ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); - if (ret) - goto err_infra; - - ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]); - if (ret) - goto err_top_aud_bus; - - ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]); - if (ret) - goto err_top_aud; - - ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]); - if (ret) - goto err_bck0; - - /* enable AFE clk */ - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0); - - /* set O3/O4 16bits */ - regmap_update_bits(afe->regmap, AFE_CONN_24BIT, - AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0); - - /* unmask all IRQs */ - regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff); - - /* enable AFE */ - regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); - return 0; - -err_bck0: - clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); -err_top_aud: - clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); -err_top_aud_bus: - clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); -err_infra: - clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); - return ret; -} - -static int mtk_afe_init_audio_clk(struct mtk_afe *afe) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(aud_clks); i++) { - afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]); - if (IS_ERR(afe->clocks[i])) { - dev_err(afe->dev, "%s devm_clk_get %s fail\n", - __func__, aud_clks[i]); - return PTR_ERR(afe->clocks[i]); - } - } - clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */ - clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */ - return 0; -} - -static int mtk_afe_pcm_dev_probe(struct platform_device *pdev) -{ - int ret, i; - unsigned int irq_id; - struct mtk_afe *afe; - struct resource *res; - - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); - if (ret) - return ret; - - afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); - if (!afe) - return -ENOMEM; - - afe->dev = &pdev->dev; - - irq_id = platform_get_irq(pdev, 0); - if (!irq_id) { - dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); - return -ENXIO; - } - ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler, - 0, "Afe_ISR_Handle", (void *)afe); - if (ret) { - dev_err(afe->dev, "could not request_irq\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, - &mtk_afe_regmap_config); - if (IS_ERR(afe->regmap)) - return PTR_ERR(afe->regmap); - - /* initial audio related clock */ - ret = mtk_afe_init_audio_clk(afe); - if (ret) { - dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n"); - return ret; - } - - for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) - afe->memif[i].data = &memif_data[i]; - - platform_set_drvdata(pdev, afe); - - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = mtk_afe_runtime_resume(&pdev->dev); - if (ret) - goto err_pm_disable; - } - - ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform); - if (ret) - goto err_pm_disable; - - ret = snd_soc_register_component(&pdev->dev, - &mtk_afe_pcm_dai_component, - mtk_afe_pcm_dais, - ARRAY_SIZE(mtk_afe_pcm_dais)); - if (ret) - goto err_platform; - - ret = snd_soc_register_component(&pdev->dev, - &mtk_afe_hdmi_dai_component, - mtk_afe_hdmi_dais, - ARRAY_SIZE(mtk_afe_hdmi_dais)); - if (ret) - goto err_comp; - - dev_info(&pdev->dev, "MTK AFE driver initialized.\n"); - return 0; - -err_comp: - snd_soc_unregister_component(&pdev->dev); -err_platform: - snd_soc_unregister_platform(&pdev->dev); -err_pm_disable: - pm_runtime_disable(&pdev->dev); - return ret; -} - -static int mtk_afe_pcm_dev_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - if (!pm_runtime_status_suspended(&pdev->dev)) - mtk_afe_runtime_suspend(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static const struct of_device_id mtk_afe_pcm_dt_match[] = { - { .compatible = "mediatek,mt8173-afe-pcm", }, - { } -}; -MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match); - -static const struct dev_pm_ops mtk_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume, - NULL) -}; - -static struct platform_driver mtk_afe_pcm_driver = { - .driver = { - .name = "mtk-afe-pcm", - .of_match_table = mtk_afe_pcm_dt_match, - .pm = &mtk_afe_pm_ops, - }, - .probe = mtk_afe_pcm_dev_probe, - .remove = mtk_afe_pcm_dev_remove, -}; - -module_platform_driver(mtk_afe_pcm_driver); - -MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver"); -MODULE_AUTHOR("Koro Chen koro.chen@mediatek.com"); -MODULE_LICENSE("GPL v2");
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"; + };
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");
participants (1)
-
Garlic Tseng