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
--
1.9.1
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 <[email protected]>
---
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 <[email protected]>
- *
- * 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 <[email protected]>");
-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 <[email protected]>
- *
- * 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 <[email protected]>");
-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 <[email protected]>
- *
- * 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 <[email protected]>");
-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 <[email protected]>
- *
- * 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 <[email protected]>");
-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 <[email protected]>
+ *
+ * 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 <[email protected]>");
+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 <[email protected]>
+ *
+ * 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 <[email protected]>");
+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 <[email protected]>
+ *
+ * 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 <[email protected]>");
+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 <[email protected]>
+ *
+ * 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 <[email protected]>");
+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 <[email protected]>
+ * Sascha Hauer <[email protected]>
+ * Hidalgo Huang <[email protected]>
+ * Ir Lian <[email protected]>
+ *
+ * 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 <[email protected]>
+ * Sascha Hauer <[email protected]>
+ * Hidalgo Huang <[email protected]>
+ * Ir Lian <[email protected]>
+ *
+ * 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 <[email protected]>");
+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 <[email protected]>
- * Sascha Hauer <[email protected]>
- * Hidalgo Huang <[email protected]>
- * Ir Lian <[email protected]>
- *
- * 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 <[email protected]>
- * Sascha Hauer <[email protected]>
- * Hidalgo Huang <[email protected]>
- * Ir Lian <[email protected]>
- *
- * 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 <[email protected]>");
-MODULE_LICENSE("GPL v2");
--
1.9.1
Add documents for mt2701-cs42448 machine driver and mt2701 platform driver.
Signed-off-by: Garlic Tseng <[email protected]>
---
.../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";
+ };
--
1.9.1
Add mt2701 platform driver. There are some BLOCK_COMMENT_STYLE error from
checkpatch and they will be fixed after BT functions finished.
Signed-off-by: Garlic Tseng <[email protected]>
---
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 ++
7 files changed, 2560 insertions(+)
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-common.h
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-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
diff --git a/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c
new file mode 100644
index 0000000..eb1334a
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c
@@ -0,0 +1,259 @@
+/*
+ * mtk2701-afe-clock-ctrl.c -- Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mtk2701-reg.h"
+#include "mtk2701-afe-common.h"
+#include "mtk2701-afe-clock-ctrl.h"
+
+void mtk2701_afe_enable_clock(struct mtk_afe *afe, int en)
+{
+ if (en) {
+ mtk2701_turn_on_a1sys_clock(afe);
+ mtk2701_turn_on_a2sys_clock(afe);
+ mtk2701_turn_on_afe_clock(afe);
+ regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+ AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
+ AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ AFE_DAC_CON0_AFE_ON, AFE_DAC_CON0_AFE_ON);
+ regmap_write(afe->regmap, PWR2_TOP_CON, PWR2_TOP_CON_INIT_VAL);
+ regmap_write(afe->regmap, PWR1_ASM_CON1,
+ PWR1_ASM_CON1_INIT_VAL);
+ regmap_write(afe->regmap, PWR2_ASM_CON1,
+ PWR2_ASM_CON1_INIT_VAL);
+ } else {
+ mtk2701_turn_off_afe_clock(afe);
+ mtk2701_turn_off_a1sys_clock(afe);
+ mtk2701_turn_off_a2sys_clock(afe);
+ regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+ AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ AFE_DAC_CON0_AFE_ON, 0);
+ }
+}
+
+void mtk2701_turn_on_a1sys_clock(struct mtk_afe *afe)
+{
+ int ret = 0;
+ struct audio_clock_attr *aud_clks = afe->aud_clks;
+
+ /* Set Mux */
+ ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__,
+ aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock_data->name,
+ ret);
+
+ ret = clk_set_parent(aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock,
+ aud_clks[AUDCLK_TOP_AUD1PLL_98M].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock_data->name,
+ aud_clks[AUDCLK_TOP_AUD1PLL_98M].clock_data->name, ret);
+
+ /* Set Divider */
+ ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__,
+ aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock_data->name,
+ ret);
+ ret = clk_set_rate(aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock,
+ 98304000 / 2);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock_data->name,
+ 98304000 / 2, ret);
+
+ /* Enable clock gate */
+ ret = clk_enable(aud_clks[AUDCLK_TOP_AUD_48K_TIMING].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_AUD_48K_TIMING].clock_data->name,
+ ret);
+ /* Enable infra audio */
+ ret = clk_enable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__,
+ aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock_data->name, ret);
+}
+
+void mtk2701_turn_off_a1sys_clock(struct mtk_afe *afe)
+{
+ struct audio_clock_attr *aud_clks = afe->aud_clks;
+
+ clk_disable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock);
+ clk_disable(aud_clks[AUDCLK_TOP_AUD_48K_TIMING].clock);
+ clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUD_MUX1_DIV].clock);
+ clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUD_MUX1_SEL].clock);
+}
+
+void mtk2701_turn_on_a2sys_clock(struct mtk_afe *afe)
+{
+ int ret = 0;
+ struct audio_clock_attr *aud_clks = afe->aud_clks;
+ /* Set Mux */
+ ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__,
+ aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock_data->name,
+ ret);
+ ret = clk_set_parent(aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock,
+ aud_clks[AUDCLK_TOP_AUD2PLL_90M].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock_data->name,
+ aud_clks[AUDCLK_TOP_AUD2PLL_90M].clock_data->name, ret);
+ /* Set Divider */
+ ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__,
+ aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock_data->name,
+ ret);
+ ret = clk_set_rate(aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock,
+ 90316800 / 2);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock_data->name,
+ 90316800 / 2, ret);
+
+ /* Enable clock gate */
+ ret = clk_enable(aud_clks[AUDCLK_TOP_AUD_44K_TIMING].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_AUD_44K_TIMING].clock_data->name,
+ ret);
+ /* Enable infra audio */
+ ret = clk_enable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__,
+ aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock_data->name, ret);
+}
+
+void mtk2701_turn_off_a2sys_clock(struct mtk_afe *afe)
+{
+ struct audio_clock_attr *aud_clks = afe->aud_clks;
+
+ clk_disable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock);
+ clk_disable(aud_clks[AUDCLK_TOP_AUD_44K_TIMING].clock);
+ clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUD_MUX2_DIV].clock);
+ clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUD_MUX2_SEL].clock);
+}
+
+void mtk2701_turn_on_afe_clock(struct mtk_afe *afe)
+{
+ int ret;
+ struct audio_clock_attr *aud_clks = afe->aud_clks;
+
+ /*MT_CG_INFRA_AUDIO, INFRA_PDN_STA[5]*/
+ ret = clk_enable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__,
+ aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock_data->name, ret);
+
+ /* Set AUDCLK_TOP_AUDINTBUS to AUDCLK_TOP_SYSPLL1_D4 */
+ ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_AUDINTBUS].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__,
+ aud_clks[AUDCLK_TOP_AUDINTBUS].clock_data->name, ret);
+
+ ret = clk_set_parent(aud_clks[AUDCLK_TOP_AUDINTBUS].clock,
+ aud_clks[AUDCLK_TOP_SYSPLL1_D4].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_AUDINTBUS].clock_data->name,
+ aud_clks[AUDCLK_TOP_SYSPLL1_D4].clock_data->name, ret);
+
+ /* Set AUDCLK_TOP_ASM_H_SEL to AUDCLK_TOP_UNIVPLL2_D2*/
+ ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_ASM_H_SEL].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__,
+ aud_clks[AUDCLK_TOP_ASM_H_SEL].clock_data->name, ret);
+
+ ret = clk_set_parent(aud_clks[AUDCLK_TOP_ASM_H_SEL].clock,
+ aud_clks[AUDCLK_TOP_UNIVPLL2_D2].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_ASM_H_SEL].clock_data->name,
+ aud_clks[AUDCLK_TOP_UNIVPLL2_D2].clock_data->name, ret);
+
+ if (ret)
+ dev_err(afe->dev, "%s clk_enable %s fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_ASM_H_SEL].clock_data->name, ret);
+
+ /* Set AUDCLK_TOP_ASM_M_SEL to AUDCLK_TOP_UNIVPLL2_D4*/
+ ret = clk_prepare_enable(aud_clks[AUDCLK_TOP_ASM_M_SEL].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__,
+ aud_clks[AUDCLK_TOP_ASM_M_SEL].clock_data->name, ret);
+
+ ret = clk_set_parent(aud_clks[AUDCLK_TOP_ASM_M_SEL].clock,
+ aud_clks[AUDCLK_TOP_UNIVPLL2_D4].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[AUDCLK_TOP_ASM_M_SEL].clock_data->name,
+ aud_clks[AUDCLK_TOP_UNIVPLL2_D4].clock_data->name, ret);
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_AFE, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_APLL_CK, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_A1SYS, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_A2SYS, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
+}
+
+void mtk2701_turn_off_afe_clock(struct mtk_afe *afe)
+{
+ struct audio_clock_attr *aud_clks = afe->aud_clks;
+
+ /*MT_CG_INFRA_AUDIO,*/
+ clk_disable(aud_clks[AUDCLK_INFRA_SYS_AUDIO].clock);
+
+ clk_disable_unprepare(aud_clks[AUDCLK_TOP_AUDINTBUS].clock);
+ clk_disable_unprepare(aud_clks[AUDCLK_TOP_ASM_H_SEL].clock);
+ clk_disable_unprepare(aud_clks[AUDCLK_TOP_ASM_M_SEL].clock);
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_APLL_CK,
+ AUDIO_TOP_CON0_PDN_APLL_CK);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_A1SYS, AUDIO_TOP_CON4_PDN_A1SYS);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_A2SYS, AUDIO_TOP_CON4_PDN_A2SYS);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_AFE_CONN,
+ AUDIO_TOP_CON4_PDN_AFE_CONN);
+}
+
+MODULE_DESCRIPTION("MTK2701 afe clock control");
+MODULE_AUTHOR("Garlic Tseng <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h
new file mode 100644
index 0000000..832b1a9
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h
@@ -0,0 +1,28 @@
+/*
+ * mtk2701-afe-clock-ctrl.h -- Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK2701_AFE_CLOCK_CTRL_H_
+#define _MTK2701_AFE_CLOCK_CTRL_H_
+
+void mtk2701_afe_enable_clock(struct mtk_afe *afe, int en);
+void mtk2701_turn_on_a1sys_clock(struct mtk_afe *afe);
+void mtk2701_turn_off_a1sys_clock(struct mtk_afe *afe);
+void mtk2701_turn_on_a2sys_clock(struct mtk_afe *afe);
+void mtk2701_turn_off_a2sys_clock(struct mtk_afe *afe);
+void mtk2701_turn_on_afe_clock(struct mtk_afe *afe);
+void mtk2701_turn_off_afe_clock(struct mtk_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mtk2701-afe-common.h b/sound/soc/mediatek/mt2701/mtk2701-afe-common.h
new file mode 100644
index 0000000..daf40dd
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mtk2701-afe-common.h
@@ -0,0 +1,217 @@
+/*
+ * mtk2701-afe-common.h -- Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <[email protected]>
+ * Koro Chen <[email protected]>
+ * Sascha Hauer <[email protected]>
+ * Hidalgo Huang <[email protected]>
+ * Ir Lian <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_COMMON_H_
+#define _MTK_AFE_COMMON_H_
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#define MTK_MEMIF_STREAM_NUM (SNDRV_PCM_STREAM_LAST + 1)
+
+enum {
+ MTK_AFE_I2S_1,
+ MTK_AFE_I2S_2,
+ MTK_AFE_I2S_3,
+ MTK_AFE_I2S_4,
+ MTK_I2S_NUM,
+};
+
+enum {
+ MTK_AFE_MEMIF_1,
+ MTK_AFE_MEMIF_2,
+ MTK_AFE_MEMIF_3,
+ MTK_AFE_MEMIF_4,
+ MTK_AFE_MEMIF_5,
+ MTK_AFE_MEMIF_SINGLE_NUM,
+ MTK_AFE_MEMIF_M = MTK_AFE_MEMIF_SINGLE_NUM,
+ MTK_AFE_MEMIF_NUM,
+ MTK_AFE_IO_I2S = MTK_AFE_MEMIF_NUM,
+ MTK_AFE_IO_2ND_I2S,
+ MTK_AFE_IO_3RD_I2S,
+ MTK_AFE_IO_4TH_I2S,
+ MTK_AFE_IO_5TH_I2S,
+ MTK_AFE_IO_6TH_I2S,
+ MTK_AFE_IO_MRG_O,
+ MTK_AFE_IO_MRG_I,
+};
+
+enum {
+ /*need for DAIBT, will implement before review*/
+/*
+ IRQ_AFE_IRQ1,
+ IRQ_AFE_IRQ2,
+*/
+ IRQ_ASYS_START,
+ IRQ_ASYS_IRQ1 = IRQ_ASYS_START,
+ IRQ_ASYS_IRQ2,
+ IRQ_ASYS_IRQ3,
+ IRQ_ASYS_END,
+ IRQ_NUM = IRQ_ASYS_END,
+};
+
+enum {
+ DIV_ID_MCLK_TO_BCK,
+ DIV_ID_BCK_TO_LRCK,
+};
+
+/*2701 clock def*/
+enum audio_system_clock_type {
+ AUDCLK_INFRA_SYS_AUDIO,
+ AUDCLK_TOP_AUD_MUX1_SEL,
+ AUDCLK_TOP_AUD_MUX2_SEL,
+ AUDCLK_TOP_AUD_MUX1_DIV,
+ AUDCLK_TOP_AUD_MUX2_DIV,
+ AUDCLK_TOP_AUD_48K_TIMING,
+ AUDCLK_TOP_AUD_44K_TIMING,
+ AUDCLK_TOP_AUDPLL_MUX_SEL,
+ AUDCLK_TOP_APLL_SEL,
+ AUDCLK_TOP_AUD1PLL_98M,
+ AUDCLK_TOP_AUD2PLL_90M,
+ AUDCLK_TOP_HADDS2PLL_98M,
+ AUDCLK_TOP_HADDS2PLL_294M,
+ AUDCLK_TOP_AUDPLL,
+ AUDCLK_TOP_AUDPLL_D4,
+ AUDCLK_TOP_AUDPLL_D8,
+ AUDCLK_TOP_AUDPLL_D16,
+ AUDCLK_TOP_AUDPLL_D24,
+ AUDCLK_TOP_AUDINTBUS,
+ AUDCLK_CLK_26M,
+ AUDCLK_TOP_SYSPLL1_D4,
+ AUDCLK_TOP_AUD_K1_SRC_SEL,
+ AUDCLK_TOP_AUD_K2_SRC_SEL,
+ AUDCLK_TOP_AUD_K3_SRC_SEL,
+ AUDCLK_TOP_AUD_K4_SRC_SEL,
+ AUDCLK_TOP_AUD_K5_SRC_SEL,
+ AUDCLK_TOP_AUD_K6_SRC_SEL,
+ AUDCLK_TOP_AUD_K1_SRC_DIV,
+ AUDCLK_TOP_AUD_K2_SRC_DIV,
+ AUDCLK_TOP_AUD_K3_SRC_DIV,
+ AUDCLK_TOP_AUD_K4_SRC_DIV,
+ AUDCLK_TOP_AUD_K5_SRC_DIV,
+ AUDCLK_TOP_AUD_K6_SRC_DIV,
+ AUDCLK_TOP_AUD_I2S1_MCLK,
+ AUDCLK_TOP_AUD_I2S2_MCLK,
+ AUDCLK_TOP_AUD_I2S3_MCLK,
+ AUDCLK_TOP_AUD_I2S4_MCLK,
+ AUDCLK_TOP_AUD_I2S5_MCLK,
+ AUDCLK_TOP_AUD_I2S6_MCLK,
+ AUDCLK_TOP_ASM_M_SEL,
+ AUDCLK_TOP_ASM_H_SEL,
+ AUDCLK_TOP_UNIVPLL2_D4,
+ AUDCLK_TOP_UNIVPLL2_D2,
+ AUDCLK_TOP_SYSPLL_D5,
+ CLOCK_NUM
+};
+
+struct audio_clock_attr_data {
+ const char *name;
+ const bool prepare_once;
+};
+
+struct audio_clock_attr {
+ struct audio_clock_attr_data *clock_data;
+ bool is_prepared;
+ struct clk *clock;
+};
+
+struct mtk_afe;
+struct snd_pcm_substream;
+
+struct mtk_afe_memif_data {
+ int id;
+ const char *name;
+ int reg_ofs_base;
+ int reg_ofs_cur;
+ int fs_reg;
+ int fs_shift;
+ int mono_reg;
+ int mono_shift;
+ int enable_shift;
+ int hd_reg;
+ int hd_shift;
+ int agent_disable_shift;
+};
+
+struct mtk_afe_irq_data {
+ int irq_id;
+ int irq_cnt_reg;
+ int irq_cnt_shift;
+ int irq_cnt_maskbit;
+ int irq_fs_reg;
+ int irq_fs_shift;
+ int irq_fs_maskbit;
+ int irq_en_reg;
+ int irq_en_shift;
+ int irq_occupy;
+};
+
+struct mtk_afe_irq {
+ const struct mtk_afe_irq_data *irq_data;
+ int irq_occupyed;
+ struct mtk_afe_memif *memif;
+ void (*isr)(struct mtk_afe *afe, struct mtk_afe_memif *memif);
+};
+
+struct mtk_afe_memif {
+ unsigned int phys_buf_addr;
+ int buffer_size;
+ unsigned int hw_ptr;
+ struct snd_pcm_substream *substream;
+ const struct mtk_afe_memif_data *data;
+ struct mtk_afe_irq *irq;
+};
+
+struct mtk_i2s_data {
+ int i2s_ctrl_reg;
+ int i2s_pwn_shift;
+ int i2s_asrc_fs_shift;
+ int i2s_asrc_fs_mask;
+};
+
+enum mtk_i2s_dir {
+ I2S_OUT,
+ I2S_IN,
+ I2S_DIR_NUM,
+};
+
+struct mtk_i2s_path {
+ int dai_id;
+ int mclk_rate;
+ int div_mclk_to_bck;
+ int div_bck_to_lrck;
+ int format;
+ snd_pcm_format_t stream_fmt;
+ int on[I2S_DIR_NUM];
+ int occupied[I2S_DIR_NUM];
+ const struct mtk_i2s_data *i2s_data[2];
+};
+
+struct mtk_afe {
+ void __iomem *base_addr;
+ struct device *dev;
+ struct regmap *regmap;
+ struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM][MTK_MEMIF_STREAM_NUM];
+ struct audio_clock_attr aud_clks[CLOCK_NUM];
+ struct mtk_afe_irq irqs[IRQ_NUM];
+ struct mtk_i2s_path i2s_path[MTK_I2S_NUM];
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c
new file mode 100644
index 0000000..630caec
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c
@@ -0,0 +1,1752 @@
+/*
+ * Mediatek ALSA SoC AFE platform driver for 2701
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <[email protected]>
+ * Koro Chen <[email protected]>
+ * Hidalgo Huang <[email protected]>
+ * Ir Lian <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+
+#include "mtk2701-afe-common.h"
+
+#include "mtk2701-afe-clock-ctrl.h"
+#include "mtk2701-irq.h"
+#include "mtk2701-reg.h"
+
+#define AFE_BASE_END_OFFSET 8
+#define AFE_IRQ_STATUS_BITS 0xff
+#define PLL_DOMAIN_0_RATE 98304000
+#define PLL_DOMAIN_1_RATE 90316800
+
+static const struct snd_pcm_hardware mtk2701_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 1024 * 256,
+ .periods_min = 4,
+ .periods_max = 1024,
+ .buffer_bytes_max = 1024 * 1024 * 16,
+ .fifo_size = 0,
+};
+
+static snd_pcm_uframes_t mtk2701_afe_pcm_pointer
+ (struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int stream_dir = substream->stream;
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id][stream_dir];
+
+ return bytes_to_frames(substream->runtime, memif->hw_ptr);
+}
+
+static const struct snd_pcm_ops mtk2701_afe_pcm_ops = {
+ .ioctl = snd_pcm_lib_ioctl,
+ .pointer = mtk2701_afe_pcm_pointer,
+};
+
+static int mtk2701_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ size_t size;
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+
+ size = mtk2701_afe_hardware.buffer_bytes_max;
+ return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
+}
+
+static void mtk2701_afe_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static const struct snd_soc_platform_driver mtk2701_afe_pcm_platform = {
+ .ops = &mtk2701_afe_pcm_ops,
+ .pcm_new = mtk2701_afe_pcm_new,
+ .pcm_free = mtk2701_afe_pcm_free,
+};
+
+struct mtk2701_afe_rate {
+ unsigned int rate;
+ unsigned int regvalue;
+};
+
+static const struct mtk2701_afe_rate mtk2701_afe_i2s_rates[] = {
+ { .rate = 8000, .regvalue = 0 },
+ { .rate = 12000, .regvalue = 1 },
+ { .rate = 16000, .regvalue = 2 },
+ { .rate = 24000, .regvalue = 3 },
+ { .rate = 32000, .regvalue = 4 },
+ { .rate = 48000, .regvalue = 5 },
+ { .rate = 96000, .regvalue = 6 },
+ { .rate = 192000, .regvalue = 7 },
+ { .rate = 384000, .regvalue = 8 },
+ { .rate = 7350, .regvalue = 16 },
+ { .rate = 11025, .regvalue = 17 },
+ { .rate = 14700, .regvalue = 18 },
+ { .rate = 22050, .regvalue = 19 },
+ { .rate = 29400, .regvalue = 20 },
+ { .rate = 44100, .regvalue = 21 },
+ { .rate = 88200, .regvalue = 22 },
+ { .rate = 176400, .regvalue = 23 },
+ { .rate = 352800, .regvalue = 24 },
+};
+
+static const struct mtk2701_afe_rate mtk2701_afe_irq_rates[] = {
+ { .rate = 8000, .regvalue = 0 },
+ { .rate = 12000, .regvalue = 1 },
+ { .rate = 16000, .regvalue = 2 },
+ { .rate = 24000, .regvalue = 3 },
+ { .rate = 32000, .regvalue = 4 },
+ { .rate = 48000, .regvalue = 5 },
+ { .rate = 96000, .regvalue = 6 },
+ { .rate = 192000, .regvalue = 7 },
+ { .rate = 11025, .regvalue = 9 },
+ { .rate = 22050, .regvalue = 0xb },
+ { .rate = 44100, .regvalue = 0xd },
+ { .rate = 88200, .regvalue = 0xe },
+ { .rate = 176400, .regvalue = 0xf },
+};
+
+void mtk2701_mclk_configuration(struct mtk_afe *afe, int id, int domain,
+ int mclk)
+{
+ int ret;
+ int aud_src_div_id = AUDCLK_TOP_AUD_K1_SRC_DIV + id;
+ int aud_src_clk_id = AUDCLK_TOP_AUD_K1_SRC_SEL + id;
+ struct audio_clock_attr *clks = afe->aud_clks;
+
+ /* Set MCLK Kx_SRC_SEL(domain) */
+ ret = clk_prepare_enable(clks[aud_src_clk_id].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, clks[aud_src_clk_id].clock_data->name, ret);
+
+ if (domain == 0) {
+ ret = clk_set_parent(clks[aud_src_clk_id].clock,
+ clks[AUDCLK_TOP_AUD_MUX1_SEL].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, clks[aud_src_clk_id].clock_data->name,
+ clks[AUDCLK_TOP_AUD_MUX1_SEL].clock_data->name,
+ ret);
+ } else {
+ ret = clk_set_parent(clks[aud_src_clk_id].clock,
+ clks[AUDCLK_TOP_AUD_MUX2_SEL].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, clks[aud_src_clk_id].clock_data->name,
+ clks[AUDCLK_TOP_AUD_MUX2_SEL].clock_data->name,
+ ret);
+ }
+ clk_disable_unprepare(clks[aud_src_clk_id].clock);
+
+ /* Set MCLK Kx_SRC_DIV(divider) */
+ ret = clk_prepare_enable(clks[aud_src_div_id].clock);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, clks[aud_src_div_id].clock_data->name, ret);
+
+ ret = clk_set_rate(clks[aud_src_div_id].clock, mclk);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
+ clks[aud_src_div_id].clock_data->name, mclk, ret);
+ clk_disable_unprepare(clks[aud_src_div_id].clock);
+}
+
+int mtk2701_dai_num_to_i2s(struct mtk_afe *afe, int num)
+{
+ int val = num - MTK_AFE_IO_I2S;
+
+ if (val < 0 || val > MTK_I2S_NUM) {
+ dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
+ __func__, num, val);
+ return -1;
+ }
+ return val;
+}
+
+static int mtk2701_afe_i2s_fs(unsigned int sample_rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk2701_afe_i2s_rates); i++)
+ if (mtk2701_afe_i2s_rates[i].rate == sample_rate)
+ return mtk2701_afe_i2s_rates[i].regvalue;
+
+ return -EINVAL;
+}
+
+/*need for BT, will implement BT before upstream*/
+/*
+static int mtk2701_afe_irq_fs(unsigned int sample_rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk2701_afe_irq_rates); i++)
+ if (mtk2701_afe_irq_rates[i].rate == sample_rate)
+ return mtk2701_afe_irq_rates[i].regvalue;
+
+ return -EINVAL;
+}
+*/
+
+static int mtk2701_afe_i2s_enable_clks(struct mtk_afe *afe,
+ struct audio_clock_attr *clk_attr)
+{
+ int ret;
+
+ if (clk_attr->clock_data->prepare_once)
+ ret = clk_enable(clk_attr->clock);
+ else
+ ret = clk_prepare_enable(clk_attr->clock);
+ if (ret) {
+ dev_err(afe->dev, "Failed to enable %s\n",
+ clk_attr->clock_data->name);
+ return ret;
+ }
+ return 0;
+}
+
+static int mtk2701_afe_i2s_disable_clks(struct mtk_afe *afe,
+ struct audio_clock_attr *clk_attr)
+{
+ if (clk_attr->clock_data->prepare_once)
+ clk_disable(clk_attr->clock);
+ else
+ clk_disable_unprepare(clk_attr->clock);
+ return 0;
+}
+
+static int mtk2701_afe_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id);
+
+ /*enable mclk*/
+ mtk2701_afe_i2s_enable_clks(afe,
+ &afe->aud_clks[AUDCLK_TOP_AUD_I2S1_MCLK + i2s_num]);
+ return 0;
+}
+
+static int mtk2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ int dir_invert)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id);
+ struct mtk_i2s_path *i2s_path = &afe->i2s_path[i2s_num];
+ const struct mtk_i2s_data *i2s_data;
+ int stream_dir = substream->stream;
+
+ if (dir_invert) {
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+ else
+ stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+ }
+ i2s_data = i2s_path->i2s_data[stream_dir];
+
+ i2s_path->on[stream_dir]--;
+ if (i2s_path->on[stream_dir] < 0) {
+ dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
+ i2s_path->on[stream_dir], stream_dir);
+ i2s_path->on[stream_dir] = 0;
+ }
+ if (i2s_path->on[stream_dir])
+ return 0;
+
+ /*disable i2s*/
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+ ASYS_I2S_CON_I2S_EN, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ 1 << i2s_data->i2s_pwn_shift,
+ 1 << i2s_data->i2s_pwn_shift);
+ return 0;
+}
+
+static void mtk2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id);
+ struct mtk_i2s_path *i2s_path = &afe->i2s_path[i2s_num];
+
+ if (i2s_path->occupied[substream->stream]) {
+ i2s_path->occupied[substream->stream] = 0;
+ } else {
+ dev_info(afe->dev,
+ "i2s not occpuied but someone want to shutdown it.\n");
+ goto I2S_UNSTART;
+ }
+
+ mtk2701_afe_i2s_path_shutdown(substream, dai, 0);
+
+ /*need to disable i2s-out path when disable i2s-in*/
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mtk2701_afe_i2s_path_shutdown(substream, dai, 1);
+
+I2S_UNSTART:
+ /*disable mclk*/
+ mtk2701_afe_i2s_disable_clks(afe,
+ &afe->aud_clks[AUDCLK_TOP_AUD_I2S1_MCLK + i2s_num]);
+}
+
+static int mtk2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ int dir_invert)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id);
+ struct mtk_i2s_path *i2s_path = &afe->i2s_path[i2s_num];
+ const struct mtk_i2s_data *i2s_data;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ int reg, fs, w_len = 1;
+ int stream_dir = substream->stream;
+ unsigned int mask = 0, val = 0;
+
+ if (dir_invert) {
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+ else
+ stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+ }
+ i2s_data = i2s_path->i2s_data[stream_dir];
+
+ /*no need to enable if already done*/
+ i2s_path->on[stream_dir]++;
+
+ if (i2s_path->on[stream_dir] != 1)
+ return 0;
+
+ fs = mtk2701_afe_i2s_fs(runtime->rate);
+
+ if (i2s_path->div_bck_to_lrck == 32)
+ w_len = 0;
+ else if (i2s_path->div_bck_to_lrck == 64)
+ w_len = 1;
+ else
+ dev_warn(dai->dev, "%s() bad bit count %d\n", __func__,
+ afe->i2s_path[i2s_num].div_bck_to_lrck);
+
+ mask = ASYS_I2S_CON_FS |
+ ASYS_I2S_CON_MULTI_CH | /*0*/
+ ASYS_I2S_CON_I2S_COUPLE_MODE | /*0*/
+ ASYS_I2S_CON_I2S_MODE |
+ ASYS_I2S_CON_WIDE_MODE;
+
+ val = ASYS_I2S_CON_FS_SET(fs) |
+ ASYS_I2S_CON_I2S_MODE |
+ ASYS_I2S_CON_WIDE_MODE_SET(w_len);
+
+ if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
+ mask |= ASYS_I2S_IN_PHASE_FIX;
+ val |= ASYS_I2S_IN_PHASE_FIX;
+ }
+
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
+
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ASMO_TIMING_CON1;
+ else
+ reg = ASMI_TIMING_CON1;
+
+ regmap_update_bits(afe->regmap, reg,
+ i2s_data->i2s_asrc_fs_mask << i2s_data->i2s_asrc_fs_shift,
+ fs << i2s_data->i2s_asrc_fs_shift);
+
+ /*enable i2s*/
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ 1 << i2s_data->i2s_pwn_shift,
+ 0 << i2s_data->i2s_pwn_shift);
+
+ /*reset irq hw status before enable*/
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+ ASYS_I2S_CON_RESET, ASYS_I2S_CON_RESET);
+ udelay(1);
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+ ASYS_I2S_CON_RESET, 0);
+ udelay(1);
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+ ASYS_I2S_CON_I2S_EN, ASYS_I2S_CON_I2S_EN);
+ return 0;
+}
+
+static int mtk2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int clk_domain;
+
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id);
+ struct mtk_i2s_path *i2s_path = &afe->i2s_path[i2s_num];
+ const int mclk_rate = i2s_path->mclk_rate;
+
+ if (i2s_path->occupied[substream->stream])
+ return -EBUSY;
+ i2s_path->occupied[substream->stream] = 1;
+
+ if (PLL_DOMAIN_0_RATE % mclk_rate == 0) {
+ clk_domain = 0;
+ } else if (PLL_DOMAIN_1_RATE % mclk_rate == 0) {
+ clk_domain = 1;
+ } else {
+ dev_err(dai->dev, "%s() bad mclk rate %d\n",
+ __func__, mclk_rate);
+ return -EINVAL;
+ }
+ mtk2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mtk2701_i2s_path_prepare_enable(substream, dai, 0);
+ } else {
+ /*need to enable i2s-out path when enable i2s-in*/
+ /*prepare for another direction "out"*/
+ mtk2701_i2s_path_prepare_enable(substream, dai, 1);
+ /*prepare for "in"*/
+ mtk2701_i2s_path_prepare_enable(substream, dai, 0);
+ }
+
+ return 0;
+}
+
+static int mtk2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct mtk_afe *afe = dev_get_drvdata(dai->dev);
+ int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id);
+ /* mclk */
+ if (dir == SND_SOC_CLOCK_IN) {
+ dev_warn(dai->dev,
+ "%s() warning: mt2701 doesn't support mclk input\n",
+ __func__);
+ return -EINVAL;
+ }
+ afe->i2s_path[i2s_num].mclk_rate = freq;
+ return 0;
+}
+
+static int mtk2701_afe_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id,
+ int div)
+{
+ struct mtk_afe *afe = dev_get_drvdata(dai->dev);
+ int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id);
+
+ switch (div_id) {
+ case DIV_ID_MCLK_TO_BCK:
+ afe->i2s_path[i2s_num].div_mclk_to_bck = div;
+ break;
+ case DIV_ID_BCK_TO_LRCK:
+ afe->i2s_path[i2s_num].div_bck_to_lrck = div;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mtk2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_afe *afe = dev_get_drvdata(dai->dev);
+ int i2s_num = mtk2701_dai_num_to_i2s(afe, dai->id);
+
+ afe = dev_get_drvdata(dai->dev);
+ afe->i2s_path[i2s_num].format = fmt;
+ return 0;
+}
+
+static int mtk2701_playback_mem_avail(struct mtk_afe *afe, int memif_num)
+{
+ struct mtk_afe_memif *memif_tmp;
+
+ if (memif_num >= MTK_AFE_MEMIF_1 &&
+ memif_num < MTK_AFE_MEMIF_SINGLE_NUM) {
+ memif_tmp =
+ &afe->memif[MTK_AFE_MEMIF_M][SNDRV_PCM_STREAM_PLAYBACK];
+ if (memif_tmp->substream)
+ return 0;
+ } else if (memif_num == MTK_AFE_MEMIF_M) {
+ int i;
+
+ for (i = MTK_AFE_MEMIF_1; i < MTK_AFE_MEMIF_SINGLE_NUM; ++i) {
+ memif_tmp = &afe->memif[i][SNDRV_PCM_STREAM_PLAYBACK];
+ if (memif_tmp->substream)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int mtk2701_afe_dais_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_dir = substream->stream;
+ int memif_num = rtd->cpu_dai->id;
+ struct mtk_afe_memif *memif = &afe->memif[memif_num][stream_dir];
+ int is_dlm = 0;
+ int ret, i;
+ struct mtk_afe_memif *memif_tmp;
+
+ if (memif->substream) {
+ dev_warn(afe->dev, "%s memif is occupied, stream_dir %d, memif_num = %d\n",
+ __func__, stream_dir, memif_num);
+ return -EBUSY;
+ }
+
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK &&
+ !mtk2701_playback_mem_avail(afe, memif_num)) {
+ dev_warn(afe->dev, "%s memif is not available, stream_dir %d, memif_num %d\n",
+ __func__, stream_dir, memif_num);
+ return -EBUSY;
+ }
+
+ if (memif_num == MTK_AFE_MEMIF_M &&
+ stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ is_dlm = 1;
+
+ memif->substream = substream;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+ /*enable agent*/
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON5,
+ 1 << memif->data->agent_disable_shift,
+ 0 << memif->data->agent_disable_shift);
+ if (is_dlm) {
+ for (i = MTK_AFE_MEMIF_1; i < MTK_AFE_MEMIF_SINGLE_NUM; ++i) {
+ memif_tmp = &afe->memif[i][SNDRV_PCM_STREAM_PLAYBACK];
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON5,
+ 1 << memif_tmp->data->agent_disable_shift,
+ 0 << memif_tmp->data->agent_disable_shift);
+ }
+ }
+
+ snd_soc_set_runtime_hwparams(substream, &mtk2701_afe_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+ /*require irq resource*/
+ if (!memif->irq) {
+ int irq_id = mtk2701_asys_irq_acquire(afe);
+
+ if (irq_id != IRQ_NUM) {
+ /* link */
+ memif->irq = &afe->irqs[irq_id];
+ afe->irqs[irq_id].memif = memif;
+ afe->irqs[irq_id].isr = mtk2701_memif_isr;
+ } else {
+ dev_err(afe->dev, "%s() error: no more asys irq\n",
+ __func__);
+ }
+ }
+ return ret;
+}
+
+static void mtk2701_afe_dais_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int stream_dir = substream->stream;
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id][stream_dir];
+ int irq_id, i;
+ int is_dlm = 0;
+ struct mtk_afe_memif *memif_tmp;
+
+ irq_id = memif->irq->irq_data->irq_id;
+ if (rtd->cpu_dai->id == MTK_AFE_MEMIF_M &&
+ stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ is_dlm = 1;
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON5,
+ 1 << memif->data->agent_disable_shift,
+ 1 << memif->data->agent_disable_shift);
+ if (is_dlm) {
+ for (i = MTK_AFE_MEMIF_1; i < MTK_AFE_MEMIF_SINGLE_NUM; ++i) {
+ memif_tmp = &afe->memif[i][SNDRV_PCM_STREAM_PLAYBACK];
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON5,
+ 1 << memif_tmp->data->agent_disable_shift,
+ 1 << memif_tmp->data->agent_disable_shift);
+ }
+ }
+ mtk2701_asys_irq_release(afe, irq_id);
+ memif->irq = NULL;
+ afe->irqs[irq_id].memif = NULL;
+ afe->irqs[irq_id].isr = NULL;
+ memif->substream = NULL;
+}
+
+static int mtk2701_afe_dais_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int mtk2701_afe_dais_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int mtk2701_afe_dais_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int stream_dir = substream->stream;
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id][stream_dir];
+ int is_dlm = 0;
+ int hd_audio = 0;
+ int fs;
+ int channels = runtime->channels;
+
+ if (rtd->cpu_dai->id == MTK_AFE_MEMIF_M &&
+ stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ is_dlm = 1;
+
+ /*set hd mode*/
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ hd_audio = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ hd_audio = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ hd_audio = 1;
+ break;
+ default:
+ dev_err(afe->dev, "%s() error: unsupported format %d\n",
+ __func__, substream->runtime->format);
+ break;
+ }
+
+ regmap_update_bits(afe->regmap, memif->data->hd_reg,
+ 1 << memif->data->hd_shift,
+ hd_audio << memif->data->hd_shift);
+
+ memif->phys_buf_addr = substream->runtime->dma_addr;
+ memif->buffer_size = substream->runtime->dma_bytes;
+ memif->hw_ptr = 0;
+
+ /* set rate */
+ if (memif->data->fs_shift < 0)
+ return 0;
+
+ fs = mtk2701_afe_i2s_fs(runtime->rate);
+ if (fs < 0)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap, memif->data->fs_reg,
+ 0x1f << memif->data->fs_shift,
+ fs << memif->data->fs_shift);
+ /* set channel */
+ if (memif->data->mono_shift >= 0) {
+ unsigned int mono = (runtime->channels == 1) ? 1 : 0;
+
+ regmap_update_bits(afe->regmap, memif->data->mono_reg,
+ 1 << memif->data->mono_shift,
+ mono << memif->data->mono_shift);
+ }
+ /* start */
+ regmap_write(afe->regmap,
+ memif->data->reg_ofs_base, memif->phys_buf_addr);
+ /* end */
+ regmap_write(afe->regmap,
+ memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+ memif->phys_buf_addr + memif->buffer_size - 1);
+
+ if (is_dlm) { /*setting for multi-ch playback*/
+ regmap_update_bits(afe->regmap,
+ AFE_MEMIF_PBUF_SIZE,
+ AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+ AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE);
+ regmap_update_bits(afe->regmap,
+ AFE_MEMIF_PBUF_SIZE,
+ AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK,
+ AFE_MEMIF_PBUF_SIZE_DLM_32BYTES);
+ regmap_update_bits(afe->regmap,
+ AFE_MEMIF_PBUF_SIZE,
+ AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK,
+ AFE_MEMIF_PBUF_SIZE_DLM_CH(channels));
+
+ } else if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(afe->regmap,
+ AFE_MEMIF_PBUF_SIZE,
+ AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+ AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE);
+ }
+ return 0;
+}
+
+static int mtk2701_afe_dais_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int stream_dir = substream->stream;
+ struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id][stream_dir];
+ struct mtk_afe_memif *memif_tmp;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ unsigned int counter = runtime->period_size;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /*memory interface enable*/
+ if (memif->data->enable_shift >= 0)
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ 1 << memif->data->enable_shift,
+ 1 << memif->data->enable_shift);
+
+ /* set irq counter */
+ regmap_update_bits(afe->regmap,
+ memif->irq->irq_data->irq_cnt_reg,
+ memif->irq->irq_data->irq_cnt_maskbit
+ << memif->irq->irq_data->irq_cnt_shift,
+ counter << (memif->irq->irq_data->irq_cnt_shift));
+ /* set irq fs */
+ if (memif->irq->irq_data->irq_fs_shift >= 0) {
+ int fs;
+ /*need for BT, will implement it before upstream*/
+ /*
+ if(memif->irqdata->irq_id == IRQ_AFE_IRQ1 ||
+ memif->irqdata->irq_id == IRQ_AFE_IRQ2 )
+ fs = mtk2701_afe_irq_fs(runtime->rate);
+ else
+ fs = mtk2701_afe_i2s_fs(runtime->rate);
+ */
+ fs = mtk2701_afe_i2s_fs(runtime->rate);
+ if (fs < 0)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ memif->irq->irq_data->irq_fs_reg,
+ memif->irq->irq_data->irq_fs_maskbit
+ << memif->irq->irq_data->irq_fs_shift,
+ fs << memif->irq->irq_data->irq_fs_shift);
+ }
+
+ if (rtd->cpu_dai->id == MTK_AFE_MEMIF_M &&
+ stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ memif_tmp = &afe->memif[MTK_AFE_MEMIF_1][stream_dir];
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ 1 << memif_tmp->data->enable_shift,
+ 1 << memif_tmp->data->enable_shift);
+ }
+ /* enable interrupt */
+ regmap_update_bits(afe->regmap,
+ memif->irq->irq_data->irq_en_reg,
+ 1 << memif->irq->irq_data->irq_en_shift,
+ 1 << memif->irq->irq_data->irq_en_shift);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* disable interrupt */
+ regmap_update_bits(afe->regmap,
+ memif->irq->irq_data->irq_en_reg,
+ 1 << memif->irq->irq_data->irq_en_shift,
+ 0 << memif->irq->irq_data->irq_en_shift);
+ memif->hw_ptr = 0;
+ /*memory interface disable*/
+ if (memif->data->enable_shift >= 0)
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ 1 << memif->data->enable_shift, 0);
+ if (rtd->cpu_dai->id == MTK_AFE_MEMIF_M &&
+ stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ memif_tmp = &afe->memif[MTK_AFE_MEMIF_1][stream_dir];
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ 1 << memif_tmp->data->enable_shift,
+ 0);
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mtk2701_afe_dai_ops = {
+ .startup = mtk2701_afe_dais_startup,
+ .shutdown = mtk2701_afe_dais_shutdown,
+ .hw_params = mtk2701_afe_dais_hw_params,
+ .hw_free = mtk2701_afe_dais_hw_free,
+ .prepare = mtk2701_afe_dais_prepare,
+ .trigger = mtk2701_afe_dais_trigger,
+};
+
+/* BE DAIs */
+static const struct snd_soc_dai_ops mtk2701_afe_i2s_ops = {
+ .startup = mtk2701_afe_i2s_startup,
+ .shutdown = mtk2701_afe_i2s_shutdown,
+ .prepare = mtk2701_afe_i2s_prepare,
+ .set_sysclk = mtk2701_afe_i2s_set_sysclk,
+ .set_clkdiv = mtk2701_afe_i2s_set_clkdiv,
+ .set_fmt = mtk2701_afe_i2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver mtk2701_afe_pcm_dais[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "PCM0",
+ .id = MTK_AFE_MEMIF_1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mtk2701_afe_dai_ops,
+ },
+ {
+ .name = "PCM_multi",
+ .id = MTK_AFE_MEMIF_M,
+ .playback = {
+ .stream_name = "DLM",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+ },
+ .ops = &mtk2701_afe_dai_ops,
+ },
+ {
+ .name = "PCM1",
+ .id = MTK_AFE_MEMIF_2,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+ },
+ .ops = &mtk2701_afe_dai_ops,
+ },
+ {
+ /* BE DAIs */
+ .name = "I2S0",
+ .id = MTK_AFE_IO_I2S,
+ .playback = {
+ .stream_name = "I2S0 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+ },
+ .capture = {
+ .stream_name = "I2S0 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+ },
+ .ops = &mtk2701_afe_i2s_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "I2S1",
+ .id = MTK_AFE_IO_2ND_I2S,
+ .playback = {
+ .stream_name = "I2S1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .capture = {
+ .stream_name = "I2S1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mtk2701_afe_i2s_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "I2S2",
+ .id = MTK_AFE_IO_3RD_I2S,
+ .playback = {
+ .stream_name = "I2S2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .capture = {
+ .stream_name = "I2S2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mtk2701_afe_i2s_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "I2S3",
+ .id = MTK_AFE_IO_4TH_I2S,
+ .playback = {
+ .stream_name = "I2S3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .capture = {
+ .stream_name = "I2S3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mtk2701_afe_i2s_ops,
+ .symmetric_rates = 1,
+ },
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o00_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o01_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o02_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_CONN2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o03_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o15_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I12 Switch", AFE_CONN15, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o16_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I13 Switch", AFE_CONN16, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o17_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o18_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o19_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o20_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o21_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o22_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o23_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_o24_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s0[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s0", ASYS_I2SO1_CON, 26, 1,
+ 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s1[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s1", ASYS_I2SO2_CON, 26, 1,
+ 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s2[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s2", PWR2_TOP_CON, 17, 1,
+ 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s3[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s3", PWR2_TOP_CON, 18, 1,
+ 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_i2s4[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s4", PWR2_TOP_CON, 19, 1,
+ 0),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc0[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out0", AUDIO_TOP_CON4, 14, 1,
+ 1),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc1[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out1", AUDIO_TOP_CON4, 15, 1,
+ 1),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc2[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out2", PWR2_TOP_CON, 6, 1,
+ 1),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc3[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out3", PWR2_TOP_CON, 7, 1,
+ 1),
+};
+
+static const struct snd_kcontrol_new mtk2701_afe_multi_ch_out_asrc4[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out4", PWR2_TOP_CON, 8, 1,
+ 1),
+};
+
+static const struct snd_soc_dapm_widget mtk2701_afe_pcm_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I02", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0, mtk2701_afe_o00_mix,
+ ARRAY_SIZE(mtk2701_afe_o00_mix)),
+ SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0, mtk2701_afe_o01_mix,
+ ARRAY_SIZE(mtk2701_afe_o01_mix)),
+ SND_SOC_DAPM_MIXER("O02", SND_SOC_NOPM, 0, 0, mtk2701_afe_o02_mix,
+ ARRAY_SIZE(mtk2701_afe_o02_mix)),
+ SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, mtk2701_afe_o03_mix,
+ ARRAY_SIZE(mtk2701_afe_o03_mix)),
+ SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0, mtk2701_afe_o15_mix,
+ ARRAY_SIZE(mtk2701_afe_o15_mix)),
+ SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0, mtk2701_afe_o16_mix,
+ ARRAY_SIZE(mtk2701_afe_o16_mix)),
+ SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0, mtk2701_afe_o17_mix,
+ ARRAY_SIZE(mtk2701_afe_o17_mix)),
+ SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0, mtk2701_afe_o18_mix,
+ ARRAY_SIZE(mtk2701_afe_o18_mix)),
+ SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0, mtk2701_afe_o19_mix,
+ ARRAY_SIZE(mtk2701_afe_o19_mix)),
+ SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0, mtk2701_afe_o20_mix,
+ ARRAY_SIZE(mtk2701_afe_o20_mix)),
+ SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0, mtk2701_afe_o21_mix,
+ ARRAY_SIZE(mtk2701_afe_o21_mix)),
+ SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0, mtk2701_afe_o22_mix,
+ ARRAY_SIZE(mtk2701_afe_o22_mix)),
+ SND_SOC_DAPM_MIXER("I12I13", SND_SOC_NOPM, 0, 0,
+ mtk2701_afe_multi_ch_out_i2s0,
+ ARRAY_SIZE(mtk2701_afe_multi_ch_out_i2s0)),
+ SND_SOC_DAPM_MIXER("I14I15", SND_SOC_NOPM, 0, 0,
+ mtk2701_afe_multi_ch_out_i2s1,
+ ARRAY_SIZE(mtk2701_afe_multi_ch_out_i2s1)),
+ SND_SOC_DAPM_MIXER("I16I17", SND_SOC_NOPM, 0, 0,
+ mtk2701_afe_multi_ch_out_i2s2,
+ ARRAY_SIZE(mtk2701_afe_multi_ch_out_i2s2)),
+ SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0,
+ mtk2701_afe_multi_ch_out_i2s3,
+ ARRAY_SIZE(mtk2701_afe_multi_ch_out_i2s3)),
+
+ SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0,
+ mtk2701_afe_multi_ch_out_asrc0,
+ ARRAY_SIZE(mtk2701_afe_multi_ch_out_asrc0)),
+ SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0,
+ mtk2701_afe_multi_ch_out_asrc1,
+ ARRAY_SIZE(mtk2701_afe_multi_ch_out_asrc1)),
+ SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0,
+ mtk2701_afe_multi_ch_out_asrc2,
+ ARRAY_SIZE(mtk2701_afe_multi_ch_out_asrc2)),
+ SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0,
+ mtk2701_afe_multi_ch_out_asrc3,
+ ARRAY_SIZE(mtk2701_afe_multi_ch_out_asrc3)),
+};
+
+static const struct snd_soc_dapm_route mtk2701_afe_pcm_routes[] = {
+ {"I12", NULL, "DL1"},
+ {"I13", NULL, "DL1"},
+ {"I2S0 Playback", NULL, "O15"},
+ {"I2S0 Playback", NULL, "O16"},
+ {"I2S1 Playback", NULL, "O17"},
+ {"I2S1 Playback", NULL, "O18"},
+ {"I2S2 Playback", NULL, "O19"},
+ {"I2S2 Playback", NULL, "O20"},
+ {"I2S3 Playback", NULL, "O21"},
+ {"I2S3 Playback", NULL, "O22"},
+
+ {"UL1", NULL, "O00"},
+ {"UL1", NULL, "O01"},
+ {"UL2", NULL, "O02"},
+ {"UL2", NULL, "O03"},
+ {"I00", NULL, "I2S0 Capture"},
+ {"I01", NULL, "I2S0 Capture"},
+
+ {"I02", NULL, "I2S1 Capture"},
+ {"I03", NULL, "I2S1 Capture"},
+ /*I02,03 link to UL2, also need to open I2S0*/
+ {"I02", NULL, "I2S0 Capture"},
+ {"I03", NULL, "I2S0 Capture"},
+
+ {"ASRC_O0", "Multi ch asrc out0", "DLM"},
+ {"ASRC_O1", "Multi ch asrc out1", "DLM"},
+ {"ASRC_O2", "Multi ch asrc out2", "DLM"},
+ {"ASRC_O3", "Multi ch asrc out3", "DLM"},
+
+ {"I12I13", "Multi ch Out I2s0", "ASRC_O0"},
+ {"I14I15", "Multi ch Out I2s1", "ASRC_O1"},
+ {"I16I17", "Multi ch Out I2s2", "ASRC_O2"},
+ {"I18I19", "Multi ch Out I2s3", "ASRC_O3"},
+
+ { "I12", NULL, "I12I13" },
+ { "I13", NULL, "I12I13" },
+ { "I14", NULL, "I14I15" },
+ { "I15", NULL, "I14I15" },
+ { "I16", NULL, "I16I17" },
+ { "I17", NULL, "I16I17" },
+ { "I18", NULL, "I18I19" },
+ { "I19", NULL, "I18I19" },
+
+ { "O00", "I00 Switch", "I00" },
+ { "O01", "I01 Switch", "I01" },
+ { "O02", "I02 Switch", "I02" },
+ { "O03", "I03 Switch", "I03" },
+
+ { "O15", "I12 Switch", "I12" },
+ { "O16", "I13 Switch", "I13" },
+ { "O17", "I14 Switch", "I14" },
+ { "O18", "I15 Switch", "I15" },
+ { "O19", "I16 Switch", "I16" },
+ { "O20", "I17 Switch", "I17" },
+ { "O21", "I18 Switch", "I18" },
+ { "O22", "I19 Switch", "I19" },
+};
+
+static const struct snd_soc_component_driver mtk2701_afe_pcm_dai_component = {
+ .name = "mtk-afe-pcm-dai",
+ .dapm_widgets = mtk2701_afe_pcm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mtk2701_afe_pcm_widgets),
+ .dapm_routes = mtk2701_afe_pcm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mtk2701_afe_pcm_routes),
+};
+
+static const struct mtk_afe_memif_data
+ memif_data[MTK_AFE_MEMIF_NUM][MTK_MEMIF_STREAM_NUM] = {
+ {
+ {
+ .name = "DL1",
+ .id = MTK_AFE_MEMIF_1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 0,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 16,
+ .enable_shift = 1,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 0,
+ .agent_disable_shift = 6,
+ },
+ {
+ .name = "UL1",
+ .id = MTK_AFE_MEMIF_1,
+ .reg_ofs_base = AFE_VUL_BASE,
+ .reg_ofs_cur = AFE_VUL_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 0,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 0,
+ .enable_shift = 10,
+ .hd_reg = AFE_MEMIF_HD_CON1,
+ .hd_shift = 0,
+ .agent_disable_shift = 0,
+ }
+ },
+ {
+ {
+ .name = "DL2",
+ .id = MTK_AFE_MEMIF_2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 5,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 17,
+ .enable_shift = 2,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 2,
+ .agent_disable_shift = 7,
+ },
+ {
+ .name = "UL2",
+ .id = MTK_AFE_MEMIF_2,
+ .reg_ofs_base = AFE_UL2_BASE,
+ .reg_ofs_cur = AFE_UL2_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 5,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 2,
+ .enable_shift = 11,
+ .hd_reg = AFE_MEMIF_HD_CON1,
+ .hd_shift = 2,
+ .agent_disable_shift = 1,
+ }
+ },
+ {
+ {
+ .name = "DL3",
+ .id = MTK_AFE_MEMIF_3,
+ .reg_ofs_base = AFE_DL3_BASE,
+ .reg_ofs_cur = AFE_DL3_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 10,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 18,
+ .enable_shift = 3,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 4,
+ .agent_disable_shift = 8,
+ },
+ {
+ .name = "UL3",
+ .id = MTK_AFE_MEMIF_3,
+ .reg_ofs_base = AFE_UL3_BASE,
+ .reg_ofs_cur = AFE_UL3_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 10,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 4,
+ .enable_shift = 12,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 0,
+ .agent_disable_shift = 2,
+ }
+ },
+ {
+ {
+ .name = "DL4",
+ .id = MTK_AFE_MEMIF_4,
+ .reg_ofs_base = AFE_DL4_BASE,
+ .reg_ofs_cur = AFE_DL4_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 15,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 19,
+ .enable_shift = 4,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 6,
+ .agent_disable_shift = 9,
+ },
+ {
+ .name = "UL4",
+ .id = MTK_AFE_MEMIF_4,
+ .reg_ofs_base = AFE_UL4_BASE,
+ .reg_ofs_cur = AFE_UL4_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 15,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 6,
+ .enable_shift = 13,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 6,
+ .agent_disable_shift = 3,
+ }
+ },
+ {
+ {
+ .name = "DL5",
+ .id = MTK_AFE_MEMIF_5,
+ .reg_ofs_base = AFE_DL5_BASE,
+ .reg_ofs_cur = AFE_DL5_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 20,
+ .mono_reg = AFE_DAC_CON3,
+ .mono_shift = 20,
+ .enable_shift = 5,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 8,
+ .agent_disable_shift = 10,
+ },
+ {
+ .name = "UL5",
+ .id = MTK_AFE_MEMIF_5,
+ .reg_ofs_base = AFE_UL5_BASE,
+ .reg_ofs_cur = AFE_UL5_CUR,
+ .fs_reg = AFE_DAC_CON2,
+ .fs_shift = 20,
+ .mono_reg = AFE_DAC_CON4,
+ .mono_shift = 8,
+ .enable_shift = 14,
+ .hd_reg = AFE_MEMIF_HD_CON0,
+ .hd_shift = 8,
+ .agent_disable_shift = 4,
+ }
+ },
+ {
+ {
+ .name = "DLM",
+ .id = MTK_AFE_MEMIF_M,
+ .reg_ofs_base = AFE_DLMCH_BASE,
+ .reg_ofs_cur = AFE_DLMCH_CUR,
+ .fs_reg = AFE_DAC_CON1,
+ .fs_shift = 0,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .enable_shift = 7,
+ .hd_reg = AFE_MEMIF_PBUF_SIZE,
+ .hd_shift = 28,
+ .agent_disable_shift = 12,
+ },
+ {
+ }
+ }
+};
+
+static const struct mtk_afe_irq_data irq_data[IRQ_NUM] = {
+ {
+ .irq_id = IRQ_ASYS_IRQ1,
+ .irq_cnt_reg = ASYS_IRQ1_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ1_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1f,
+ .irq_en_reg = ASYS_IRQ1_CON,
+ .irq_en_shift = 31,
+ },
+ {
+ .irq_id = IRQ_ASYS_IRQ2,
+ .irq_cnt_reg = ASYS_IRQ2_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ2_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1f,
+ .irq_en_reg = ASYS_IRQ2_CON,
+ .irq_en_shift = 31,
+ },
+ {
+ .irq_id = IRQ_ASYS_IRQ3,
+ .irq_cnt_reg = ASYS_IRQ3_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ3_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1f,
+ .irq_en_reg = ASYS_IRQ3_CON,
+ .irq_en_shift = 31,
+ }
+};
+
+static const struct mtk_i2s_data mtk2701_i2s_data[MTK_I2S_NUM][2] = {
+ {
+ {
+ .i2s_ctrl_reg = ASYS_I2SO1_CON,
+ .i2s_pwn_shift = 6,
+ .i2s_asrc_fs_shift = 0,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ {
+ .i2s_ctrl_reg = ASYS_I2SIN1_CON,
+ .i2s_pwn_shift = 0,
+ .i2s_asrc_fs_shift = 0,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ },
+ {
+ {
+ .i2s_ctrl_reg = ASYS_I2SO2_CON,
+ .i2s_pwn_shift = 7,
+ .i2s_asrc_fs_shift = 5,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ {
+ .i2s_ctrl_reg = ASYS_I2SIN2_CON,
+ .i2s_pwn_shift = 1,
+ .i2s_asrc_fs_shift = 5,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ },
+ {
+ {
+ .i2s_ctrl_reg = ASYS_I2SO3_CON,
+ .i2s_pwn_shift = 8,
+ .i2s_asrc_fs_shift = 10,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ {
+ .i2s_ctrl_reg = ASYS_I2SIN3_CON,
+ .i2s_pwn_shift = 2,
+ .i2s_asrc_fs_shift = 10,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ },
+ {
+ {
+ .i2s_ctrl_reg = ASYS_I2SO4_CON,
+ .i2s_pwn_shift = 9,
+ .i2s_asrc_fs_shift = 15,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ {
+ .i2s_ctrl_reg = ASYS_I2SIN4_CON,
+ .i2s_pwn_shift = 3,
+ .i2s_asrc_fs_shift = 15,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ },
+};
+
+static struct audio_clock_attr_data aud_clks_data[CLOCK_NUM] = {
+ [AUDCLK_INFRA_SYS_AUDIO] = {"infra_sys_audio_clk", true},
+ [AUDCLK_TOP_AUD_MUX1_SEL] = {"top_audio_mux1_sel", false},
+ [AUDCLK_TOP_AUD_MUX2_SEL] = {"top_audio_mux2_sel", false},
+ [AUDCLK_TOP_AUD_MUX1_DIV] = {"top_audio_mux1_div", false},
+ [AUDCLK_TOP_AUD_MUX2_DIV] = {"top_audio_mux2_div", false},
+ [AUDCLK_TOP_AUD_48K_TIMING] = {"top_audio_48k_timing", true},
+ [AUDCLK_TOP_AUD_44K_TIMING] = {"top_audio_44k_timing", true},
+ [AUDCLK_TOP_AUDPLL_MUX_SEL] = {"top_audpll_mux_sel", false},
+ [AUDCLK_TOP_APLL_SEL] = {"top_apll_sel", false},
+ [AUDCLK_TOP_AUD1PLL_98M] = {"top_aud1_pll_98M", false},
+ [AUDCLK_TOP_AUD2PLL_90M] = {"top_aud2_pll_90M", false},
+ [AUDCLK_TOP_HADDS2PLL_98M] = {"top_hadds2_pll_98M", false},
+ [AUDCLK_TOP_HADDS2PLL_294M] = {"top_hadds2_pll_294M", false},
+ [AUDCLK_TOP_AUDPLL] = {"top_audpll", false},
+ [AUDCLK_TOP_AUDPLL_D4] = {"top_audpll_d4", false},
+ [AUDCLK_TOP_AUDPLL_D8] = {"top_audpll_d8", false},
+ [AUDCLK_TOP_AUDPLL_D16] = {"top_audpll_d16", false},
+ [AUDCLK_TOP_AUDPLL_D24] = {"top_audpll_d24", false},
+ [AUDCLK_TOP_AUDINTBUS] = {"top_audintbus_sel", false},
+ [AUDCLK_CLK_26M] = {"clk_26m", false },
+ [AUDCLK_TOP_SYSPLL1_D4] = {"top_syspll1_d4", false},
+ [AUDCLK_TOP_AUD_K1_SRC_SEL] = {"top_aud_k1_src_sel", false},
+ [AUDCLK_TOP_AUD_K2_SRC_SEL] = {"top_aud_k2_src_sel", false},
+ [AUDCLK_TOP_AUD_K3_SRC_SEL] = {"top_aud_k3_src_sel", false},
+ [AUDCLK_TOP_AUD_K4_SRC_SEL] = {"top_aud_k4_src_sel", false},
+ [AUDCLK_TOP_AUD_K5_SRC_SEL] = {"top_aud_k5_src_sel", false},
+ [AUDCLK_TOP_AUD_K6_SRC_SEL] = {"top_aud_k6_src_sel", false},
+ [AUDCLK_TOP_AUD_K1_SRC_DIV] = {"top_aud_k1_src_div", false},
+ [AUDCLK_TOP_AUD_K2_SRC_DIV] = {"top_aud_k2_src_div", false},
+ [AUDCLK_TOP_AUD_K3_SRC_DIV] = {"top_aud_k3_src_div", false},
+ [AUDCLK_TOP_AUD_K4_SRC_DIV] = {"top_aud_k4_src_div", false},
+ [AUDCLK_TOP_AUD_K5_SRC_DIV] = {"top_aud_k5_src_div", false},
+ [AUDCLK_TOP_AUD_K6_SRC_DIV] = {"top_aud_k6_src_div", false},
+ [AUDCLK_TOP_AUD_I2S1_MCLK] = {"top_aud_i2s1_mclk", true},
+ [AUDCLK_TOP_AUD_I2S2_MCLK] = {"top_aud_i2s2_mclk", true},
+ [AUDCLK_TOP_AUD_I2S3_MCLK] = {"top_aud_i2s3_mclk", true},
+ [AUDCLK_TOP_AUD_I2S4_MCLK] = {"top_aud_i2s4_mclk", true},
+ [AUDCLK_TOP_AUD_I2S5_MCLK] = {"top_aud_i2s5_mclk", true},
+ [AUDCLK_TOP_AUD_I2S6_MCLK] = {"top_aud_i2s6_mclk", true},
+ [AUDCLK_TOP_ASM_M_SEL] = {"top_asm_m_sel", false},
+ [AUDCLK_TOP_ASM_H_SEL] = {"top_asm_h_sel", false},
+ [AUDCLK_TOP_UNIVPLL2_D4] = {"top_univpll2_d4", false},
+ [AUDCLK_TOP_UNIVPLL2_D2] = {"top_univpll2_d2", false},
+ [AUDCLK_TOP_SYSPLL_D5] = {"top_syspll_d5", false},
+};
+
+static const struct regmap_config mtk2701_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AFE_END_ADDR,
+ .cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mtk2701_asys_isr(int irq_id, void *dev)
+{
+ int id;
+ struct mtk_afe *afe = dev;
+ struct mtk_afe_irq *irq;
+ u32 status;
+
+ status = mtk2701_asys_irq_status(afe);
+ mtk2701_asys_irq_clear(afe, status);
+
+ for (id = IRQ_ASYS_START; id < IRQ_ASYS_END; ++id) {
+ irq = &afe->irqs[id];
+ if (status & (0x1 << (id - IRQ_ASYS_START)) && irq->isr)
+ irq->isr(afe, irq->memif);
+ }
+ return IRQ_HANDLED;
+}
+
+static int mtk2701_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_afe *afe = dev_get_drvdata(dev);
+
+ mtk2701_afe_enable_clock(afe, 0);
+ return 0;
+}
+
+static int mtk2701_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_afe *afe = dev_get_drvdata(dev);
+
+ pr_warn("%s\n", __func__);
+ mtk2701_afe_enable_clock(afe, 1);
+ return 0;
+}
+
+static int mtk2701_afe_init_audio_clk(struct mtk_afe *afe)
+{
+ size_t i;
+ int ret = 0;
+ struct audio_clock_attr *aud_clks = afe->aud_clks;
+
+ for (i = 0; i < CLOCK_NUM; i++) {
+ aud_clks[i].clock = devm_clk_get(afe->dev,
+ aud_clks[i].clock_data->name);
+ if (IS_ERR(aud_clks[i].clock)) {
+ dev_err(afe->dev, "%s devm_clk_get %s fail\n",
+ __func__, aud_clks[i].clock_data->name);
+ return PTR_ERR(aud_clks[i].clock);
+ }
+ }
+ for (i = 0; i < CLOCK_NUM; i++) {
+ if (aud_clks[i].clock_data->prepare_once) {
+ ret = clk_prepare(aud_clks[i].clock);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare %s fail %d\n",
+ __func__, aud_clks[i].clock_data->name,
+ ret);
+ break;
+ }
+ aud_clks[i].is_prepared = true;
+ }
+ }
+ return ret;
+}
+
+static int mtk2701_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ int ret, i, j;
+ unsigned int irq_id;
+ struct mtk_afe *afe;
+ struct resource *res;
+
+ ret = 0;
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ afe->dev = &pdev->dev;
+/* need for BT*/
+/*
+ irq_id = platform_get_irq(pdev, 0);
+ if (!irq_id) {
+ dev_err(afe->dev, "np %s no first irq\n", afe->dev->of_node->name);
+ return -ENXIO;
+ }
+
+ //TODO, change it to no flag.
+ //TODO check irq handler
+ ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler,
+ IRQF_TRIGGER_LOW, "afe-isr", (void *)afe);
+ if (ret) {
+ dev_err(afe->dev, "could not request_irq for afe-isr\n");
+ return ret;
+ }
+*/
+ irq_id = platform_get_irq(pdev, 1);
+ if (!irq_id) {
+ dev_err(afe->dev, "%s no second irq\n",
+ afe->dev->of_node->name);
+ return -ENXIO;
+ }
+ ret = devm_request_irq(afe->dev, irq_id, mtk2701_asys_isr,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret) {
+ dev_err(afe->dev, "could not request_irq for asys-isr\n");
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &mtk2701_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
+ for (j = 0; j < MTK_MEMIF_STREAM_NUM; ++j)
+ afe->memif[i][j].data = &memif_data[i][j];
+ for (i = 0; i < IRQ_NUM; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ for (i = 0; i < CLOCK_NUM; i++)
+ afe->aud_clks[i].clock_data = &aud_clks_data[i];
+
+ for (i = 0; i < MTK_I2S_NUM; i++) {
+ afe->i2s_path[i].i2s_data[I2S_OUT]
+ = &mtk2701_i2s_data[i][I2S_OUT];
+ afe->i2s_path[i].i2s_data[I2S_IN]
+ = &mtk2701_i2s_data[i][I2S_IN];
+ }
+
+ /* initial audio related clock */
+ ret = mtk2701_afe_init_audio_clk(afe);
+ platform_set_drvdata(pdev, afe);
+
+ ret = snd_soc_register_platform(&pdev->dev, &mtk2701_afe_pcm_platform);
+ if (ret) {
+ dev_warn(afe->dev, "err_platform\n");
+ goto err_platform;
+ }
+
+ ret = snd_soc_register_component(&pdev->dev,
+ &mtk2701_afe_pcm_dai_component,
+ mtk2701_afe_pcm_dais,
+ ARRAY_SIZE(mtk2701_afe_pcm_dais));
+ if (ret) {
+ dev_warn(afe->dev, "err_dai_component\n");
+ goto err_dai_component;
+ }
+ /*enable afe clock*/
+ mtk2701_afe_enable_clock(afe, 1);
+ return 0;
+err_platform:
+ snd_soc_unregister_platform(&pdev->dev);
+err_dai_component:
+ snd_soc_unregister_component(&pdev->dev);
+ return ret;
+}
+
+static int mtk2701_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct mtk_afe *afe = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_component(&pdev->dev);
+ snd_soc_unregister_platform(&pdev->dev);
+ /*disable afe clock*/
+ mtk2701_afe_enable_clock(afe, 0);
+ return 0;
+}
+
+static const struct of_device_id mtk2701_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt2701-audio", }
+};
+MODULE_DEVICE_TABLE(of, mtk2701_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mtk2701_afe_pm_ops = {
+ SET_RUNTIME_PM_OPS(mtk2701_afe_runtime_suspend,
+ mtk2701_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mtk2701_afe_pcm_driver = {
+ .driver = {
+ .name = "mt2701-audio",
+ .owner = THIS_MODULE,
+ .of_match_table = mtk2701_afe_pcm_dt_match,
+#ifdef CONFIG_PM
+ .pm = &mtk2701_afe_pm_ops,
+#endif
+ },
+ .probe = mtk2701_afe_pcm_dev_probe,
+ .remove = mtk2701_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mtk2701_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701");
+MODULE_AUTHOR("Garlic Tseng <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mtk2701-irq.c b/sound/soc/mediatek/mt2701/mtk2701-irq.c
new file mode 100644
index 0000000..a319ae5
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mtk2701-irq.c
@@ -0,0 +1,109 @@
+/*
+ * mtk2701-irq.c -- Mediatek 2701 audio driver irq function
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "mtk2701-reg.h"
+#include "mtk2701-afe-common.h"
+#include "mtk2701-irq.h"
+
+u32 mtk2701_asys_irq_status(struct mtk_afe *afe)
+{
+ u32 status = 0;
+
+ regmap_read(afe->regmap, ASYS_IRQ_STATUS, &status);
+ return status;
+}
+
+void mtk2701_asys_irq_clear(struct mtk_afe *afe, u32 status)
+{
+ regmap_write(afe->regmap, ASYS_IRQ_CLR, status);
+}
+
+void mtk2701_memif_isr(struct mtk_afe *afe, struct mtk_afe_memif *memif)
+{
+ if (memif) {
+ u32 base, cur;
+
+ mtk2701_afe_memif_base(afe, memif, &base);
+ mtk2701_afe_memif_pointer(afe, memif, &cur);
+ memif->hw_ptr = cur - base;
+ snd_pcm_period_elapsed(memif->substream);
+ }
+}
+
+int mtk2701_afe_memif_base(struct mtk_afe *afe, struct mtk_afe_memif *memif,
+ u32 *base)
+{
+ if (!memif || !memif->data) {
+ dev_err(afe->dev, "%s() error: invalid memif %p\n",
+ __func__, memif);
+ return -EINVAL;
+ }
+ if (!base)
+ return -ENOMEM;
+ regmap_read(afe->regmap, memif->data->reg_ofs_base, base);
+ return 0;
+}
+
+int mtk2701_afe_memif_pointer(struct mtk_afe *afe, struct mtk_afe_memif *memif,
+ u32 *cur_ptr)
+{
+ if (!memif || !memif->data) {
+ dev_err(afe->dev, "%s() error: invalid memif %p\n",
+ __func__, memif);
+ return -EINVAL;
+ }
+ if (!cur_ptr)
+ return -ENOMEM;
+ regmap_read(afe->regmap, memif->data->reg_ofs_cur, cur_ptr);
+ return 0;
+}
+
+static DEFINE_MUTEX(asys_irqs_lock);
+int mtk2701_asys_irq_acquire(struct mtk_afe *afe)
+{
+ int i;
+
+ mutex_lock(&asys_irqs_lock);
+ for (i = IRQ_ASYS_START; i < IRQ_ASYS_END; ++i) {
+ if (afe->irqs[i].irq_occupyed == 0) {
+ afe->irqs[i].irq_occupyed = 1;
+ mutex_unlock(&asys_irqs_lock);
+ return i;
+ }
+ }
+ mutex_unlock(&asys_irqs_lock);
+ return IRQ_NUM;
+}
+
+int mtk2701_asys_irq_release(struct mtk_afe *afe, int irq_id)
+{
+ mutex_lock(&asys_irqs_lock);
+ if (irq_id >= IRQ_ASYS_START && irq_id < IRQ_ASYS_END) {
+ afe->irqs[irq_id].irq_occupyed = 0;
+ mutex_unlock(&asys_irqs_lock);
+ return 0;
+ }
+ mutex_unlock(&asys_irqs_lock);
+ return -EINVAL;
+}
+
+MODULE_DESCRIPTION("MTK2701 irq control");
+MODULE_AUTHOR("Garlic Tseng <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mtk2701-irq.h b/sound/soc/mediatek/mt2701/mtk2701-irq.h
new file mode 100644
index 0000000..637d304
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mtk2701-irq.h
@@ -0,0 +1,30 @@
+/*
+ * mtk2701-irq.h -- Mediatek 2701 audio driver irq function definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_2701_IRQ_H_
+#define _MTK_2701_IRQ_H_
+
+u32 mtk2701_asys_irq_status(struct mtk_afe *afe);
+void mtk2701_asys_irq_clear(struct mtk_afe *afe, u32 status);
+void mtk2701_memif_isr(struct mtk_afe *afe, struct mtk_afe_memif *memif);
+int mtk2701_afe_memif_base(struct mtk_afe *afe, struct mtk_afe_memif *memif,
+ u32 *base);
+int mtk2701_afe_memif_pointer(struct mtk_afe *afe, struct mtk_afe_memif *memif,
+ u32 *cur_ptr);
+int mtk2701_asys_irq_acquire(struct mtk_afe *afe);
+int mtk2701_asys_irq_release(struct mtk_afe *afe, int irq_id);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mtk2701-reg.h b/sound/soc/mediatek/mt2701/mtk2701-reg.h
new file mode 100644
index 0000000..4f83dcc
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mtk2701-reg.h
@@ -0,0 +1,165 @@
+/*
+ * mtk2701-reg.h -- Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK2701_REG_H_
+#define _MTK2701_REG_H_
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mtk2701-afe-common.h"
+
+/*****************************************************************************
+ * R E G I S T E R D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_TOP_CON5 0x0014
+#define ASMI_TIMING_CON1 0x0100
+#define ASMO_TIMING_CON1 0x0104
+#define PWR1_ASM_CON1 0x0108
+#define ASYS_TOP_CON 0x0600
+#define ASYS_I2SIN1_CON 0x0604
+#define ASYS_I2SIN2_CON 0x0608
+#define ASYS_I2SIN3_CON 0x060C
+#define ASYS_I2SIN4_CON 0x0610
+#define ASYS_I2SIN5_CON 0x0614
+#define ASYS_I2SO1_CON 0x061C
+#define ASYS_I2SO2_CON 0x0620
+#define ASYS_I2SO3_CON 0x0624
+#define ASYS_I2SO4_CON 0x0628
+#define ASYS_I2SO5_CON 0x062C
+#define PWR2_TOP_CON 0x0634
+#define AFE_CONN0 0x06C0
+#define AFE_CONN1 0x06C4
+#define AFE_CONN2 0x06C8
+#define AFE_CONN3 0x06CC
+#define AFE_CONN15 0x06FC
+#define AFE_CONN16 0x0700
+#define AFE_CONN17 0x0704
+#define AFE_CONN18 0x0708
+#define AFE_CONN19 0x070C
+#define AFE_CONN20 0x0710
+#define AFE_CONN21 0x0714
+#define AFE_CONN22 0x0718
+#define AFE_CONN23 0x071C
+#define AFE_CONN24 0x0720
+#define ASYS_IRQ1_CON 0x0780
+#define ASYS_IRQ2_CON 0x0784
+#define ASYS_IRQ3_CON 0x0788
+#define ASYS_IRQ_CLR 0x07C0
+#define ASYS_IRQ_STATUS 0x07C4
+#define PWR2_ASM_CON1 0x1070
+#define AFE_DAC_CON0 0x1200
+#define AFE_DAC_CON1 0x1204
+#define AFE_DAC_CON2 0x1208
+#define AFE_DAC_CON3 0x120C
+#define AFE_DAC_CON4 0x1210
+#define AFE_MEMIF_HD_CON1 0x121C
+#define AFE_MEMIF_PBUF_SIZE 0x1238
+#define AFE_MEMIF_HD_CON0 0x123C
+#define AFE_DL1_BASE 0x1240
+#define AFE_DL1_CUR 0x1244
+#define AFE_DL2_BASE 0x1250
+#define AFE_DL2_CUR 0x1254
+#define AFE_DL3_BASE 0x1260
+#define AFE_DL3_CUR 0x1264
+#define AFE_DL4_BASE 0x1270
+#define AFE_DL4_CUR 0x1274
+#define AFE_DL5_BASE 0x1280
+#define AFE_DL5_CUR 0x1284
+#define AFE_DLMCH_BASE 0x12A0
+#define AFE_DLMCH_CUR 0x12A4
+#define AFE_VUL_BASE 0x1300
+#define AFE_VUL_CUR 0x130C
+#define AFE_UL2_BASE 0x1310
+#define AFE_UL2_END 0x1318
+#define AFE_UL2_CUR 0x131C
+#define AFE_UL3_BASE 0x1320
+#define AFE_UL3_END 0x1328
+#define AFE_UL3_CUR 0x132C
+#define AFE_UL4_BASE 0x1330
+#define AFE_UL4_END 0x1338
+#define AFE_UL4_CUR 0x133C
+#define AFE_UL5_BASE 0x1340
+#define AFE_UL5_END 0x1348
+#define AFE_UL5_CUR 0x134C
+
+/*AUDIO_TOP_CON0 (0x0000)*/
+#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON (0x3 << 0)
+#define AUDIO_TOP_CON0_PDN_AFE (0x1 << 2)
+#define AUDIO_TOP_CON0_PDN_APLL_CK (0x1 << 23)
+
+/*AUDIO_TOP_CON4 (0x0010)*/
+#define AUDIO_TOP_CON4_I2SO1_PWN (0x1 << 6)
+#define AUDIO_TOP_CON4_PDN_A1SYS (0x1 << 21)
+#define AUDIO_TOP_CON4_PDN_A2SYS (0x1 << 22)
+#define AUDIO_TOP_CON4_PDN_AFE_CONN (0x1 << 23)
+
+/*PWR1_ASM_CON1 (0x0108)*/
+#define PWR1_ASM_CON1_INIT_VAL (0x492)
+
+/* ASYS_I2SO1_CON (0x061C)*/
+#define ASYS_I2SO1_CON_FS (0x1f << 8)
+#define ASYS_I2SO1_CON_FS_SET(x) ((x) << 8)
+#define ASYS_I2SO1_CON_MULTI_CH (0x1 << 16)
+#define ASYS_I2SO1_CON_SIDEGEN (0x1 << 30)
+#define ASYS_I2SO1_CON_I2S_EN (0x1 << 0)
+/*0:EIAJ 1:I2S*/
+#define ASYS_I2SO1_CON_I2S_MODE (0x1 << 3)
+#define ASYS_I2SO1_CON_WIDE_MODE (0x1 << 1)
+#define ASYS_I2SO1_CON_WIDE_MODE_SET(x) ((x) << 1)
+
+/*PWR2_TOP_CON (0x0634)*/
+#define PWR2_TOP_CON_INIT_VAL (0xffe1ffff)
+
+/*ASYS_IRQ_CLR (0x07C0)*/
+#define ASYS_IRQ_CLR_ALL (0xffffffff)
+
+/*PWR2_ASM_CON1 (0x1070)*/
+#define PWR2_ASM_CON1_INIT_VAL (0x492492)
+
+/*AFE_DAC_CON0 (0x1200)*/
+#define AFE_DAC_CON0_AFE_ON (0x1 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE (0x1238)*/
+#define AFE_MEMIF_PBUF_SIZE_DLM_MASK (0x1 << 29)
+#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE (0x0 << 29)
+#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE (0x1 << 29)
+#define DLMCH_BIT_WIDTH_MASK (0x1 << 28)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK (0xF << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x) ((x) << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK (0x3 << 12)
+#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES (0x1 << 12)
+
+/*I2S in/out register bit control*/
+#define ASYS_I2S_CON_FS (0x1f << 8)
+#define ASYS_I2S_CON_FS_SET(x) ((x) << 8)
+#define ASYS_I2S_CON_MULTI_CH (0x1 << 16)
+#define ASYS_I2S_CON_RESET (0x1 << 30)
+#define ASYS_I2S_CON_I2S_EN (0x1 << 0)
+#define ASYS_I2S_CON_I2S_COUPLE_MODE (0x1 << 17)
+/*0:EIAJ 1:I2S*/
+#define ASYS_I2S_CON_I2S_MODE (0x1 << 3)
+#define ASYS_I2S_CON_WIDE_MODE (0x1 << 1)
+#define ASYS_I2S_CON_WIDE_MODE_SET(x) ((x) << 1)
+#define ASYS_I2S_IN_PHASE_FIX (0x1 << 31)
+
+#define AFE_END_ADDR 0x15e0
+#endif
--
1.9.1
Add machine driver and config option for MT2701.
Signed-off-by: Garlic Tseng <[email protected]>
---
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 <[email protected]>
+ * Garlic Tseng <[email protected]>
+ *
+ *
+ * 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 <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 cs42448 soc card");
--
1.9.1