10d1d7a66SGarlic Tseng /* 20d1d7a66SGarlic Tseng * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver 30d1d7a66SGarlic Tseng * 40d1d7a66SGarlic Tseng * Copyright (c) 2015 MediaTek Inc. 50d1d7a66SGarlic Tseng * Author: Koro Chen <koro.chen@mediatek.com> 60d1d7a66SGarlic Tseng * 70d1d7a66SGarlic Tseng * This program is free software; you can redistribute it and/or modify 80d1d7a66SGarlic Tseng * it under the terms of the GNU General Public License version 2 and 90d1d7a66SGarlic Tseng * only version 2 as published by the Free Software Foundation. 100d1d7a66SGarlic Tseng * 110d1d7a66SGarlic Tseng * This program is distributed in the hope that it will be useful, 120d1d7a66SGarlic Tseng * but WITHOUT ANY WARRANTY; without even the implied warranty of 130d1d7a66SGarlic Tseng * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 140d1d7a66SGarlic Tseng * GNU General Public License for more details. 150d1d7a66SGarlic Tseng */ 160d1d7a66SGarlic Tseng 170d1d7a66SGarlic Tseng #include <linux/module.h> 180d1d7a66SGarlic Tseng #include <sound/soc.h> 190d1d7a66SGarlic Tseng #include <sound/jack.h> 200d1d7a66SGarlic Tseng #include <linux/gpio.h> 210d1d7a66SGarlic Tseng #include "../../codecs/max98090.h" 220d1d7a66SGarlic Tseng 230d1d7a66SGarlic Tseng static struct snd_soc_jack mt8173_max98090_jack; 240d1d7a66SGarlic Tseng 250d1d7a66SGarlic Tseng static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { 260d1d7a66SGarlic Tseng { 270d1d7a66SGarlic Tseng .pin = "Headphone", 280d1d7a66SGarlic Tseng .mask = SND_JACK_HEADPHONE, 290d1d7a66SGarlic Tseng }, 300d1d7a66SGarlic Tseng { 310d1d7a66SGarlic Tseng .pin = "Headset Mic", 320d1d7a66SGarlic Tseng .mask = SND_JACK_MICROPHONE, 330d1d7a66SGarlic Tseng }, 340d1d7a66SGarlic Tseng }; 350d1d7a66SGarlic Tseng 360d1d7a66SGarlic Tseng static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { 370d1d7a66SGarlic Tseng SND_SOC_DAPM_SPK("Speaker", NULL), 380d1d7a66SGarlic Tseng SND_SOC_DAPM_MIC("Int Mic", NULL), 390d1d7a66SGarlic Tseng SND_SOC_DAPM_HP("Headphone", NULL), 400d1d7a66SGarlic Tseng SND_SOC_DAPM_MIC("Headset Mic", NULL), 410d1d7a66SGarlic Tseng }; 420d1d7a66SGarlic Tseng 430d1d7a66SGarlic Tseng static const struct snd_soc_dapm_route mt8173_max98090_routes[] = { 440d1d7a66SGarlic Tseng {"Speaker", NULL, "SPKL"}, 450d1d7a66SGarlic Tseng {"Speaker", NULL, "SPKR"}, 460d1d7a66SGarlic Tseng {"DMICL", NULL, "Int Mic"}, 470d1d7a66SGarlic Tseng {"Headphone", NULL, "HPL"}, 480d1d7a66SGarlic Tseng {"Headphone", NULL, "HPR"}, 490d1d7a66SGarlic Tseng {"Headset Mic", NULL, "MICBIAS"}, 500d1d7a66SGarlic Tseng {"IN34", NULL, "Headset Mic"}, 510d1d7a66SGarlic Tseng }; 520d1d7a66SGarlic Tseng 530d1d7a66SGarlic Tseng static const struct snd_kcontrol_new mt8173_max98090_controls[] = { 540d1d7a66SGarlic Tseng SOC_DAPM_PIN_SWITCH("Speaker"), 550d1d7a66SGarlic Tseng SOC_DAPM_PIN_SWITCH("Int Mic"), 560d1d7a66SGarlic Tseng SOC_DAPM_PIN_SWITCH("Headphone"), 570d1d7a66SGarlic Tseng SOC_DAPM_PIN_SWITCH("Headset Mic"), 580d1d7a66SGarlic Tseng }; 590d1d7a66SGarlic Tseng 600d1d7a66SGarlic Tseng static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream, 610d1d7a66SGarlic Tseng struct snd_pcm_hw_params *params) 620d1d7a66SGarlic Tseng { 630d1d7a66SGarlic Tseng struct snd_soc_pcm_runtime *rtd = substream->private_data; 640d1d7a66SGarlic Tseng struct snd_soc_dai *codec_dai = rtd->codec_dai; 650d1d7a66SGarlic Tseng 660d1d7a66SGarlic Tseng return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, 670d1d7a66SGarlic Tseng SND_SOC_CLOCK_IN); 680d1d7a66SGarlic Tseng } 690d1d7a66SGarlic Tseng 70424dfbf2SBhumika Goyal static const struct snd_soc_ops mt8173_max98090_ops = { 710d1d7a66SGarlic Tseng .hw_params = mt8173_max98090_hw_params, 720d1d7a66SGarlic Tseng }; 730d1d7a66SGarlic Tseng 740d1d7a66SGarlic Tseng static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) 750d1d7a66SGarlic Tseng { 760d1d7a66SGarlic Tseng int ret; 770d1d7a66SGarlic Tseng struct snd_soc_card *card = runtime->card; 784c66b9d1SKuninori Morimoto struct snd_soc_component *component = runtime->codec_dai->component; 790d1d7a66SGarlic Tseng 800d1d7a66SGarlic Tseng /* enable jack detection */ 810d1d7a66SGarlic Tseng ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, 82b1b9e0d3SEnric Balletbo i Serra &mt8173_max98090_jack, 83b1b9e0d3SEnric Balletbo i Serra mt8173_max98090_jack_pins, 84b1b9e0d3SEnric Balletbo i Serra ARRAY_SIZE(mt8173_max98090_jack_pins)); 850d1d7a66SGarlic Tseng if (ret) { 86b1b9e0d3SEnric Balletbo i Serra dev_err(card->dev, "Can't create a new Jack %d\n", ret); 870d1d7a66SGarlic Tseng return ret; 880d1d7a66SGarlic Tseng } 890d1d7a66SGarlic Tseng 904c66b9d1SKuninori Morimoto return max98090_mic_detect(component, &mt8173_max98090_jack); 910d1d7a66SGarlic Tseng } 920d1d7a66SGarlic Tseng 930d1d7a66SGarlic Tseng /* Digital audio interface glue - connects codec <---> CPU */ 940d1d7a66SGarlic Tseng static struct snd_soc_dai_link mt8173_max98090_dais[] = { 950d1d7a66SGarlic Tseng /* Front End DAI links */ 960d1d7a66SGarlic Tseng { 970d1d7a66SGarlic Tseng .name = "MAX98090 Playback", 980d1d7a66SGarlic Tseng .stream_name = "MAX98090 Playback", 990d1d7a66SGarlic Tseng .cpu_dai_name = "DL1", 1000d1d7a66SGarlic Tseng .codec_name = "snd-soc-dummy", 1010d1d7a66SGarlic Tseng .codec_dai_name = "snd-soc-dummy-dai", 1020d1d7a66SGarlic Tseng .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 1030d1d7a66SGarlic Tseng .dynamic = 1, 1040d1d7a66SGarlic Tseng .dpcm_playback = 1, 1050d1d7a66SGarlic Tseng }, 1060d1d7a66SGarlic Tseng { 1070d1d7a66SGarlic Tseng .name = "MAX98090 Capture", 1080d1d7a66SGarlic Tseng .stream_name = "MAX98090 Capture", 1090d1d7a66SGarlic Tseng .cpu_dai_name = "VUL", 1100d1d7a66SGarlic Tseng .codec_name = "snd-soc-dummy", 1110d1d7a66SGarlic Tseng .codec_dai_name = "snd-soc-dummy-dai", 1120d1d7a66SGarlic Tseng .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, 1130d1d7a66SGarlic Tseng .dynamic = 1, 1140d1d7a66SGarlic Tseng .dpcm_capture = 1, 1150d1d7a66SGarlic Tseng }, 1160d1d7a66SGarlic Tseng /* Back End DAI links */ 1170d1d7a66SGarlic Tseng { 1180d1d7a66SGarlic Tseng .name = "Codec", 1190d1d7a66SGarlic Tseng .cpu_dai_name = "I2S", 1200d1d7a66SGarlic Tseng .no_pcm = 1, 1210d1d7a66SGarlic Tseng .codec_dai_name = "HiFi", 1220d1d7a66SGarlic Tseng .init = mt8173_max98090_init, 1230d1d7a66SGarlic Tseng .ops = &mt8173_max98090_ops, 1240d1d7a66SGarlic Tseng .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 1250d1d7a66SGarlic Tseng SND_SOC_DAIFMT_CBS_CFS, 1260d1d7a66SGarlic Tseng .dpcm_playback = 1, 1270d1d7a66SGarlic Tseng .dpcm_capture = 1, 1280d1d7a66SGarlic Tseng }, 1290d1d7a66SGarlic Tseng }; 1300d1d7a66SGarlic Tseng 1310d1d7a66SGarlic Tseng static struct snd_soc_card mt8173_max98090_card = { 1320d1d7a66SGarlic Tseng .name = "mt8173-max98090", 1330d1d7a66SGarlic Tseng .owner = THIS_MODULE, 1340d1d7a66SGarlic Tseng .dai_link = mt8173_max98090_dais, 1350d1d7a66SGarlic Tseng .num_links = ARRAY_SIZE(mt8173_max98090_dais), 1360d1d7a66SGarlic Tseng .controls = mt8173_max98090_controls, 1370d1d7a66SGarlic Tseng .num_controls = ARRAY_SIZE(mt8173_max98090_controls), 1380d1d7a66SGarlic Tseng .dapm_widgets = mt8173_max98090_widgets, 1390d1d7a66SGarlic Tseng .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), 1400d1d7a66SGarlic Tseng .dapm_routes = mt8173_max98090_routes, 1410d1d7a66SGarlic Tseng .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), 1420d1d7a66SGarlic Tseng }; 1430d1d7a66SGarlic Tseng 1440d1d7a66SGarlic Tseng static int mt8173_max98090_dev_probe(struct platform_device *pdev) 1450d1d7a66SGarlic Tseng { 1460d1d7a66SGarlic Tseng struct snd_soc_card *card = &mt8173_max98090_card; 1470d1d7a66SGarlic Tseng struct device_node *codec_node, *platform_node; 1480d1d7a66SGarlic Tseng int ret, i; 1490d1d7a66SGarlic Tseng 1500d1d7a66SGarlic Tseng platform_node = of_parse_phandle(pdev->dev.of_node, 1510d1d7a66SGarlic Tseng "mediatek,platform", 0); 1520d1d7a66SGarlic Tseng if (!platform_node) { 1530d1d7a66SGarlic Tseng dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); 1540d1d7a66SGarlic Tseng return -EINVAL; 1550d1d7a66SGarlic Tseng } 1560d1d7a66SGarlic Tseng for (i = 0; i < card->num_links; i++) { 1570d1d7a66SGarlic Tseng if (mt8173_max98090_dais[i].platform_name) 1580d1d7a66SGarlic Tseng continue; 1590d1d7a66SGarlic Tseng mt8173_max98090_dais[i].platform_of_node = platform_node; 1600d1d7a66SGarlic Tseng } 1610d1d7a66SGarlic Tseng 1620d1d7a66SGarlic Tseng codec_node = of_parse_phandle(pdev->dev.of_node, 1630d1d7a66SGarlic Tseng "mediatek,audio-codec", 0); 1640d1d7a66SGarlic Tseng if (!codec_node) { 1650d1d7a66SGarlic Tseng dev_err(&pdev->dev, 1660d1d7a66SGarlic Tseng "Property 'audio-codec' missing or invalid\n"); 1670d1d7a66SGarlic Tseng return -EINVAL; 1680d1d7a66SGarlic Tseng } 1690d1d7a66SGarlic Tseng for (i = 0; i < card->num_links; i++) { 1700d1d7a66SGarlic Tseng if (mt8173_max98090_dais[i].codec_name) 1710d1d7a66SGarlic Tseng continue; 1720d1d7a66SGarlic Tseng mt8173_max98090_dais[i].codec_of_node = codec_node; 1730d1d7a66SGarlic Tseng } 1740d1d7a66SGarlic Tseng card->dev = &pdev->dev; 1750d1d7a66SGarlic Tseng 1760d1d7a66SGarlic Tseng ret = devm_snd_soc_register_card(&pdev->dev, card); 1770d1d7a66SGarlic Tseng if (ret) 1780d1d7a66SGarlic Tseng dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", 1790d1d7a66SGarlic Tseng __func__, ret); 1800d1d7a66SGarlic Tseng return ret; 1810d1d7a66SGarlic Tseng } 1820d1d7a66SGarlic Tseng 1830d1d7a66SGarlic Tseng static const struct of_device_id mt8173_max98090_dt_match[] = { 1840d1d7a66SGarlic Tseng { .compatible = "mediatek,mt8173-max98090", }, 1850d1d7a66SGarlic Tseng { } 1860d1d7a66SGarlic Tseng }; 1870d1d7a66SGarlic Tseng MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); 1880d1d7a66SGarlic Tseng 1890d1d7a66SGarlic Tseng static struct platform_driver mt8173_max98090_driver = { 1900d1d7a66SGarlic Tseng .driver = { 1910d1d7a66SGarlic Tseng .name = "mt8173-max98090", 1920d1d7a66SGarlic Tseng .of_match_table = mt8173_max98090_dt_match, 1930d1d7a66SGarlic Tseng #ifdef CONFIG_PM 1940d1d7a66SGarlic Tseng .pm = &snd_soc_pm_ops, 1950d1d7a66SGarlic Tseng #endif 1960d1d7a66SGarlic Tseng }, 1970d1d7a66SGarlic Tseng .probe = mt8173_max98090_dev_probe, 1980d1d7a66SGarlic Tseng }; 1990d1d7a66SGarlic Tseng 2000d1d7a66SGarlic Tseng module_platform_driver(mt8173_max98090_driver); 2010d1d7a66SGarlic Tseng 2020d1d7a66SGarlic Tseng /* Module information */ 2030d1d7a66SGarlic Tseng MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); 2040d1d7a66SGarlic Tseng MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); 2050d1d7a66SGarlic Tseng MODULE_LICENSE("GPL v2"); 2060d1d7a66SGarlic Tseng MODULE_ALIAS("platform:mt8173-max98090"); 2070d1d7a66SGarlic Tseng 208