bells.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * Bells audio support
  3. *
  4. * Copyright 2012 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/wm5102.h"
  17. #include "../codecs/wm9081.h"
  18. /*
  19. * 44.1kHz based clocks for the SYSCLK domain, use a very high clock
  20. * to allow all the DSP functionality to be enabled if desired.
  21. */
  22. #define SYSCLK_RATE (44100 * 1024)
  23. /* 48kHz based clocks for the ASYNC domain */
  24. #define ASYNCCLK_RATE (48000 * 512)
  25. /* BCLK2 is fixed at this currently */
  26. #define BCLK2_RATE (64 * 8000)
  27. /*
  28. * Expect a 24.576MHz crystal if one is fitted (the driver will function
  29. * if this is not fitted).
  30. */
  31. #define MCLK_RATE 24576000
  32. #define SYS_AUDIO_RATE 44100
  33. #define SYS_MCLK_RATE (SYS_AUDIO_RATE * 256)
  34. #define DAI_AP_DSP 0
  35. #define DAI_DSP_CODEC 1
  36. #define DAI_CODEC_CP 2
  37. #define DAI_CODEC_SUB 3
  38. static int bells_set_bias_level(struct snd_soc_card *card,
  39. struct snd_soc_dapm_context *dapm,
  40. enum snd_soc_bias_level level)
  41. {
  42. struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
  43. struct snd_soc_codec *codec = codec_dai->codec;
  44. int ret;
  45. if (dapm->dev != codec_dai->dev)
  46. return 0;
  47. switch (level) {
  48. case SND_SOC_BIAS_PREPARE:
  49. if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
  50. ret = snd_soc_codec_set_pll(codec, WM5102_FLL1,
  51. ARIZONA_FLL_SRC_MCLK1,
  52. MCLK_RATE,
  53. SYSCLK_RATE);
  54. if (ret < 0)
  55. pr_err("Failed to start FLL: %d\n", ret);
  56. ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
  57. ARIZONA_FLL_SRC_AIF2BCLK,
  58. BCLK2_RATE,
  59. ASYNCCLK_RATE);
  60. if (ret < 0)
  61. pr_err("Failed to start FLL: %d\n", ret);
  62. }
  63. break;
  64. default:
  65. break;
  66. }
  67. return 0;
  68. }
  69. static int bells_set_bias_level_post(struct snd_soc_card *card,
  70. struct snd_soc_dapm_context *dapm,
  71. enum snd_soc_bias_level level)
  72. {
  73. struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
  74. struct snd_soc_codec *codec = codec_dai->codec;
  75. int ret;
  76. if (dapm->dev != codec_dai->dev)
  77. return 0;
  78. switch (level) {
  79. case SND_SOC_BIAS_STANDBY:
  80. ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
  81. if (ret < 0) {
  82. pr_err("Failed to stop FLL: %d\n", ret);
  83. return ret;
  84. }
  85. ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 0, 0, 0);
  86. if (ret < 0) {
  87. pr_err("Failed to stop FLL: %d\n", ret);
  88. return ret;
  89. }
  90. break;
  91. default:
  92. break;
  93. }
  94. dapm->bias_level = level;
  95. return 0;
  96. }
  97. static int bells_late_probe(struct snd_soc_card *card)
  98. {
  99. struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec;
  100. struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec;
  101. struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
  102. struct snd_soc_dai *aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai;
  103. struct snd_soc_dai *aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai;
  104. struct snd_soc_dai *wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai;
  105. int ret;
  106. ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
  107. if (ret != 0) {
  108. dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
  109. return ret;
  110. }
  111. ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
  112. if (ret != 0) {
  113. dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
  114. return ret;
  115. }
  116. ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
  117. if (ret != 0) {
  118. dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
  119. return ret;
  120. }
  121. ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
  122. if (ret != 0) {
  123. dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
  124. return ret;
  125. }
  126. ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
  127. ARIZONA_CLK_SRC_FLL1, SYSCLK_RATE,
  128. SND_SOC_CLOCK_IN);
  129. if (ret != 0) {
  130. dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
  131. return ret;
  132. }
  133. ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0,
  134. SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
  135. if (ret != 0) {
  136. dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
  137. return ret;
  138. }
  139. ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
  140. ARIZONA_CLK_SRC_FLL2, ASYNCCLK_RATE,
  141. SND_SOC_CLOCK_IN);
  142. if (ret != 0) {
  143. dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
  144. return ret;
  145. }
  146. ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK,
  147. 0, SYS_MCLK_RATE, 0);
  148. if (ret != 0) {
  149. dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
  150. return ret;
  151. }
  152. return 0;
  153. }
  154. static const struct snd_soc_pcm_stream baseband_params = {
  155. .formats = SNDRV_PCM_FMTBIT_S32_LE,
  156. .rate_min = 8000,
  157. .rate_max = 8000,
  158. .channels_min = 2,
  159. .channels_max = 2,
  160. };
  161. static const struct snd_soc_pcm_stream sub_params = {
  162. .formats = SNDRV_PCM_FMTBIT_S32_LE,
  163. .rate_min = SYS_AUDIO_RATE,
  164. .rate_max = SYS_AUDIO_RATE,
  165. .channels_min = 2,
  166. .channels_max = 2,
  167. };
  168. static struct snd_soc_dai_link bells_dai_wm5102[] = {
  169. {
  170. .name = "CPU-DSP",
  171. .stream_name = "CPU-DSP",
  172. .cpu_dai_name = "samsung-i2s.0",
  173. .codec_dai_name = "wm0010-sdi1",
  174. .platform_name = "samsung-audio",
  175. .codec_name = "spi0.0",
  176. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  177. | SND_SOC_DAIFMT_CBM_CFM,
  178. },
  179. {
  180. .name = "DSP-CODEC",
  181. .stream_name = "DSP-CODEC",
  182. .cpu_dai_name = "wm0010-sdi2",
  183. .codec_dai_name = "wm5102-aif1",
  184. .codec_name = "wm5102-codec",
  185. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  186. | SND_SOC_DAIFMT_CBM_CFM,
  187. .params = &sub_params,
  188. .ignore_suspend = 1,
  189. },
  190. {
  191. .name = "Baseband",
  192. .stream_name = "Baseband",
  193. .cpu_dai_name = "wm5102-aif2",
  194. .codec_dai_name = "wm1250-ev1",
  195. .codec_name = "wm1250-ev1.1-0027",
  196. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  197. | SND_SOC_DAIFMT_CBM_CFM,
  198. .ignore_suspend = 1,
  199. .params = &baseband_params,
  200. },
  201. {
  202. .name = "Sub",
  203. .stream_name = "Sub",
  204. .cpu_dai_name = "wm5102-aif3",
  205. .codec_dai_name = "wm9081-hifi",
  206. .codec_name = "wm9081.1-006c",
  207. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  208. | SND_SOC_DAIFMT_CBS_CFS,
  209. .ignore_suspend = 1,
  210. .params = &sub_params,
  211. },
  212. };
  213. static struct snd_soc_dai_link bells_dai_wm5110[] = {
  214. {
  215. .name = "CPU-DSP",
  216. .stream_name = "CPU-DSP",
  217. .cpu_dai_name = "samsung-i2s.0",
  218. .codec_dai_name = "wm0010-sdi1",
  219. .platform_name = "samsung-audio",
  220. .codec_name = "spi0.0",
  221. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  222. | SND_SOC_DAIFMT_CBM_CFM,
  223. },
  224. {
  225. .name = "DSP-CODEC",
  226. .stream_name = "DSP-CODEC",
  227. .cpu_dai_name = "wm0010-sdi2",
  228. .codec_dai_name = "wm5110-aif1",
  229. .codec_name = "wm5110-codec",
  230. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  231. | SND_SOC_DAIFMT_CBM_CFM,
  232. .params = &sub_params,
  233. .ignore_suspend = 1,
  234. },
  235. {
  236. .name = "Baseband",
  237. .stream_name = "Baseband",
  238. .cpu_dai_name = "wm5110-aif2",
  239. .codec_dai_name = "wm1250-ev1",
  240. .codec_name = "wm1250-ev1.1-0027",
  241. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  242. | SND_SOC_DAIFMT_CBM_CFM,
  243. .ignore_suspend = 1,
  244. .params = &baseband_params,
  245. },
  246. {
  247. .name = "Sub",
  248. .stream_name = "Sub",
  249. .cpu_dai_name = "wm5102-aif3",
  250. .codec_dai_name = "wm9081-hifi",
  251. .codec_name = "wm9081.1-006c",
  252. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  253. | SND_SOC_DAIFMT_CBS_CFS,
  254. .ignore_suspend = 1,
  255. .params = &sub_params,
  256. },
  257. };
  258. static struct snd_soc_codec_conf bells_codec_conf[] = {
  259. {
  260. .dev_name = "wm9081.1-006c",
  261. .name_prefix = "Sub",
  262. },
  263. };
  264. static struct snd_soc_dapm_route bells_routes[] = {
  265. { "Sub CLK_SYS", NULL, "OPCLK" },
  266. };
  267. static struct snd_soc_card bells_cards[] = {
  268. {
  269. .name = "Bells WM5102",
  270. .owner = THIS_MODULE,
  271. .dai_link = bells_dai_wm5102,
  272. .num_links = ARRAY_SIZE(bells_dai_wm5102),
  273. .codec_conf = bells_codec_conf,
  274. .num_configs = ARRAY_SIZE(bells_codec_conf),
  275. .late_probe = bells_late_probe,
  276. .dapm_routes = bells_routes,
  277. .num_dapm_routes = ARRAY_SIZE(bells_routes),
  278. .set_bias_level = bells_set_bias_level,
  279. .set_bias_level_post = bells_set_bias_level_post,
  280. },
  281. {
  282. .name = "Bells WM5110",
  283. .owner = THIS_MODULE,
  284. .dai_link = bells_dai_wm5110,
  285. .num_links = ARRAY_SIZE(bells_dai_wm5110),
  286. .codec_conf = bells_codec_conf,
  287. .num_configs = ARRAY_SIZE(bells_codec_conf),
  288. .late_probe = bells_late_probe,
  289. .dapm_routes = bells_routes,
  290. .num_dapm_routes = ARRAY_SIZE(bells_routes),
  291. .set_bias_level = bells_set_bias_level,
  292. .set_bias_level_post = bells_set_bias_level_post,
  293. },
  294. };
  295. static __devinit int bells_probe(struct platform_device *pdev)
  296. {
  297. int ret;
  298. bells_cards[pdev->id].dev = &pdev->dev;
  299. ret = snd_soc_register_card(&bells_cards[pdev->id]);
  300. if (ret) {
  301. dev_err(&pdev->dev,
  302. "snd_soc_register_card(%s) failed: %d\n",
  303. bells_cards[pdev->id].name, ret);
  304. return ret;
  305. }
  306. return 0;
  307. }
  308. static int __devexit bells_remove(struct platform_device *pdev)
  309. {
  310. snd_soc_unregister_card(&bells_cards[pdev->id]);
  311. return 0;
  312. }
  313. static struct platform_driver bells_driver = {
  314. .driver = {
  315. .name = "bells",
  316. .owner = THIS_MODULE,
  317. .pm = &snd_soc_pm_ops,
  318. },
  319. .probe = bells_probe,
  320. .remove = __devexit_p(bells_remove),
  321. };
  322. module_platform_driver(bells_driver);
  323. MODULE_DESCRIPTION("Bells audio support");
  324. MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  325. MODULE_LICENSE("GPL");
  326. MODULE_ALIAS("platform:bells");