| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * aquila_wm8994.c | |
| 3 * | |
| 4 * Copyright (C) 2010 Samsung Electronics Co.Ltd | |
| 5 * Author: Chanwoo Choi <cw00.choi@samsung.com> | |
| 6 * | |
| 7 * This program is free software; you can redistribute it and/or modify it | |
| 8 * under the terms of the GNU General Public License as published by the | |
| 9 * Free Software Foundation; either version 2 of the License, or (at your | |
| 10 * option) any later version. | |
| 11 * | |
| 12 */ | |
| 13 | |
| 14 #include <linux/module.h> | |
| 15 #include <linux/moduleparam.h> | |
| 16 #include <linux/io.h> | |
| 17 #include <linux/platform_device.h> | |
| 18 #include <sound/soc.h> | |
| 19 #include <sound/soc-dapm.h> | |
| 20 #include <sound/jack.h> | |
| 21 #include <asm/mach-types.h> | |
| 22 #include <mach/gpio.h> | |
| 23 #include <mach/regs-clock.h> | |
| 24 | |
| 25 #include <linux/mfd/wm8994/core.h> | |
| 26 #include <linux/mfd/wm8994/registers.h> | |
| 27 #include "../codecs/wm8994.h" | |
| 28 #include "s3c-dma.h" | |
| 29 #include "s3c64xx-i2s.h" | |
| 30 | |
| 31 static struct snd_soc_card aquila; | |
| 32 static struct platform_device *aquila_snd_device; | |
| 33 | |
| 34 /* 3.5 pie jack */ | |
| 35 static struct snd_soc_jack jack; | |
| 36 | |
| 37 /* 3.5 pie jack detection DAPM pins */ | |
| 38 static struct snd_soc_jack_pin jack_pins[] = { | |
| 39 { | |
| 40 .pin = "Headset Mic", | |
| 41 .mask = SND_JACK_MICROPHONE, | |
| 42 }, { | |
| 43 .pin = "Headset Stereophone", | |
| 44 .mask = SND_JACK_HEADPHONE | SND_JACK_MECHANICAL | | |
| 45 SND_JACK_AVOUT, | |
| 46 }, | |
| 47 }; | |
| 48 | |
| 49 /* 3.5 pie jack detection gpios */ | |
| 50 static struct snd_soc_jack_gpio jack_gpios[] = { | |
| 51 { | |
| 52 .gpio = S5PV210_GPH0(6), | |
| 53 .name = "DET_3.5", | |
| 54 .report = SND_JACK_HEADSET | SND_JACK_MECHANICAL | | |
| 55 SND_JACK_AVOUT, | |
| 56 .debounce_time = 200, | |
| 57 }, | |
| 58 }; | |
| 59 | |
| 60 static const struct snd_soc_dapm_widget aquila_dapm_widgets[] = { | |
| 61 SND_SOC_DAPM_SPK("Ext Spk", NULL), | |
| 62 SND_SOC_DAPM_SPK("Ext Rcv", NULL), | |
| 63 SND_SOC_DAPM_HP("Headset Stereophone", NULL), | |
| 64 SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
| 65 SND_SOC_DAPM_MIC("Main Mic", NULL), | |
| 66 SND_SOC_DAPM_MIC("2nd Mic", NULL), | |
| 67 SND_SOC_DAPM_LINE("Radio In", NULL), | |
| 68 }; | |
| 69 | |
| 70 static const struct snd_soc_dapm_route aquila_dapm_routes[] = { | |
| 71 {"Ext Spk", NULL, "SPKOUTLP"}, | |
| 72 {"Ext Spk", NULL, "SPKOUTLN"}, | |
| 73 | |
| 74 {"Ext Rcv", NULL, "HPOUT2N"}, | |
| 75 {"Ext Rcv", NULL, "HPOUT2P"}, | |
| 76 | |
| 77 {"Headset Stereophone", NULL, "HPOUT1L"}, | |
| 78 {"Headset Stereophone", NULL, "HPOUT1R"}, | |
| 79 | |
| 80 {"IN1RN", NULL, "Headset Mic"}, | |
| 81 {"IN1RP", NULL, "Headset Mic"}, | |
| 82 | |
| 83 {"IN1RN", NULL, "2nd Mic"}, | |
| 84 {"IN1RP", NULL, "2nd Mic"}, | |
| 85 | |
| 86 {"IN1LN", NULL, "Main Mic"}, | |
| 87 {"IN1LP", NULL, "Main Mic"}, | |
| 88 | |
| 89 {"IN2LN", NULL, "Radio In"}, | |
| 90 {"IN2RN", NULL, "Radio In"}, | |
| 91 }; | |
| 92 | |
| 93 static int aquila_wm8994_init(struct snd_soc_pcm_runtime *rtd) | |
| 94 { | |
| 95 struct snd_soc_codec *codec = rtd->codec; | |
| 96 int ret; | |
| 97 | |
| 98 /* add aquila specific widgets */ | |
| 99 snd_soc_dapm_new_controls(codec, aquila_dapm_widgets, | |
| 100 ARRAY_SIZE(aquila_dapm_widgets)); | |
| 101 | |
| 102 /* set up aquila specific audio routes */ | |
| 103 snd_soc_dapm_add_routes(codec, aquila_dapm_routes, | |
| 104 ARRAY_SIZE(aquila_dapm_routes)); | |
| 105 | |
| 106 /* set endpoints to not connected */ | |
| 107 snd_soc_dapm_nc_pin(codec, "IN2LP:VXRN"); | |
| 108 snd_soc_dapm_nc_pin(codec, "IN2RP:VXRP"); | |
| 109 snd_soc_dapm_nc_pin(codec, "LINEOUT1N"); | |
| 110 snd_soc_dapm_nc_pin(codec, "LINEOUT1P"); | |
| 111 snd_soc_dapm_nc_pin(codec, "LINEOUT2N"); | |
| 112 snd_soc_dapm_nc_pin(codec, "LINEOUT2P"); | |
| 113 snd_soc_dapm_nc_pin(codec, "SPKOUTRN"); | |
| 114 snd_soc_dapm_nc_pin(codec, "SPKOUTRP"); | |
| 115 | |
| 116 snd_soc_dapm_sync(codec); | |
| 117 | |
| 118 /* Headset jack detection */ | |
| 119 ret = snd_soc_jack_new(&aquila, "Headset Jack", | |
| 120 SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_AVOUT, | |
| 121 &jack); | |
| 122 if (ret) | |
| 123 return ret; | |
| 124 | |
| 125 ret = snd_soc_jack_add_pins(&jack, ARRAY_SIZE(jack_pins), jack_pins); | |
| 126 if (ret) | |
| 127 return ret; | |
| 128 | |
| 129 ret = snd_soc_jack_add_gpios(&jack, ARRAY_SIZE(jack_gpios), jack_gpios); | |
| 130 if (ret) | |
| 131 return ret; | |
| 132 | |
| 133 return 0; | |
| 134 } | |
| 135 | |
| 136 static int aquila_hifi_hw_params(struct snd_pcm_substream *substream, | |
| 137 struct snd_pcm_hw_params *params) | |
| 138 { | |
| 139 struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
| 140 struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
| 141 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
| 142 unsigned int pll_out = 24000000; | |
| 143 int ret = 0; | |
| 144 | |
| 145 /* set the cpu DAI configuration */ | |
| 146 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | |
| 147 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | |
| 148 if (ret < 0) | |
| 149 return ret; | |
| 150 | |
| 151 /* set the cpu system clock */ | |
| 152 ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK, | |
| 153 0, SND_SOC_CLOCK_IN); | |
| 154 if (ret < 0) | |
| 155 return ret; | |
| 156 | |
| 157 /* set codec DAI configuration */ | |
| 158 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | |
| 159 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | |
| 160 if (ret < 0) | |
| 161 return ret; | |
| 162 | |
| 163 /* set the codec FLL */ | |
| 164 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, 0, pll_out, | |
| 165 params_rate(params) * 256); | |
| 166 if (ret < 0) | |
| 167 return ret; | |
| 168 | |
| 169 /* set the codec system clock */ | |
| 170 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, | |
| 171 params_rate(params) * 256, SND_SOC_CLOCK_IN); | |
| 172 if (ret < 0) | |
| 173 return ret; | |
| 174 | |
| 175 return 0; | |
| 176 } | |
| 177 | |
| 178 static struct snd_soc_ops aquila_hifi_ops = { | |
| 179 .hw_params = aquila_hifi_hw_params, | |
| 180 }; | |
| 181 | |
| 182 static int aquila_voice_hw_params(struct snd_pcm_substream *substream, | |
| 183 struct snd_pcm_hw_params *params) | |
| 184 { | |
| 185 struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
| 186 struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
| 187 unsigned int pll_out = 24000000; | |
| 188 int ret = 0; | |
| 189 | |
| 190 if (params_rate(params) != 8000) | |
| 191 return -EINVAL; | |
| 192 | |
| 193 /* set codec DAI configuration */ | |
| 194 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J | | |
| 195 SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); | |
| 196 if (ret < 0) | |
| 197 return ret; | |
| 198 | |
| 199 /* set the codec FLL */ | |
| 200 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, 0, pll_out, | |
| 201 params_rate(params) * 256); | |
| 202 if (ret < 0) | |
| 203 return ret; | |
| 204 | |
| 205 /* set the codec system clock */ | |
| 206 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2, | |
| 207 params_rate(params) * 256, SND_SOC_CLOCK_IN); | |
| 208 if (ret < 0) | |
| 209 return ret; | |
| 210 | |
| 211 return 0; | |
| 212 } | |
| 213 | |
| 214 static struct snd_soc_dai_driver voice_dai = { | |
| 215 .name = "aquila-voice-dai", | |
| 216 .playback = { | |
| 217 .channels_min = 1, | |
| 218 .channels_max = 2, | |
| 219 .rates = SNDRV_PCM_RATE_8000, | |
| 220 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | |
| 221 .capture = { | |
| 222 .channels_min = 1, | |
| 223 .channels_max = 2, | |
| 224 .rates = SNDRV_PCM_RATE_8000, | |
| 225 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | |
| 226 }; | |
| 227 | |
| 228 static struct snd_soc_ops aquila_voice_ops = { | |
| 229 .hw_params = aquila_voice_hw_params, | |
| 230 }; | |
| 231 | |
| 232 static struct snd_soc_dai_link aquila_dai[] = { | |
| 233 { | |
| 234 .name = "WM8994", | |
| 235 .stream_name = "WM8994 HiFi", | |
| 236 .cpu_dai_name = "s3c64xx-i2s-v4", | |
| 237 .codec_dai_name = "wm8994-hifi", | |
| 238 .platform_name = "s3c24xx-pcm-audio", | |
| 239 .codec_name = "wm8994-codec.0-0x1a", | |
| 240 .init = aquila_wm8994_init, | |
| 241 .ops = &aquila_hifi_ops, | |
| 242 }, { | |
| 243 .name = "WM8994 Voice", | |
| 244 .stream_name = "Voice", | |
| 245 .cpu_dai_name = "aquila-voice-dai", | |
| 246 .codec_dai_name = "wm8994-voice", | |
| 247 .platform_name = "s3c24xx-pcm-audio", | |
| 248 .codec_name = "wm8994-codec.0-0x1a", | |
| 249 .ops = &aquila_voice_ops, | |
| 250 }, | |
| 251 }; | |
| 252 | |
| 253 static struct snd_soc_card aquila = { | |
| 254 .name = "aquila", | |
| 255 .dai_link = aquila_dai, | |
| 256 .num_links = ARRAY_SIZE(aquila_dai), | |
| 257 }; | |
| 258 | |
| 259 static int __init aquila_init(void) | |
| 260 { | |
| 261 int ret; | |
| 262 | |
| 263 if (!machine_is_aquila()) | |
| 264 return -ENODEV; | |
| 265 | |
| 266 aquila_snd_device = platform_device_alloc("soc-audio", -1); | |
| 267 if (!aquila_snd_device) | |
| 268 return -ENOMEM; | |
| 269 | |
| 270 /* register voice DAI here */ | |
| 271 ret = snd_soc_register_dai(&aquila_snd_device->dev, &voice_dai); | |
| 272 if (ret) | |
| 273 return ret; | |
| 274 | |
| 275 platform_set_drvdata(aquila_snd_device, &aquila); | |
| 276 ret = platform_device_add(aquila_snd_device); | |
| 277 | |
| 278 if (ret) | |
| 279 platform_device_put(aquila_snd_device); | |
| 280 | |
| 281 return ret; | |
| 282 } | |
| 283 | |
| 284 static void __exit aquila_exit(void) | |
| 285 { | |
| 286 platform_device_unregister(aquila_snd_device); | |
| 287 } | |
| 288 | |
| 289 module_init(aquila_init); | |
| 290 module_exit(aquila_exit); | |
| 291 | |
| 292 /* Module information */ | |
| 293 MODULE_DESCRIPTION("ALSA SoC WM8994 Aquila(S5PC110)"); | |
| 294 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); | |
| 295 MODULE_LICENSE("GPL"); | |
| OLD | NEW |