speyside.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /*
  2. * Speyside 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/wm8996.h"
  17. #include "../codecs/wm9081.h"
  18. #define WM8996_HPSEL_GPIO 214
  19. #define MCLK_AUDIO_RATE (512 * 48000)
  20. static int speyside_set_bias_level(struct snd_soc_card *card,
  21. struct snd_soc_dapm_context *dapm,
  22. enum snd_soc_bias_level level)
  23. {
  24. struct snd_soc_pcm_runtime *rtd;
  25. struct snd_soc_dai *codec_dai;
  26. int ret;
  27. rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
  28. codec_dai = rtd->codec_dai;
  29. if (dapm->dev != codec_dai->dev)
  30. return 0;
  31. switch (level) {
  32. case SND_SOC_BIAS_STANDBY:
  33. ret = snd_soc_dai_set_sysclk(codec_dai, WM8996_SYSCLK_MCLK2,
  34. 32768, SND_SOC_CLOCK_IN);
  35. if (ret < 0)
  36. return ret;
  37. ret = snd_soc_dai_set_pll(codec_dai, WM8996_FLL_MCLK2,
  38. 0, 0, 0);
  39. if (ret < 0) {
  40. pr_err("Failed to stop FLL\n");
  41. return ret;
  42. }
  43. break;
  44. default:
  45. break;
  46. }
  47. return 0;
  48. }
  49. static int speyside_set_bias_level_post(struct snd_soc_card *card,
  50. struct snd_soc_dapm_context *dapm,
  51. enum snd_soc_bias_level level)
  52. {
  53. struct snd_soc_pcm_runtime *rtd;
  54. struct snd_soc_dai *codec_dai;
  55. int ret;
  56. rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
  57. codec_dai = rtd->codec_dai;
  58. if (dapm->dev != codec_dai->dev)
  59. return 0;
  60. switch (level) {
  61. case SND_SOC_BIAS_PREPARE:
  62. if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
  63. ret = snd_soc_dai_set_pll(codec_dai, 0,
  64. WM8996_FLL_MCLK2,
  65. 32768, MCLK_AUDIO_RATE);
  66. if (ret < 0) {
  67. pr_err("Failed to start FLL\n");
  68. return ret;
  69. }
  70. ret = snd_soc_dai_set_sysclk(codec_dai,
  71. WM8996_SYSCLK_FLL,
  72. MCLK_AUDIO_RATE,
  73. SND_SOC_CLOCK_IN);
  74. if (ret < 0)
  75. return ret;
  76. }
  77. break;
  78. default:
  79. break;
  80. }
  81. card->dapm.bias_level = level;
  82. return 0;
  83. }
  84. static struct snd_soc_jack speyside_headset;
  85. /* Headset jack detection DAPM pins */
  86. static struct snd_soc_jack_pin speyside_headset_pins[] = {
  87. {
  88. .pin = "Headset Mic",
  89. .mask = SND_JACK_MICROPHONE,
  90. },
  91. };
  92. /* Default the headphone selection to active high */
  93. static int speyside_jack_polarity;
  94. static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
  95. struct snd_soc_dapm_widget *sink)
  96. {
  97. if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
  98. return 1;
  99. if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
  100. return 1;
  101. return 0;
  102. }
  103. static void speyside_set_polarity(struct snd_soc_codec *codec,
  104. int polarity)
  105. {
  106. speyside_jack_polarity = !polarity;
  107. gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
  108. /* Re-run DAPM to make sure we're using the correct mic bias */
  109. snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
  110. }
  111. static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd)
  112. {
  113. struct snd_soc_dai *dai = rtd->codec_dai;
  114. int ret;
  115. ret = snd_soc_dai_set_sysclk(dai, 0, MCLK_AUDIO_RATE, 0);
  116. if (ret < 0)
  117. return ret;
  118. return 0;
  119. }
  120. static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd)
  121. {
  122. struct snd_soc_dai *dai = rtd->codec_dai;
  123. struct snd_soc_codec *codec = rtd->codec;
  124. int ret;
  125. ret = snd_soc_dai_set_sysclk(dai, WM8996_SYSCLK_MCLK2, 32768, 0);
  126. if (ret < 0)
  127. return ret;
  128. ret = gpio_request(WM8996_HPSEL_GPIO, "HP_SEL");
  129. if (ret != 0)
  130. pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
  131. gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
  132. ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
  133. SND_JACK_HEADSET | SND_JACK_BTN_0,
  134. &speyside_headset, speyside_headset_pins,
  135. ARRAY_SIZE(speyside_headset_pins));
  136. if (ret)
  137. return ret;
  138. wm8996_detect(codec, &speyside_headset, speyside_set_polarity);
  139. return 0;
  140. }
  141. static int speyside_late_probe(struct snd_soc_card *card)
  142. {
  143. snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
  144. snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
  145. snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC");
  146. snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC");
  147. snd_soc_dapm_ignore_suspend(&card->dapm, "Main Speaker");
  148. snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output");
  149. snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input");
  150. return 0;
  151. }
  152. static const struct snd_soc_pcm_stream dsp_codec_params = {
  153. .formats = SNDRV_PCM_FMTBIT_S32_LE,
  154. .rate_min = 48000,
  155. .rate_max = 48000,
  156. .channels_min = 2,
  157. .channels_max = 2,
  158. };
  159. static struct snd_soc_dai_link speyside_dai[] = {
  160. {
  161. .name = "CPU-DSP",
  162. .stream_name = "CPU-DSP",
  163. .cpu_dai_name = "samsung-i2s.0",
  164. .codec_dai_name = "wm0010-sdi1",
  165. .platform_name = "samsung-i2s.0",
  166. .codec_name = "spi0.0",
  167. .init = speyside_wm0010_init,
  168. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  169. | SND_SOC_DAIFMT_CBM_CFM,
  170. },
  171. {
  172. .name = "DSP-CODEC",
  173. .stream_name = "DSP-CODEC",
  174. .cpu_dai_name = "wm0010-sdi2",
  175. .codec_dai_name = "wm8996-aif1",
  176. .codec_name = "wm8996.1-001a",
  177. .init = speyside_wm8996_init,
  178. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  179. | SND_SOC_DAIFMT_CBM_CFM,
  180. .params = &dsp_codec_params,
  181. .ignore_suspend = 1,
  182. },
  183. {
  184. .name = "Baseband",
  185. .stream_name = "Baseband",
  186. .cpu_dai_name = "wm8996-aif2",
  187. .codec_dai_name = "wm1250-ev1",
  188. .codec_name = "wm1250-ev1.1-0027",
  189. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
  190. | SND_SOC_DAIFMT_CBM_CFM,
  191. .ignore_suspend = 1,
  192. },
  193. };
  194. static int speyside_wm9081_init(struct snd_soc_component *component)
  195. {
  196. struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
  197. /* At any time the WM9081 is active it will have this clock */
  198. return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0,
  199. MCLK_AUDIO_RATE, 0);
  200. }
  201. static struct snd_soc_aux_dev speyside_aux_dev[] = {
  202. {
  203. .name = "wm9081",
  204. .codec_name = "wm9081.1-006c",
  205. .init = speyside_wm9081_init,
  206. },
  207. };
  208. static struct snd_soc_codec_conf speyside_codec_conf[] = {
  209. {
  210. .dev_name = "wm9081.1-006c",
  211. .name_prefix = "Sub",
  212. },
  213. };
  214. static const struct snd_kcontrol_new controls[] = {
  215. SOC_DAPM_PIN_SWITCH("Main Speaker"),
  216. SOC_DAPM_PIN_SWITCH("Main DMIC"),
  217. SOC_DAPM_PIN_SWITCH("Main AMIC"),
  218. SOC_DAPM_PIN_SWITCH("WM1250 Input"),
  219. SOC_DAPM_PIN_SWITCH("WM1250 Output"),
  220. SOC_DAPM_PIN_SWITCH("Headphone"),
  221. };
  222. static struct snd_soc_dapm_widget widgets[] = {
  223. SND_SOC_DAPM_HP("Headphone", NULL),
  224. SND_SOC_DAPM_MIC("Headset Mic", NULL),
  225. SND_SOC_DAPM_SPK("Main Speaker", NULL),
  226. SND_SOC_DAPM_MIC("Main AMIC", NULL),
  227. SND_SOC_DAPM_MIC("Main DMIC", NULL),
  228. };
  229. static struct snd_soc_dapm_route audio_paths[] = {
  230. { "IN1RN", NULL, "MICB1" },
  231. { "IN1RP", NULL, "MICB1" },
  232. { "IN1RN", NULL, "MICB2" },
  233. { "IN1RP", NULL, "MICB2" },
  234. { "MICB1", NULL, "Headset Mic", speyside_get_micbias },
  235. { "MICB2", NULL, "Headset Mic", speyside_get_micbias },
  236. { "IN1LP", NULL, "MICB2" },
  237. { "IN1RN", NULL, "MICB1" },
  238. { "MICB2", NULL, "Main AMIC" },
  239. { "DMIC1DAT", NULL, "MICB1" },
  240. { "DMIC2DAT", NULL, "MICB1" },
  241. { "MICB1", NULL, "Main DMIC" },
  242. { "Headphone", NULL, "HPOUT1L" },
  243. { "Headphone", NULL, "HPOUT1R" },
  244. { "Sub IN1", NULL, "HPOUT2L" },
  245. { "Sub IN2", NULL, "HPOUT2R" },
  246. { "Main Speaker", NULL, "Sub SPKN" },
  247. { "Main Speaker", NULL, "Sub SPKP" },
  248. { "Main Speaker", NULL, "SPKDAT" },
  249. };
  250. static struct snd_soc_card speyside = {
  251. .name = "Speyside",
  252. .owner = THIS_MODULE,
  253. .dai_link = speyside_dai,
  254. .num_links = ARRAY_SIZE(speyside_dai),
  255. .aux_dev = speyside_aux_dev,
  256. .num_aux_devs = ARRAY_SIZE(speyside_aux_dev),
  257. .codec_conf = speyside_codec_conf,
  258. .num_configs = ARRAY_SIZE(speyside_codec_conf),
  259. .set_bias_level = speyside_set_bias_level,
  260. .set_bias_level_post = speyside_set_bias_level_post,
  261. .controls = controls,
  262. .num_controls = ARRAY_SIZE(controls),
  263. .dapm_widgets = widgets,
  264. .num_dapm_widgets = ARRAY_SIZE(widgets),
  265. .dapm_routes = audio_paths,
  266. .num_dapm_routes = ARRAY_SIZE(audio_paths),
  267. .fully_routed = true,
  268. .late_probe = speyside_late_probe,
  269. };
  270. static int speyside_probe(struct platform_device *pdev)
  271. {
  272. struct snd_soc_card *card = &speyside;
  273. int ret;
  274. card->dev = &pdev->dev;
  275. ret = devm_snd_soc_register_card(&pdev->dev, card);
  276. if (ret)
  277. dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
  278. ret);
  279. return ret;
  280. }
  281. static struct platform_driver speyside_driver = {
  282. .driver = {
  283. .name = "speyside",
  284. .pm = &snd_soc_pm_ops,
  285. },
  286. .probe = speyside_probe,
  287. };
  288. module_platform_driver(speyside_driver);
  289. MODULE_DESCRIPTION("Speyside audio support");
  290. MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  291. MODULE_LICENSE("GPL");
  292. MODULE_ALIAS("platform:speyside");