corgi.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * corgi.c -- SoC audio for Corgi
  3. *
  4. * Copyright 2005 Wolfson Microelectronics PLC.
  5. * Copyright 2005 Openedhand Ltd.
  6. *
  7. * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
  8. * Richard Purdie <richard@openedhand.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. * 30th Nov 2005 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 <sound/core.h>
  25. #include <sound/pcm.h>
  26. #include <sound/soc.h>
  27. #include <sound/soc-dapm.h>
  28. #include <asm/mach-types.h>
  29. #include <asm/hardware/scoop.h>
  30. #include <asm/arch/pxa-regs.h>
  31. #include <asm/arch/hardware.h>
  32. #include <asm/arch/corgi.h>
  33. #include <asm/arch/audio.h>
  34. #include "../codecs/wm8731.h"
  35. #include "pxa2xx-pcm.h"
  36. #include "pxa2xx-i2s.h"
  37. #define CORGI_HP 0
  38. #define CORGI_MIC 1
  39. #define CORGI_LINE 2
  40. #define CORGI_HEADSET 3
  41. #define CORGI_HP_OFF 4
  42. #define CORGI_SPK_ON 0
  43. #define CORGI_SPK_OFF 1
  44. /* audio clock in Hz - rounded from 12.235MHz */
  45. #define CORGI_AUDIO_CLOCK 12288000
  46. static int corgi_jack_func;
  47. static int corgi_spk_func;
  48. static void corgi_ext_control(struct snd_soc_codec *codec)
  49. {
  50. int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
  51. /* set up jack connection */
  52. switch (corgi_jack_func) {
  53. case CORGI_HP:
  54. hp = 1;
  55. /* set = unmute headphone */
  56. set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
  57. set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
  58. break;
  59. case CORGI_MIC:
  60. mic = 1;
  61. /* reset = mute headphone */
  62. reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
  63. reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
  64. break;
  65. case CORGI_LINE:
  66. line = 1;
  67. reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
  68. reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
  69. break;
  70. case CORGI_HEADSET:
  71. hs = 1;
  72. mic = 1;
  73. reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
  74. set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
  75. break;
  76. }
  77. if (corgi_spk_func == CORGI_SPK_ON)
  78. spk = 1;
  79. /* set the enpoints to their new connetion states */
  80. snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
  81. snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
  82. snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
  83. snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
  84. snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
  85. /* signal a DAPM event */
  86. snd_soc_dapm_sync_endpoints(codec);
  87. }
  88. static int corgi_startup(struct snd_pcm_substream *substream)
  89. {
  90. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  91. struct snd_soc_codec *codec = rtd->socdev->codec;
  92. /* check the jack status at stream startup */
  93. corgi_ext_control(codec);
  94. return 0;
  95. }
  96. /* we need to unmute the HP at shutdown as the mute burns power on corgi */
  97. static int corgi_shutdown(struct snd_pcm_substream *substream)
  98. {
  99. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  100. struct snd_soc_codec *codec = rtd->socdev->codec;
  101. /* set = unmute headphone */
  102. set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
  103. set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
  104. return 0;
  105. }
  106. static int corgi_hw_params(struct snd_pcm_substream *substream,
  107. struct snd_pcm_hw_params *params)
  108. {
  109. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  110. struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
  111. struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
  112. unsigned int clk = 0;
  113. int ret = 0;
  114. switch (params_rate(params)) {
  115. case 8000:
  116. case 16000:
  117. case 48000:
  118. case 96000:
  119. clk = 12288000;
  120. break;
  121. case 11025:
  122. case 22050:
  123. case 44100:
  124. clk = 11289600;
  125. break;
  126. }
  127. /* set codec DAI configuration */
  128. ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
  129. SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
  130. if (ret < 0)
  131. return ret;
  132. /* set cpu DAI configuration */
  133. ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
  134. SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
  135. if (ret < 0)
  136. return ret;
  137. /* set the codec system clock for DAC and ADC */
  138. ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
  139. SND_SOC_CLOCK_IN);
  140. if (ret < 0)
  141. return ret;
  142. /* set the I2S system clock as input (unused) */
  143. ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
  144. SND_SOC_CLOCK_IN);
  145. if (ret < 0)
  146. return ret;
  147. return 0;
  148. }
  149. static struct snd_soc_ops corgi_ops = {
  150. .startup = corgi_startup,
  151. .hw_params = corgi_hw_params,
  152. .shutdown = corgi_shutdown,
  153. };
  154. static int corgi_get_jack(struct snd_kcontrol *kcontrol,
  155. struct snd_ctl_elem_value *ucontrol)
  156. {
  157. ucontrol->value.integer.value[0] = corgi_jack_func;
  158. return 0;
  159. }
  160. static int corgi_set_jack(struct snd_kcontrol *kcontrol,
  161. struct snd_ctl_elem_value *ucontrol)
  162. {
  163. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  164. if (corgi_jack_func == ucontrol->value.integer.value[0])
  165. return 0;
  166. corgi_jack_func = ucontrol->value.integer.value[0];
  167. corgi_ext_control(codec);
  168. return 1;
  169. }
  170. static int corgi_get_spk(struct snd_kcontrol *kcontrol,
  171. struct snd_ctl_elem_value *ucontrol)
  172. {
  173. ucontrol->value.integer.value[0] = corgi_spk_func;
  174. return 0;
  175. }
  176. static int corgi_set_spk(struct snd_kcontrol *kcontrol,
  177. struct snd_ctl_elem_value *ucontrol)
  178. {
  179. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  180. if (corgi_spk_func == ucontrol->value.integer.value[0])
  181. return 0;
  182. corgi_spk_func = ucontrol->value.integer.value[0];
  183. corgi_ext_control(codec);
  184. return 1;
  185. }
  186. static int corgi_amp_event(struct snd_soc_dapm_widget *w,
  187. struct snd_kcontrol *k, int event)
  188. {
  189. if (SND_SOC_DAPM_EVENT_ON(event))
  190. set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
  191. else
  192. reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
  193. return 0;
  194. }
  195. static int corgi_mic_event(struct snd_soc_dapm_widget *w,
  196. struct snd_kcontrol *k, int event)
  197. {
  198. if (SND_SOC_DAPM_EVENT_ON(event))
  199. set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
  200. else
  201. reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
  202. return 0;
  203. }
  204. /* corgi machine dapm widgets */
  205. static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
  206. SND_SOC_DAPM_HP("Headphone Jack", NULL),
  207. SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
  208. SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
  209. SND_SOC_DAPM_LINE("Line Jack", NULL),
  210. SND_SOC_DAPM_HP("Headset Jack", NULL),
  211. };
  212. /* Corgi machine audio map (connections to the codec pins) */
  213. static const char *audio_map[][3] = {
  214. /* headset Jack - in = micin, out = LHPOUT*/
  215. {"Headset Jack", NULL, "LHPOUT"},
  216. /* headphone connected to LHPOUT1, RHPOUT1 */
  217. {"Headphone Jack", NULL, "LHPOUT"},
  218. {"Headphone Jack", NULL, "RHPOUT"},
  219. /* speaker connected to LOUT, ROUT */
  220. {"Ext Spk", NULL, "ROUT"},
  221. {"Ext Spk", NULL, "LOUT"},
  222. /* mic is connected to MICIN (via right channel of headphone jack) */
  223. {"MICIN", NULL, "Mic Jack"},
  224. /* Same as the above but no mic bias for line signals */
  225. {"MICIN", NULL, "Line Jack"},
  226. {NULL, NULL, NULL},
  227. };
  228. static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
  229. "Off"};
  230. static const char *spk_function[] = {"On", "Off"};
  231. static const struct soc_enum corgi_enum[] = {
  232. SOC_ENUM_SINGLE_EXT(5, jack_function),
  233. SOC_ENUM_SINGLE_EXT(2, spk_function),
  234. };
  235. static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
  236. SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
  237. corgi_set_jack),
  238. SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
  239. corgi_set_spk),
  240. };
  241. /*
  242. * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
  243. */
  244. static int corgi_wm8731_init(struct snd_soc_codec *codec)
  245. {
  246. int i, err;
  247. snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
  248. snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
  249. /* Add corgi specific controls */
  250. for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
  251. err = snd_ctl_add(codec->card,
  252. snd_soc_cnew(&wm8731_corgi_controls[i],codec, NULL));
  253. if (err < 0)
  254. return err;
  255. }
  256. /* Add corgi specific widgets */
  257. for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
  258. snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
  259. }
  260. /* Set up corgi specific audio path audio_map */
  261. for(i = 0; audio_map[i][0] != NULL; i++) {
  262. snd_soc_dapm_connect_input(codec, audio_map[i][0],
  263. audio_map[i][1], audio_map[i][2]);
  264. }
  265. snd_soc_dapm_sync_endpoints(codec);
  266. return 0;
  267. }
  268. /* corgi digital audio interface glue - connects codec <--> CPU */
  269. static struct snd_soc_dai_link corgi_dai = {
  270. .name = "WM8731",
  271. .stream_name = "WM8731",
  272. .cpu_dai = &pxa_i2s_dai,
  273. .codec_dai = &wm8731_dai,
  274. .init = corgi_wm8731_init,
  275. .ops = &corgi_ops,
  276. };
  277. /* corgi audio machine driver */
  278. static struct snd_soc_machine snd_soc_machine_corgi = {
  279. .name = "Corgi",
  280. .dai_link = &corgi_dai,
  281. .num_links = 1,
  282. };
  283. /* corgi audio private data */
  284. static struct wm8731_setup_data corgi_wm8731_setup = {
  285. .i2c_address = 0x1b,
  286. };
  287. /* corgi audio subsystem */
  288. static struct snd_soc_device corgi_snd_devdata = {
  289. .machine = &snd_soc_machine_corgi,
  290. .platform = &pxa2xx_soc_platform,
  291. .codec_dev = &soc_codec_dev_wm8731,
  292. .codec_data = &corgi_wm8731_setup,
  293. };
  294. static struct platform_device *corgi_snd_device;
  295. static int __init corgi_init(void)
  296. {
  297. int ret;
  298. if (!(machine_is_corgi() || machine_is_shepherd() || machine_is_husky()))
  299. return -ENODEV;
  300. corgi_snd_device = platform_device_alloc("soc-audio", -1);
  301. if (!corgi_snd_device)
  302. return -ENOMEM;
  303. platform_set_drvdata(corgi_snd_device, &corgi_snd_devdata);
  304. corgi_snd_devdata.dev = &corgi_snd_device->dev;
  305. ret = platform_device_add(corgi_snd_device);
  306. if (ret)
  307. platform_device_put(corgi_snd_device);
  308. return ret;
  309. }
  310. static void __exit corgi_exit(void)
  311. {
  312. platform_device_unregister(corgi_snd_device);
  313. }
  314. module_init(corgi_init);
  315. module_exit(corgi_exit);
  316. /* Module information */
  317. MODULE_AUTHOR("Richard Purdie");
  318. MODULE_DESCRIPTION("ALSA SoC Corgi");
  319. MODULE_LICENSE("GPL");