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 = asoc_substream_to_rtd(substream);
  51        struct snd_soc_dai *codec_dai;
  52        int i, ret;
  53
  54        for_each_rtd_codec_dais(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 = asoc_rtd_to_codec(runtime, 0)->component;
  81        struct snd_soc_component *component_sub = asoc_rtd_to_codec(runtime, 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
 114
 115enum {
 116        DAI_LINK_PLAYBACK,
 117        DAI_LINK_CAPTURE,
 118        DAI_LINK_HDMI,
 119        DAI_LINK_CODEC_I2S,
 120        DAI_LINK_HDMI_I2S,
 121        DAI_LINK_INTERCODEC
 122};
 123
 124SND_SOC_DAILINK_DEFS(playback,
 125        DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
 126        DAILINK_COMP_ARRAY(COMP_DUMMY()),
 127        DAILINK_COMP_ARRAY(COMP_EMPTY()));
 128
 129SND_SOC_DAILINK_DEFS(capture,
 130        DAILINK_COMP_ARRAY(COMP_CPU("VUL")),
 131        DAILINK_COMP_ARRAY(COMP_DUMMY()),
 132        DAILINK_COMP_ARRAY(COMP_EMPTY()));
 133
 134SND_SOC_DAILINK_DEFS(hdmi_pcm,
 135        DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
 136        DAILINK_COMP_ARRAY(COMP_DUMMY()),
 137        DAILINK_COMP_ARRAY(COMP_EMPTY()));
 138
 139SND_SOC_DAILINK_DEFS(codec,
 140        DAILINK_COMP_ARRAY(COMP_CPU("I2S")),
 141        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1"),
 142                           COMP_CODEC(NULL, "rt5677-aif1")),
 143        DAILINK_COMP_ARRAY(COMP_EMPTY()));
 144
 145SND_SOC_DAILINK_DEFS(hdmi_be,
 146        DAILINK_COMP_ARRAY(COMP_CPU("HDMIO")),
 147        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
 148        DAILINK_COMP_ARRAY(COMP_EMPTY()));
 149
 150SND_SOC_DAILINK_DEFS(intercodec,
 151        DAILINK_COMP_ARRAY(COMP_DUMMY()),
 152        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5677-aif2")),
 153        DAILINK_COMP_ARRAY(COMP_DUMMY()));
 154
 155/* Digital audio interface glue - connects codec <---> CPU */
 156static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
 157        /* Front End DAI links */
 158        [DAI_LINK_PLAYBACK] = {
 159                .name = "rt5650_rt5676 Playback",
 160                .stream_name = "rt5650_rt5676 Playback",
 161                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 162                .dynamic = 1,
 163                .dpcm_playback = 1,
 164                SND_SOC_DAILINK_REG(playback),
 165        },
 166        [DAI_LINK_CAPTURE] = {
 167                .name = "rt5650_rt5676 Capture",
 168                .stream_name = "rt5650_rt5676 Capture",
 169                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 170                .dynamic = 1,
 171                .dpcm_capture = 1,
 172                SND_SOC_DAILINK_REG(capture),
 173        },
 174        [DAI_LINK_HDMI] = {
 175                .name = "HDMI",
 176                .stream_name = "HDMI PCM",
 177                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 178                .dynamic = 1,
 179                .dpcm_playback = 1,
 180                SND_SOC_DAILINK_REG(hdmi_pcm),
 181        },
 182
 183        /* Back End DAI links */
 184        [DAI_LINK_CODEC_I2S] = {
 185                .name = "Codec",
 186                .no_pcm = 1,
 187                .init = mt8173_rt5650_rt5676_init,
 188                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 189                           SND_SOC_DAIFMT_CBS_CFS,
 190                .ops = &mt8173_rt5650_rt5676_ops,
 191                .ignore_pmdown_time = 1,
 192                .dpcm_playback = 1,
 193                .dpcm_capture = 1,
 194                SND_SOC_DAILINK_REG(codec),
 195        },
 196        [DAI_LINK_HDMI_I2S] = {
 197                .name = "HDMI BE",
 198                .no_pcm = 1,
 199                .dpcm_playback = 1,
 200                SND_SOC_DAILINK_REG(hdmi_be),
 201        },
 202        /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
 203        [DAI_LINK_INTERCODEC] = {
 204                .name = "rt5650_rt5676 intercodec",
 205                .stream_name = "rt5650_rt5676 intercodec",
 206                .no_pcm = 1,
 207                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 208                           SND_SOC_DAIFMT_CBM_CFM,
 209                SND_SOC_DAILINK_REG(intercodec),
 210        },
 211};
 212
 213static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = {
 214        {
 215                .name_prefix = "Sub",
 216        },
 217};
 218
 219static struct snd_soc_card mt8173_rt5650_rt5676_card = {
 220        .name = "mtk-rt5650-rt5676",
 221        .owner = THIS_MODULE,
 222        .dai_link = mt8173_rt5650_rt5676_dais,
 223        .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais),
 224        .codec_conf = mt8173_rt5650_rt5676_codec_conf,
 225        .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf),
 226        .controls = mt8173_rt5650_rt5676_controls,
 227        .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls),
 228        .dapm_widgets = mt8173_rt5650_rt5676_widgets,
 229        .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets),
 230        .dapm_routes = mt8173_rt5650_rt5676_routes,
 231        .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes),
 232};
 233
 234static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
 235{
 236        struct snd_soc_card *card = &mt8173_rt5650_rt5676_card;
 237        struct device_node *platform_node;
 238        struct snd_soc_dai_link *dai_link;
 239        int i, ret;
 240
 241        platform_node = of_parse_phandle(pdev->dev.of_node,
 242                                         "mediatek,platform", 0);
 243        if (!platform_node) {
 244                dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
 245                return -EINVAL;
 246        }
 247
 248        for_each_card_prelinks(card, i, dai_link) {
 249                if (dai_link->platforms->name)
 250                        continue;
 251                dai_link->platforms->of_node = platform_node;
 252        }
 253
 254        mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node =
 255                of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
 256        if (!mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {
 257                dev_err(&pdev->dev,
 258                        "Property 'audio-codec' missing or invalid\n");
 259                return -EINVAL;
 260        }
 261        mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =
 262                of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
 263        if (!mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node) {
 264                dev_err(&pdev->dev,
 265                        "Property 'audio-codec' missing or invalid\n");
 266                return -EINVAL;
 267        }
 268        mt8173_rt5650_rt5676_codec_conf[0].dlc.of_node =
 269                mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node;
 270
 271        mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codecs->of_node =
 272                mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node;
 273
 274        mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codecs->of_node =
 275                of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 2);
 276        if (!mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codecs->of_node) {
 277                dev_err(&pdev->dev,
 278                        "Property 'audio-codec' missing or invalid\n");
 279                return -EINVAL;
 280        }
 281
 282        card->dev = &pdev->dev;
 283
 284        ret = devm_snd_soc_register_card(&pdev->dev, card);
 285        if (ret)
 286                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
 287                        __func__, ret);
 288        return ret;
 289}
 290
 291static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = {
 292        { .compatible = "mediatek,mt8173-rt5650-rt5676", },
 293        { }
 294};
 295MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match);
 296
 297static struct platform_driver mt8173_rt5650_rt5676_driver = {
 298        .driver = {
 299                   .name = "mtk-rt5650-rt5676",
 300                   .of_match_table = mt8173_rt5650_rt5676_dt_match,
 301#ifdef CONFIG_PM
 302                   .pm = &snd_soc_pm_ops,
 303#endif
 304        },
 305        .probe = mt8173_rt5650_rt5676_dev_probe,
 306};
 307
 308module_platform_driver(mt8173_rt5650_rt5676_driver);
 309
 310/* Module information */
 311MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver");
 312MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
 313MODULE_LICENSE("GPL v2");
 314MODULE_ALIAS("platform:mtk-rt5650-rt5676");
 315
 316