1d5943db3SSylwester Nawrocki // SPDX-License-Identifier: GPL-2.0+ 2d5943db3SSylwester Nawrocki // 3d5943db3SSylwester Nawrocki // Lowland audio support 4d5943db3SSylwester Nawrocki // 5d5943db3SSylwester Nawrocki // Copyright 2011 Wolfson Microelectronics 6abda5dfdSMark Brown 7abda5dfdSMark Brown #include <sound/soc.h> 8abda5dfdSMark Brown #include <sound/soc-dapm.h> 9abda5dfdSMark Brown #include <sound/jack.h> 10abda5dfdSMark Brown #include <linux/gpio.h> 11abda5dfdSMark Brown #include <linux/module.h> 12abda5dfdSMark Brown 13abda5dfdSMark Brown #include "../codecs/wm5100.h" 14abda5dfdSMark Brown #include "../codecs/wm9081.h" 15abda5dfdSMark Brown 16abda5dfdSMark Brown #define MCLK1_RATE (44100 * 512) 17abda5dfdSMark Brown #define CLKOUT_RATE (44100 * 256) 18abda5dfdSMark Brown 19abda5dfdSMark Brown static struct snd_soc_jack lowland_headset; 20abda5dfdSMark Brown 21abda5dfdSMark Brown /* Headset jack detection DAPM pins */ 22abda5dfdSMark Brown static struct snd_soc_jack_pin lowland_headset_pins[] = { 23abda5dfdSMark Brown { 24abda5dfdSMark Brown .pin = "Headphone", 25*c9d34018SAlper Nebi Yasak .mask = SND_JACK_HEADPHONE, 26abda5dfdSMark Brown }, 27abda5dfdSMark Brown { 28abda5dfdSMark Brown .pin = "Headset Mic", 29abda5dfdSMark Brown .mask = SND_JACK_MICROPHONE, 30abda5dfdSMark Brown }, 31*c9d34018SAlper Nebi Yasak { 32*c9d34018SAlper Nebi Yasak .pin = "Line Out", 33*c9d34018SAlper Nebi Yasak .mask = SND_JACK_LINEOUT, 34*c9d34018SAlper Nebi Yasak }, 35abda5dfdSMark Brown }; 36abda5dfdSMark Brown 37abda5dfdSMark Brown static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) 38abda5dfdSMark Brown { 397de6b6bcSKuninori Morimoto struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; 40abda5dfdSMark Brown int ret; 41abda5dfdSMark Brown 4261195838SKuninori Morimoto ret = snd_soc_component_set_sysclk(component, WM5100_CLK_SYSCLK, 43abda5dfdSMark Brown WM5100_CLKSRC_MCLK1, MCLK1_RATE, 44abda5dfdSMark Brown SND_SOC_CLOCK_IN); 45abda5dfdSMark Brown if (ret < 0) { 46abda5dfdSMark Brown pr_err("Failed to set SYSCLK clock source: %d\n", ret); 47abda5dfdSMark Brown return ret; 48abda5dfdSMark Brown } 49abda5dfdSMark Brown 50abda5dfdSMark Brown /* Clock OPCLK, used by the other audio components. */ 5161195838SKuninori Morimoto ret = snd_soc_component_set_sysclk(component, WM5100_CLK_OPCLK, 0, 52abda5dfdSMark Brown CLKOUT_RATE, 0); 53abda5dfdSMark Brown if (ret < 0) { 54abda5dfdSMark Brown pr_err("Failed to set OPCLK rate: %d\n", ret); 55abda5dfdSMark Brown return ret; 56abda5dfdSMark Brown } 57abda5dfdSMark Brown 5819aed2d6SAkihiko Odaki ret = snd_soc_card_jack_new_pins(rtd->card, "Headset", 5919aed2d6SAkihiko Odaki SND_JACK_LINEOUT | SND_JACK_HEADSET | 6019aed2d6SAkihiko Odaki SND_JACK_BTN_0, 61f97e0eacSLars-Peter Clausen &lowland_headset, lowland_headset_pins, 62f97e0eacSLars-Peter Clausen ARRAY_SIZE(lowland_headset_pins)); 63abda5dfdSMark Brown if (ret) 64abda5dfdSMark Brown return ret; 65abda5dfdSMark Brown 6661195838SKuninori Morimoto wm5100_detect(component, &lowland_headset); 67abda5dfdSMark Brown 68abda5dfdSMark Brown return 0; 69abda5dfdSMark Brown } 70abda5dfdSMark Brown 7155b27847SMark Brown static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd) 7255b27847SMark Brown { 737de6b6bcSKuninori Morimoto struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; 7455b27847SMark Brown 75239ad6a1SLars-Peter Clausen snd_soc_dapm_nc_pin(&rtd->card->dapm, "LINEOUT"); 7655b27847SMark Brown 7755b27847SMark Brown /* At any time the WM9081 is active it will have this clock */ 7861195838SKuninori Morimoto return snd_soc_component_set_sysclk(component, WM9081_SYSCLK_MCLK, 0, 7955b27847SMark Brown CLKOUT_RATE, 0); 8055b27847SMark Brown } 8155b27847SMark Brown 8255b27847SMark Brown static const struct snd_soc_pcm_stream sub_params = { 8355b27847SMark Brown .formats = SNDRV_PCM_FMTBIT_S32_LE, 8455b27847SMark Brown .rate_min = 44100, 8555b27847SMark Brown .rate_max = 44100, 8655b27847SMark Brown .channels_min = 2, 8755b27847SMark Brown .channels_max = 2, 8855b27847SMark Brown }; 8955b27847SMark Brown 9079899194SKuninori Morimoto SND_SOC_DAILINK_DEFS(cpu, 9179899194SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 9279899194SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CODEC("wm5100.1-001a", "wm5100-aif1")), 9379899194SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 9479899194SKuninori Morimoto 9579899194SKuninori Morimoto SND_SOC_DAILINK_DEFS(baseband, 9679899194SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("wm5100-aif2")), 9779899194SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1"))); 9879899194SKuninori Morimoto 9979899194SKuninori Morimoto SND_SOC_DAILINK_DEFS(speaker, 10079899194SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CPU("wm5100-aif3")), 10179899194SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi"))); 10279899194SKuninori Morimoto 103abda5dfdSMark Brown static struct snd_soc_dai_link lowland_dai[] = { 104abda5dfdSMark Brown { 105abda5dfdSMark Brown .name = "CPU", 106abda5dfdSMark Brown .stream_name = "CPU", 107277b6fdaSMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 108277b6fdaSMark Brown SND_SOC_DAIFMT_CBM_CFM, 109abda5dfdSMark Brown .init = lowland_wm5100_init, 11079899194SKuninori Morimoto SND_SOC_DAILINK_REG(cpu), 111abda5dfdSMark Brown }, 112abda5dfdSMark Brown { 113abda5dfdSMark Brown .name = "Baseband", 114abda5dfdSMark Brown .stream_name = "Baseband", 115277b6fdaSMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 116277b6fdaSMark Brown SND_SOC_DAIFMT_CBM_CFM, 117abda5dfdSMark Brown .ignore_suspend = 1, 11879899194SKuninori Morimoto SND_SOC_DAILINK_REG(baseband), 119abda5dfdSMark Brown }, 120abda5dfdSMark Brown { 12155b27847SMark Brown .name = "Sub Speaker", 12255b27847SMark Brown .stream_name = "Sub Speaker", 12355b27847SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 12455b27847SMark Brown SND_SOC_DAIFMT_CBM_CFM, 12555b27847SMark Brown .ignore_suspend = 1, 126e7a73b05SKuninori Morimoto .c2c_params = &sub_params, 127e7a73b05SKuninori Morimoto .num_c2c_params = 1, 128abda5dfdSMark Brown .init = lowland_wm9081_init, 12979899194SKuninori Morimoto SND_SOC_DAILINK_REG(speaker), 130abda5dfdSMark Brown }, 131abda5dfdSMark Brown }; 132abda5dfdSMark Brown 133abda5dfdSMark Brown static struct snd_soc_codec_conf lowland_codec_conf[] = { 134abda5dfdSMark Brown { 135bfc5b22cSKuninori Morimoto .dlc = COMP_CODEC_CONF("wm9081.1-006c"), 136abda5dfdSMark Brown .name_prefix = "Sub", 137abda5dfdSMark Brown }, 138abda5dfdSMark Brown }; 139abda5dfdSMark Brown 140abda5dfdSMark Brown static const struct snd_kcontrol_new controls[] = { 141abda5dfdSMark Brown SOC_DAPM_PIN_SWITCH("Main Speaker"), 142abda5dfdSMark Brown SOC_DAPM_PIN_SWITCH("Main DMIC"), 143abda5dfdSMark Brown SOC_DAPM_PIN_SWITCH("Main AMIC"), 144abda5dfdSMark Brown SOC_DAPM_PIN_SWITCH("WM1250 Input"), 145abda5dfdSMark Brown SOC_DAPM_PIN_SWITCH("WM1250 Output"), 146abda5dfdSMark Brown SOC_DAPM_PIN_SWITCH("Headphone"), 147*c9d34018SAlper Nebi Yasak SOC_DAPM_PIN_SWITCH("Line Out"), 148abda5dfdSMark Brown }; 149abda5dfdSMark Brown 1505449fd7bSRikard Falkeborn static const struct snd_soc_dapm_widget widgets[] = { 151abda5dfdSMark Brown SND_SOC_DAPM_HP("Headphone", NULL), 152abda5dfdSMark Brown SND_SOC_DAPM_MIC("Headset Mic", NULL), 153*c9d34018SAlper Nebi Yasak SND_SOC_DAPM_LINE("Line Out", NULL), 154abda5dfdSMark Brown 155abda5dfdSMark Brown SND_SOC_DAPM_SPK("Main Speaker", NULL), 156abda5dfdSMark Brown 157abda5dfdSMark Brown SND_SOC_DAPM_MIC("Main AMIC", NULL), 158abda5dfdSMark Brown SND_SOC_DAPM_MIC("Main DMIC", NULL), 159abda5dfdSMark Brown }; 160abda5dfdSMark Brown 1615449fd7bSRikard Falkeborn static const struct snd_soc_dapm_route audio_paths[] = { 162abda5dfdSMark Brown { "Sub IN1", NULL, "HPOUT2L" }, 163abda5dfdSMark Brown { "Sub IN2", NULL, "HPOUT2R" }, 164abda5dfdSMark Brown 165abda5dfdSMark Brown { "Main Speaker", NULL, "Sub SPKN" }, 166abda5dfdSMark Brown { "Main Speaker", NULL, "Sub SPKP" }, 167abda5dfdSMark Brown { "Main Speaker", NULL, "SPKDAT1" }, 168abda5dfdSMark Brown }; 169abda5dfdSMark Brown 170abda5dfdSMark Brown static struct snd_soc_card lowland = { 171abda5dfdSMark Brown .name = "Lowland", 172095d79dcSAxel Lin .owner = THIS_MODULE, 173abda5dfdSMark Brown .dai_link = lowland_dai, 174abda5dfdSMark Brown .num_links = ARRAY_SIZE(lowland_dai), 175abda5dfdSMark Brown .codec_conf = lowland_codec_conf, 176abda5dfdSMark Brown .num_configs = ARRAY_SIZE(lowland_codec_conf), 177abda5dfdSMark Brown 178abda5dfdSMark Brown .controls = controls, 179abda5dfdSMark Brown .num_controls = ARRAY_SIZE(controls), 180abda5dfdSMark Brown .dapm_widgets = widgets, 181abda5dfdSMark Brown .num_dapm_widgets = ARRAY_SIZE(widgets), 182abda5dfdSMark Brown .dapm_routes = audio_paths, 183abda5dfdSMark Brown .num_dapm_routes = ARRAY_SIZE(audio_paths), 184abda5dfdSMark Brown }; 185abda5dfdSMark Brown 186fdca21adSBill Pemberton static int lowland_probe(struct platform_device *pdev) 187abda5dfdSMark Brown { 188abda5dfdSMark Brown struct snd_soc_card *card = &lowland; 189abda5dfdSMark Brown int ret; 190abda5dfdSMark Brown 191abda5dfdSMark Brown card->dev = &pdev->dev; 192abda5dfdSMark Brown 193c583883eSTushar Behera ret = devm_snd_soc_register_card(&pdev->dev, card); 19427c6eaebSKuninori Morimoto if (ret) 19527c6eaebSKuninori Morimoto dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); 196c583883eSTushar Behera 197abda5dfdSMark Brown return ret; 198abda5dfdSMark Brown } 199abda5dfdSMark Brown 200abda5dfdSMark Brown static struct platform_driver lowland_driver = { 201abda5dfdSMark Brown .driver = { 202abda5dfdSMark Brown .name = "lowland", 203abda5dfdSMark Brown .pm = &snd_soc_pm_ops, 204abda5dfdSMark Brown }, 205abda5dfdSMark Brown .probe = lowland_probe, 206abda5dfdSMark Brown }; 207abda5dfdSMark Brown 208e00c3f55SMark Brown module_platform_driver(lowland_driver); 209abda5dfdSMark Brown 210abda5dfdSMark Brown MODULE_DESCRIPTION("Lowland audio support"); 211abda5dfdSMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 212abda5dfdSMark Brown MODULE_LICENSE("GPL"); 213abda5dfdSMark Brown MODULE_ALIAS("platform:lowland"); 214