1 /* 2 * Lowland audio support 3 * 4 * Copyright 2011 Wolfson Microelectronics 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 */ 11 12 #include <sound/soc.h> 13 #include <sound/soc-dapm.h> 14 #include <sound/jack.h> 15 #include <linux/gpio.h> 16 #include <linux/module.h> 17 18 #include "../codecs/wm5100.h" 19 #include "../codecs/wm9081.h" 20 21 #define MCLK1_RATE (44100 * 512) 22 #define CLKOUT_RATE (44100 * 256) 23 24 static int lowland_hw_params(struct snd_pcm_substream *substream, 25 struct snd_pcm_hw_params *params) 26 { 27 struct snd_soc_pcm_runtime *rtd = substream->private_data; 28 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 29 struct snd_soc_dai *codec_dai = rtd->codec_dai; 30 int ret; 31 32 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S 33 | SND_SOC_DAIFMT_NB_NF 34 | SND_SOC_DAIFMT_CBM_CFM); 35 if (ret < 0) 36 return ret; 37 38 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S 39 | SND_SOC_DAIFMT_NB_NF 40 | SND_SOC_DAIFMT_CBM_CFM); 41 if (ret < 0) 42 return ret; 43 44 return 0; 45 } 46 47 static struct snd_soc_ops lowland_ops = { 48 .hw_params = lowland_hw_params, 49 }; 50 51 static struct snd_soc_jack lowland_headset; 52 53 /* Headset jack detection DAPM pins */ 54 static struct snd_soc_jack_pin lowland_headset_pins[] = { 55 { 56 .pin = "Headphone", 57 .mask = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, 58 }, 59 { 60 .pin = "Headset Mic", 61 .mask = SND_JACK_MICROPHONE, 62 }, 63 }; 64 65 static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) 66 { 67 struct snd_soc_codec *codec = rtd->codec; 68 int ret; 69 70 ret = snd_soc_codec_set_sysclk(codec, WM5100_CLK_SYSCLK, 71 WM5100_CLKSRC_MCLK1, MCLK1_RATE, 72 SND_SOC_CLOCK_IN); 73 if (ret < 0) { 74 pr_err("Failed to set SYSCLK clock source: %d\n", ret); 75 return ret; 76 } 77 78 /* Clock OPCLK, used by the other audio components. */ 79 ret = snd_soc_codec_set_sysclk(codec, WM5100_CLK_OPCLK, 0, 80 CLKOUT_RATE, 0); 81 if (ret < 0) { 82 pr_err("Failed to set OPCLK rate: %d\n", ret); 83 return ret; 84 } 85 86 ret = snd_soc_jack_new(codec, "Headset", 87 SND_JACK_LINEOUT | SND_JACK_HEADSET | 88 SND_JACK_BTN_0, 89 &lowland_headset); 90 if (ret) 91 return ret; 92 93 ret = snd_soc_jack_add_pins(&lowland_headset, 94 ARRAY_SIZE(lowland_headset_pins), 95 lowland_headset_pins); 96 if (ret) 97 return ret; 98 99 wm5100_detect(codec, &lowland_headset); 100 101 return 0; 102 } 103 104 static struct snd_soc_dai_link lowland_dai[] = { 105 { 106 .name = "CPU", 107 .stream_name = "CPU", 108 .cpu_dai_name = "samsung-i2s.0", 109 .codec_dai_name = "wm5100-aif1", 110 .platform_name = "samsung-audio", 111 .codec_name = "wm5100.1-001a", 112 .ops = &lowland_ops, 113 .init = lowland_wm5100_init, 114 }, 115 { 116 .name = "Baseband", 117 .stream_name = "Baseband", 118 .cpu_dai_name = "wm5100-aif2", 119 .codec_dai_name = "wm1250-ev1", 120 .codec_name = "wm1250-ev1.1-0027", 121 .ops = &lowland_ops, 122 .ignore_suspend = 1, 123 }, 124 }; 125 126 static int lowland_wm9081_init(struct snd_soc_dapm_context *dapm) 127 { 128 snd_soc_dapm_nc_pin(dapm, "LINEOUT"); 129 130 /* At any time the WM9081 is active it will have this clock */ 131 return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, 0, 132 CLKOUT_RATE, 0); 133 } 134 135 static struct snd_soc_aux_dev lowland_aux_dev[] = { 136 { 137 .name = "wm9081", 138 .codec_name = "wm9081.1-006c", 139 .init = lowland_wm9081_init, 140 }, 141 }; 142 143 static struct snd_soc_codec_conf lowland_codec_conf[] = { 144 { 145 .dev_name = "wm9081.1-006c", 146 .name_prefix = "Sub", 147 }, 148 }; 149 150 static const struct snd_kcontrol_new controls[] = { 151 SOC_DAPM_PIN_SWITCH("Main Speaker"), 152 SOC_DAPM_PIN_SWITCH("Main DMIC"), 153 SOC_DAPM_PIN_SWITCH("Main AMIC"), 154 SOC_DAPM_PIN_SWITCH("WM1250 Input"), 155 SOC_DAPM_PIN_SWITCH("WM1250 Output"), 156 SOC_DAPM_PIN_SWITCH("Headphone"), 157 }; 158 159 static struct snd_soc_dapm_widget widgets[] = { 160 SND_SOC_DAPM_HP("Headphone", NULL), 161 SND_SOC_DAPM_MIC("Headset Mic", NULL), 162 163 SND_SOC_DAPM_SPK("Main Speaker", NULL), 164 165 SND_SOC_DAPM_MIC("Main AMIC", NULL), 166 SND_SOC_DAPM_MIC("Main DMIC", NULL), 167 }; 168 169 static struct snd_soc_dapm_route audio_paths[] = { 170 { "Sub IN1", NULL, "HPOUT2L" }, 171 { "Sub IN2", NULL, "HPOUT2R" }, 172 173 { "Main Speaker", NULL, "Sub SPKN" }, 174 { "Main Speaker", NULL, "Sub SPKP" }, 175 { "Main Speaker", NULL, "SPKDAT1" }, 176 }; 177 178 static struct snd_soc_card lowland = { 179 .name = "Lowland", 180 .owner = THIS_MODULE, 181 .dai_link = lowland_dai, 182 .num_links = ARRAY_SIZE(lowland_dai), 183 .aux_dev = lowland_aux_dev, 184 .num_aux_devs = ARRAY_SIZE(lowland_aux_dev), 185 .codec_conf = lowland_codec_conf, 186 .num_configs = ARRAY_SIZE(lowland_codec_conf), 187 188 .controls = controls, 189 .num_controls = ARRAY_SIZE(controls), 190 .dapm_widgets = widgets, 191 .num_dapm_widgets = ARRAY_SIZE(widgets), 192 .dapm_routes = audio_paths, 193 .num_dapm_routes = ARRAY_SIZE(audio_paths), 194 }; 195 196 static __devinit int lowland_probe(struct platform_device *pdev) 197 { 198 struct snd_soc_card *card = &lowland; 199 int ret; 200 201 card->dev = &pdev->dev; 202 203 ret = snd_soc_register_card(card); 204 if (ret) { 205 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", 206 ret); 207 return ret; 208 } 209 210 return 0; 211 } 212 213 static int __devexit lowland_remove(struct platform_device *pdev) 214 { 215 struct snd_soc_card *card = platform_get_drvdata(pdev); 216 217 snd_soc_unregister_card(card); 218 219 return 0; 220 } 221 222 static struct platform_driver lowland_driver = { 223 .driver = { 224 .name = "lowland", 225 .owner = THIS_MODULE, 226 .pm = &snd_soc_pm_ops, 227 }, 228 .probe = lowland_probe, 229 .remove = __devexit_p(lowland_remove), 230 }; 231 232 module_platform_driver(lowland_driver); 233 234 MODULE_DESCRIPTION("Lowland audio support"); 235 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 236 MODULE_LICENSE("GPL"); 237 MODULE_ALIAS("platform:lowland"); 238