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