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 { 270cd08b10SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 28c8ac8212SKuninori Morimoto struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 29c8ac8212SKuninori Morimoto struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 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 47b664e06dSKuninori Morimoto SND_SOC_DAILINK_DEFS(playback, 48b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("PCMO0")), 49b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_DUMMY()), 50b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY())); 51b664e06dSKuninori Morimoto 52b664e06dSKuninori Morimoto SND_SOC_DAILINK_DEFS(capture, 53b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("PCM0")), 54b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_DUMMY()), 55b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY())); 56b664e06dSKuninori Morimoto 57b664e06dSKuninori Morimoto SND_SOC_DAILINK_DEFS(codec, 58b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("I2S0")), 59b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")), 60b664e06dSKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY())); 61b664e06dSKuninori Morimoto 628625c1dbSRyder Lee static struct snd_soc_dai_link mt2701_wm8960_dai_links[] = { 638625c1dbSRyder Lee /* FE */ 648625c1dbSRyder Lee { 658625c1dbSRyder Lee .name = "wm8960-playback", 668625c1dbSRyder Lee .stream_name = "wm8960-playback", 678625c1dbSRyder Lee .trigger = {SND_SOC_DPCM_TRIGGER_POST, 688625c1dbSRyder Lee SND_SOC_DPCM_TRIGGER_POST}, 698625c1dbSRyder Lee .dynamic = 1, 708625c1dbSRyder Lee .dpcm_playback = 1, 71b664e06dSKuninori Morimoto SND_SOC_DAILINK_REG(playback), 728625c1dbSRyder Lee }, 738625c1dbSRyder Lee { 748625c1dbSRyder Lee .name = "wm8960-capture", 758625c1dbSRyder Lee .stream_name = "wm8960-capture", 768625c1dbSRyder Lee .trigger = {SND_SOC_DPCM_TRIGGER_POST, 778625c1dbSRyder Lee SND_SOC_DPCM_TRIGGER_POST}, 788625c1dbSRyder Lee .dynamic = 1, 798625c1dbSRyder Lee .dpcm_capture = 1, 80b664e06dSKuninori Morimoto SND_SOC_DAILINK_REG(capture), 818625c1dbSRyder Lee }, 828625c1dbSRyder Lee /* BE */ 838625c1dbSRyder Lee { 848625c1dbSRyder Lee .name = "wm8960-codec", 858625c1dbSRyder Lee .no_pcm = 1, 868625c1dbSRyder Lee .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS 878625c1dbSRyder Lee | SND_SOC_DAIFMT_GATED, 888625c1dbSRyder Lee .ops = &mt2701_wm8960_be_ops, 898625c1dbSRyder Lee .dpcm_playback = 1, 908625c1dbSRyder Lee .dpcm_capture = 1, 91b664e06dSKuninori Morimoto SND_SOC_DAILINK_REG(codec), 928625c1dbSRyder Lee }, 938625c1dbSRyder Lee }; 948625c1dbSRyder Lee 958625c1dbSRyder Lee static struct snd_soc_card mt2701_wm8960_card = { 968625c1dbSRyder Lee .name = "mt2701-wm8960", 978625c1dbSRyder Lee .owner = THIS_MODULE, 988625c1dbSRyder Lee .dai_link = mt2701_wm8960_dai_links, 998625c1dbSRyder Lee .num_links = ARRAY_SIZE(mt2701_wm8960_dai_links), 1008625c1dbSRyder Lee .controls = mt2701_wm8960_controls, 1018625c1dbSRyder Lee .num_controls = ARRAY_SIZE(mt2701_wm8960_controls), 1028625c1dbSRyder Lee .dapm_widgets = mt2701_wm8960_widgets, 1038625c1dbSRyder Lee .num_dapm_widgets = ARRAY_SIZE(mt2701_wm8960_widgets), 1048625c1dbSRyder Lee }; 1058625c1dbSRyder Lee 1068625c1dbSRyder Lee static int mt2701_wm8960_machine_probe(struct platform_device *pdev) 1078625c1dbSRyder Lee { 1088625c1dbSRyder Lee struct snd_soc_card *card = &mt2701_wm8960_card; 1098625c1dbSRyder Lee struct device_node *platform_node, *codec_node; 1107fe072b4SKuninori Morimoto struct snd_soc_dai_link *dai_link; 1118625c1dbSRyder Lee int ret, i; 1128625c1dbSRyder Lee 1138625c1dbSRyder Lee platform_node = of_parse_phandle(pdev->dev.of_node, 1148625c1dbSRyder Lee "mediatek,platform", 0); 1158625c1dbSRyder Lee if (!platform_node) { 1168625c1dbSRyder Lee dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); 1178625c1dbSRyder Lee return -EINVAL; 1188625c1dbSRyder Lee } 1197fe072b4SKuninori Morimoto for_each_card_prelinks(card, i, dai_link) { 120b664e06dSKuninori Morimoto if (dai_link->platforms->name) 1218625c1dbSRyder Lee continue; 122b664e06dSKuninori Morimoto dai_link->platforms->of_node = platform_node; 1238625c1dbSRyder Lee } 1248625c1dbSRyder Lee 1258625c1dbSRyder Lee card->dev = &pdev->dev; 1268625c1dbSRyder Lee 1278625c1dbSRyder Lee codec_node = of_parse_phandle(pdev->dev.of_node, 1288625c1dbSRyder Lee "mediatek,audio-codec", 0); 1298625c1dbSRyder Lee if (!codec_node) { 1308625c1dbSRyder Lee dev_err(&pdev->dev, 1318625c1dbSRyder Lee "Property 'audio-codec' missing or invalid\n"); 1328625c1dbSRyder Lee return -EINVAL; 1338625c1dbSRyder Lee } 1347fe072b4SKuninori Morimoto for_each_card_prelinks(card, i, dai_link) { 135b664e06dSKuninori Morimoto if (dai_link->codecs->name) 1368625c1dbSRyder Lee continue; 137b664e06dSKuninori Morimoto dai_link->codecs->of_node = codec_node; 1388625c1dbSRyder Lee } 1398625c1dbSRyder Lee 1408625c1dbSRyder Lee ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); 1418625c1dbSRyder Lee if (ret) { 1428625c1dbSRyder Lee dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); 1438625c1dbSRyder Lee return ret; 1448625c1dbSRyder Lee } 1458625c1dbSRyder Lee 1468625c1dbSRyder Lee ret = devm_snd_soc_register_card(&pdev->dev, card); 1478625c1dbSRyder Lee if (ret) 1488625c1dbSRyder Lee dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", 1498625c1dbSRyder Lee __func__, ret); 1508625c1dbSRyder Lee 1518625c1dbSRyder Lee return ret; 1528625c1dbSRyder Lee } 1538625c1dbSRyder Lee 1548625c1dbSRyder Lee #ifdef CONFIG_OF 1558625c1dbSRyder Lee static const struct of_device_id mt2701_wm8960_machine_dt_match[] = { 1568625c1dbSRyder Lee {.compatible = "mediatek,mt2701-wm8960-machine",}, 1578625c1dbSRyder Lee {} 1588625c1dbSRyder Lee }; 1598625c1dbSRyder Lee #endif 1608625c1dbSRyder Lee 1618625c1dbSRyder Lee static struct platform_driver mt2701_wm8960_machine = { 1628625c1dbSRyder Lee .driver = { 1638625c1dbSRyder Lee .name = "mt2701-wm8960", 1648625c1dbSRyder Lee #ifdef CONFIG_OF 1658625c1dbSRyder Lee .of_match_table = mt2701_wm8960_machine_dt_match, 1668625c1dbSRyder Lee #endif 1678625c1dbSRyder Lee }, 1688625c1dbSRyder Lee .probe = mt2701_wm8960_machine_probe, 1698625c1dbSRyder Lee }; 1708625c1dbSRyder Lee 1718625c1dbSRyder Lee module_platform_driver(mt2701_wm8960_machine); 1728625c1dbSRyder Lee 1738625c1dbSRyder Lee /* Module information */ 1748625c1dbSRyder Lee MODULE_DESCRIPTION("MT2701 WM8960 ALSA SoC machine driver"); 1758625c1dbSRyder Lee MODULE_AUTHOR("Ryder Lee <ryder.lee@mediatek.com>"); 1768625c1dbSRyder Lee MODULE_LICENSE("GPL v2"); 1778625c1dbSRyder Lee MODULE_ALIAS("mt2701 wm8960 soc card"); 1788625c1dbSRyder Lee 179