linux/sound/soc/mediatek/mt8173/mt8173-rt5650.c
<<
>>
Prefs
   1/*
   2 * mt8173-rt5650.c  --  MT8173 machine driver with RT5650 codecs
   3 *
   4 * Copyright (c) 2016 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
  24#define MCLK_FOR_CODECS         12288000
  25
  26enum mt8173_rt5650_mclk {
  27        MT8173_RT5650_MCLK_EXTERNAL = 0,
  28        MT8173_RT5650_MCLK_INTERNAL,
  29};
  30
  31struct mt8173_rt5650_platform_data {
  32        enum mt8173_rt5650_mclk pll_from;
  33        /* 0 = external oscillator; 1 = internal source from mt8173 */
  34};
  35
  36static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {
  37        .pll_from = MT8173_RT5650_MCLK_EXTERNAL,
  38};
  39
  40static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
  41        SND_SOC_DAPM_SPK("Speaker", NULL),
  42        SND_SOC_DAPM_MIC("Int Mic", NULL),
  43        SND_SOC_DAPM_HP("Headphone", NULL),
  44        SND_SOC_DAPM_MIC("Headset Mic", NULL),
  45};
  46
  47static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {
  48        {"Speaker", NULL, "SPOL"},
  49        {"Speaker", NULL, "SPOR"},
  50        {"DMIC L1", NULL, "Int Mic"},
  51        {"DMIC R1", NULL, "Int Mic"},
  52        {"Headphone", NULL, "HPOL"},
  53        {"Headphone", NULL, "HPOR"},
  54        {"Headset Mic", NULL, "micbias1"},
  55        {"Headset Mic", NULL, "micbias2"},
  56        {"IN1P", NULL, "Headset Mic"},
  57        {"IN1N", NULL, "Headset Mic"},
  58};
  59
  60static const struct snd_kcontrol_new mt8173_rt5650_controls[] = {
  61        SOC_DAPM_PIN_SWITCH("Speaker"),
  62        SOC_DAPM_PIN_SWITCH("Int Mic"),
  63        SOC_DAPM_PIN_SWITCH("Headphone"),
  64        SOC_DAPM_PIN_SWITCH("Headset Mic"),
  65};
  66
  67static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
  68                                   struct snd_pcm_hw_params *params)
  69{
  70        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  71        unsigned int mclk_clock;
  72        int i, ret;
  73
  74        switch (mt8173_rt5650_priv.pll_from) {
  75        case MT8173_RT5650_MCLK_EXTERNAL:
  76                /* mclk = 12.288M */
  77                mclk_clock = MCLK_FOR_CODECS;
  78                break;
  79        case MT8173_RT5650_MCLK_INTERNAL:
  80                /* mclk = sampling rate*256 */
  81                mclk_clock = params_rate(params) * 256;
  82                break;
  83        default:
  84                /* mclk = 12.288M */
  85                mclk_clock = MCLK_FOR_CODECS;
  86                break;
  87        }
  88
  89        for (i = 0; i < rtd->num_codecs; i++) {
  90                struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
  91
  92                /* pll from mclk */
  93                ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,
  94                                          params_rate(params) * 512);
  95                if (ret)
  96                        return ret;
  97
  98                /* sysclk from pll */
  99                ret = snd_soc_dai_set_sysclk(codec_dai, 1,
 100                                             params_rate(params) * 512,
 101                                             SND_SOC_CLOCK_IN);
 102                if (ret)
 103                        return ret;
 104        }
 105        return 0;
 106}
 107
 108static struct snd_soc_ops mt8173_rt5650_ops = {
 109        .hw_params = mt8173_rt5650_hw_params,
 110};
 111
 112static struct snd_soc_jack mt8173_rt5650_jack;
 113
 114static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
 115{
 116        struct snd_soc_card *card = runtime->card;
 117        struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
 118        const char *codec_capture_dai = runtime->codec_dais[1]->name;
 119        int ret;
 120
 121        rt5645_sel_asrc_clk_src(codec,
 122                                RT5645_DA_STEREO_FILTER,
 123                                RT5645_CLK_SEL_I2S1_ASRC);
 124
 125        if (!strcmp(codec_capture_dai, "rt5645-aif1")) {
 126                rt5645_sel_asrc_clk_src(codec,
 127                                        RT5645_AD_STEREO_FILTER,
 128                                        RT5645_CLK_SEL_I2S1_ASRC);
 129        } else if (!strcmp(codec_capture_dai, "rt5645-aif2")) {
 130                rt5645_sel_asrc_clk_src(codec,
 131                                        RT5645_AD_STEREO_FILTER,
 132                                        RT5645_CLK_SEL_I2S2_ASRC);
 133        } else {
 134                dev_warn(card->dev,
 135                         "Only one dai codec found in DTS, enabled rt5645 AD filter\n");
 136                rt5645_sel_asrc_clk_src(codec,
 137                                        RT5645_AD_STEREO_FILTER,
 138                                        RT5645_CLK_SEL_I2S1_ASRC);
 139        }
 140
 141        /* enable jack detection */
 142        ret = snd_soc_card_jack_new(card, "Headset Jack",
 143                                    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
 144                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
 145                                    SND_JACK_BTN_2 | SND_JACK_BTN_3,
 146                                    &mt8173_rt5650_jack, NULL, 0);
 147        if (ret) {
 148                dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
 149                return ret;
 150        }
 151
 152        return rt5645_set_jack_detect(codec,
 153                                      &mt8173_rt5650_jack,
 154                                      &mt8173_rt5650_jack,
 155                                      &mt8173_rt5650_jack);
 156}
 157
 158static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = {
 159        {
 160                /* Playback */
 161                .dai_name = "rt5645-aif1",
 162        },
 163        {
 164                /* Capture */
 165                .dai_name = "rt5645-aif1",
 166        },
 167};
 168
 169enum {
 170        DAI_LINK_PLAYBACK,
 171        DAI_LINK_CAPTURE,
 172        DAI_LINK_HDMI,
 173        DAI_LINK_CODEC_I2S,
 174        DAI_LINK_HDMI_I2S,
 175};
 176
 177/* Digital audio interface glue - connects codec <---> CPU */
 178static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
 179        /* Front End DAI links */
 180        [DAI_LINK_PLAYBACK] = {
 181                .name = "rt5650 Playback",
 182                .stream_name = "rt5650 Playback",
 183                .cpu_dai_name = "DL1",
 184                .codec_name = "snd-soc-dummy",
 185                .codec_dai_name = "snd-soc-dummy-dai",
 186                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 187                .dynamic = 1,
 188                .dpcm_playback = 1,
 189        },
 190        [DAI_LINK_CAPTURE] = {
 191                .name = "rt5650 Capture",
 192                .stream_name = "rt5650 Capture",
 193                .cpu_dai_name = "VUL",
 194                .codec_name = "snd-soc-dummy",
 195                .codec_dai_name = "snd-soc-dummy-dai",
 196                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 197                .dynamic = 1,
 198                .dpcm_capture = 1,
 199        },
 200        [DAI_LINK_HDMI] = {
 201                .name = "HDMI",
 202                .stream_name = "HDMI PCM",
 203                .cpu_dai_name = "HDMI",
 204                .codec_name = "snd-soc-dummy",
 205                .codec_dai_name = "snd-soc-dummy-dai",
 206                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 207                .dynamic = 1,
 208                .dpcm_playback = 1,
 209        },
 210        /* Back End DAI links */
 211        [DAI_LINK_CODEC_I2S] = {
 212                .name = "Codec",
 213                .cpu_dai_name = "I2S",
 214                .no_pcm = 1,
 215                .codecs = mt8173_rt5650_codecs,
 216                .num_codecs = 2,
 217                .init = mt8173_rt5650_init,
 218                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 219                           SND_SOC_DAIFMT_CBS_CFS,
 220                .ops = &mt8173_rt5650_ops,
 221                .ignore_pmdown_time = 1,
 222                .dpcm_playback = 1,
 223                .dpcm_capture = 1,
 224        },
 225        [DAI_LINK_HDMI_I2S] = {
 226                .name = "HDMI BE",
 227                .cpu_dai_name = "HDMIO",
 228                .no_pcm = 1,
 229                .codec_dai_name = "i2s-hifi",
 230                .dpcm_playback = 1,
 231        },
 232};
 233
 234static struct snd_soc_card mt8173_rt5650_card = {
 235        .name = "mtk-rt5650",
 236        .owner = THIS_MODULE,
 237        .dai_link = mt8173_rt5650_dais,
 238        .num_links = ARRAY_SIZE(mt8173_rt5650_dais),
 239        .controls = mt8173_rt5650_controls,
 240        .num_controls = ARRAY_SIZE(mt8173_rt5650_controls),
 241        .dapm_widgets = mt8173_rt5650_widgets,
 242        .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets),
 243        .dapm_routes = mt8173_rt5650_routes,
 244        .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes),
 245};
 246
 247static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
 248{
 249        struct snd_soc_card *card = &mt8173_rt5650_card;
 250        struct device_node *platform_node;
 251        struct device_node *np;
 252        const char *codec_capture_dai;
 253        int i, ret;
 254
 255        platform_node = of_parse_phandle(pdev->dev.of_node,
 256                                         "mediatek,platform", 0);
 257        if (!platform_node) {
 258                dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
 259                return -EINVAL;
 260        }
 261
 262        for (i = 0; i < card->num_links; i++) {
 263                if (mt8173_rt5650_dais[i].platform_name)
 264                        continue;
 265                mt8173_rt5650_dais[i].platform_of_node = platform_node;
 266        }
 267
 268        mt8173_rt5650_codecs[0].of_node =
 269                of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
 270        if (!mt8173_rt5650_codecs[0].of_node) {
 271                dev_err(&pdev->dev,
 272                        "Property 'audio-codec' missing or invalid\n");
 273                return -EINVAL;
 274        }
 275        mt8173_rt5650_codecs[1].of_node = mt8173_rt5650_codecs[0].of_node;
 276
 277        if (of_find_node_by_name(platform_node, "codec-capture")) {
 278                np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
 279                if (!np) {
 280                        dev_err(&pdev->dev,
 281                                "%s: Can't find codec-capture DT node\n",
 282                                __func__);
 283                        return -EINVAL;
 284                }
 285                ret = snd_soc_of_get_dai_name(np, &codec_capture_dai);
 286                if (ret < 0) {
 287                        dev_err(&pdev->dev,
 288                                "%s codec_capture_dai name fail %d\n",
 289                                __func__, ret);
 290                        return ret;
 291                }
 292                mt8173_rt5650_codecs[1].dai_name = codec_capture_dai;
 293        }
 294
 295        if (device_property_present(&pdev->dev, "mediatek,mclk")) {
 296                ret = device_property_read_u32(&pdev->dev,
 297                                               "mediatek,mclk",
 298                                               &mt8173_rt5650_priv.pll_from);
 299                if (ret) {
 300                        dev_err(&pdev->dev,
 301                                "%s snd_soc_register_card fail %d\n",
 302                                __func__, ret);
 303                }
 304        }
 305
 306        mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node =
 307                of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
 308        if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
 309                dev_err(&pdev->dev,
 310                        "Property 'audio-codec' missing or invalid\n");
 311                return -EINVAL;
 312        }
 313        card->dev = &pdev->dev;
 314        platform_set_drvdata(pdev, card);
 315
 316        ret = devm_snd_soc_register_card(&pdev->dev, card);
 317        if (ret)
 318                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
 319                        __func__, ret);
 320        return ret;
 321}
 322
 323static const struct of_device_id mt8173_rt5650_dt_match[] = {
 324        { .compatible = "mediatek,mt8173-rt5650", },
 325        { }
 326};
 327MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match);
 328
 329static struct platform_driver mt8173_rt5650_driver = {
 330        .driver = {
 331                   .name = "mtk-rt5650",
 332                   .of_match_table = mt8173_rt5650_dt_match,
 333#ifdef CONFIG_PM
 334                   .pm = &snd_soc_pm_ops,
 335#endif
 336        },
 337        .probe = mt8173_rt5650_dev_probe,
 338};
 339
 340module_platform_driver(mt8173_rt5650_driver);
 341
 342/* Module information */
 343MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver");
 344MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
 345MODULE_LICENSE("GPL v2");
 346MODULE_ALIAS("platform:mtk-rt5650");
 347
 348