18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015 MediaTek Inc. 68c2ecf20Sopenharmony_ci * Author: Koro Chen <koro.chen@mediatek.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <sound/soc.h> 118c2ecf20Sopenharmony_ci#include <sound/jack.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio.h> 138c2ecf20Sopenharmony_ci#include "../../codecs/max98090.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic struct snd_soc_jack mt8173_max98090_jack; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { 188c2ecf20Sopenharmony_ci { 198c2ecf20Sopenharmony_ci .pin = "Headphone", 208c2ecf20Sopenharmony_ci .mask = SND_JACK_HEADPHONE, 218c2ecf20Sopenharmony_ci }, 228c2ecf20Sopenharmony_ci { 238c2ecf20Sopenharmony_ci .pin = "Headset Mic", 248c2ecf20Sopenharmony_ci .mask = SND_JACK_MICROPHONE, 258c2ecf20Sopenharmony_ci }, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { 298c2ecf20Sopenharmony_ci SND_SOC_DAPM_SPK("Speaker", NULL), 308c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Int Mic", NULL), 318c2ecf20Sopenharmony_ci SND_SOC_DAPM_HP("Headphone", NULL), 328c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", NULL), 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route mt8173_max98090_routes[] = { 368c2ecf20Sopenharmony_ci {"Speaker", NULL, "SPKL"}, 378c2ecf20Sopenharmony_ci {"Speaker", NULL, "SPKR"}, 388c2ecf20Sopenharmony_ci {"DMICL", NULL, "Int Mic"}, 398c2ecf20Sopenharmony_ci {"Headphone", NULL, "HPL"}, 408c2ecf20Sopenharmony_ci {"Headphone", NULL, "HPR"}, 418c2ecf20Sopenharmony_ci {"Headset Mic", NULL, "MICBIAS"}, 428c2ecf20Sopenharmony_ci {"IN34", NULL, "Headset Mic"}, 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new mt8173_max98090_controls[] = { 468c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Speaker"), 478c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Int Mic"), 488c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headphone"), 498c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headset Mic"), 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int mt8173_max98090_hw_params(struct snd_pcm_substream *substream, 538c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 568c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, 598c2ecf20Sopenharmony_ci SND_SOC_CLOCK_IN); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const struct snd_soc_ops mt8173_max98090_ops = { 638c2ecf20Sopenharmony_ci .hw_params = mt8173_max98090_hw_params, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci struct snd_soc_card *card = runtime->card; 708c2ecf20Sopenharmony_ci struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* enable jack detection */ 738c2ecf20Sopenharmony_ci ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, 748c2ecf20Sopenharmony_ci &mt8173_max98090_jack, 758c2ecf20Sopenharmony_ci mt8173_max98090_jack_pins, 768c2ecf20Sopenharmony_ci ARRAY_SIZE(mt8173_max98090_jack_pins)); 778c2ecf20Sopenharmony_ci if (ret) { 788c2ecf20Sopenharmony_ci dev_err(card->dev, "Can't create a new Jack %d\n", ret); 798c2ecf20Sopenharmony_ci return ret; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return max98090_mic_detect(component, &mt8173_max98090_jack); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(playback, 868c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("DL1")), 878c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_DUMMY()), 888c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(capture, 918c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("VUL")), 928c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_DUMMY()), 938c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(hifi, 968c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("I2S")), 978c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")), 988c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_EMPTY())); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Digital audio interface glue - connects codec <---> CPU */ 1018c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link mt8173_max98090_dais[] = { 1028c2ecf20Sopenharmony_ci /* Front End DAI links */ 1038c2ecf20Sopenharmony_ci { 1048c2ecf20Sopenharmony_ci .name = "MAX98090 Playback", 1058c2ecf20Sopenharmony_ci .stream_name = "MAX98090 Playback", 1068c2ecf20Sopenharmony_ci .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 1078c2ecf20Sopenharmony_ci .dynamic = 1, 1088c2ecf20Sopenharmony_ci .dpcm_playback = 1, 1098c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(playback), 1108c2ecf20Sopenharmony_ci }, 1118c2ecf20Sopenharmony_ci { 1128c2ecf20Sopenharmony_ci .name = "MAX98090 Capture", 1138c2ecf20Sopenharmony_ci .stream_name = "MAX98090 Capture", 1148c2ecf20Sopenharmony_ci .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 1158c2ecf20Sopenharmony_ci .dynamic = 1, 1168c2ecf20Sopenharmony_ci .dpcm_capture = 1, 1178c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(capture), 1188c2ecf20Sopenharmony_ci }, 1198c2ecf20Sopenharmony_ci /* Back End DAI links */ 1208c2ecf20Sopenharmony_ci { 1218c2ecf20Sopenharmony_ci .name = "Codec", 1228c2ecf20Sopenharmony_ci .no_pcm = 1, 1238c2ecf20Sopenharmony_ci .init = mt8173_max98090_init, 1248c2ecf20Sopenharmony_ci .ops = &mt8173_max98090_ops, 1258c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 1268c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS, 1278c2ecf20Sopenharmony_ci .dpcm_playback = 1, 1288c2ecf20Sopenharmony_ci .dpcm_capture = 1, 1298c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(hifi), 1308c2ecf20Sopenharmony_ci }, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct snd_soc_card mt8173_max98090_card = { 1348c2ecf20Sopenharmony_ci .name = "mt8173-max98090", 1358c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1368c2ecf20Sopenharmony_ci .dai_link = mt8173_max98090_dais, 1378c2ecf20Sopenharmony_ci .num_links = ARRAY_SIZE(mt8173_max98090_dais), 1388c2ecf20Sopenharmony_ci .controls = mt8173_max98090_controls, 1398c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(mt8173_max98090_controls), 1408c2ecf20Sopenharmony_ci .dapm_widgets = mt8173_max98090_widgets, 1418c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), 1428c2ecf20Sopenharmony_ci .dapm_routes = mt8173_max98090_routes, 1438c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int mt8173_max98090_dev_probe(struct platform_device *pdev) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct snd_soc_card *card = &mt8173_max98090_card; 1498c2ecf20Sopenharmony_ci struct device_node *codec_node, *platform_node; 1508c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link; 1518c2ecf20Sopenharmony_ci int ret, i; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci platform_node = of_parse_phandle(pdev->dev.of_node, 1548c2ecf20Sopenharmony_ci "mediatek,platform", 0); 1558c2ecf20Sopenharmony_ci if (!platform_node) { 1568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 1608c2ecf20Sopenharmony_ci if (dai_link->platforms->name) 1618c2ecf20Sopenharmony_ci continue; 1628c2ecf20Sopenharmony_ci dai_link->platforms->of_node = platform_node; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci codec_node = of_parse_phandle(pdev->dev.of_node, 1668c2ecf20Sopenharmony_ci "mediatek,audio-codec", 0); 1678c2ecf20Sopenharmony_ci if (!codec_node) { 1688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 1698c2ecf20Sopenharmony_ci "Property 'audio-codec' missing or invalid\n"); 1708c2ecf20Sopenharmony_ci ret = -EINVAL; 1718c2ecf20Sopenharmony_ci goto put_platform_node; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 1748c2ecf20Sopenharmony_ci if (dai_link->codecs->name) 1758c2ecf20Sopenharmony_ci continue; 1768c2ecf20Sopenharmony_ci dai_link->codecs->of_node = codec_node; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci card->dev = &pdev->dev; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_card(&pdev->dev, card); 1818c2ecf20Sopenharmony_ci if (ret) 1828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", 1838c2ecf20Sopenharmony_ci __func__, ret); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci of_node_put(codec_node); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciput_platform_node: 1888c2ecf20Sopenharmony_ci of_node_put(platform_node); 1898c2ecf20Sopenharmony_ci return ret; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic const struct of_device_id mt8173_max98090_dt_match[] = { 1938c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt8173-max98090", }, 1948c2ecf20Sopenharmony_ci { } 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct platform_driver mt8173_max98090_driver = { 1998c2ecf20Sopenharmony_ci .driver = { 2008c2ecf20Sopenharmony_ci .name = "mt8173-max98090", 2018c2ecf20Sopenharmony_ci .of_match_table = mt8173_max98090_dt_match, 2028c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2038c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 2048c2ecf20Sopenharmony_ci#endif 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci .probe = mt8173_max98090_dev_probe, 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cimodule_platform_driver(mt8173_max98090_driver); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* Module information */ 2128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); 2138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); 2148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2158c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mt8173-max98090"); 2168c2ecf20Sopenharmony_ci 217