smdk2450_wm8580.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /*
  2. * smdk2450_wm8580.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/wm8580.h"
  41. #include "s3c-pcm.h"
  42. #include "s3c-i2s.h"
  43. /* define the scenarios */
  44. #define SMDK_AUDIO_OFF 0
  45. #define SMDK_CAPTURE_MIC1 3
  46. #define SMDK_STEREO_TO_HEADPHONES 2
  47. #define SMDK_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 smdk_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;
  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<<14);
  67. regs |= S3C2443_CLKSRC_I2S_EPLLDIV;
  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;
  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. pll_out = 18432000;
  100. prescaler = 24;
  101. break;
  102. case 11025:
  103. pll_out = 16934400;
  104. prescaler = 32;
  105. break;
  106. case 16000:
  107. pll_out = 18432000;
  108. prescaler = 12;
  109. break;
  110. case 22050:
  111. pll_out = 16934400;
  112. prescaler = 16;
  113. break;
  114. case 32000:
  115. pll_out = 18432000;
  116. prescaler = 6;
  117. break;
  118. case 44100:
  119. pll_out = 16934400;
  120. prescaler = 8;
  121. break;
  122. case 48000:
  123. pll_out = 18432000;
  124. prescaler = 4;
  125. break;
  126. case 64000:
  127. pll_out = 18432000;
  128. prescaler = 3;
  129. break;
  130. case 88200:
  131. pll_out = 16934400;
  132. prescaler = 4;
  133. break;
  134. case 96000:
  135. pll_out = 18432000;
  136. prescaler = 2;
  137. break;
  138. }
  139. /* set cpu DAI configuration */
  140. ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
  141. SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
  142. SND_SOC_DAIFMT_CBS_CFS );
  143. if (ret < 0)
  144. return ret;
  145. /* set MCLK division for sample rate */
  146. ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
  147. S3C2410_IISMOD_32FS );
  148. if (ret < 0)
  149. return ret;
  150. /* set prescaler division for sample rate */
  151. ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
  152. // ((prescaler/2 - 1) << 0x8));
  153. ((prescaler - 1) << 0x8));
  154. if (ret < 0)
  155. return ret;
  156. return 0;
  157. }
  158. static int smdk_hifi_hw_free(struct snd_pcm_substream *substream)
  159. {
  160. // struct snd_soc_pcm_runtime *rtd = substream->private_data;
  161. // struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
  162. /* disable the PLL */
  163. //return codec_dai->dai_ops.set_pll(codec_dai, WM8580_PLL1, 0, 0);
  164. //return codec_dai->dai_ops.set_pll(codec_dai, 0, 0, 0);
  165. return 0;
  166. }
  167. /*
  168. * Neo1973 WM8580 HiFi DAI opserations.
  169. */
  170. static struct snd_soc_ops smdk_hifi_ops = {
  171. .hw_params = smdk_hifi_hw_params,
  172. .hw_free = smdk_hifi_hw_free,
  173. };
  174. static int smdk_scenario = 0;
  175. static int smdk_get_scenario(struct snd_kcontrol *kcontrol,
  176. struct snd_ctl_elem_value *ucontrol)
  177. {
  178. ucontrol->value.integer.value[0] = smdk_scenario;
  179. return 0;
  180. }
  181. static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
  182. {
  183. switch(smdk_scenario) {
  184. case SMDK_AUDIO_OFF:
  185. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  186. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  187. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  188. break;
  189. case SMDK_STEREO_TO_HEADPHONES:
  190. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
  191. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  192. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  193. break;
  194. case SMDK_CAPTURE_MIC1:
  195. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  196. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 1);
  197. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 0);
  198. break;
  199. case SMDK_CAPTURE_LINE_IN:
  200. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
  201. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 0);
  202. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 1);
  203. break;
  204. default:
  205. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
  206. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 1);
  207. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 1);
  208. break;
  209. }
  210. snd_soc_dapm_sync_endpoints(codec);
  211. return 0;
  212. }
  213. static int smdk_set_scenario(struct snd_kcontrol *kcontrol,
  214. struct snd_ctl_elem_value *ucontrol)
  215. {
  216. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  217. if (smdk_scenario == ucontrol->value.integer.value[0])
  218. return 0;
  219. smdk_scenario = ucontrol->value.integer.value[0];
  220. set_scenario_endpoints(codec, smdk_scenario);
  221. return 1;
  222. }
  223. static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
  224. SND_SOC_DAPM_HP("Headphone Jack", NULL),
  225. SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
  226. SND_SOC_DAPM_LINE("Line In Jack", NULL),
  227. };
  228. /* example machine audio_mapnections */
  229. static const char* audio_map[][3] = {
  230. {"Headphone Jack", NULL, "LOUT1"},
  231. {"Headphone Jack", NULL, "ROUT1"},
  232. /* mic is connected to mic1 - with bias */
  233. /* mic is connected to mic1 - with bias */
  234. {"MIC1", NULL, "Mic1 Jack"},
  235. {"LINE1", NULL, "Line In Jack"},
  236. {"LINE2", NULL, "Line In Jack"},
  237. /* Connect the ALC pins */
  238. {"ACIN", NULL, "ACOP"},
  239. {NULL, NULL, NULL},
  240. };
  241. static const char *smdk_scenarios[] = {
  242. "Off",
  243. "Capture Line In",
  244. "Headphones",
  245. "Capture Mic1",
  246. };
  247. static const struct soc_enum smdk_scenario_enum[] = {
  248. SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(smdk_scenarios),smdk_scenarios),
  249. };
  250. static const struct snd_kcontrol_new wm8580_smdk_controls[] = {
  251. SOC_ENUM_EXT("SMDK Mode", smdk_scenario_enum[0],
  252. smdk_get_scenario, smdk_set_scenario),
  253. };
  254. /*
  255. * This is an example machine initialisation for a wm8580 connected to a
  256. * smdk2450. It is missing logic to detect hp/mic insertions and logic
  257. * to re-route the audio in such an event.
  258. */
  259. static int smdk_wm8580_init(struct snd_soc_codec *codec)
  260. {
  261. int i, err;
  262. /* set endpoints to default mode */
  263. set_scenario_endpoints(codec, SMDK_AUDIO_OFF);
  264. /* Add smdk2450 specific widgets */
  265. for (i = 0; i < ARRAY_SIZE(wm8580_dapm_widgets); i++)
  266. snd_soc_dapm_new_control(codec, &wm8580_dapm_widgets[i]);
  267. /* add smdk2450 specific controls */
  268. for (i = 0; i < ARRAY_SIZE(wm8580_smdk_controls); i++) {
  269. err = snd_ctl_add(codec->card,
  270. snd_soc_cnew(&wm8580_smdk_controls[i],
  271. codec, NULL));
  272. if (err < 0)
  273. return err;
  274. }
  275. /* set up smdk2450 specific audio path audio_mapnects */
  276. for (i = 0; audio_map[i][0] != NULL; i++) {
  277. snd_soc_dapm_connect_input(codec, audio_map[i][0],
  278. audio_map[i][1], audio_map[i][2]);
  279. }
  280. /* always connected */
  281. snd_soc_dapm_set_endpoint(codec, "Mic1 Jack", 1);
  282. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
  283. snd_soc_dapm_set_endpoint(codec, "Line In Jack", 1);
  284. snd_soc_dapm_sync_endpoints(codec);
  285. return 0;
  286. }
  287. static struct snd_soc_dai_link smdk_dai[] = {
  288. { /* Hifi Playback - for similatious use with voice below */
  289. .name = "WM8580",
  290. .stream_name = "WM8580 HiFi",
  291. .cpu_dai = &s3c_i2s_dai,
  292. .codec_dai = &wm8580_dai[0],
  293. .init = smdk_wm8580_init,
  294. .ops = &smdk_hifi_ops,
  295. },
  296. };
  297. static struct snd_soc_machine smdk2450 = {
  298. .name = "smdk2450",
  299. .dai_link = smdk_dai,
  300. .num_links = ARRAY_SIZE(smdk_dai),
  301. };
  302. static struct wm8580_setup_data smdk_wm8580_setup = {
  303. /* wm8580 i2c address 0x36 for write*/
  304. .i2c_address = 0x1b,
  305. };
  306. static struct snd_soc_device smdk_snd_devdata = {
  307. .machine = &smdk2450,
  308. .platform = &s3c24xx_soc_platform,
  309. .codec_dev = &soc_codec_dev_wm8580,
  310. .codec_data = &smdk_wm8580_setup,
  311. };
  312. static struct platform_device *smdk_snd_device;
  313. static int __init s3c_init(void)
  314. {
  315. int ret;
  316. smdk_snd_device = platform_device_alloc("soc-audio", -1);
  317. if (!smdk_snd_device)
  318. return -ENOMEM;
  319. platform_set_drvdata(smdk_snd_device, &smdk_snd_devdata);
  320. smdk_snd_devdata.dev = &smdk_snd_device->dev;
  321. ret = platform_device_add(smdk_snd_device);
  322. if (ret)
  323. platform_device_put(smdk_snd_device);
  324. return ret;
  325. }
  326. static void __exit s3c_exit(void)
  327. {
  328. platform_device_unregister(smdk_snd_device);
  329. }
  330. module_init(s3c_init);
  331. module_exit(s3c_exit);
  332. /* Module information */
  333. MODULE_AUTHOR("Ryu Euiyoul");
  334. MODULE_DESCRIPTION("ALSA SoC WM8580 SMDK");
  335. MODULE_LICENSE("GPL");