2016-04-14 07:37:44

by Garlic Tseng

[permalink] [raw]
Subject: [alsa-devel] [RFC PATCH 0/4] ASoC: Mediatek: Add support for MT2701 SOC

This patch adds basic support for Mediatek AFE for MT2701 SoC.
The patch is based on broonie tree "for-next" branch.

The interface to connect with BT chip is not finish yet, but as an upstream
rookie I really need some comments. Any comments will be greatly appreciated.

Garlic Tseng (4):
ASoC: mediatek: Refine mt8173 driver
ASoC: mediatek: add documents for mt2701
ASoC: mediatek: add mt2701 platform driver implementation.
ASoC: mediatek: Add mt2701-cs42448 driver and config option.

.../devicetree/bindings/sound/mt2701-cs42448.txt | 39 +
.../devicetree/bindings/sound/mtk2701-afe-pcm.txt | 150 ++
sound/soc/mediatek/Kconfig | 21 +
sound/soc/mediatek/Makefile | 15 +-
sound/soc/mediatek/mt2701/Makefile | 20 +
sound/soc/mediatek/mt2701/mt2701-cs42448.c | 376 +++++
sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c | 259 +++
sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h | 28 +
sound/soc/mediatek/mt2701/mtk2701-afe-common.h | 217 +++
sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c | 1752 ++++++++++++++++++++
sound/soc/mediatek/mt2701/mtk2701-irq.c | 109 ++
sound/soc/mediatek/mt2701/mtk2701-irq.h | 30 +
sound/soc/mediatek/mt2701/mtk2701-reg.h | 165 ++
sound/soc/mediatek/mt8173-max98090.c | 213 ---
sound/soc/mediatek/mt8173-rt5650-rt5514.c | 258 ---
sound/soc/mediatek/mt8173-rt5650-rt5676.c | 288 ----
sound/soc/mediatek/mt8173-rt5650.c | 236 ---
sound/soc/mediatek/mt8173/Makefile | 7 +
sound/soc/mediatek/mt8173/mt8173-max98090.c | 213 +++
sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 258 +++
sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c | 288 ++++
sound/soc/mediatek/mt8173/mt8173-rt5650.c | 236 +++
sound/soc/mediatek/mt8173/mtk-afe-common.h | 101 ++
sound/soc/mediatek/mt8173/mtk-afe-pcm.c | 1333 +++++++++++++++
sound/soc/mediatek/mtk-afe-common.h | 101 --
sound/soc/mediatek/mtk-afe-pcm.c | 1333 ---------------
26 files changed, 5610 insertions(+), 2436 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
create mode 100644 Documentation/devicetree/bindings/sound/mtk2701-afe-pcm.txt
create mode 100644 sound/soc/mediatek/mt2701/Makefile
create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.c
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-clock-ctrl.h
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-common.h
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-afe-pcm.c
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-irq.c
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-irq.h
create mode 100644 sound/soc/mediatek/mt2701/mtk2701-reg.h
delete mode 100644 sound/soc/mediatek/mt8173-max98090.c
delete mode 100644 sound/soc/mediatek/mt8173-rt5650-rt5514.c
delete mode 100644 sound/soc/mediatek/mt8173-rt5650-rt5676.c
delete mode 100644 sound/soc/mediatek/mt8173-rt5650.c
create mode 100644 sound/soc/mediatek/mt8173/Makefile
create mode 100644 sound/soc/mediatek/mt8173/mt8173-max98090.c
create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
create mode 100644 sound/soc/mediatek/mt8173/mt8173-rt5650.c
create mode 100644 sound/soc/mediatek/mt8173/mtk-afe-common.h
create mode 100644 sound/soc/mediatek/mt8173/mtk-afe-pcm.c
delete mode 100644 sound/soc/mediatek/mtk-afe-common.h
delete mode 100644 sound/soc/mediatek/mtk-afe-pcm.c

--
1.9.1


2016-04-14 07:38:21

by Garlic Tseng

[permalink] [raw]
Subject: [alsa-devel] [RFC PATCH 1/4] ASoC: mediatek: Refine mt8173 driver

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, &reg_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, &reg_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

2016-04-14 07:38:44

by Garlic Tseng

[permalink] [raw]
Subject: [alsa-devel] [RFC PATCH 2/4] ASoC: mediatek: add documents for mt2701

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

2016-04-14 07:38:53

by Garlic Tseng

[permalink] [raw]
Subject: [alsa-devel] [RFC PATCH 3/4] ASoC: mediatek: add mt2701 platform driver implementation.

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

2016-04-14 07:38:59

by Garlic Tseng

[permalink] [raw]
Subject: [alsa-devel] [RFC PATCH 4/4] ASoC: mediatek: Add mt2701-cs42448 driver and config option.

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