18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Lowland audio support 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright 2011 Wolfson Microelectronics 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <sound/soc.h> 88c2ecf20Sopenharmony_ci#include <sound/soc-dapm.h> 98c2ecf20Sopenharmony_ci#include <sound/jack.h> 108c2ecf20Sopenharmony_ci#include <linux/gpio.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "../codecs/wm5100.h" 148c2ecf20Sopenharmony_ci#include "../codecs/wm9081.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define MCLK1_RATE (44100 * 512) 178c2ecf20Sopenharmony_ci#define CLKOUT_RATE (44100 * 256) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic struct snd_soc_jack lowland_headset; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Headset jack detection DAPM pins */ 228c2ecf20Sopenharmony_cistatic struct snd_soc_jack_pin lowland_headset_pins[] = { 238c2ecf20Sopenharmony_ci { 248c2ecf20Sopenharmony_ci .pin = "Headphone", 258c2ecf20Sopenharmony_ci .mask = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, 268c2ecf20Sopenharmony_ci }, 278c2ecf20Sopenharmony_ci { 288c2ecf20Sopenharmony_ci .pin = "Headset Mic", 298c2ecf20Sopenharmony_ci .mask = SND_JACK_MICROPHONE, 308c2ecf20Sopenharmony_ci }, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; 368c2ecf20Sopenharmony_ci int ret; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci ret = snd_soc_component_set_sysclk(component, WM5100_CLK_SYSCLK, 398c2ecf20Sopenharmony_ci WM5100_CLKSRC_MCLK1, MCLK1_RATE, 408c2ecf20Sopenharmony_ci SND_SOC_CLOCK_IN); 418c2ecf20Sopenharmony_ci if (ret < 0) { 428c2ecf20Sopenharmony_ci pr_err("Failed to set SYSCLK clock source: %d\n", ret); 438c2ecf20Sopenharmony_ci return ret; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* Clock OPCLK, used by the other audio components. */ 478c2ecf20Sopenharmony_ci ret = snd_soc_component_set_sysclk(component, WM5100_CLK_OPCLK, 0, 488c2ecf20Sopenharmony_ci CLKOUT_RATE, 0); 498c2ecf20Sopenharmony_ci if (ret < 0) { 508c2ecf20Sopenharmony_ci pr_err("Failed to set OPCLK rate: %d\n", ret); 518c2ecf20Sopenharmony_ci return ret; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT | 558c2ecf20Sopenharmony_ci SND_JACK_HEADSET | SND_JACK_BTN_0, 568c2ecf20Sopenharmony_ci &lowland_headset, lowland_headset_pins, 578c2ecf20Sopenharmony_ci ARRAY_SIZE(lowland_headset_pins)); 588c2ecf20Sopenharmony_ci if (ret) 598c2ecf20Sopenharmony_ci return ret; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci wm5100_detect(component, &lowland_headset); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci snd_soc_dapm_nc_pin(&rtd->card->dapm, "LINEOUT"); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* At any time the WM9081 is active it will have this clock */ 738c2ecf20Sopenharmony_ci return snd_soc_component_set_sysclk(component, WM9081_SYSCLK_MCLK, 0, 748c2ecf20Sopenharmony_ci CLKOUT_RATE, 0); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct snd_soc_pcm_stream sub_params = { 788c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S32_LE, 798c2ecf20Sopenharmony_ci .rate_min = 44100, 808c2ecf20Sopenharmony_ci .rate_max = 44100, 818c2ecf20Sopenharmony_ci .channels_min = 2, 828c2ecf20Sopenharmony_ci .channels_max = 2, 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(cpu, 868c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 878c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("wm5100.1-001a", "wm5100-aif1")), 888c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(baseband, 918c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("wm5100-aif2")), 928c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1"))); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(speaker, 958c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("wm5100-aif3")), 968c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi"))); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link lowland_dai[] = { 998c2ecf20Sopenharmony_ci { 1008c2ecf20Sopenharmony_ci .name = "CPU", 1018c2ecf20Sopenharmony_ci .stream_name = "CPU", 1028c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 1038c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFM, 1048c2ecf20Sopenharmony_ci .init = lowland_wm5100_init, 1058c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(cpu), 1068c2ecf20Sopenharmony_ci }, 1078c2ecf20Sopenharmony_ci { 1088c2ecf20Sopenharmony_ci .name = "Baseband", 1098c2ecf20Sopenharmony_ci .stream_name = "Baseband", 1108c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 1118c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFM, 1128c2ecf20Sopenharmony_ci .ignore_suspend = 1, 1138c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(baseband), 1148c2ecf20Sopenharmony_ci }, 1158c2ecf20Sopenharmony_ci { 1168c2ecf20Sopenharmony_ci .name = "Sub Speaker", 1178c2ecf20Sopenharmony_ci .stream_name = "Sub Speaker", 1188c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 1198c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFM, 1208c2ecf20Sopenharmony_ci .ignore_suspend = 1, 1218c2ecf20Sopenharmony_ci .params = &sub_params, 1228c2ecf20Sopenharmony_ci .init = lowland_wm9081_init, 1238c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(speaker), 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct snd_soc_codec_conf lowland_codec_conf[] = { 1288c2ecf20Sopenharmony_ci { 1298c2ecf20Sopenharmony_ci .dlc = COMP_CODEC_CONF("wm9081.1-006c"), 1308c2ecf20Sopenharmony_ci .name_prefix = "Sub", 1318c2ecf20Sopenharmony_ci }, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new controls[] = { 1358c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Main Speaker"), 1368c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Main DMIC"), 1378c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Main AMIC"), 1388c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("WM1250 Input"), 1398c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("WM1250 Output"), 1408c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headphone"), 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic struct snd_soc_dapm_widget widgets[] = { 1448c2ecf20Sopenharmony_ci SND_SOC_DAPM_HP("Headphone", NULL), 1458c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", NULL), 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci SND_SOC_DAPM_SPK("Main Speaker", NULL), 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Main AMIC", NULL), 1508c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Main DMIC", NULL), 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic struct snd_soc_dapm_route audio_paths[] = { 1548c2ecf20Sopenharmony_ci { "Sub IN1", NULL, "HPOUT2L" }, 1558c2ecf20Sopenharmony_ci { "Sub IN2", NULL, "HPOUT2R" }, 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci { "Main Speaker", NULL, "Sub SPKN" }, 1588c2ecf20Sopenharmony_ci { "Main Speaker", NULL, "Sub SPKP" }, 1598c2ecf20Sopenharmony_ci { "Main Speaker", NULL, "SPKDAT1" }, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic struct snd_soc_card lowland = { 1638c2ecf20Sopenharmony_ci .name = "Lowland", 1648c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1658c2ecf20Sopenharmony_ci .dai_link = lowland_dai, 1668c2ecf20Sopenharmony_ci .num_links = ARRAY_SIZE(lowland_dai), 1678c2ecf20Sopenharmony_ci .codec_conf = lowland_codec_conf, 1688c2ecf20Sopenharmony_ci .num_configs = ARRAY_SIZE(lowland_codec_conf), 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci .controls = controls, 1718c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(controls), 1728c2ecf20Sopenharmony_ci .dapm_widgets = widgets, 1738c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(widgets), 1748c2ecf20Sopenharmony_ci .dapm_routes = audio_paths, 1758c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(audio_paths), 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int lowland_probe(struct platform_device *pdev) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct snd_soc_card *card = &lowland; 1818c2ecf20Sopenharmony_ci int ret; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci card->dev = &pdev->dev; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_card(&pdev->dev, card); 1868c2ecf20Sopenharmony_ci if (ret) 1878c2ecf20Sopenharmony_ci dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return ret; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic struct platform_driver lowland_driver = { 1938c2ecf20Sopenharmony_ci .driver = { 1948c2ecf20Sopenharmony_ci .name = "lowland", 1958c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 1968c2ecf20Sopenharmony_ci }, 1978c2ecf20Sopenharmony_ci .probe = lowland_probe, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cimodule_platform_driver(lowland_driver); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Lowland audio support"); 2038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 2048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2058c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:lowland"); 206