Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * mt2701-cs42448.c  --  MT2701 CS42448 ALSA SoC machine driver
0004  *
0005  * Copyright (c) 2016 MediaTek Inc.
0006  * Author: Ir Lian <ir.lian@mediatek.com>
0007  *     Garlic Tseng <garlic.tseng@mediatek.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <sound/soc.h>
0012 #include <linux/delay.h>
0013 #include <linux/gpio.h>
0014 #include <linux/pinctrl/consumer.h>
0015 #include <linux/of_gpio.h>
0016 
0017 #include "mt2701-afe-common.h"
0018 
0019 struct mt2701_cs42448_private {
0020     int i2s1_in_mux;
0021     int i2s1_in_mux_gpio_sel_1;
0022     int i2s1_in_mux_gpio_sel_2;
0023 };
0024 
0025 static const char * const i2sin_mux_switch_text[] = {
0026     "ADC_SDOUT2",
0027     "ADC_SDOUT3",
0028     "I2S_IN_1",
0029     "I2S_IN_2",
0030 };
0031 
0032 static const struct soc_enum i2sin_mux_enum =
0033     SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
0034 
0035 static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
0036                      struct snd_ctl_elem_value *ucontrol)
0037 {
0038     struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
0039     struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
0040 
0041     ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
0042     return 0;
0043 }
0044 
0045 static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
0046                      struct snd_ctl_elem_value *ucontrol)
0047 {
0048     struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
0049     struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
0050 
0051     if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
0052         return 0;
0053 
0054     switch (ucontrol->value.integer.value[0]) {
0055     case 0:
0056         gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
0057         gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
0058         break;
0059     case 1:
0060         gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
0061         gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
0062         break;
0063     case 2:
0064         gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
0065         gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
0066         break;
0067     case 3:
0068         gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
0069         gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
0070         break;
0071     default:
0072         dev_warn(card->dev, "%s invalid setting\n", __func__);
0073     }
0074 
0075     priv->i2s1_in_mux = ucontrol->value.integer.value[0];
0076     return 0;
0077 }
0078 
0079 static const struct snd_soc_dapm_widget
0080             mt2701_cs42448_asoc_card_dapm_widgets[] = {
0081     SND_SOC_DAPM_LINE("Line Out Jack", NULL),
0082     SND_SOC_DAPM_MIC("AMIC", NULL),
0083     SND_SOC_DAPM_LINE("Tuner In", NULL),
0084     SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
0085     SND_SOC_DAPM_LINE("AUX In", NULL),
0086 };
0087 
0088 static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
0089     SOC_DAPM_PIN_SWITCH("Line Out Jack"),
0090     SOC_DAPM_PIN_SWITCH("AMIC"),
0091     SOC_DAPM_PIN_SWITCH("Tuner In"),
0092     SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
0093     SOC_DAPM_PIN_SWITCH("AUX In"),
0094     SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
0095              mt2701_cs42448_i2sin1_mux_get,
0096              mt2701_cs42448_i2sin1_mux_set),
0097 };
0098 
0099 static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
0100 
0101 static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
0102         .count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
0103         .list = mt2701_cs42448_sampling_rates,
0104         .mask = 0,
0105 };
0106 
0107 static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
0108 {
0109     int err;
0110 
0111     err = snd_pcm_hw_constraint_list(substream->runtime, 0,
0112                      SNDRV_PCM_HW_PARAM_RATE,
0113                      &mt2701_cs42448_constraints_rates);
0114     if (err < 0) {
0115         dev_err(substream->pcm->card->dev,
0116             "%s snd_pcm_hw_constraint_list failed: 0x%x\n",
0117             __func__, err);
0118         return err;
0119     }
0120     return 0;
0121 }
0122 
0123 static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
0124     .startup = mt2701_cs42448_fe_ops_startup,
0125 };
0126 
0127 static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
0128                        struct snd_pcm_hw_params *params)
0129 {
0130     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0131     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0132     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0133     unsigned int mclk_rate;
0134     unsigned int rate = params_rate(params);
0135     unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
0136     unsigned int div_bck_over_lrck = 64;
0137 
0138     mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
0139 
0140     /* mt2701 mclk */
0141     snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
0142 
0143     /* codec mclk */
0144     snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
0145 
0146     return 0;
0147 }
0148 
0149 static const struct snd_soc_ops mt2701_cs42448_be_ops = {
0150     .hw_params = mt2701_cs42448_be_ops_hw_params
0151 };
0152 
0153 enum {
0154     DAI_LINK_FE_MULTI_CH_OUT,
0155     DAI_LINK_FE_PCM0_IN,
0156     DAI_LINK_FE_PCM1_IN,
0157     DAI_LINK_FE_BT_OUT,
0158     DAI_LINK_FE_BT_IN,
0159     DAI_LINK_BE_I2S0,
0160     DAI_LINK_BE_I2S1,
0161     DAI_LINK_BE_I2S2,
0162     DAI_LINK_BE_I2S3,
0163     DAI_LINK_BE_MRG_BT,
0164 };
0165 
0166 SND_SOC_DAILINK_DEFS(fe_multi_ch_out,
0167     DAILINK_COMP_ARRAY(COMP_CPU("PCM_multi")),
0168     DAILINK_COMP_ARRAY(COMP_DUMMY()),
0169     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0170 
0171 SND_SOC_DAILINK_DEFS(fe_pcm0_in,
0172     DAILINK_COMP_ARRAY(COMP_CPU("PCM0")),
0173     DAILINK_COMP_ARRAY(COMP_DUMMY()),
0174     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0175 
0176 SND_SOC_DAILINK_DEFS(fe_pcm1_in,
0177     DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
0178     DAILINK_COMP_ARRAY(COMP_DUMMY()),
0179     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0180 
0181 SND_SOC_DAILINK_DEFS(fe_bt_out,
0182     DAILINK_COMP_ARRAY(COMP_CPU("PCM_BT_DL")),
0183     DAILINK_COMP_ARRAY(COMP_DUMMY()),
0184     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0185 
0186 SND_SOC_DAILINK_DEFS(fe_bt_in,
0187     DAILINK_COMP_ARRAY(COMP_CPU("PCM_BT_UL")),
0188     DAILINK_COMP_ARRAY(COMP_DUMMY()),
0189     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0190 
0191 SND_SOC_DAILINK_DEFS(be_i2s0,
0192     DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
0193     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
0194     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0195 
0196 SND_SOC_DAILINK_DEFS(be_i2s1,
0197     DAILINK_COMP_ARRAY(COMP_CPU("I2S1")),
0198     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
0199     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0200 
0201 SND_SOC_DAILINK_DEFS(be_i2s2,
0202     DAILINK_COMP_ARRAY(COMP_CPU("I2S2")),
0203     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
0204     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0205 
0206 SND_SOC_DAILINK_DEFS(be_i2s3,
0207     DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
0208     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
0209     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0210 
0211 SND_SOC_DAILINK_DEFS(be_mrg_bt,
0212     DAILINK_COMP_ARRAY(COMP_CPU("MRG BT")),
0213     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "bt-sco-pcm-wb")),
0214     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0215 
0216 static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
0217     /* FE */
0218     [DAI_LINK_FE_MULTI_CH_OUT] = {
0219         .name = "mt2701-cs42448-multi-ch-out",
0220         .stream_name = "mt2701-cs42448-multi-ch-out",
0221         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
0222                 SND_SOC_DPCM_TRIGGER_POST},
0223         .ops = &mt2701_cs42448_48k_fe_ops,
0224         .dynamic = 1,
0225         .dpcm_playback = 1,
0226         SND_SOC_DAILINK_REG(fe_multi_ch_out),
0227     },
0228     [DAI_LINK_FE_PCM0_IN] = {
0229         .name = "mt2701-cs42448-pcm0",
0230         .stream_name = "mt2701-cs42448-pcm0-data-UL",
0231         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
0232                 SND_SOC_DPCM_TRIGGER_POST},
0233         .ops = &mt2701_cs42448_48k_fe_ops,
0234         .dynamic = 1,
0235         .dpcm_capture = 1,
0236         SND_SOC_DAILINK_REG(fe_pcm0_in),
0237     },
0238     [DAI_LINK_FE_PCM1_IN] = {
0239         .name = "mt2701-cs42448-pcm1-data-UL",
0240         .stream_name = "mt2701-cs42448-pcm1-data-UL",
0241         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
0242                 SND_SOC_DPCM_TRIGGER_POST},
0243         .ops = &mt2701_cs42448_48k_fe_ops,
0244         .dynamic = 1,
0245         .dpcm_capture = 1,
0246         SND_SOC_DAILINK_REG(fe_pcm1_in),
0247     },
0248     [DAI_LINK_FE_BT_OUT] = {
0249         .name = "mt2701-cs42448-pcm-BT-out",
0250         .stream_name = "mt2701-cs42448-pcm-BT",
0251         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
0252                 SND_SOC_DPCM_TRIGGER_POST},
0253         .dynamic = 1,
0254         .dpcm_playback = 1,
0255         SND_SOC_DAILINK_REG(fe_bt_out),
0256     },
0257     [DAI_LINK_FE_BT_IN] = {
0258         .name = "mt2701-cs42448-pcm-BT-in",
0259         .stream_name = "mt2701-cs42448-pcm-BT",
0260         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
0261                 SND_SOC_DPCM_TRIGGER_POST},
0262         .dynamic = 1,
0263         .dpcm_capture = 1,
0264         SND_SOC_DAILINK_REG(fe_bt_in),
0265     },
0266     /* BE */
0267     [DAI_LINK_BE_I2S0] = {
0268         .name = "mt2701-cs42448-I2S0",
0269         .no_pcm = 1,
0270         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
0271              | SND_SOC_DAIFMT_GATED,
0272         .ops = &mt2701_cs42448_be_ops,
0273         .dpcm_playback = 1,
0274         .dpcm_capture = 1,
0275         SND_SOC_DAILINK_REG(be_i2s0),
0276     },
0277     [DAI_LINK_BE_I2S1] = {
0278         .name = "mt2701-cs42448-I2S1",
0279         .no_pcm = 1,
0280         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
0281              | SND_SOC_DAIFMT_GATED,
0282         .ops = &mt2701_cs42448_be_ops,
0283         .dpcm_playback = 1,
0284         .dpcm_capture = 1,
0285         SND_SOC_DAILINK_REG(be_i2s1),
0286     },
0287     [DAI_LINK_BE_I2S2] = {
0288         .name = "mt2701-cs42448-I2S2",
0289         .no_pcm = 1,
0290         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
0291              | SND_SOC_DAIFMT_GATED,
0292         .ops = &mt2701_cs42448_be_ops,
0293         .dpcm_playback = 1,
0294         .dpcm_capture = 1,
0295         SND_SOC_DAILINK_REG(be_i2s2),
0296     },
0297     [DAI_LINK_BE_I2S3] = {
0298         .name = "mt2701-cs42448-I2S3",
0299         .no_pcm = 1,
0300         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
0301              | SND_SOC_DAIFMT_GATED,
0302         .ops = &mt2701_cs42448_be_ops,
0303         .dpcm_playback = 1,
0304         .dpcm_capture = 1,
0305         SND_SOC_DAILINK_REG(be_i2s3),
0306     },
0307     [DAI_LINK_BE_MRG_BT] = {
0308         .name = "mt2701-cs42448-MRG-BT",
0309         .no_pcm = 1,
0310         .dpcm_playback = 1,
0311         .dpcm_capture = 1,
0312         SND_SOC_DAILINK_REG(be_mrg_bt),
0313     },
0314 };
0315 
0316 static struct snd_soc_card mt2701_cs42448_soc_card = {
0317     .name = "mt2701-cs42448",
0318     .owner = THIS_MODULE,
0319     .dai_link = mt2701_cs42448_dai_links,
0320     .num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
0321     .controls = mt2701_cs42448_controls,
0322     .num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
0323     .dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
0324     .num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
0325 };
0326 
0327 static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
0328 {
0329     struct snd_soc_card *card = &mt2701_cs42448_soc_card;
0330     int ret;
0331     int i;
0332     struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
0333     struct mt2701_cs42448_private *priv =
0334         devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
0335                  GFP_KERNEL);
0336     struct device *dev = &pdev->dev;
0337     struct snd_soc_dai_link *dai_link;
0338 
0339     if (!priv)
0340         return -ENOMEM;
0341 
0342     platform_node = of_parse_phandle(pdev->dev.of_node,
0343                      "mediatek,platform", 0);
0344     if (!platform_node) {
0345         dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
0346         return -EINVAL;
0347     }
0348     for_each_card_prelinks(card, i, dai_link) {
0349         if (dai_link->platforms->name)
0350             continue;
0351         dai_link->platforms->of_node = platform_node;
0352     }
0353 
0354     card->dev = dev;
0355 
0356     codec_node = of_parse_phandle(pdev->dev.of_node,
0357                       "mediatek,audio-codec", 0);
0358     if (!codec_node) {
0359         dev_err(&pdev->dev,
0360             "Property 'audio-codec' missing or invalid\n");
0361         return -EINVAL;
0362     }
0363     for_each_card_prelinks(card, i, dai_link) {
0364         if (dai_link->codecs->name)
0365             continue;
0366         dai_link->codecs->of_node = codec_node;
0367     }
0368 
0369     codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
0370                          "mediatek,audio-codec-bt-mrg", 0);
0371     if (!codec_node_bt_mrg) {
0372         dev_err(&pdev->dev,
0373             "Property 'audio-codec-bt-mrg' missing or invalid\n");
0374         return -EINVAL;
0375     }
0376     mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codecs->of_node
0377                             = codec_node_bt_mrg;
0378 
0379     ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
0380     if (ret) {
0381         dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
0382         return ret;
0383     }
0384 
0385     priv->i2s1_in_mux_gpio_sel_1 =
0386         of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
0387     if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
0388         ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
0389                     "i2s1_in_mux_gpio_sel_1");
0390         if (ret)
0391             dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
0392                  __func__, ret);
0393         gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
0394     }
0395 
0396     priv->i2s1_in_mux_gpio_sel_2 =
0397         of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
0398     if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
0399         ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
0400                     "i2s1_in_mux_gpio_sel_2");
0401         if (ret)
0402             dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
0403                  __func__, ret);
0404         gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
0405     }
0406     snd_soc_card_set_drvdata(card, priv);
0407 
0408     ret = devm_snd_soc_register_card(&pdev->dev, card);
0409 
0410     if (ret)
0411         dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
0412             __func__, ret);
0413     return ret;
0414 }
0415 
0416 #ifdef CONFIG_OF
0417 static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
0418     {.compatible = "mediatek,mt2701-cs42448-machine",},
0419     {}
0420 };
0421 #endif
0422 
0423 static struct platform_driver mt2701_cs42448_machine = {
0424     .driver = {
0425         .name = "mt2701-cs42448",
0426            #ifdef CONFIG_OF
0427            .of_match_table = mt2701_cs42448_machine_dt_match,
0428            #endif
0429     },
0430     .probe = mt2701_cs42448_machine_probe,
0431 };
0432 
0433 module_platform_driver(mt2701_cs42448_machine);
0434 
0435 /* Module information */
0436 MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
0437 MODULE_AUTHOR("Ir Lian <ir.lian@mediatek.com>");
0438 MODULE_LICENSE("GPL v2");
0439 MODULE_ALIAS("mt2701 cs42448 soc card");