13a280ed1SRyder Lee // SPDX-License-Identifier: GPL-2.0 20d1d7a66SGarlic Tseng /* 30d1d7a66SGarlic Tseng * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver 40d1d7a66SGarlic Tseng * 50d1d7a66SGarlic Tseng * Copyright (c) 2015 MediaTek Inc. 60d1d7a66SGarlic Tseng * Author: Koro Chen <koro.chen@mediatek.com> 70d1d7a66SGarlic Tseng */ 80d1d7a66SGarlic Tseng 90d1d7a66SGarlic Tseng #include <linux/module.h> 100d1d7a66SGarlic Tseng #include <sound/soc.h> 110d1d7a66SGarlic Tseng #include <sound/jack.h> 120d1d7a66SGarlic Tseng #include <linux/gpio.h> 130d1d7a66SGarlic Tseng #include "../../codecs/max98090.h" 140d1d7a66SGarlic Tseng 150d1d7a66SGarlic Tseng static struct snd_soc_jack mt8173_max98090_jack; 160d1d7a66SGarlic Tseng 170d1d7a66SGarlic Tseng static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { 180d1d7a66SGarlic Tseng { 190d1d7a66SGarlic Tseng .pin = "Headphone", 200d1d7a66SGarlic Tseng .mask = SND_JACK_HEADPHONE, 210d1d7a66SGarlic Tseng }, 220d1d7a66SGarlic Tseng { 230d1d7a66SGarlic Tseng .pin = "Headset Mic", 240d1d7a66SGarlic Tseng .mask = SND_JACK_MICROPHONE, 250d1d7a66SGarlic Tseng }, 260d1d7a66SGarlic Tseng }; 270d1d7a66SGarlic Tseng 280d1d7a66SGarlic Tseng static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { 290d1d7a66SGarlic Tseng SND_SOC_DAPM_SPK("Speaker", NULL), 300d1d7a66SGarlic Tseng SND_SOC_DAPM_MIC("Int Mic", NULL), 310d1d7a66SGarlic Tseng SND_SOC_DAPM_HP("Headphone", NULL), 320d1d7a66SGarlic Tseng SND_SOC_DAPM_MIC("Headset Mic", NULL), 330d1d7a66SGarlic Tseng }; 340d1d7a66SGarlic Tseng 350d1d7a66SGarlic Tseng static const struct snd_soc_dapm_route mt8173_max98090_routes[] = { 360d1d7a66SGarlic Tseng {"Speaker", NULL, "SPKL"}, 370d1d7a66SGarlic Tseng {"Speaker", NULL, "SPKR"}, 380d1d7a66SGarlic Tseng {"DMICL", NULL, "Int Mic"}, 390d1d7a66SGarlic Tseng {"Headphone", NULL, "HPL"}, 400d1d7a66SGarlic Tseng {"Headphone", NULL, "HPR"}, 410d1d7a66SGarlic Tseng {"Headset Mic", NULL, "MICBIAS"}, 420d1d7a66SGarlic Tseng {"IN34", NULL, "Headset Mic"}, 430d1d7a66SGarlic Tseng }; 440d1d7a66SGarlic Tseng 450d1d7a66SGarlic Tseng static const struct snd_kcontrol_new mt8173_max98090_controls[] = { 460d1d7a66SGarlic Tseng SOC_DAPM_PIN_SWITCH("Speaker"), 470d1d7a66SGarlic Tseng SOC_DAPM_PIN_SWITCH("Int Mic"), 480d1d7a66SGarlic Tseng SOC_DAPM_PIN_SWITCH("Headphone"), 490d1d7a66SGarlic Tseng SOC_DAPM_PIN_SWITCH("Headset Mic"), 500d1d7a66SGarlic Tseng }; 510d1d7a66SGarlic Tseng 520d1d7a66SGarlic Tseng static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream, 530d1d7a66SGarlic Tseng struct snd_pcm_hw_params *params) 540d1d7a66SGarlic Tseng { 550d1d7a66SGarlic Tseng struct snd_soc_pcm_runtime *rtd = substream->private_data; 56c8ac8212SKuninori Morimoto struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 570d1d7a66SGarlic Tseng 580d1d7a66SGarlic Tseng return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, 590d1d7a66SGarlic Tseng SND_SOC_CLOCK_IN); 600d1d7a66SGarlic Tseng } 610d1d7a66SGarlic Tseng 62424dfbf2SBhumika Goyal static const struct snd_soc_ops mt8173_max98090_ops = { 630d1d7a66SGarlic Tseng .hw_params = mt8173_max98090_hw_params, 640d1d7a66SGarlic Tseng }; 650d1d7a66SGarlic Tseng 660d1d7a66SGarlic Tseng static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) 670d1d7a66SGarlic Tseng { 680d1d7a66SGarlic Tseng int ret; 690d1d7a66SGarlic Tseng struct snd_soc_card *card = runtime->card; 70c8ac8212SKuninori Morimoto struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; 710d1d7a66SGarlic Tseng 720d1d7a66SGarlic Tseng /* enable jack detection */ 730d1d7a66SGarlic Tseng ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, 74b1b9e0d3SEnric Balletbo i Serra &mt8173_max98090_jack, 75b1b9e0d3SEnric Balletbo i Serra mt8173_max98090_jack_pins, 76b1b9e0d3SEnric Balletbo i Serra ARRAY_SIZE(mt8173_max98090_jack_pins)); 770d1d7a66SGarlic Tseng if (ret) { 78b1b9e0d3SEnric Balletbo i Serra dev_err(card->dev, "Can't create a new Jack %d\n", ret); 790d1d7a66SGarlic Tseng return ret; 800d1d7a66SGarlic Tseng } 810d1d7a66SGarlic Tseng 824c66b9d1SKuninori Morimoto return max98090_mic_detect(component, &mt8173_max98090_jack); 830d1d7a66SGarlic Tseng } 840d1d7a66SGarlic Tseng 851ea15792SKuninori Morimoto SND_SOC_DAILINK_DEFS(playback, 861ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("DL1")), 871ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_DUMMY()), 881ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY())); 891ea15792SKuninori Morimoto 901ea15792SKuninori Morimoto SND_SOC_DAILINK_DEFS(capture, 911ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("VUL")), 921ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_DUMMY()), 931ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY())); 941ea15792SKuninori Morimoto 951ea15792SKuninori Morimoto SND_SOC_DAILINK_DEFS(hifi, 961ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("I2S")), 971ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")), 981ea15792SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY())); 991ea15792SKuninori Morimoto 1000d1d7a66SGarlic Tseng /* Digital audio interface glue - connects codec <---> CPU */ 1010d1d7a66SGarlic Tseng static struct snd_soc_dai_link mt8173_max98090_dais[] = { 1020d1d7a66SGarlic Tseng /* Front End DAI links */ 1030d1d7a66SGarlic Tseng { 1040d1d7a66SGarlic Tseng .name = "MAX98090 Playback", 1050d1d7a66SGarlic Tseng .stream_name = "MAX98090 Playback", 1060d1d7a66SGarlic Tseng .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 1070d1d7a66SGarlic Tseng .dynamic = 1, 1080d1d7a66SGarlic Tseng .dpcm_playback = 1, 1091ea15792SKuninori Morimoto SND_SOC_DAILINK_REG(playback), 1100d1d7a66SGarlic Tseng }, 1110d1d7a66SGarlic Tseng { 1120d1d7a66SGarlic Tseng .name = "MAX98090 Capture", 1130d1d7a66SGarlic Tseng .stream_name = "MAX98090 Capture", 1140d1d7a66SGarlic Tseng .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 1150d1d7a66SGarlic Tseng .dynamic = 1, 1160d1d7a66SGarlic Tseng .dpcm_capture = 1, 1171ea15792SKuninori Morimoto SND_SOC_DAILINK_REG(capture), 1180d1d7a66SGarlic Tseng }, 1190d1d7a66SGarlic Tseng /* Back End DAI links */ 1200d1d7a66SGarlic Tseng { 1210d1d7a66SGarlic Tseng .name = "Codec", 1220d1d7a66SGarlic Tseng .no_pcm = 1, 1230d1d7a66SGarlic Tseng .init = mt8173_max98090_init, 1240d1d7a66SGarlic Tseng .ops = &mt8173_max98090_ops, 1250d1d7a66SGarlic Tseng .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 1260d1d7a66SGarlic Tseng SND_SOC_DAIFMT_CBS_CFS, 1270d1d7a66SGarlic Tseng .dpcm_playback = 1, 1280d1d7a66SGarlic Tseng .dpcm_capture = 1, 1291ea15792SKuninori Morimoto SND_SOC_DAILINK_REG(hifi), 1300d1d7a66SGarlic Tseng }, 1310d1d7a66SGarlic Tseng }; 1320d1d7a66SGarlic Tseng 1330d1d7a66SGarlic Tseng static struct snd_soc_card mt8173_max98090_card = { 1340d1d7a66SGarlic Tseng .name = "mt8173-max98090", 1350d1d7a66SGarlic Tseng .owner = THIS_MODULE, 1360d1d7a66SGarlic Tseng .dai_link = mt8173_max98090_dais, 1370d1d7a66SGarlic Tseng .num_links = ARRAY_SIZE(mt8173_max98090_dais), 1380d1d7a66SGarlic Tseng .controls = mt8173_max98090_controls, 1390d1d7a66SGarlic Tseng .num_controls = ARRAY_SIZE(mt8173_max98090_controls), 1400d1d7a66SGarlic Tseng .dapm_widgets = mt8173_max98090_widgets, 1410d1d7a66SGarlic Tseng .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), 1420d1d7a66SGarlic Tseng .dapm_routes = mt8173_max98090_routes, 1430d1d7a66SGarlic Tseng .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), 1440d1d7a66SGarlic Tseng }; 1450d1d7a66SGarlic Tseng 1460d1d7a66SGarlic Tseng static int mt8173_max98090_dev_probe(struct platform_device *pdev) 1470d1d7a66SGarlic Tseng { 1480d1d7a66SGarlic Tseng struct snd_soc_card *card = &mt8173_max98090_card; 1490d1d7a66SGarlic Tseng struct device_node *codec_node, *platform_node; 1507fe072b4SKuninori Morimoto struct snd_soc_dai_link *dai_link; 1510d1d7a66SGarlic Tseng int ret, i; 1520d1d7a66SGarlic Tseng 1530d1d7a66SGarlic Tseng platform_node = of_parse_phandle(pdev->dev.of_node, 1540d1d7a66SGarlic Tseng "mediatek,platform", 0); 1550d1d7a66SGarlic Tseng if (!platform_node) { 1560d1d7a66SGarlic Tseng dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); 1570d1d7a66SGarlic Tseng return -EINVAL; 1580d1d7a66SGarlic Tseng } 1597fe072b4SKuninori Morimoto for_each_card_prelinks(card, i, dai_link) { 1601ea15792SKuninori Morimoto if (dai_link->platforms->name) 1610d1d7a66SGarlic Tseng continue; 1621ea15792SKuninori Morimoto dai_link->platforms->of_node = platform_node; 1630d1d7a66SGarlic Tseng } 1640d1d7a66SGarlic Tseng 1650d1d7a66SGarlic Tseng codec_node = of_parse_phandle(pdev->dev.of_node, 1660d1d7a66SGarlic Tseng "mediatek,audio-codec", 0); 1670d1d7a66SGarlic Tseng if (!codec_node) { 1680d1d7a66SGarlic Tseng dev_err(&pdev->dev, 1690d1d7a66SGarlic Tseng "Property 'audio-codec' missing or invalid\n"); 1700d1d7a66SGarlic Tseng return -EINVAL; 1710d1d7a66SGarlic Tseng } 1727fe072b4SKuninori Morimoto for_each_card_prelinks(card, i, dai_link) { 1731ea15792SKuninori Morimoto if (dai_link->codecs->name) 1740d1d7a66SGarlic Tseng continue; 1751ea15792SKuninori Morimoto dai_link->codecs->of_node = codec_node; 1760d1d7a66SGarlic Tseng } 1770d1d7a66SGarlic Tseng card->dev = &pdev->dev; 1780d1d7a66SGarlic Tseng 1790d1d7a66SGarlic Tseng ret = devm_snd_soc_register_card(&pdev->dev, card); 1800d1d7a66SGarlic Tseng if (ret) 1810d1d7a66SGarlic Tseng dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", 1820d1d7a66SGarlic Tseng __func__, ret); 1830d1d7a66SGarlic Tseng return ret; 1840d1d7a66SGarlic Tseng } 1850d1d7a66SGarlic Tseng 1860d1d7a66SGarlic Tseng static const struct of_device_id mt8173_max98090_dt_match[] = { 1870d1d7a66SGarlic Tseng { .compatible = "mediatek,mt8173-max98090", }, 1880d1d7a66SGarlic Tseng { } 1890d1d7a66SGarlic Tseng }; 1900d1d7a66SGarlic Tseng MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); 1910d1d7a66SGarlic Tseng 1920d1d7a66SGarlic Tseng static struct platform_driver mt8173_max98090_driver = { 1930d1d7a66SGarlic Tseng .driver = { 1940d1d7a66SGarlic Tseng .name = "mt8173-max98090", 1950d1d7a66SGarlic Tseng .of_match_table = mt8173_max98090_dt_match, 1960d1d7a66SGarlic Tseng #ifdef CONFIG_PM 1970d1d7a66SGarlic Tseng .pm = &snd_soc_pm_ops, 1980d1d7a66SGarlic Tseng #endif 1990d1d7a66SGarlic Tseng }, 2000d1d7a66SGarlic Tseng .probe = mt8173_max98090_dev_probe, 2010d1d7a66SGarlic Tseng }; 2020d1d7a66SGarlic Tseng 2030d1d7a66SGarlic Tseng module_platform_driver(mt8173_max98090_driver); 2040d1d7a66SGarlic Tseng 2050d1d7a66SGarlic Tseng /* Module information */ 2060d1d7a66SGarlic Tseng MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); 2070d1d7a66SGarlic Tseng MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); 2080d1d7a66SGarlic Tseng MODULE_LICENSE("GPL v2"); 2090d1d7a66SGarlic Tseng MODULE_ALIAS("platform:mt8173-max98090"); 2100d1d7a66SGarlic Tseng 211