smdk6400_wm8990.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /*
  2. * smdk6400_wm8990.c -- SoC audio for Neo1973
  3. *
  4. * Copyright 2007, 2008 Wolfson Microelectronics PLC.
  5. * Author: Liam Girdwood
  6. * lg@opensource.wolfsonmicro.com or linux@wolfsonmicro.com
  7. *
  8. * Copyright (C) 2007, Ryu Euiyoul <ryu.real@gmail.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify it
  11. * under the terms of the GNU General Public License as published by the
  12. * Free Software Foundation; either version 2 of the License, or (at your
  13. * option) any later version.
  14. *
  15. * Revision history
  16. * 28th Feb 2008 Initial version.
  17. *
  18. */
  19. #include <linux/module.h>
  20. #include <linux/moduleparam.h>
  21. #include <linux/timer.h>
  22. #include <linux/interrupt.h>
  23. #include <linux/platform_device.h>
  24. #include <linux/i2c.h>
  25. #include <sound/driver.h>
  26. #include <sound/core.h>
  27. #include <sound/pcm.h>
  28. #include <sound/soc.h>
  29. #include <sound/soc-dapm.h>
  30. #include <asm/mach-types.h>
  31. #include <asm/hardware/scoop.h>
  32. #include <asm/arch/regs-iis.h>
  33. #include <asm/arch/regs-gpio.h>
  34. #include <asm/hardware.h>
  35. #include <asm/arch/audio.h>
  36. #include <asm/io.h>
  37. #include <asm/arch/spi-gpio.h>
  38. #ifndef CONFIG_CPU_S3C6400
  39. #include <asm/arch/regs-clock.h>
  40. #else
  41. #include <asm/arch/regs-s3c6400-clock.h>
  42. #endif
  43. #include "../codecs/wm8990.h"
  44. #include "s3c-pcm.h"
  45. #include "s3c-i2s.h"
  46. /* define the scenarios */
  47. #define SMDK6400_AUDIO_OFF 0
  48. #define SMDK6400_CAPTURE_MIC1 3
  49. #define SMDK6400_STEREO_TO_HEADPHONES 2
  50. #define SMDK6400_CAPTURE_LINE_IN 1
  51. #ifdef CONFIG_SND_DEBUG
  52. #define s3cdbg(x...) printk(x)
  53. #else
  54. #define s3cdbg(x...)
  55. #endif
  56. /*
  57. * TODO: - We need to work out PLL values for 256FS for every rate.
  58. */
  59. static int smdk6400_hifi_hw_params(struct snd_pcm_substream *substream,
  60. struct snd_pcm_hw_params *params)
  61. {
  62. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  63. struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
  64. struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
  65. unsigned int pll_out = 0, bclk = 0;
  66. int ret = 0;
  67. unsigned int iispsr, iismod;
  68. unsigned int prescaler = 4;
  69. s3cdbg("Entered %s, rate = %d\n", __FUNCTION__, params_rate(params));
  70. /*PCLK & SCLK gating enable*/
  71. writel(readl(S3C_PCLK_GATE)|S3C_CLKCON_PCLK_IIS0, S3C_PCLK_GATE);
  72. writel(readl(S3C_SCLK_GATE)|S3C_CLKCON_SCLK_AUDIO0, S3C_SCLK_GATE);
  73. iismod = readl(S3C_IIS0MOD);
  74. iismod &=~(0x3<<3);
  75. /*Clear I2S prescaler value [13:8] and disable prescaler*/
  76. iispsr = readl(S3C_IIS0PSR);
  77. iispsr &=~((0x3f<<8)|(1<<15));
  78. writel(iispsr, S3C_IIS0PSR);
  79. s3cdbg("%s: %d , params = %d \n", __FUNCTION__, __LINE__, params_rate(params));
  80. switch (params_rate(params)) {
  81. case 8000:
  82. case 16000:
  83. case 32000:
  84. case 64000:
  85. writel(50332, S3C_EPLL_CON1);
  86. writel((1<<31)|(32<<16)|(1<<8)|(3<<0) ,S3C_EPLL_CON0);
  87. break;
  88. case 11025:
  89. case 22050:
  90. case 44100:
  91. case 88200:
  92. writel(10398, S3C_EPLL_CON1);
  93. writel((1<<31)|(45<<16)|(1<<8)|(3<<0) ,S3C_EPLL_CON0);
  94. break;
  95. case 48000:
  96. case 96000:
  97. writel(9961, S3C_EPLL_CON1);
  98. writel((1<<31)|(49<<16)|(1<<8)|(3<<0) ,S3C_EPLL_CON0);
  99. break;
  100. default:
  101. writel(0, S3C_EPLL_CON1);
  102. writel((1<<31)|(128<<16)|(25<<8)|(0<<0) ,S3C_EPLL_CON0);
  103. break;
  104. }
  105. s3cdbg("%s, IISCON: %x IISMOD: %x,IISFIC: %x,IISPSR: %x",
  106. __FUNCTION__ , readl(S3C_IIS0CON), readl(S3C_IIS0MOD),
  107. readl(S3C_IIS0FIC), readl(S3C_IIS0PSR));
  108. while(!(__raw_readl(S3C_EPLL_CON0)&(1<<30)));
  109. /* MUXepll : FOUTepll */
  110. writel(readl(S3C_CLK_SRC)|S3C_CLKSRC_EPLL_CLKSEL, S3C_CLK_SRC);
  111. /* AUDIO0 sel : FOUTepll */
  112. writel((readl(S3C_CLK_SRC)&~(0x7<<7))|(0<<7), S3C_CLK_SRC);
  113. /* CLK_DIV2 setting */
  114. writel(0x0,S3C_CLK_DIV2);
  115. iismod |= S3C_IIS0MOD_256FS;
  116. /* WM8990 can use 256 FS */
  117. switch (params_rate(params)) {
  118. case 8000:
  119. bclk = WM8990_BCLK_DIV_8;
  120. pll_out = 2048000;
  121. prescaler = 24;
  122. break;
  123. case 11025:
  124. bclk = WM8990_BCLK_DIV_8;
  125. pll_out = 2822400;
  126. prescaler = 24;
  127. break;
  128. case 16000:
  129. bclk = WM8990_BCLK_DIV_8;
  130. pll_out = 4096000;
  131. prescaler = 12;
  132. break;
  133. case 22050:
  134. bclk = WM8990_BCLK_DIV_8;
  135. pll_out = 5644800;
  136. prescaler = 12;
  137. break;
  138. case 32000:
  139. bclk = WM8990_BCLK_DIV_8;
  140. pll_out = 8192000;
  141. prescaler = 6;
  142. break;
  143. case 44100:
  144. bclk = WM8990_BCLK_DIV_8;
  145. pll_out = 11289600;
  146. prescaler = 6;
  147. break;
  148. case 48000:
  149. bclk = WM8990_BCLK_DIV_8;
  150. pll_out = 12288000;
  151. prescaler = 6;
  152. break;
  153. case 88200:
  154. bclk = WM8990_BCLK_DIV_8;
  155. pll_out = 22579200;
  156. prescaler = 3;
  157. break;
  158. case 96000:
  159. bclk = WM8990_BCLK_DIV_8;
  160. pll_out = 24576000;
  161. prescaler = 3;
  162. break;
  163. default:
  164. /* somtimes 32000 rate comes to 96000
  165. default values are same as 32000 */
  166. iismod |= S3C_IIS0MOD_384FS;
  167. bclk = WM8990_BCLK_DIV_2;
  168. pll_out = 12288000;
  169. break;
  170. }
  171. prescaler = prescaler - 1;
  172. writel(iismod , S3C_IIS0MOD);
  173. /* set codec DAI configuration */
  174. ret = codec_dai->dai_ops.set_fmt(codec_dai,
  175. SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
  176. SND_SOC_DAIFMT_CBS_CFS );
  177. if (ret < 0)
  178. return ret;
  179. /* set cpu DAI configuration */
  180. ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
  181. SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
  182. SND_SOC_DAIFMT_CBS_CFS );
  183. if (ret < 0)
  184. return ret;
  185. /* set the codec system clock for DAC and ADC */
  186. ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8990_MCLK, pll_out,
  187. SND_SOC_CLOCK_IN);
  188. if (ret < 0)
  189. return ret;
  190. /* set MCLK division for sample rate */
  191. ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
  192. S3C2410_IISMOD_32FS );
  193. if (ret < 0)
  194. return ret;
  195. #if 0 /* not needed for slave */
  196. /* set codec BCLK division for sample rate */
  197. ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8990_BCLK_DIV, bclk);
  198. if (ret < 0)
  199. return ret;
  200. #endif
  201. /* set prescaler division for sample rate */
  202. ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
  203. (prescaler << 0x8));
  204. if (ret < 0)
  205. return ret;
  206. return 0;
  207. }
  208. /*
  209. * Neo1973 WM8990 HiFi DAI opserations.
  210. */
  211. static struct snd_soc_ops smdk6400_hifi_ops = {
  212. .hw_params = smdk6400_hifi_hw_params,
  213. };
  214. static int smdk6400_scenario = 0;
  215. static int smdk6400_get_scenario(struct snd_kcontrol *kcontrol,
  216. struct snd_ctl_elem_value *ucontrol)
  217. {
  218. ucontrol->value.integer.value[0] = smdk6400_scenario;
  219. return 0;
  220. }
  221. static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
  222. {
  223. smdk6400_scenario = scenario;
  224. switch (smdk6400_scenario) {
  225. case SMDK6400_AUDIO_OFF:
  226. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  227. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  228. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  229. break;
  230. case SMDK6400_STEREO_TO_HEADPHONES:
  231. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
  232. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  233. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  234. break;
  235. case SMDK6400_CAPTURE_MIC1:
  236. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  237. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 1);
  238. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  239. break;
  240. case SMDK6400_CAPTURE_LINE_IN:
  241. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  242. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  243. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 1);
  244. break;
  245. default:
  246. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
  247. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 1);
  248. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 1);
  249. break;
  250. }
  251. snd_soc_dapm_sync_endpoints(codec);
  252. return 0;
  253. }
  254. static int smdk6400_set_scenario(struct snd_kcontrol *kcontrol,
  255. struct snd_ctl_elem_value *ucontrol)
  256. {
  257. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  258. if (smdk6400_scenario == ucontrol->value.integer.value[0])
  259. return 0;
  260. set_scenario_endpoints(codec, ucontrol->value.integer.value[0]);
  261. return 1;
  262. }
  263. static const struct snd_soc_dapm_widget wm8990_dapm_widgets[] = {
  264. SND_SOC_DAPM_HP("Headphone Jack", NULL),
  265. SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
  266. SND_SOC_DAPM_LINE("Line In Jack", NULL),
  267. };
  268. /* example machine audio_mapnections */
  269. static const char* audio_map[][3] = {
  270. /* no irq jack detect */
  271. {"Headphone Jack", NULL, "LOUT"},
  272. {"Headphone Jack", NULL, "ROUT"},
  273. /* mic is connected to LIN1 and LIN2 - with bias */
  274. {"LIN1", NULL, "Mic1 Jack"},
  275. {"LIN2", NULL, "Mic1 Jack"},
  276. {"RIN2", NULL, "Line In Jack"},
  277. {NULL, NULL, NULL},
  278. };
  279. static const char *smdk_scenarios[] = {
  280. "Off",
  281. "Capture Line In",
  282. "Headphones",
  283. "Capture Mic1",
  284. };
  285. static const struct soc_enum smdk_scenario_enum[] = {
  286. SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(smdk_scenarios),smdk_scenarios),
  287. };
  288. static const struct snd_kcontrol_new wm8990_smdk6400_controls[] = {
  289. SOC_ENUM_EXT("SMDK Mode", smdk_scenario_enum[0],
  290. smdk6400_get_scenario, smdk6400_set_scenario),
  291. };
  292. /*
  293. * This is an example machine initialisation for a wm8990 connected to a
  294. * smdk6400. It is missing logic to detect hp/mic insertions and logic
  295. * to re-route the audio in such an event.
  296. */
  297. static int smdk6400_wm8990_init(struct snd_soc_codec *codec)
  298. {
  299. int i, err;
  300. /* Add smdk6400 specific widgets */
  301. for (i = 0; i < ARRAY_SIZE(wm8990_dapm_widgets); i++)
  302. snd_soc_dapm_new_control(codec, &wm8990_dapm_widgets[i]);
  303. /* add smdk6400 specific controls */
  304. for (i = 0; i < ARRAY_SIZE(wm8990_smdk6400_controls); i++) {
  305. err = snd_ctl_add(codec->card,
  306. snd_soc_cnew(&wm8990_smdk6400_controls[i],
  307. codec, NULL));
  308. if (err < 0)
  309. return err;
  310. }
  311. /* set up smdk6400 specific audio paths */
  312. for (i = 0; audio_map[i][0] != NULL; i++) {
  313. snd_soc_dapm_connect_input(codec, audio_map[i][0],
  314. audio_map[i][1], audio_map[i][2]);
  315. }
  316. /* not connected */
  317. snd_soc_dapm_set_endpoint(codec, "RIN1", 0);
  318. snd_soc_dapm_set_endpoint(codec, "LIN3", 0);
  319. snd_soc_dapm_set_endpoint(codec, "LIN4", 0);
  320. snd_soc_dapm_set_endpoint(codec, "RIN3", 0);
  321. snd_soc_dapm_set_endpoint(codec, "RIN4", 0);
  322. snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
  323. snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
  324. snd_soc_dapm_set_endpoint(codec, "SPKP", 0);
  325. snd_soc_dapm_set_endpoint(codec, "SPKN", 0);
  326. snd_soc_dapm_set_endpoint(codec, "ROP", 0);
  327. snd_soc_dapm_set_endpoint(codec, "RON", 0);
  328. snd_soc_dapm_set_endpoint(codec, "LOP", 0);
  329. snd_soc_dapm_set_endpoint(codec, "LON", 0);
  330. /* set endpoints to default mode & sync with DAPM */
  331. set_scenario_endpoints(codec, SMDK6400_STEREO_TO_HEADPHONES);
  332. return 0;
  333. }
  334. static struct snd_soc_dai_link smdk6400_dai[] = {
  335. { /* Hifi Playback - for similatious use with voice below */
  336. .name = "WM8990",
  337. .stream_name = "WM8990 HiFi",
  338. .cpu_dai = &s3c_i2s_dai,
  339. .codec_dai = &wm8990_dai,
  340. .init = smdk6400_wm8990_init,
  341. .ops = &smdk6400_hifi_ops,
  342. },
  343. };
  344. static struct snd_soc_machine smdk6400 = {
  345. .name = "smdk6400",
  346. .dai_link = smdk6400_dai,
  347. .num_links = ARRAY_SIZE(smdk6400_dai),
  348. };
  349. static struct wm8990_setup_data smdk6400_wm8990_setup = {
  350. .i2c_address = 0x1a,
  351. };
  352. static struct snd_soc_device smdk6400_snd_devdata = {
  353. .machine = &smdk6400,
  354. .platform = &s3c24xx_soc_platform,
  355. .codec_dev = &soc_codec_dev_wm8990,
  356. .codec_data = &smdk6400_wm8990_setup,
  357. };
  358. static struct platform_device *smdk6400_snd_device;
  359. static int __init smdk6400_init(void)
  360. {
  361. int ret;
  362. smdk6400_snd_device = platform_device_alloc("soc-audio", -1);
  363. if (!smdk6400_snd_device)
  364. return -ENOMEM;
  365. platform_set_drvdata(smdk6400_snd_device, &smdk6400_snd_devdata);
  366. smdk6400_snd_devdata.dev = &smdk6400_snd_device->dev;
  367. ret = platform_device_add(smdk6400_snd_device);
  368. if (ret)
  369. platform_device_put(smdk6400_snd_device);
  370. return ret;
  371. }
  372. static void __exit smdk6400_exit(void)
  373. {
  374. platform_device_unregister(smdk6400_snd_device);
  375. }
  376. module_init(smdk6400_init);
  377. module_exit(smdk6400_exit);
  378. /* Module information */
  379. MODULE_AUTHOR("Liam Girdwood");
  380. MODULE_DESCRIPTION("ALSA SoC WM8990 SMDK6400");
  381. MODULE_LICENSE("GPL");