13a280ed1SRyder Lee // SPDX-License-Identifier: GPL-2.0 28625c1dbSRyder Lee /* 38625c1dbSRyder Lee * mt2701-wm8960.c -- MT2701 WM8960 ALSA SoC machine driver 48625c1dbSRyder Lee * 58625c1dbSRyder Lee * Copyright (c) 2017 MediaTek Inc. 68625c1dbSRyder Lee * Author: Ryder Lee <ryder.lee@mediatek.com> 78625c1dbSRyder Lee */ 88625c1dbSRyder Lee 98625c1dbSRyder Lee #include <linux/module.h> 108625c1dbSRyder Lee #include <sound/soc.h> 118625c1dbSRyder Lee 128625c1dbSRyder Lee #include "mt2701-afe-common.h" 138625c1dbSRyder Lee 148625c1dbSRyder Lee static const struct snd_soc_dapm_widget mt2701_wm8960_widgets[] = { 158625c1dbSRyder Lee SND_SOC_DAPM_HP("Headphone", NULL), 168625c1dbSRyder Lee SND_SOC_DAPM_MIC("AMIC", NULL), 178625c1dbSRyder Lee }; 188625c1dbSRyder Lee 198625c1dbSRyder Lee static const struct snd_kcontrol_new mt2701_wm8960_controls[] = { 208625c1dbSRyder Lee SOC_DAPM_PIN_SWITCH("Headphone"), 218625c1dbSRyder Lee SOC_DAPM_PIN_SWITCH("AMIC"), 228625c1dbSRyder Lee }; 238625c1dbSRyder Lee 248625c1dbSRyder Lee static int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream, 258625c1dbSRyder Lee struct snd_pcm_hw_params *params) 268625c1dbSRyder Lee { 278625c1dbSRyder Lee struct snd_soc_pcm_runtime *rtd = substream->private_data; 288625c1dbSRyder Lee struct snd_soc_dai *codec_dai = rtd->codec_dai; 298625c1dbSRyder Lee struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 308625c1dbSRyder Lee unsigned int mclk_rate; 318625c1dbSRyder Lee unsigned int rate = params_rate(params); 328625c1dbSRyder Lee unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4; 338625c1dbSRyder Lee unsigned int div_bck_over_lrck = 64; 348625c1dbSRyder Lee 358625c1dbSRyder Lee mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck; 368625c1dbSRyder Lee 378625c1dbSRyder Lee snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT); 388625c1dbSRyder Lee snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN); 398625c1dbSRyder Lee 408625c1dbSRyder Lee return 0; 418625c1dbSRyder Lee } 428625c1dbSRyder Lee 438625c1dbSRyder Lee static struct snd_soc_ops mt2701_wm8960_be_ops = { 448625c1dbSRyder Lee .hw_params = mt2701_wm8960_be_ops_hw_params 458625c1dbSRyder Lee }; 468625c1dbSRyder Lee 478625c1dbSRyder Lee static struct snd_soc_dai_link mt2701_wm8960_dai_links[] = { 488625c1dbSRyder Lee /* FE */ 498625c1dbSRyder Lee { 508625c1dbSRyder Lee .name = "wm8960-playback", 518625c1dbSRyder Lee .stream_name = "wm8960-playback", 528625c1dbSRyder Lee .cpu_dai_name = "PCMO0", 538625c1dbSRyder Lee .codec_name = "snd-soc-dummy", 548625c1dbSRyder Lee .codec_dai_name = "snd-soc-dummy-dai", 558625c1dbSRyder Lee .trigger = {SND_SOC_DPCM_TRIGGER_POST, 568625c1dbSRyder Lee SND_SOC_DPCM_TRIGGER_POST}, 578625c1dbSRyder Lee .dynamic = 1, 588625c1dbSRyder Lee .dpcm_playback = 1, 598625c1dbSRyder Lee }, 608625c1dbSRyder Lee { 618625c1dbSRyder Lee .name = "wm8960-capture", 628625c1dbSRyder Lee .stream_name = "wm8960-capture", 638625c1dbSRyder Lee .cpu_dai_name = "PCM0", 648625c1dbSRyder Lee .codec_name = "snd-soc-dummy", 658625c1dbSRyder Lee .codec_dai_name = "snd-soc-dummy-dai", 668625c1dbSRyder Lee .trigger = {SND_SOC_DPCM_TRIGGER_POST, 678625c1dbSRyder Lee SND_SOC_DPCM_TRIGGER_POST}, 688625c1dbSRyder Lee .dynamic = 1, 698625c1dbSRyder Lee .dpcm_capture = 1, 708625c1dbSRyder Lee }, 718625c1dbSRyder Lee /* BE */ 728625c1dbSRyder Lee { 738625c1dbSRyder Lee .name = "wm8960-codec", 748625c1dbSRyder Lee .cpu_dai_name = "I2S0", 758625c1dbSRyder Lee .no_pcm = 1, 768625c1dbSRyder Lee .codec_dai_name = "wm8960-hifi", 778625c1dbSRyder Lee .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS 788625c1dbSRyder Lee | SND_SOC_DAIFMT_GATED, 798625c1dbSRyder Lee .ops = &mt2701_wm8960_be_ops, 808625c1dbSRyder Lee .dpcm_playback = 1, 818625c1dbSRyder Lee .dpcm_capture = 1, 828625c1dbSRyder Lee }, 838625c1dbSRyder Lee }; 848625c1dbSRyder Lee 858625c1dbSRyder Lee static struct snd_soc_card mt2701_wm8960_card = { 868625c1dbSRyder Lee .name = "mt2701-wm8960", 878625c1dbSRyder Lee .owner = THIS_MODULE, 888625c1dbSRyder Lee .dai_link = mt2701_wm8960_dai_links, 898625c1dbSRyder Lee .num_links = ARRAY_SIZE(mt2701_wm8960_dai_links), 908625c1dbSRyder Lee .controls = mt2701_wm8960_controls, 918625c1dbSRyder Lee .num_controls = ARRAY_SIZE(mt2701_wm8960_controls), 928625c1dbSRyder Lee .dapm_widgets = mt2701_wm8960_widgets, 938625c1dbSRyder Lee .num_dapm_widgets = ARRAY_SIZE(mt2701_wm8960_widgets), 948625c1dbSRyder Lee }; 958625c1dbSRyder Lee 968625c1dbSRyder Lee static int mt2701_wm8960_machine_probe(struct platform_device *pdev) 978625c1dbSRyder Lee { 988625c1dbSRyder Lee struct snd_soc_card *card = &mt2701_wm8960_card; 998625c1dbSRyder Lee struct device_node *platform_node, *codec_node; 1008625c1dbSRyder Lee int ret, i; 1018625c1dbSRyder Lee 1028625c1dbSRyder Lee platform_node = of_parse_phandle(pdev->dev.of_node, 1038625c1dbSRyder Lee "mediatek,platform", 0); 1048625c1dbSRyder Lee if (!platform_node) { 1058625c1dbSRyder Lee dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); 1068625c1dbSRyder Lee return -EINVAL; 1078625c1dbSRyder Lee } 1088625c1dbSRyder Lee for (i = 0; i < card->num_links; i++) { 1098625c1dbSRyder Lee if (mt2701_wm8960_dai_links[i].platform_name) 1108625c1dbSRyder Lee continue; 1118625c1dbSRyder Lee mt2701_wm8960_dai_links[i].platform_of_node = platform_node; 1128625c1dbSRyder Lee } 1138625c1dbSRyder Lee 1148625c1dbSRyder Lee card->dev = &pdev->dev; 1158625c1dbSRyder Lee 1168625c1dbSRyder Lee codec_node = of_parse_phandle(pdev->dev.of_node, 1178625c1dbSRyder Lee "mediatek,audio-codec", 0); 1188625c1dbSRyder Lee if (!codec_node) { 1198625c1dbSRyder Lee dev_err(&pdev->dev, 1208625c1dbSRyder Lee "Property 'audio-codec' missing or invalid\n"); 1218625c1dbSRyder Lee return -EINVAL; 1228625c1dbSRyder Lee } 1238625c1dbSRyder Lee for (i = 0; i < card->num_links; i++) { 1248625c1dbSRyder Lee if (mt2701_wm8960_dai_links[i].codec_name) 1258625c1dbSRyder Lee continue; 1268625c1dbSRyder Lee mt2701_wm8960_dai_links[i].codec_of_node = codec_node; 1278625c1dbSRyder Lee } 1288625c1dbSRyder Lee 1298625c1dbSRyder Lee ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); 1308625c1dbSRyder Lee if (ret) { 1318625c1dbSRyder Lee dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); 1328625c1dbSRyder Lee return ret; 1338625c1dbSRyder Lee } 1348625c1dbSRyder Lee 1358625c1dbSRyder Lee ret = devm_snd_soc_register_card(&pdev->dev, card); 1368625c1dbSRyder Lee if (ret) 1378625c1dbSRyder Lee dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", 1388625c1dbSRyder Lee __func__, ret); 1398625c1dbSRyder Lee 1408625c1dbSRyder Lee return ret; 1418625c1dbSRyder Lee } 1428625c1dbSRyder Lee 1438625c1dbSRyder Lee #ifdef CONFIG_OF 1448625c1dbSRyder Lee static const struct of_device_id mt2701_wm8960_machine_dt_match[] = { 1458625c1dbSRyder Lee {.compatible = "mediatek,mt2701-wm8960-machine",}, 1468625c1dbSRyder Lee {} 1478625c1dbSRyder Lee }; 1488625c1dbSRyder Lee #endif 1498625c1dbSRyder Lee 1508625c1dbSRyder Lee static struct platform_driver mt2701_wm8960_machine = { 1518625c1dbSRyder Lee .driver = { 1528625c1dbSRyder Lee .name = "mt2701-wm8960", 1538625c1dbSRyder Lee .owner = THIS_MODULE, 1548625c1dbSRyder Lee #ifdef CONFIG_OF 1558625c1dbSRyder Lee .of_match_table = mt2701_wm8960_machine_dt_match, 1568625c1dbSRyder Lee #endif 1578625c1dbSRyder Lee }, 1588625c1dbSRyder Lee .probe = mt2701_wm8960_machine_probe, 1598625c1dbSRyder Lee }; 1608625c1dbSRyder Lee 1618625c1dbSRyder Lee module_platform_driver(mt2701_wm8960_machine); 1628625c1dbSRyder Lee 1638625c1dbSRyder Lee /* Module information */ 1648625c1dbSRyder Lee MODULE_DESCRIPTION("MT2701 WM8960 ALSA SoC machine driver"); 1658625c1dbSRyder Lee MODULE_AUTHOR("Ryder Lee <ryder.lee@mediatek.com>"); 1668625c1dbSRyder Lee MODULE_LICENSE("GPL v2"); 1678625c1dbSRyder Lee MODULE_ALIAS("mt2701 wm8960 soc card"); 1688625c1dbSRyder Lee 169