wm8958-dsp2.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. * wm8958-dsp2.c -- WM8958 DSP2 support
  3. *
  4. * Copyright 2011 Wolfson Microelectronics plc
  5. *
  6. * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #include <linux/module.h>
  13. #include <linux/moduleparam.h>
  14. #include <linux/init.h>
  15. #include <linux/delay.h>
  16. #include <linux/pm.h>
  17. #include <linux/i2c.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/slab.h>
  20. #include <sound/soc.h>
  21. #include <sound/initval.h>
  22. #include <sound/tlv.h>
  23. #include <trace/events/asoc.h>
  24. #include <linux/mfd/wm8994/core.h>
  25. #include <linux/mfd/wm8994/registers.h>
  26. #include <linux/mfd/wm8994/pdata.h>
  27. #include <linux/mfd/wm8994/gpio.h>
  28. #include "wm8994.h"
  29. static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
  30. {
  31. struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
  32. struct wm8994_pdata *pdata = wm8994->pdata;
  33. int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
  34. int ena, reg, aif, i;
  35. switch (mbc) {
  36. case 0:
  37. pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
  38. aif = 0;
  39. break;
  40. case 1:
  41. pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
  42. aif = 0;
  43. break;
  44. case 2:
  45. pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
  46. aif = 1;
  47. break;
  48. default:
  49. BUG();
  50. return;
  51. }
  52. /* We can only enable the MBC if the AIF is enabled and we
  53. * want it to be enabled. */
  54. ena = pwr_reg && wm8994->mbc_ena[mbc];
  55. reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
  56. dev_dbg(codec->dev, "MBC %d startup: %d, power: %x, DSP: %x\n",
  57. mbc, start, pwr_reg, reg);
  58. if (start && ena) {
  59. /* If the DSP is already running then noop */
  60. if (reg & WM8958_DSP2_ENA)
  61. return;
  62. /* If neither AIFnCLK is not yet enabled postpone */
  63. if (!(snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
  64. & WM8994_AIF1CLK_ENA_MASK) &&
  65. !(snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
  66. & WM8994_AIF2CLK_ENA_MASK))
  67. return;
  68. /* Switch the clock over to the appropriate AIF */
  69. snd_soc_update_bits(codec, WM8994_CLOCKING_1,
  70. WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
  71. aif << WM8958_DSP2CLK_SRC_SHIFT |
  72. WM8958_DSP2CLK_ENA);
  73. snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
  74. WM8958_DSP2_ENA, WM8958_DSP2_ENA);
  75. /* If we've got user supplied MBC settings use them */
  76. if (pdata && pdata->num_mbc_cfgs) {
  77. struct wm8958_mbc_cfg *cfg
  78. = &pdata->mbc_cfgs[wm8994->mbc_cfg];
  79. for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
  80. snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
  81. cfg->coeff_regs[i]);
  82. for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
  83. snd_soc_write(codec,
  84. i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
  85. cfg->cutoff_regs[i]);
  86. }
  87. /* Run the DSP */
  88. snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
  89. WM8958_DSP2_RUNR);
  90. /* And we're off! */
  91. snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
  92. WM8958_MBC_ENA | WM8958_MBC_SEL_MASK,
  93. mbc << WM8958_MBC_SEL_SHIFT |
  94. WM8958_MBC_ENA);
  95. } else {
  96. /* If the DSP is already stopped then noop */
  97. if (!(reg & WM8958_DSP2_ENA))
  98. return;
  99. snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
  100. WM8958_MBC_ENA, 0);
  101. snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
  102. WM8958_DSP2_ENA, 0);
  103. snd_soc_update_bits(codec, WM8994_CLOCKING_1,
  104. WM8958_DSP2CLK_ENA, 0);
  105. }
  106. }
  107. int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
  108. struct snd_kcontrol *kcontrol, int event)
  109. {
  110. struct snd_soc_codec *codec = w->codec;
  111. int i;
  112. switch (event) {
  113. case SND_SOC_DAPM_POST_PMU:
  114. case SND_SOC_DAPM_PRE_PMU:
  115. for (i = 0; i < 3; i++)
  116. wm8958_mbc_apply(codec, i, 1);
  117. break;
  118. case SND_SOC_DAPM_POST_PMD:
  119. case SND_SOC_DAPM_PRE_PMD:
  120. for (i = 0; i < 3; i++)
  121. wm8958_mbc_apply(codec, i, 0);
  122. break;
  123. }
  124. return 0;
  125. }
  126. static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
  127. struct snd_ctl_elem_value *ucontrol)
  128. {
  129. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  130. struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
  131. struct wm8994_pdata *pdata = wm8994->pdata;
  132. int value = ucontrol->value.integer.value[0];
  133. int reg;
  134. /* Don't allow on the fly reconfiguration */
  135. reg = snd_soc_read(codec, WM8994_CLOCKING_1);
  136. if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
  137. return -EBUSY;
  138. if (value >= pdata->num_mbc_cfgs)
  139. return -EINVAL;
  140. wm8994->mbc_cfg = value;
  141. return 0;
  142. }
  143. static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
  144. struct snd_ctl_elem_value *ucontrol)
  145. {
  146. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  147. struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
  148. ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
  149. return 0;
  150. }
  151. static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
  152. struct snd_ctl_elem_info *uinfo)
  153. {
  154. uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  155. uinfo->count = 1;
  156. uinfo->value.integer.min = 0;
  157. uinfo->value.integer.max = 1;
  158. return 0;
  159. }
  160. static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
  161. struct snd_ctl_elem_value *ucontrol)
  162. {
  163. int mbc = kcontrol->private_value;
  164. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  165. struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
  166. ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
  167. return 0;
  168. }
  169. static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
  170. struct snd_ctl_elem_value *ucontrol)
  171. {
  172. int mbc = kcontrol->private_value;
  173. int i;
  174. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  175. struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
  176. if (ucontrol->value.integer.value[0] > 1)
  177. return -EINVAL;
  178. for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
  179. if (mbc != i && wm8994->mbc_ena[i]) {
  180. dev_dbg(codec->dev, "MBC %d active already\n", mbc);
  181. return -EBUSY;
  182. }
  183. }
  184. wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
  185. wm8958_mbc_apply(codec, mbc, wm8994->mbc_ena[mbc]);
  186. return 0;
  187. }
  188. #define WM8958_MBC_SWITCH(xname, xval) {\
  189. .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
  190. .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
  191. .info = wm8958_mbc_info, \
  192. .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
  193. .private_value = xval }
  194. static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
  195. WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
  196. WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
  197. WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
  198. };
  199. void wm8958_dsp2_init(struct snd_soc_codec *codec)
  200. {
  201. struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
  202. struct wm8994_pdata *pdata = wm8994->pdata;
  203. int ret, i;
  204. snd_soc_add_controls(codec, wm8958_mbc_snd_controls,
  205. ARRAY_SIZE(wm8958_mbc_snd_controls));
  206. if (!pdata)
  207. return;
  208. if (pdata->num_mbc_cfgs) {
  209. struct snd_kcontrol_new control[] = {
  210. SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
  211. wm8958_get_mbc_enum, wm8958_put_mbc_enum),
  212. };
  213. /* We need an array of texts for the enum API */
  214. wm8994->mbc_texts = kmalloc(sizeof(char *)
  215. * pdata->num_mbc_cfgs, GFP_KERNEL);
  216. if (!wm8994->mbc_texts) {
  217. dev_err(wm8994->codec->dev,
  218. "Failed to allocate %d MBC config texts\n",
  219. pdata->num_mbc_cfgs);
  220. return;
  221. }
  222. for (i = 0; i < pdata->num_mbc_cfgs; i++)
  223. wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
  224. wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
  225. wm8994->mbc_enum.texts = wm8994->mbc_texts;
  226. ret = snd_soc_add_controls(wm8994->codec, control, 1);
  227. if (ret != 0)
  228. dev_err(wm8994->codec->dev,
  229. "Failed to add MBC mode controls: %d\n", ret);
  230. }
  231. }