linux/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * mt8173-rt5650-rt5676.c  --  MT8173 machine driver with RT5650/5676 codecs
   4 *
   5 * Copyright (c) 2015 MediaTek Inc.
   6 * Author: Koro Chen <koro.chen@mediatek.com>
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/gpio.h>
  11#include <linux/of_gpio.h>
  12#include <sound/soc.h>
  13#include <sound/jack.h>
  14#include "../../codecs/rt5645.h"
  15#include "../../codecs/rt5677.h"
  16
  17#define MCLK_FOR_CODECS         12288000
  18
  19static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = {
  20        SND_SOC_DAPM_SPK("Speaker", NULL),
  21        SND_SOC_DAPM_MIC("Int Mic", NULL),
  22        SND_SOC_DAPM_HP("Headphone", NULL),
  23        SND_SOC_DAPM_MIC("Headset Mic", NULL),
  24};
  25
  26static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = {
  27        {"Speaker", NULL, "SPOL"},
  28        {"Speaker", NULL, "SPOR"},
  29        {"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650  */
  30        {"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */
  31        {"Sub DMIC R1", NULL, "Int Mic"},
  32        {"Headphone", NULL, "HPOL"},
  33        {"Headphone", NULL, "HPOR"},
  34        {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650  */
  35        {"IN1P", NULL, "Headset Mic"},
  36        {"IN1N", NULL, "Headset Mic"},
  37        {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650  */
  38};
  39
  40static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = {
  41        SOC_DAPM_PIN_SWITCH("Speaker"),
  42        SOC_DAPM_PIN_SWITCH("Int Mic"),
  43        SOC_DAPM_PIN_SWITCH("Headphone"),
  44        SOC_DAPM_PIN_SWITCH("Headset Mic"),
  45};
  46
  47static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream,
  48                                          struct snd_pcm_hw_params *params)
  49{
  50        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  51        struct snd_soc_dai *codec_dai;
  52        int i, ret;
  53
  54        for_each_rtd_codec_dai(rtd, i, codec_dai) {
  55                /* pll from mclk 12.288M */
  56                ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
  57                                          params_rate(params) * 512);
  58                if (ret)
  59                        return ret;
  60
  61                /* sysclk from pll */
  62                ret = snd_soc_dai_set_sysclk(codec_dai, 1,
  63                                             params_rate(params) * 512,
  64                                             SND_SOC_CLOCK_IN);
  65                if (ret)
  66                        return ret;
  67        }
  68        return 0;
  69}
  70
  71static const struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
  72        .hw_params = mt8173_rt5650_rt5676_hw_params,
  73};
  74
  75static struct snd_soc_jack mt8173_rt5650_rt5676_jack;
  76
  77static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime)
  78{
  79        struct snd_soc_card *card = runtime->card;
  80        struct snd_soc_component *component = runtime->codec_dais[0]->component;
  81        struct snd_soc_component *component_sub = runtime->codec_dais[1]->component;
  82        int ret;
  83
  84        rt5645_sel_asrc_clk_src(component,
  85                                RT5645_DA_STEREO_FILTER |
  86                                RT5645_AD_STEREO_FILTER,
  87                                RT5645_CLK_SEL_I2S1_ASRC);
  88        rt5677_sel_asrc_clk_src(component_sub,
  89                                RT5677_DA_STEREO_FILTER |
  90                                RT5677_AD_STEREO1_FILTER,
  91                                RT5677_CLK_SEL_I2S1_ASRC);
  92        rt5677_sel_asrc_clk_src(component_sub,
  93                                RT5677_AD_STEREO2_FILTER |
  94                                RT5677_I2S2_SOURCE,
  95                                RT5677_CLK_SEL_I2S2_ASRC);
  96
  97        /* enable jack detection */
  98        ret = snd_soc_card_jack_new(card, "Headset Jack",
  99                                    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
 100                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
 101                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
 102                                    &mt8173_rt5650_rt5676_jack, NULL, 0);
 103        if (ret) {
 104                dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
 105                return ret;
 106        }
 107
 108        return rt5645_set_jack_detect(component,
 109                                      &mt8173_rt5650_rt5676_jack,
 110                                      &mt8173_rt5650_rt5676_jack,
 111                                      &mt8173_rt5650_rt5676_jack);
 112}
 113
 114static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = {
 115        {
 116                .dai_name = "rt5645-aif1",
 117        },
 118        {
 119                .dai_name = "rt5677-aif1",
 120        },
 121};
 122
 123enum {
 124        DAI_LINK_PLAYBACK,
 125        DAI_LINK_CAPTURE,
 126        DAI_LINK_HDMI,
 127        DAI_LINK_CODEC_I2S,
 128        DAI_LINK_HDMI_I2S,
 129        DAI_LINK_INTERCODEC
 130};
 131
 132/* Digital audio interface glue - connects codec <---> CPU */
 133static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
 134        /* Front End DAI links */
 135        [DAI_LINK_PLAYBACK] = {
 136                .name = "rt5650_rt5676 Playback",
 137                .stream_name = "rt5650_rt5676 Playback",
 138                .cpu_dai_name = "DL1",
 139                .codec_name = "snd-soc-dummy",
 140                .codec_dai_name = "snd-soc-dummy-dai",
 141                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 142                .dynamic = 1,
 143                .dpcm_playback = 1,
 144        },
 145        [DAI_LINK_CAPTURE] = {
 146                .name = "rt5650_rt5676 Capture",
 147                .stream_name = "rt5650_rt5676 Capture",
 148                .cpu_dai_name = "VUL",
 149                .codec_name = "snd-soc-dummy",
 150                .codec_dai_name = "snd-soc-dummy-dai",
 151                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 152                .dynamic = 1,
 153                .dpcm_capture = 1,
 154        },
 155        [DAI_LINK_HDMI] = {
 156                .name = "HDMI",
 157                .stream_name = "HDMI PCM",
 158                .cpu_dai_name = "HDMI",
 159                .codec_name = "snd-soc-dummy",
 160                .codec_dai_name = "snd-soc-dummy-dai",
 161                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 162                .dynamic = 1,
 163                .dpcm_playback = 1,
 164        },
 165
 166        /* Back End DAI links */
 167        [DAI_LINK_CODEC_I2S] = {
 168                .name = "Codec",
 169                .cpu_dai_name = "I2S",
 170                .no_pcm = 1,
 171                .codecs = mt8173_rt5650_rt5676_codecs,
 172                .num_codecs = 2,
 173                .init = mt8173_rt5650_rt5676_init,
 174                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 175                           SND_SOC_DAIFMT_CBS_CFS,
 176                .ops = &mt8173_rt5650_rt5676_ops,
 177                .ignore_pmdown_time = 1,
 178                .dpcm_playback = 1,
 179                .dpcm_capture = 1,
 180        },
 181        [DAI_LINK_HDMI_I2S] = {
 182                .name = "HDMI BE",
 183                .cpu_dai_name = "HDMIO",
 184                .no_pcm = 1,
 185                .codec_dai_name = "i2s-hifi",
 186                .dpcm_playback = 1,
 187        },
 188        /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
 189        [DAI_LINK_INTERCODEC] = {
 190                .name = "rt5650_rt5676 intercodec",
 191                .stream_name = "rt5650_rt5676 intercodec",
 192                .cpu_dai_name = "snd-soc-dummy-dai",
 193                .platform_name = "snd-soc-dummy",
 194                .no_pcm = 1,
 195                .codec_dai_name = "rt5677-aif2",
 196                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 197                           SND_SOC_DAIFMT_CBM_CFM,
 198        },
 199
 200};
 201
 202static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = {
 203        {
 204                .name_prefix = "Sub",
 205        },
 206};
 207
 208static struct snd_soc_card mt8173_rt5650_rt5676_card = {
 209        .name = "mtk-rt5650-rt5676",
 210        .owner = THIS_MODULE,
 211        .dai_link = mt8173_rt5650_rt5676_dais,
 212        .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais),
 213        .codec_conf = mt8173_rt5650_rt5676_codec_conf,
 214        .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf),
 215        .controls = mt8173_rt5650_rt5676_controls,
 216        .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls),
 217        .dapm_widgets = mt8173_rt5650_rt5676_widgets,
 218        .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets),
 219        .dapm_routes = mt8173_rt5650_rt5676_routes,
 220        .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes),
 221};
 222
 223static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
 224{
 225        struct snd_soc_card *card = &mt8173_rt5650_rt5676_card;
 226        struct device_node *platform_node;
 227        struct snd_soc_dai_link *dai_link;
 228        int i, ret;
 229
 230        platform_node = of_parse_phandle(pdev->dev.of_node,
 231                                         "mediatek,platform", 0);
 232        if (!platform_node) {
 233                dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
 234                return -EINVAL;
 235        }
 236
 237        for_each_card_prelinks(card, i, dai_link) {
 238                if (dai_link->platform_name)
 239                        continue;
 240                dai_link->platform_of_node = platform_node;
 241        }
 242
 243        mt8173_rt5650_rt5676_codecs[0].of_node =
 244                of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
 245        if (!mt8173_rt5650_rt5676_codecs[0].of_node) {
 246                dev_err(&pdev->dev,
 247                        "Property 'audio-codec' missing or invalid\n");
 248                return -EINVAL;
 249        }
 250        mt8173_rt5650_rt5676_codecs[1].of_node =
 251                of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
 252        if (!mt8173_rt5650_rt5676_codecs[1].of_node) {
 253                dev_err(&pdev->dev,
 254                        "Property 'audio-codec' missing or invalid\n");
 255                return -EINVAL;
 256        }
 257        mt8173_rt5650_rt5676_codec_conf[0].of_node =
 258                mt8173_rt5650_rt5676_codecs[1].of_node;
 259
 260        mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node =
 261                mt8173_rt5650_rt5676_codecs[1].of_node;
 262
 263        mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node =
 264                of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 2);
 265        if (!mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
 266                dev_err(&pdev->dev,
 267                        "Property 'audio-codec' missing or invalid\n");
 268                return -EINVAL;
 269        }
 270
 271        card->dev = &pdev->dev;
 272
 273        ret = devm_snd_soc_register_card(&pdev->dev, card);
 274        if (ret)
 275                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
 276                        __func__, ret);
 277        return ret;
 278}
 279
 280static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = {
 281        { .compatible = "mediatek,mt8173-rt5650-rt5676", },
 282        { }
 283};
 284MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match);
 285
 286static struct platform_driver mt8173_rt5650_rt5676_driver = {
 287        .driver = {
 288                   .name = "mtk-rt5650-rt5676",
 289                   .of_match_table = mt8173_rt5650_rt5676_dt_match,
 290#ifdef CONFIG_PM
 291                   .pm = &snd_soc_pm_ops,
 292#endif
 293        },
 294        .probe = mt8173_rt5650_rt5676_dev_probe,
 295};
 296
 297module_platform_driver(mt8173_rt5650_rt5676_driver);
 298
 299/* Module information */
 300MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver");
 301MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
 302MODULE_LICENSE("GPL v2");
 303MODULE_ALIAS("platform:mtk-rt5650-rt5676");
 304
 305