linux/sound/soc/mediatek/mt2701/mt2701-cs42448.c
<<
>>
Prefs
   1/*
   2 * mt2701-cs42448.c  --  MT2701 CS42448 ALSA SoC machine driver
   3 *
   4 * Copyright (c) 2016 MediaTek Inc.
   5 * Author: Ir Lian <ir.lian@mediatek.com>
   6 *              Garlic Tseng <garlic.tseng@mediatek.com>
   7 *
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 and
  11 * only version 2 as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/module.h>
  20#include <sound/soc.h>
  21#include <linux/delay.h>
  22#include <linux/gpio.h>
  23#include <linux/pinctrl/consumer.h>
  24#include <linux/of_gpio.h>
  25
  26#include "mt2701-afe-common.h"
  27
  28struct mt2701_cs42448_private {
  29        int i2s1_in_mux;
  30        int i2s1_in_mux_gpio_sel_1;
  31        int i2s1_in_mux_gpio_sel_2;
  32};
  33
  34static const char * const i2sin_mux_switch_text[] = {
  35        "ADC_SDOUT2",
  36        "ADC_SDOUT3",
  37        "I2S_IN_1",
  38        "I2S_IN_2",
  39};
  40
  41static const struct soc_enum i2sin_mux_enum =
  42        SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
  43
  44static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
  45                                         struct snd_ctl_elem_value *ucontrol)
  46{
  47        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
  48        struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
  49
  50        ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
  51        return 0;
  52}
  53
  54static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
  55                                         struct snd_ctl_elem_value *ucontrol)
  56{
  57        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
  58        struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
  59
  60        if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
  61                return 0;
  62
  63        switch (ucontrol->value.integer.value[0]) {
  64        case 0:
  65                gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
  66                gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
  67                break;
  68        case 1:
  69                gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
  70                gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
  71                break;
  72        case 2:
  73                gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
  74                gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
  75                break;
  76        case 3:
  77                gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
  78                gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
  79                break;
  80        default:
  81                dev_warn(card->dev, "%s invalid setting\n", __func__);
  82        }
  83
  84        priv->i2s1_in_mux = ucontrol->value.integer.value[0];
  85        return 0;
  86}
  87
  88static const struct snd_soc_dapm_widget
  89                        mt2701_cs42448_asoc_card_dapm_widgets[] = {
  90        SND_SOC_DAPM_LINE("Line Out Jack", NULL),
  91        SND_SOC_DAPM_MIC("AMIC", NULL),
  92        SND_SOC_DAPM_LINE("Tuner In", NULL),
  93        SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
  94        SND_SOC_DAPM_LINE("AUX In", NULL),
  95};
  96
  97static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
  98        SOC_DAPM_PIN_SWITCH("Line Out Jack"),
  99        SOC_DAPM_PIN_SWITCH("AMIC"),
 100        SOC_DAPM_PIN_SWITCH("Tuner In"),
 101        SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
 102        SOC_DAPM_PIN_SWITCH("AUX In"),
 103        SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
 104                     mt2701_cs42448_i2sin1_mux_get,
 105                     mt2701_cs42448_i2sin1_mux_set),
 106};
 107
 108static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
 109
 110static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
 111                .count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
 112                .list = mt2701_cs42448_sampling_rates,
 113                .mask = 0,
 114};
 115
 116static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
 117{
 118        int err;
 119
 120        err = snd_pcm_hw_constraint_list(substream->runtime, 0,
 121                                         SNDRV_PCM_HW_PARAM_RATE,
 122                                         &mt2701_cs42448_constraints_rates);
 123        if (err < 0) {
 124                dev_err(substream->pcm->card->dev,
 125                        "%s snd_pcm_hw_constraint_list failed: 0x%x\n",
 126                        __func__, err);
 127                return err;
 128        }
 129        return 0;
 130}
 131
 132static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
 133        .startup = mt2701_cs42448_fe_ops_startup,
 134};
 135
 136static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
 137                                           struct snd_pcm_hw_params *params)
 138{
 139        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 140        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 141        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 142        unsigned int mclk_rate;
 143        unsigned int rate = params_rate(params);
 144        unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
 145        unsigned int div_bck_over_lrck = 64;
 146
 147        mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
 148
 149        /* mt2701 mclk */
 150        snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
 151
 152        /* codec mclk */
 153        snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
 154
 155        return 0;
 156}
 157
 158static struct snd_soc_ops mt2701_cs42448_be_ops = {
 159        .hw_params = mt2701_cs42448_be_ops_hw_params
 160};
 161
 162enum {
 163        DAI_LINK_FE_MULTI_CH_OUT,
 164        DAI_LINK_FE_PCM0_IN,
 165        DAI_LINK_FE_PCM1_IN,
 166        DAI_LINK_FE_BT_OUT,
 167        DAI_LINK_FE_BT_IN,
 168        DAI_LINK_BE_I2S0,
 169        DAI_LINK_BE_I2S1,
 170        DAI_LINK_BE_I2S2,
 171        DAI_LINK_BE_I2S3,
 172        DAI_LINK_BE_MRG_BT,
 173};
 174
 175static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
 176        /* FE */
 177        [DAI_LINK_FE_MULTI_CH_OUT] = {
 178                .name = "mt2701-cs42448-multi-ch-out",
 179                .stream_name = "mt2701-cs42448-multi-ch-out",
 180                .cpu_dai_name = "PCM_multi",
 181                .codec_name = "snd-soc-dummy",
 182                .codec_dai_name = "snd-soc-dummy-dai",
 183                .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 184                            SND_SOC_DPCM_TRIGGER_POST},
 185                .ops = &mt2701_cs42448_48k_fe_ops,
 186                .dynamic = 1,
 187                .dpcm_playback = 1,
 188        },
 189        [DAI_LINK_FE_PCM0_IN] = {
 190                .name = "mt2701-cs42448-pcm0",
 191                .stream_name = "mt2701-cs42448-pcm0-data-UL",
 192                .cpu_dai_name = "PCM0",
 193                .codec_name = "snd-soc-dummy",
 194                .codec_dai_name = "snd-soc-dummy-dai",
 195                .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 196                            SND_SOC_DPCM_TRIGGER_POST},
 197                .ops = &mt2701_cs42448_48k_fe_ops,
 198                .dynamic = 1,
 199                .dpcm_capture = 1,
 200        },
 201        [DAI_LINK_FE_PCM1_IN] = {
 202                .name = "mt2701-cs42448-pcm1-data-UL",
 203                .stream_name = "mt2701-cs42448-pcm1-data-UL",
 204                .cpu_dai_name = "PCM1",
 205                .codec_name = "snd-soc-dummy",
 206                .codec_dai_name = "snd-soc-dummy-dai",
 207                .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 208                            SND_SOC_DPCM_TRIGGER_POST},
 209                .ops = &mt2701_cs42448_48k_fe_ops,
 210                .dynamic = 1,
 211                .dpcm_capture = 1,
 212        },
 213        [DAI_LINK_FE_BT_OUT] = {
 214                .name = "mt2701-cs42448-pcm-BT-out",
 215                .stream_name = "mt2701-cs42448-pcm-BT",
 216                .cpu_dai_name = "PCM_BT_DL",
 217                .codec_name = "snd-soc-dummy",
 218                .codec_dai_name = "snd-soc-dummy-dai",
 219                .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 220                            SND_SOC_DPCM_TRIGGER_POST},
 221                .dynamic = 1,
 222                .dpcm_playback = 1,
 223        },
 224        [DAI_LINK_FE_BT_IN] = {
 225                .name = "mt2701-cs42448-pcm-BT-in",
 226                .stream_name = "mt2701-cs42448-pcm-BT",
 227                .cpu_dai_name = "PCM_BT_UL",
 228                .codec_name = "snd-soc-dummy",
 229                .codec_dai_name = "snd-soc-dummy-dai",
 230                .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 231                            SND_SOC_DPCM_TRIGGER_POST},
 232                .dynamic = 1,
 233                .dpcm_capture = 1,
 234        },
 235        /* BE */
 236        [DAI_LINK_BE_I2S0] = {
 237                .name = "mt2701-cs42448-I2S0",
 238                .cpu_dai_name = "I2S0",
 239                .no_pcm = 1,
 240                .codec_dai_name = "cs42448",
 241                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
 242                         | SND_SOC_DAIFMT_GATED,
 243                .ops = &mt2701_cs42448_be_ops,
 244                .dpcm_playback = 1,
 245                .dpcm_capture = 1,
 246        },
 247        [DAI_LINK_BE_I2S1] = {
 248                .name = "mt2701-cs42448-I2S1",
 249                .cpu_dai_name = "I2S1",
 250                .no_pcm = 1,
 251                .codec_dai_name = "cs42448",
 252                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
 253                         | SND_SOC_DAIFMT_GATED,
 254                .ops = &mt2701_cs42448_be_ops,
 255                .dpcm_playback = 1,
 256                .dpcm_capture = 1,
 257        },
 258        [DAI_LINK_BE_I2S2] = {
 259                .name = "mt2701-cs42448-I2S2",
 260                .cpu_dai_name = "I2S2",
 261                .no_pcm = 1,
 262                .codec_dai_name = "cs42448",
 263                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
 264                         | SND_SOC_DAIFMT_GATED,
 265                .ops = &mt2701_cs42448_be_ops,
 266                .dpcm_playback = 1,
 267                .dpcm_capture = 1,
 268        },
 269        [DAI_LINK_BE_I2S3] = {
 270                .name = "mt2701-cs42448-I2S3",
 271                .cpu_dai_name = "I2S3",
 272                .no_pcm = 1,
 273                .codec_dai_name = "cs42448",
 274                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
 275                         | SND_SOC_DAIFMT_GATED,
 276                .ops = &mt2701_cs42448_be_ops,
 277                .dpcm_playback = 1,
 278                .dpcm_capture = 1,
 279        },
 280        [DAI_LINK_BE_MRG_BT] = {
 281                .name = "mt2701-cs42448-MRG-BT",
 282                .cpu_dai_name = "MRG BT",
 283                .no_pcm = 1,
 284                .codec_dai_name = "bt-sco-pcm-wb",
 285                .dpcm_playback = 1,
 286                .dpcm_capture = 1,
 287        },
 288};
 289
 290static struct snd_soc_card mt2701_cs42448_soc_card = {
 291        .name = "mt2701-cs42448",
 292        .owner = THIS_MODULE,
 293        .dai_link = mt2701_cs42448_dai_links,
 294        .num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
 295        .controls = mt2701_cs42448_controls,
 296        .num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
 297        .dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
 298        .num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
 299};
 300
 301static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
 302{
 303        struct snd_soc_card *card = &mt2701_cs42448_soc_card;
 304        int ret;
 305        int i;
 306        struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
 307        struct mt2701_cs42448_private *priv =
 308                devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
 309                             GFP_KERNEL);
 310        struct device *dev = &pdev->dev;
 311
 312        if (!priv)
 313                return -ENOMEM;
 314
 315        platform_node = of_parse_phandle(pdev->dev.of_node,
 316                                         "mediatek,platform", 0);
 317        if (!platform_node) {
 318                dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
 319                return -EINVAL;
 320        }
 321        for (i = 0; i < card->num_links; i++) {
 322                if (mt2701_cs42448_dai_links[i].platform_name)
 323                        continue;
 324                mt2701_cs42448_dai_links[i].platform_of_node = platform_node;
 325        }
 326
 327        card->dev = dev;
 328
 329        codec_node = of_parse_phandle(pdev->dev.of_node,
 330                                      "mediatek,audio-codec", 0);
 331        if (!codec_node) {
 332                dev_err(&pdev->dev,
 333                        "Property 'audio-codec' missing or invalid\n");
 334                return -EINVAL;
 335        }
 336        for (i = 0; i < card->num_links; i++) {
 337                if (mt2701_cs42448_dai_links[i].codec_name)
 338                        continue;
 339                mt2701_cs42448_dai_links[i].codec_of_node = codec_node;
 340        }
 341
 342        codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
 343                                             "mediatek,audio-codec-bt-mrg", 0);
 344        if (!codec_node_bt_mrg) {
 345                dev_err(&pdev->dev,
 346                        "Property 'audio-codec-bt-mrg' missing or invalid\n");
 347                return -EINVAL;
 348        }
 349        mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
 350                                                        = codec_node_bt_mrg;
 351
 352        ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
 353        if (ret) {
 354                dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
 355                return ret;
 356        }
 357
 358        priv->i2s1_in_mux_gpio_sel_1 =
 359                of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
 360        if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
 361                ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
 362                                        "i2s1_in_mux_gpio_sel_1");
 363                if (ret)
 364                        dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
 365                                 __func__, ret);
 366                gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
 367        }
 368
 369        priv->i2s1_in_mux_gpio_sel_2 =
 370                of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
 371        if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
 372                ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
 373                                        "i2s1_in_mux_gpio_sel_2");
 374                if (ret)
 375                        dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
 376                                 __func__, ret);
 377                gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
 378        }
 379        snd_soc_card_set_drvdata(card, priv);
 380
 381        ret = devm_snd_soc_register_card(&pdev->dev, card);
 382
 383        if (ret)
 384                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
 385                        __func__, ret);
 386        return ret;
 387}
 388
 389#ifdef CONFIG_OF
 390static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
 391        {.compatible = "mediatek,mt2701-cs42448-machine",},
 392        {}
 393};
 394#endif
 395
 396static struct platform_driver mt2701_cs42448_machine = {
 397        .driver = {
 398                .name = "mt2701-cs42448",
 399                   #ifdef CONFIG_OF
 400                   .of_match_table = mt2701_cs42448_machine_dt_match,
 401                   #endif
 402        },
 403        .probe = mt2701_cs42448_machine_probe,
 404};
 405
 406module_platform_driver(mt2701_cs42448_machine);
 407
 408/* Module information */
 409MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
 410MODULE_AUTHOR("Ir Lian <ir.lian@mediatek.com>");
 411MODULE_LICENSE("GPL v2");
 412MODULE_ALIAS("mt2701 cs42448 soc card");
 413