littlemill.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /*
  2. * Littlemill audio support
  3. *
  4. * Copyright 2011 Wolfson Microelectronics
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation; either version 2 of the License, or (at your
  9. * option) any later version.
  10. */
  11. #include <sound/soc.h>
  12. #include <sound/soc-dapm.h>
  13. #include <sound/jack.h>
  14. #include <linux/gpio.h>
  15. #include <linux/module.h>
  16. #include "../codecs/wm8994.h"
  17. static int sample_rate = 44100;
  18. static int littlemill_set_bias_level(struct snd_soc_card *card,
  19. struct snd_soc_dapm_context *dapm,
  20. enum snd_soc_bias_level level)
  21. {
  22. struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
  23. int ret;
  24. if (dapm->dev != aif1_dai->dev)
  25. return 0;
  26. switch (level) {
  27. case SND_SOC_BIAS_PREPARE:
  28. /*
  29. * If we've not already clocked things via hw_params()
  30. * then do so now, otherwise these are noops.
  31. */
  32. if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
  33. ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
  34. WM8994_FLL_SRC_MCLK2, 32768,
  35. sample_rate * 512);
  36. if (ret < 0) {
  37. pr_err("Failed to start FLL: %d\n", ret);
  38. return ret;
  39. }
  40. ret = snd_soc_dai_set_sysclk(aif1_dai,
  41. WM8994_SYSCLK_FLL1,
  42. sample_rate * 512,
  43. SND_SOC_CLOCK_IN);
  44. if (ret < 0) {
  45. pr_err("Failed to set SYSCLK: %d\n", ret);
  46. return ret;
  47. }
  48. }
  49. break;
  50. default:
  51. break;
  52. }
  53. return 0;
  54. }
  55. static int littlemill_set_bias_level_post(struct snd_soc_card *card,
  56. struct snd_soc_dapm_context *dapm,
  57. enum snd_soc_bias_level level)
  58. {
  59. struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
  60. struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
  61. int ret;
  62. if (dapm->dev != aif1_dai->dev)
  63. return 0;
  64. switch (level) {
  65. case SND_SOC_BIAS_STANDBY:
  66. ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,
  67. 32768, SND_SOC_CLOCK_IN);
  68. if (ret < 0) {
  69. pr_err("Failed to switch away from FLL2: %d\n", ret);
  70. return ret;
  71. }
  72. ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
  73. 0, 0, 0);
  74. if (ret < 0) {
  75. pr_err("Failed to stop FLL2: %d\n", ret);
  76. return ret;
  77. }
  78. ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
  79. 32768, SND_SOC_CLOCK_IN);
  80. if (ret < 0) {
  81. pr_err("Failed to switch away from FLL1: %d\n", ret);
  82. return ret;
  83. }
  84. ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
  85. 0, 0, 0);
  86. if (ret < 0) {
  87. pr_err("Failed to stop FLL1: %d\n", ret);
  88. return ret;
  89. }
  90. break;
  91. case SND_SOC_BIAS_PREPARE:
  92. ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
  93. WM8994_FLL_SRC_BCLK, 64 * 8000,
  94. 8000 * 256);
  95. if (ret < 0) {
  96. pr_err("Failed to start FLL: %d\n", ret);
  97. return ret;
  98. }
  99. ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_FLL2,
  100. 8000 * 256,
  101. SND_SOC_CLOCK_IN);
  102. if (ret < 0) {
  103. pr_err("Failed to set SYSCLK: %d\n", ret);
  104. return ret;
  105. }
  106. break;
  107. default:
  108. break;
  109. }
  110. dapm->bias_level = level;
  111. return 0;
  112. }
  113. static int littlemill_hw_params(struct snd_pcm_substream *substream,
  114. struct snd_pcm_hw_params *params)
  115. {
  116. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  117. struct snd_soc_dai *codec_dai = rtd->codec_dai;
  118. int ret;
  119. sample_rate = params_rate(params);
  120. ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,
  121. WM8994_FLL_SRC_MCLK2, 32768,
  122. sample_rate * 512);
  123. if (ret < 0) {
  124. pr_err("Failed to start FLL: %d\n", ret);
  125. return ret;
  126. }
  127. ret = snd_soc_dai_set_sysclk(codec_dai,
  128. WM8994_SYSCLK_FLL1,
  129. sample_rate * 512,
  130. SND_SOC_CLOCK_IN);
  131. if (ret < 0) {
  132. pr_err("Failed to set SYSCLK: %d\n", ret);
  133. return ret;
  134. }
  135. return 0;
  136. }
  137. static struct snd_soc_ops littlemill_ops = {
  138. .hw_params = littlemill_hw_params,
  139. };
  140. static const struct snd_soc_pcm_stream baseband_params = {
  141. .formats = SNDRV_PCM_FMTBIT_S32_LE,
  142. .rate_min = 8000,
  143. .rate_max = 8000,
  144. .channels_min = 2,
  145. .channels_max = 2,
  146. };
  147. static struct snd_soc_dai_link littlemill_dai[] = {
  148. {
  149. .name = "CPU",
  150. .stream_name = "CPU",
  151. .cpu_dai_name = "samsung-i2s.0",
  152. .codec_dai_name = "wm8994-aif1",
  153. .platform_name = "samsung-audio",
  154. .codec_name = "wm8994-codec",
  155. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  156. | SND_SOC_DAIFMT_CBM_CFM,
  157. .ops = &littlemill_ops,
  158. },
  159. {
  160. .name = "Baseband",
  161. .stream_name = "Baseband",
  162. .cpu_dai_name = "wm8994-aif2",
  163. .codec_dai_name = "wm1250-ev1",
  164. .codec_name = "wm1250-ev1.1-0027",
  165. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  166. | SND_SOC_DAIFMT_CBM_CFM,
  167. .ignore_suspend = 1,
  168. .params = &baseband_params,
  169. },
  170. };
  171. static struct snd_soc_dapm_widget widgets[] = {
  172. SND_SOC_DAPM_HP("Headphone", NULL),
  173. SND_SOC_DAPM_MIC("AMIC", NULL),
  174. SND_SOC_DAPM_MIC("DMIC", NULL),
  175. };
  176. static struct snd_soc_dapm_route audio_paths[] = {
  177. { "Headphone", NULL, "HPOUT1L" },
  178. { "Headphone", NULL, "HPOUT1R" },
  179. { "AMIC", NULL, "MICBIAS1" }, /* Default for AMICBIAS jumper */
  180. { "IN1LN", NULL, "AMIC" },
  181. { "DMIC", NULL, "MICBIAS2" }, /* Default for DMICBIAS jumper */
  182. { "DMIC1DAT", NULL, "DMIC" },
  183. { "DMIC2DAT", NULL, "DMIC" },
  184. };
  185. static struct snd_soc_jack littlemill_headset;
  186. static int littlemill_late_probe(struct snd_soc_card *card)
  187. {
  188. struct snd_soc_codec *codec = card->rtd[0].codec;
  189. struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
  190. struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
  191. int ret;
  192. ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
  193. 32768, SND_SOC_CLOCK_IN);
  194. if (ret < 0)
  195. return ret;
  196. ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,
  197. 32768, SND_SOC_CLOCK_IN);
  198. if (ret < 0)
  199. return ret;
  200. ret = snd_soc_jack_new(codec, "Headset",
  201. SND_JACK_HEADSET | SND_JACK_MECHANICAL |
  202. SND_JACK_BTN_0 | SND_JACK_BTN_1 |
  203. SND_JACK_BTN_2 | SND_JACK_BTN_3 |
  204. SND_JACK_BTN_4 | SND_JACK_BTN_5,
  205. &littlemill_headset);
  206. if (ret)
  207. return ret;
  208. /* This will check device compatibility itself */
  209. wm8958_mic_detect(codec, &littlemill_headset, NULL, NULL);
  210. /* As will this */
  211. wm8994_mic_detect(codec, &littlemill_headset, 1);
  212. return 0;
  213. }
  214. static struct snd_soc_card littlemill = {
  215. .name = "Littlemill",
  216. .owner = THIS_MODULE,
  217. .dai_link = littlemill_dai,
  218. .num_links = ARRAY_SIZE(littlemill_dai),
  219. .set_bias_level = littlemill_set_bias_level,
  220. .set_bias_level_post = littlemill_set_bias_level_post,
  221. .dapm_widgets = widgets,
  222. .num_dapm_widgets = ARRAY_SIZE(widgets),
  223. .dapm_routes = audio_paths,
  224. .num_dapm_routes = ARRAY_SIZE(audio_paths),
  225. .late_probe = littlemill_late_probe,
  226. };
  227. static __devinit int littlemill_probe(struct platform_device *pdev)
  228. {
  229. struct snd_soc_card *card = &littlemill;
  230. int ret;
  231. card->dev = &pdev->dev;
  232. ret = snd_soc_register_card(card);
  233. if (ret) {
  234. dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
  235. ret);
  236. return ret;
  237. }
  238. return 0;
  239. }
  240. static int __devexit littlemill_remove(struct platform_device *pdev)
  241. {
  242. struct snd_soc_card *card = platform_get_drvdata(pdev);
  243. snd_soc_unregister_card(card);
  244. return 0;
  245. }
  246. static struct platform_driver littlemill_driver = {
  247. .driver = {
  248. .name = "littlemill",
  249. .owner = THIS_MODULE,
  250. .pm = &snd_soc_pm_ops,
  251. },
  252. .probe = littlemill_probe,
  253. .remove = __devexit_p(littlemill_remove),
  254. };
  255. module_platform_driver(littlemill_driver);
  256. MODULE_DESCRIPTION("Littlemill audio support");
  257. MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  258. MODULE_LICENSE("GPL");
  259. MODULE_ALIAS("platform:littlemill");