smdk2450_wm8753.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /*
  2. * smdk6400_wm8753.c -- SoC audio for Neo1973
  3. *
  4. * Copyright 2007 Wolfson Microelectronics PLC.
  5. * Author: Graeme Gregory
  6. * graeme.gregory@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. * 20th Jan 2007 Initial version.
  17. * 05th Feb 2007 Rename all to Neo1973
  18. *
  19. */
  20. #include <linux/module.h>
  21. #include <linux/moduleparam.h>
  22. #include <linux/timer.h>
  23. #include <linux/interrupt.h>
  24. #include <linux/platform_device.h>
  25. #include <linux/i2c.h>
  26. #include <sound/driver.h>
  27. #include <sound/core.h>
  28. #include <sound/pcm.h>
  29. #include <sound/soc.h>
  30. #include <sound/soc-dapm.h>
  31. #include <asm/mach-types.h>
  32. #include <asm/hardware/scoop.h>
  33. #include <asm/arch/regs-iis.h>
  34. #include <asm/arch/regs-gpio.h>
  35. #include <asm/hardware.h>
  36. #include <asm/arch/audio.h>
  37. #include <asm/io.h>
  38. #include <asm/arch/spi-gpio.h>
  39. #include <asm/arch/regs-s3c2450-clock.h>
  40. #include "../codecs/wm8753.h"
  41. #include "s3c-pcm.h"
  42. #include "s3c-i2s.h"
  43. /* define the scenarios */
  44. #define SMDK6400_AUDIO_OFF 0
  45. #define SMDK6400_CAPTURE_MIC1 3
  46. #define SMDK6400_STEREO_TO_HEADPHONES 2
  47. #define SMDK6400_CAPTURE_LINE_IN 1
  48. #ifdef CONFIG_SND_DEBUG
  49. #define s3cdbg(x...) printk(x)
  50. #else
  51. #define s3cdbg(x...)
  52. #endif
  53. static int smdk6400_hifi_hw_params(struct snd_pcm_substream *substream,
  54. struct snd_pcm_hw_params *params)
  55. {
  56. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  57. struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
  58. struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
  59. unsigned int pll_out = 0, bclk = 0;
  60. int ret = 0;
  61. unsigned int regs;
  62. unsigned int prescaler=0;
  63. s3cdbg("Entered %s, rate = %d\n", __FUNCTION__, params_rate(params));
  64. /* Select Clock source EPLL */
  65. regs = readl(S3C2443_CLKSRC);
  66. regs &= ~(3<<12);
  67. regs |= S3C2450_CLKSRC_I2S1_EPLL;
  68. regs = (regs & ~(3<<7))|(2<<7);
  69. writel(regs, S3C2443_CLKSRC);
  70. regs |= (1<<6);
  71. writel(regs, S3C2443_CLKSRC);
  72. regs = readl(S3C2443_SCLKCON);
  73. regs |= S3C2443_SCLKCON_I2SCLK_1;
  74. writel(regs, S3C2443_SCLKCON);
  75. s3cdbg("%s: %d , params = %d \n", __FUNCTION__, __LINE__, params_rate(params));
  76. switch (params_rate(params)) {
  77. case 8000:
  78. case 16000:
  79. case 32000:
  80. case 48000:
  81. case 64000:
  82. case 96000:
  83. writel(9962, S3C2450_EPLLCON_K);
  84. writel((49<<16)|(1<<8)|(3<<0) ,S3C2443_EPLLCON);
  85. break;
  86. case 11025:
  87. case 22050:
  88. case 44100:
  89. case 88200:
  90. writel(10381, S3C2450_EPLLCON_K);
  91. writel((45<<16)|(1<<8)|(2<<0) ,S3C2443_EPLLCON);
  92. break;
  93. default:
  94. printk("Unsupported rate = %d\n", params_rate(params));
  95. break;
  96. }
  97. switch (params_rate(params)) {
  98. case 8000:
  99. bclk = WM8753_BCLK_DIV_16;
  100. pll_out = 18432000;
  101. prescaler = 24;
  102. break;
  103. case 11025:
  104. bclk = WM8753_BCLK_DIV_16;
  105. pll_out = 16934400;
  106. prescaler = 32;
  107. break;
  108. case 16000:
  109. bclk = WM8753_BCLK_DIV_8;
  110. pll_out = 18432000;
  111. prescaler = 12;
  112. break;
  113. case 22050:
  114. bclk = WM8753_BCLK_DIV_2;
  115. pll_out = 16934400;
  116. prescaler = 16;
  117. break;
  118. case 32000:
  119. bclk = WM8753_BCLK_DIV_4;
  120. pll_out = 18432000;
  121. prescaler = 6;
  122. break;
  123. case 44100:
  124. bclk = WM8753_BCLK_DIV_4;
  125. pll_out = 16934400;
  126. prescaler = 8;
  127. break;
  128. case 48000:
  129. bclk = WM8753_BCLK_DIV_4;
  130. pll_out = 18432000;
  131. prescaler = 4;
  132. break;
  133. case 64000:
  134. bclk = WM8753_BCLK_DIV_4;
  135. pll_out = 18432000;
  136. prescaler = 3;
  137. break;
  138. case 88200:
  139. bclk = WM8753_BCLK_DIV_4;
  140. pll_out = 16934400;
  141. prescaler = 4;
  142. break;
  143. case 96000:
  144. bclk = WM8753_BCLK_DIV_4;
  145. pll_out = 18432000;
  146. prescaler = 2;
  147. break;
  148. }
  149. /* set codec DAI configuration */
  150. ret = codec_dai->dai_ops.set_fmt(codec_dai,
  151. SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
  152. SND_SOC_DAIFMT_CBS_CFS );
  153. if (ret < 0)
  154. return ret;
  155. /* set cpu DAI configuration */
  156. ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
  157. SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
  158. SND_SOC_DAIFMT_CBS_CFS );
  159. if (ret < 0)
  160. return ret;
  161. /* set the codec system clock for DAC and ADC */
  162. ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
  163. SND_SOC_CLOCK_IN);
  164. if (ret < 0)
  165. return ret;
  166. /* set MCLK division for sample rate */
  167. ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
  168. S3C2410_IISMOD_32FS );
  169. if (ret < 0)
  170. return ret;
  171. /* set codec BCLK division for sample rate */
  172. ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
  173. if (ret < 0)
  174. return ret;
  175. /* set prescaler division for sample rate */
  176. ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
  177. ((prescaler/2 - 1) << 0x8));
  178. if (ret < 0)
  179. return ret;
  180. #if 0
  181. /* set the codec register set for capture and play */
  182. ret = codec_dai->dai_ops.set_tdm_slot(codec_dai, substream->stream, 2);
  183. if (ret < 0)
  184. return ret;
  185. #endif
  186. return 0;
  187. }
  188. static int smdk6400_hifi_hw_free(struct snd_pcm_substream *substream)
  189. {
  190. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  191. struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
  192. /* disable the PLL */
  193. return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
  194. }
  195. /*
  196. * Neo1973 WM8753 HiFi DAI opserations.
  197. */
  198. static struct snd_soc_ops smdk6400_hifi_ops = {
  199. .hw_params = smdk6400_hifi_hw_params,
  200. .hw_free = smdk6400_hifi_hw_free,
  201. };
  202. static int smdk6400_scenario = 0;
  203. static int smdk6400_get_scenario(struct snd_kcontrol *kcontrol,
  204. struct snd_ctl_elem_value *ucontrol)
  205. {
  206. ucontrol->value.integer.value[0] = smdk6400_scenario;
  207. return 0;
  208. }
  209. static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
  210. {
  211. switch(smdk6400_scenario) {
  212. case SMDK6400_AUDIO_OFF:
  213. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  214. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  215. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  216. break;
  217. case SMDK6400_STEREO_TO_HEADPHONES:
  218. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
  219. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  220. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  221. break;
  222. case SMDK6400_CAPTURE_MIC1:
  223. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  224. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 1);
  225. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  226. break;
  227. case SMDK6400_CAPTURE_LINE_IN:
  228. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  229. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  230. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 1);
  231. break;
  232. default:
  233. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
  234. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 1);
  235. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 1);
  236. break;
  237. }
  238. snd_soc_dapm_sync_endpoints(codec);
  239. return 0;
  240. }
  241. static int smdk6400_set_scenario(struct snd_kcontrol *kcontrol,
  242. struct snd_ctl_elem_value *ucontrol)
  243. {
  244. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  245. if (smdk6400_scenario == ucontrol->value.integer.value[0])
  246. return 0;
  247. smdk6400_scenario = ucontrol->value.integer.value[0];
  248. set_scenario_endpoints(codec, smdk6400_scenario);
  249. return 1;
  250. }
  251. static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
  252. SND_SOC_DAPM_HP("Headphone Jack", NULL),
  253. SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
  254. SND_SOC_DAPM_LINE("Line In Jack", NULL),
  255. };
  256. /* example machine audio_mapnections */
  257. static const char* audio_map[][3] = {
  258. {"Headphone Jack", NULL, "LOUT1"},
  259. {"Headphone Jack", NULL, "ROUT1"},
  260. /* mic is connected to mic1 - with bias */
  261. /* mic is connected to mic1 - with bias */
  262. {"MIC1", NULL, "Mic1 Jack"},
  263. {"LINE1", NULL, "Line In Jack"},
  264. {"LINE2", NULL, "Line In Jack"},
  265. /* Connect the ALC pins */
  266. {"ACIN", NULL, "ACOP"},
  267. {NULL, NULL, NULL},
  268. };
  269. static const char *smdk_scenarios[] = {
  270. "Off",
  271. "Capture Line In",
  272. "Headphones",
  273. "Capture Mic1",
  274. };
  275. static const struct soc_enum smdk_scenario_enum[] = {
  276. SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(smdk_scenarios),smdk_scenarios),
  277. };
  278. static const struct snd_kcontrol_new wm8753_smdk6400_controls[] = {
  279. SOC_ENUM_EXT("SMDK Mode", smdk_scenario_enum[0],
  280. smdk6400_get_scenario, smdk6400_set_scenario),
  281. };
  282. /*
  283. * This is an example machine initialisation for a wm8753 connected to a
  284. * smdk6400. It is missing logic to detect hp/mic insertions and logic
  285. * to re-route the audio in such an event.
  286. */
  287. static int smdk6400_wm8753_init(struct snd_soc_codec *codec)
  288. {
  289. int i, err;
  290. /* set endpoints to default mode */
  291. set_scenario_endpoints(codec, SMDK6400_AUDIO_OFF);
  292. /* Add smdk6400 specific widgets */
  293. for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
  294. snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
  295. /* add smdk6400 specific controls */
  296. for (i = 0; i < ARRAY_SIZE(wm8753_smdk6400_controls); i++) {
  297. err = snd_ctl_add(codec->card,
  298. snd_soc_cnew(&wm8753_smdk6400_controls[i],
  299. codec, NULL));
  300. if (err < 0)
  301. return err;
  302. }
  303. /* set up smdk6400 specific audio path audio_mapnects */
  304. for (i = 0; audio_map[i][0] != NULL; i++) {
  305. snd_soc_dapm_connect_input(codec, audio_map[i][0],
  306. audio_map[i][1], audio_map[i][2]);
  307. }
  308. /* always connected */
  309. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 1);
  310. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
  311. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 1);
  312. snd_soc_dapm_sync_endpoints(codec);
  313. return 0;
  314. }
  315. static struct snd_soc_dai_link smdk6400_dai[] = {
  316. { /* Hifi Playback - for similatious use with voice below */
  317. .name = "WM8753",
  318. .stream_name = "WM8753 HiFi",
  319. .cpu_dai = &s3c_i2s_dai,
  320. .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
  321. .init = smdk6400_wm8753_init,
  322. .ops = &smdk6400_hifi_ops,
  323. },
  324. };
  325. static struct snd_soc_machine smdk6400 = {
  326. .name = "smdk2450",
  327. .dai_link = smdk6400_dai,
  328. .num_links = ARRAY_SIZE(smdk6400_dai),
  329. };
  330. static struct wm8753_setup_data smdk6400_wm8753_setup = {
  331. /* wm8753 i2c address 0x34 for write*/
  332. .i2c_address = 0x1a,
  333. };
  334. static struct snd_soc_device smdk6400_snd_devdata = {
  335. .machine = &smdk6400,
  336. .platform = &s3c24xx_soc_platform,
  337. .codec_dev = &soc_codec_dev_wm8753,
  338. .codec_data = &smdk6400_wm8753_setup,
  339. };
  340. static struct platform_device *smdk6400_snd_device;
  341. static int __init smdk6400_init(void)
  342. {
  343. int ret;
  344. smdk6400_snd_device = platform_device_alloc("soc-audio", -1);
  345. if (!smdk6400_snd_device)
  346. return -ENOMEM;
  347. platform_set_drvdata(smdk6400_snd_device, &smdk6400_snd_devdata);
  348. smdk6400_snd_devdata.dev = &smdk6400_snd_device->dev;
  349. ret = platform_device_add(smdk6400_snd_device);
  350. if (ret)
  351. platform_device_put(smdk6400_snd_device);
  352. return ret;
  353. }
  354. static void __exit smdk6400_exit(void)
  355. {
  356. platform_device_unregister(smdk6400_snd_device);
  357. }
  358. module_init(smdk6400_init);
  359. module_exit(smdk6400_exit);
  360. /* Module information */
  361. MODULE_AUTHOR("Ryu Euiyoul");
  362. MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
  363. MODULE_LICENSE("GPL");