18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mt2701-wm8960.c -- MT2701 WM8960 ALSA SoC machine driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017 MediaTek Inc. 68c2ecf20Sopenharmony_ci * Author: Ryder Lee <ryder.lee@mediatek.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <sound/soc.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "mt2701-afe-common.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget mt2701_wm8960_widgets[] = { 158c2ecf20Sopenharmony_ci SND_SOC_DAPM_HP("Headphone", NULL), 168c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("AMIC", NULL), 178c2ecf20Sopenharmony_ci}; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new mt2701_wm8960_controls[] = { 208c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headphone"), 218c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("AMIC"), 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream, 258c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 288c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 298c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 308c2ecf20Sopenharmony_ci unsigned int mclk_rate; 318c2ecf20Sopenharmony_ci unsigned int rate = params_rate(params); 328c2ecf20Sopenharmony_ci unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4; 338c2ecf20Sopenharmony_ci unsigned int div_bck_over_lrck = 64; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT); 388c2ecf20Sopenharmony_ci snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct snd_soc_ops mt2701_wm8960_be_ops = { 448c2ecf20Sopenharmony_ci .hw_params = mt2701_wm8960_be_ops_hw_params 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(playback, 488c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("PCMO0")), 498c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_DUMMY()), 508c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(capture, 538c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("PCM0")), 548c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_DUMMY()), 558c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(codec, 588c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("I2S0")), 598c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")), 608c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link mt2701_wm8960_dai_links[] = { 638c2ecf20Sopenharmony_ci /* FE */ 648c2ecf20Sopenharmony_ci { 658c2ecf20Sopenharmony_ci .name = "wm8960-playback", 668c2ecf20Sopenharmony_ci .stream_name = "wm8960-playback", 678c2ecf20Sopenharmony_ci .trigger = {SND_SOC_DPCM_TRIGGER_POST, 688c2ecf20Sopenharmony_ci SND_SOC_DPCM_TRIGGER_POST}, 698c2ecf20Sopenharmony_ci .dynamic = 1, 708c2ecf20Sopenharmony_ci .dpcm_playback = 1, 718c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(playback), 728c2ecf20Sopenharmony_ci }, 738c2ecf20Sopenharmony_ci { 748c2ecf20Sopenharmony_ci .name = "wm8960-capture", 758c2ecf20Sopenharmony_ci .stream_name = "wm8960-capture", 768c2ecf20Sopenharmony_ci .trigger = {SND_SOC_DPCM_TRIGGER_POST, 778c2ecf20Sopenharmony_ci SND_SOC_DPCM_TRIGGER_POST}, 788c2ecf20Sopenharmony_ci .dynamic = 1, 798c2ecf20Sopenharmony_ci .dpcm_capture = 1, 808c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(capture), 818c2ecf20Sopenharmony_ci }, 828c2ecf20Sopenharmony_ci /* BE */ 838c2ecf20Sopenharmony_ci { 848c2ecf20Sopenharmony_ci .name = "wm8960-codec", 858c2ecf20Sopenharmony_ci .no_pcm = 1, 868c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS 878c2ecf20Sopenharmony_ci | SND_SOC_DAIFMT_GATED, 888c2ecf20Sopenharmony_ci .ops = &mt2701_wm8960_be_ops, 898c2ecf20Sopenharmony_ci .dpcm_playback = 1, 908c2ecf20Sopenharmony_ci .dpcm_capture = 1, 918c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(codec), 928c2ecf20Sopenharmony_ci }, 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic struct snd_soc_card mt2701_wm8960_card = { 968c2ecf20Sopenharmony_ci .name = "mt2701-wm8960", 978c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 988c2ecf20Sopenharmony_ci .dai_link = mt2701_wm8960_dai_links, 998c2ecf20Sopenharmony_ci .num_links = ARRAY_SIZE(mt2701_wm8960_dai_links), 1008c2ecf20Sopenharmony_ci .controls = mt2701_wm8960_controls, 1018c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(mt2701_wm8960_controls), 1028c2ecf20Sopenharmony_ci .dapm_widgets = mt2701_wm8960_widgets, 1038c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(mt2701_wm8960_widgets), 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int mt2701_wm8960_machine_probe(struct platform_device *pdev) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct snd_soc_card *card = &mt2701_wm8960_card; 1098c2ecf20Sopenharmony_ci struct device_node *platform_node, *codec_node; 1108c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link; 1118c2ecf20Sopenharmony_ci int ret, i; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci platform_node = of_parse_phandle(pdev->dev.of_node, 1148c2ecf20Sopenharmony_ci "mediatek,platform", 0); 1158c2ecf20Sopenharmony_ci if (!platform_node) { 1168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 1208c2ecf20Sopenharmony_ci if (dai_link->platforms->name) 1218c2ecf20Sopenharmony_ci continue; 1228c2ecf20Sopenharmony_ci dai_link->platforms->of_node = platform_node; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci card->dev = &pdev->dev; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci codec_node = of_parse_phandle(pdev->dev.of_node, 1288c2ecf20Sopenharmony_ci "mediatek,audio-codec", 0); 1298c2ecf20Sopenharmony_ci if (!codec_node) { 1308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 1318c2ecf20Sopenharmony_ci "Property 'audio-codec' missing or invalid\n"); 1328c2ecf20Sopenharmony_ci ret = -EINVAL; 1338c2ecf20Sopenharmony_ci goto put_platform_node; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 1368c2ecf20Sopenharmony_ci if (dai_link->codecs->name) 1378c2ecf20Sopenharmony_ci continue; 1388c2ecf20Sopenharmony_ci dai_link->codecs->of_node = codec_node; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); 1428c2ecf20Sopenharmony_ci if (ret) { 1438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); 1448c2ecf20Sopenharmony_ci goto put_codec_node; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_card(&pdev->dev, card); 1488c2ecf20Sopenharmony_ci if (ret) 1498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", 1508c2ecf20Sopenharmony_ci __func__, ret); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciput_codec_node: 1538c2ecf20Sopenharmony_ci of_node_put(codec_node); 1548c2ecf20Sopenharmony_ciput_platform_node: 1558c2ecf20Sopenharmony_ci of_node_put(platform_node); 1568c2ecf20Sopenharmony_ci return ret; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 1608c2ecf20Sopenharmony_cistatic const struct of_device_id mt2701_wm8960_machine_dt_match[] = { 1618c2ecf20Sopenharmony_ci {.compatible = "mediatek,mt2701-wm8960-machine",}, 1628c2ecf20Sopenharmony_ci {} 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic struct platform_driver mt2701_wm8960_machine = { 1678c2ecf20Sopenharmony_ci .driver = { 1688c2ecf20Sopenharmony_ci .name = "mt2701-wm8960", 1698c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 1708c2ecf20Sopenharmony_ci .of_match_table = mt2701_wm8960_machine_dt_match, 1718c2ecf20Sopenharmony_ci#endif 1728c2ecf20Sopenharmony_ci }, 1738c2ecf20Sopenharmony_ci .probe = mt2701_wm8960_machine_probe, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cimodule_platform_driver(mt2701_wm8960_machine); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* Module information */ 1798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MT2701 WM8960 ALSA SoC machine driver"); 1808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ryder Lee <ryder.lee@mediatek.com>"); 1818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1828c2ecf20Sopenharmony_ciMODULE_ALIAS("mt2701 wm8960 soc card"); 1838c2ecf20Sopenharmony_ci 184