0001
0002
0003
0004
0005
0006
0007
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
0141 snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
0142
0143
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
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
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
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");