wm8770.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * wm8770.c -- WM8770 ALSA SoC Audio driver
  4. *
  5. * Copyright 2010 Wolfson Microelectronics plc
  6. *
  7. * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
  8. */
  9. #include <linux/module.h>
  10. #include <linux/moduleparam.h>
  11. #include <linux/init.h>
  12. #include <linux/delay.h>
  13. #include <linux/of_device.h>
  14. #include <linux/pm.h>
  15. #include <linux/spi/spi.h>
  16. #include <linux/regmap.h>
  17. #include <linux/regulator/consumer.h>
  18. #include <linux/slab.h>
  19. #include <sound/core.h>
  20. #include <sound/pcm.h>
  21. #include <sound/pcm_params.h>
  22. #include <sound/soc.h>
  23. #include <sound/initval.h>
  24. #include <sound/tlv.h>
  25. #include "wm8770.h"
  26. #define WM8770_NUM_SUPPLIES 3
  27. static const char *wm8770_supply_names[WM8770_NUM_SUPPLIES] = {
  28. "AVDD1",
  29. "AVDD2",
  30. "DVDD"
  31. };
  32. static const struct reg_default wm8770_reg_defaults[] = {
  33. { 0, 0x7f },
  34. { 1, 0x7f },
  35. { 2, 0x7f },
  36. { 3, 0x7f },
  37. { 4, 0x7f },
  38. { 5, 0x7f },
  39. { 6, 0x7f },
  40. { 7, 0x7f },
  41. { 8, 0x7f },
  42. { 9, 0xff },
  43. { 10, 0xff },
  44. { 11, 0xff },
  45. { 12, 0xff },
  46. { 13, 0xff },
  47. { 14, 0xff },
  48. { 15, 0xff },
  49. { 16, 0xff },
  50. { 17, 0xff },
  51. { 18, 0 },
  52. { 19, 0x90 },
  53. { 20, 0 },
  54. { 21, 0 },
  55. { 22, 0x22 },
  56. { 23, 0x22 },
  57. { 24, 0x3e },
  58. { 25, 0xc },
  59. { 26, 0xc },
  60. { 27, 0x100 },
  61. { 28, 0x189 },
  62. { 29, 0x189 },
  63. { 30, 0x8770 },
  64. };
  65. static bool wm8770_volatile_reg(struct device *dev, unsigned int reg)
  66. {
  67. switch (reg) {
  68. case WM8770_RESET:
  69. return true;
  70. default:
  71. return false;
  72. }
  73. }
  74. struct wm8770_priv {
  75. struct regmap *regmap;
  76. struct regulator_bulk_data supplies[WM8770_NUM_SUPPLIES];
  77. struct notifier_block disable_nb[WM8770_NUM_SUPPLIES];
  78. struct snd_soc_component *component;
  79. int sysclk;
  80. };
  81. static int vout12supply_event(struct snd_soc_dapm_widget *w,
  82. struct snd_kcontrol *kcontrol, int event);
  83. static int vout34supply_event(struct snd_soc_dapm_widget *w,
  84. struct snd_kcontrol *kcontrol, int event);
  85. /*
  86. * We can't use the same notifier block for more than one supply and
  87. * there's no way I can see to get from a callback to the caller
  88. * except container_of().
  89. */
  90. #define WM8770_REGULATOR_EVENT(n) \
  91. static int wm8770_regulator_event_##n(struct notifier_block *nb, \
  92. unsigned long event, void *data) \
  93. { \
  94. struct wm8770_priv *wm8770 = container_of(nb, struct wm8770_priv, \
  95. disable_nb[n]); \
  96. if (event & REGULATOR_EVENT_DISABLE) { \
  97. regcache_mark_dirty(wm8770->regmap); \
  98. } \
  99. return 0; \
  100. }
  101. WM8770_REGULATOR_EVENT(0)
  102. WM8770_REGULATOR_EVENT(1)
  103. WM8770_REGULATOR_EVENT(2)
  104. static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0);
  105. static const DECLARE_TLV_DB_SCALE(dac_dig_tlv, -12750, 50, 1);
  106. static const DECLARE_TLV_DB_SCALE(dac_alg_tlv, -12700, 100, 1);
  107. static const char *dac_phase_text[][2] = {
  108. { "DAC1 Normal", "DAC1 Inverted" },
  109. { "DAC2 Normal", "DAC2 Inverted" },
  110. { "DAC3 Normal", "DAC3 Inverted" },
  111. { "DAC4 Normal", "DAC4 Inverted" },
  112. };
  113. static const struct soc_enum dac_phase[] = {
  114. SOC_ENUM_DOUBLE(WM8770_DACPHASE, 0, 1, 2, dac_phase_text[0]),
  115. SOC_ENUM_DOUBLE(WM8770_DACPHASE, 2, 3, 2, dac_phase_text[1]),
  116. SOC_ENUM_DOUBLE(WM8770_DACPHASE, 4, 5, 2, dac_phase_text[2]),
  117. SOC_ENUM_DOUBLE(WM8770_DACPHASE, 6, 7, 2, dac_phase_text[3]),
  118. };
  119. static const struct snd_kcontrol_new wm8770_snd_controls[] = {
  120. /* global DAC playback controls */
  121. SOC_SINGLE_TLV("DAC Playback Volume", WM8770_MSDIGVOL, 0, 255, 0,
  122. dac_dig_tlv),
  123. SOC_SINGLE("DAC Playback Switch", WM8770_DACMUTE, 4, 1, 1),
  124. SOC_SINGLE("DAC Playback ZC Switch", WM8770_DACCTRL1, 0, 1, 0),
  125. /* global VOUT playback controls */
  126. SOC_SINGLE_TLV("VOUT Playback Volume", WM8770_MSALGVOL, 0, 127, 0,
  127. dac_alg_tlv),
  128. SOC_SINGLE("VOUT Playback ZC Switch", WM8770_MSALGVOL, 7, 1, 0),
  129. /* VOUT1/2/3/4 specific controls */
  130. SOC_DOUBLE_R_TLV("VOUT1 Playback Volume", WM8770_VOUT1LVOL,
  131. WM8770_VOUT1RVOL, 0, 127, 0, dac_alg_tlv),
  132. SOC_DOUBLE_R("VOUT1 Playback ZC Switch", WM8770_VOUT1LVOL,
  133. WM8770_VOUT1RVOL, 7, 1, 0),
  134. SOC_DOUBLE_R_TLV("VOUT2 Playback Volume", WM8770_VOUT2LVOL,
  135. WM8770_VOUT2RVOL, 0, 127, 0, dac_alg_tlv),
  136. SOC_DOUBLE_R("VOUT2 Playback ZC Switch", WM8770_VOUT2LVOL,
  137. WM8770_VOUT2RVOL, 7, 1, 0),
  138. SOC_DOUBLE_R_TLV("VOUT3 Playback Volume", WM8770_VOUT3LVOL,
  139. WM8770_VOUT3RVOL, 0, 127, 0, dac_alg_tlv),
  140. SOC_DOUBLE_R("VOUT3 Playback ZC Switch", WM8770_VOUT3LVOL,
  141. WM8770_VOUT3RVOL, 7, 1, 0),
  142. SOC_DOUBLE_R_TLV("VOUT4 Playback Volume", WM8770_VOUT4LVOL,
  143. WM8770_VOUT4RVOL, 0, 127, 0, dac_alg_tlv),
  144. SOC_DOUBLE_R("VOUT4 Playback ZC Switch", WM8770_VOUT4LVOL,
  145. WM8770_VOUT4RVOL, 7, 1, 0),
  146. /* DAC1/2/3/4 specific controls */
  147. SOC_DOUBLE_R_TLV("DAC1 Playback Volume", WM8770_DAC1LVOL,
  148. WM8770_DAC1RVOL, 0, 255, 0, dac_dig_tlv),
  149. SOC_SINGLE("DAC1 Deemphasis Switch", WM8770_DACCTRL2, 0, 1, 0),
  150. SOC_ENUM("DAC1 Phase", dac_phase[0]),
  151. SOC_DOUBLE_R_TLV("DAC2 Playback Volume", WM8770_DAC2LVOL,
  152. WM8770_DAC2RVOL, 0, 255, 0, dac_dig_tlv),
  153. SOC_SINGLE("DAC2 Deemphasis Switch", WM8770_DACCTRL2, 1, 1, 0),
  154. SOC_ENUM("DAC2 Phase", dac_phase[1]),
  155. SOC_DOUBLE_R_TLV("DAC3 Playback Volume", WM8770_DAC3LVOL,
  156. WM8770_DAC3RVOL, 0, 255, 0, dac_dig_tlv),
  157. SOC_SINGLE("DAC3 Deemphasis Switch", WM8770_DACCTRL2, 2, 1, 0),
  158. SOC_ENUM("DAC3 Phase", dac_phase[2]),
  159. SOC_DOUBLE_R_TLV("DAC4 Playback Volume", WM8770_DAC4LVOL,
  160. WM8770_DAC4RVOL, 0, 255, 0, dac_dig_tlv),
  161. SOC_SINGLE("DAC4 Deemphasis Switch", WM8770_DACCTRL2, 3, 1, 0),
  162. SOC_ENUM("DAC4 Phase", dac_phase[3]),
  163. /* ADC specific controls */
  164. SOC_DOUBLE_R_TLV("Capture Volume", WM8770_ADCLCTRL, WM8770_ADCRCTRL,
  165. 0, 31, 0, adc_tlv),
  166. SOC_DOUBLE_R("Capture Switch", WM8770_ADCLCTRL, WM8770_ADCRCTRL,
  167. 5, 1, 1),
  168. /* other controls */
  169. SOC_SINGLE("ADC 128x Oversampling Switch", WM8770_MSTRCTRL, 3, 1, 0),
  170. SOC_SINGLE("ADC Highpass Filter Switch", WM8770_IFACECTRL, 8, 1, 1)
  171. };
  172. static const char *ain_text[] = {
  173. "AIN1", "AIN2", "AIN3", "AIN4",
  174. "AIN5", "AIN6", "AIN7", "AIN8"
  175. };
  176. static SOC_ENUM_DOUBLE_DECL(ain_enum,
  177. WM8770_ADCMUX, 0, 4, ain_text);
  178. static const struct snd_kcontrol_new ain_mux =
  179. SOC_DAPM_ENUM("Capture Mux", ain_enum);
  180. static const struct snd_kcontrol_new vout1_mix_controls[] = {
  181. SOC_DAPM_SINGLE("DAC1 Switch", WM8770_OUTMUX1, 0, 1, 0),
  182. SOC_DAPM_SINGLE("AUX1 Switch", WM8770_OUTMUX1, 1, 1, 0),
  183. SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 2, 1, 0)
  184. };
  185. static const struct snd_kcontrol_new vout2_mix_controls[] = {
  186. SOC_DAPM_SINGLE("DAC2 Switch", WM8770_OUTMUX1, 3, 1, 0),
  187. SOC_DAPM_SINGLE("AUX2 Switch", WM8770_OUTMUX1, 4, 1, 0),
  188. SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 5, 1, 0)
  189. };
  190. static const struct snd_kcontrol_new vout3_mix_controls[] = {
  191. SOC_DAPM_SINGLE("DAC3 Switch", WM8770_OUTMUX2, 0, 1, 0),
  192. SOC_DAPM_SINGLE("AUX3 Switch", WM8770_OUTMUX2, 1, 1, 0),
  193. SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 2, 1, 0)
  194. };
  195. static const struct snd_kcontrol_new vout4_mix_controls[] = {
  196. SOC_DAPM_SINGLE("DAC4 Switch", WM8770_OUTMUX2, 3, 1, 0),
  197. SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 4, 1, 0)
  198. };
  199. static const struct snd_soc_dapm_widget wm8770_dapm_widgets[] = {
  200. SND_SOC_DAPM_INPUT("AUX1"),
  201. SND_SOC_DAPM_INPUT("AUX2"),
  202. SND_SOC_DAPM_INPUT("AUX3"),
  203. SND_SOC_DAPM_INPUT("AIN1"),
  204. SND_SOC_DAPM_INPUT("AIN2"),
  205. SND_SOC_DAPM_INPUT("AIN3"),
  206. SND_SOC_DAPM_INPUT("AIN4"),
  207. SND_SOC_DAPM_INPUT("AIN5"),
  208. SND_SOC_DAPM_INPUT("AIN6"),
  209. SND_SOC_DAPM_INPUT("AIN7"),
  210. SND_SOC_DAPM_INPUT("AIN8"),
  211. SND_SOC_DAPM_MUX("Capture Mux", WM8770_ADCMUX, 8, 1, &ain_mux),
  212. SND_SOC_DAPM_ADC("ADC", "Capture", WM8770_PWDNCTRL, 1, 1),
  213. SND_SOC_DAPM_DAC("DAC1", "Playback", WM8770_PWDNCTRL, 2, 1),
  214. SND_SOC_DAPM_DAC("DAC2", "Playback", WM8770_PWDNCTRL, 3, 1),
  215. SND_SOC_DAPM_DAC("DAC3", "Playback", WM8770_PWDNCTRL, 4, 1),
  216. SND_SOC_DAPM_DAC("DAC4", "Playback", WM8770_PWDNCTRL, 5, 1),
  217. SND_SOC_DAPM_SUPPLY("VOUT12 Supply", SND_SOC_NOPM, 0, 0,
  218. vout12supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
  219. SND_SOC_DAPM_SUPPLY("VOUT34 Supply", SND_SOC_NOPM, 0, 0,
  220. vout34supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
  221. SND_SOC_DAPM_MIXER("VOUT1 Mixer", SND_SOC_NOPM, 0, 0,
  222. vout1_mix_controls, ARRAY_SIZE(vout1_mix_controls)),
  223. SND_SOC_DAPM_MIXER("VOUT2 Mixer", SND_SOC_NOPM, 0, 0,
  224. vout2_mix_controls, ARRAY_SIZE(vout2_mix_controls)),
  225. SND_SOC_DAPM_MIXER("VOUT3 Mixer", SND_SOC_NOPM, 0, 0,
  226. vout3_mix_controls, ARRAY_SIZE(vout3_mix_controls)),
  227. SND_SOC_DAPM_MIXER("VOUT4 Mixer", SND_SOC_NOPM, 0, 0,
  228. vout4_mix_controls, ARRAY_SIZE(vout4_mix_controls)),
  229. SND_SOC_DAPM_OUTPUT("VOUT1"),
  230. SND_SOC_DAPM_OUTPUT("VOUT2"),
  231. SND_SOC_DAPM_OUTPUT("VOUT3"),
  232. SND_SOC_DAPM_OUTPUT("VOUT4")
  233. };
  234. static const struct snd_soc_dapm_route wm8770_intercon[] = {
  235. { "Capture Mux", "AIN1", "AIN1" },
  236. { "Capture Mux", "AIN2", "AIN2" },
  237. { "Capture Mux", "AIN3", "AIN3" },
  238. { "Capture Mux", "AIN4", "AIN4" },
  239. { "Capture Mux", "AIN5", "AIN5" },
  240. { "Capture Mux", "AIN6", "AIN6" },
  241. { "Capture Mux", "AIN7", "AIN7" },
  242. { "Capture Mux", "AIN8", "AIN8" },
  243. { "ADC", NULL, "Capture Mux" },
  244. { "VOUT1 Mixer", NULL, "VOUT12 Supply" },
  245. { "VOUT1 Mixer", "DAC1 Switch", "DAC1" },
  246. { "VOUT1 Mixer", "AUX1 Switch", "AUX1" },
  247. { "VOUT1 Mixer", "Bypass Switch", "Capture Mux" },
  248. { "VOUT2 Mixer", NULL, "VOUT12 Supply" },
  249. { "VOUT2 Mixer", "DAC2 Switch", "DAC2" },
  250. { "VOUT2 Mixer", "AUX2 Switch", "AUX2" },
  251. { "VOUT2 Mixer", "Bypass Switch", "Capture Mux" },
  252. { "VOUT3 Mixer", NULL, "VOUT34 Supply" },
  253. { "VOUT3 Mixer", "DAC3 Switch", "DAC3" },
  254. { "VOUT3 Mixer", "AUX3 Switch", "AUX3" },
  255. { "VOUT3 Mixer", "Bypass Switch", "Capture Mux" },
  256. { "VOUT4 Mixer", NULL, "VOUT34 Supply" },
  257. { "VOUT4 Mixer", "DAC4 Switch", "DAC4" },
  258. { "VOUT4 Mixer", "Bypass Switch", "Capture Mux" },
  259. { "VOUT1", NULL, "VOUT1 Mixer" },
  260. { "VOUT2", NULL, "VOUT2 Mixer" },
  261. { "VOUT3", NULL, "VOUT3 Mixer" },
  262. { "VOUT4", NULL, "VOUT4 Mixer" }
  263. };
  264. static int vout12supply_event(struct snd_soc_dapm_widget *w,
  265. struct snd_kcontrol *kcontrol, int event)
  266. {
  267. struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
  268. switch (event) {
  269. case SND_SOC_DAPM_PRE_PMU:
  270. snd_soc_component_update_bits(component, WM8770_OUTMUX1, 0x180, 0);
  271. break;
  272. case SND_SOC_DAPM_POST_PMD:
  273. snd_soc_component_update_bits(component, WM8770_OUTMUX1, 0x180, 0x180);
  274. break;
  275. }
  276. return 0;
  277. }
  278. static int vout34supply_event(struct snd_soc_dapm_widget *w,
  279. struct snd_kcontrol *kcontrol, int event)
  280. {
  281. struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
  282. switch (event) {
  283. case SND_SOC_DAPM_PRE_PMU:
  284. snd_soc_component_update_bits(component, WM8770_OUTMUX2, 0x180, 0);
  285. break;
  286. case SND_SOC_DAPM_POST_PMD:
  287. snd_soc_component_update_bits(component, WM8770_OUTMUX2, 0x180, 0x180);
  288. break;
  289. }
  290. return 0;
  291. }
  292. static int wm8770_reset(struct snd_soc_component *component)
  293. {
  294. return snd_soc_component_write(component, WM8770_RESET, 0);
  295. }
  296. static int wm8770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
  297. {
  298. struct snd_soc_component *component;
  299. int iface, master;
  300. component = dai->component;
  301. switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
  302. case SND_SOC_DAIFMT_CBM_CFM:
  303. master = 0x100;
  304. break;
  305. case SND_SOC_DAIFMT_CBS_CFS:
  306. master = 0;
  307. break;
  308. default:
  309. return -EINVAL;
  310. }
  311. iface = 0;
  312. switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  313. case SND_SOC_DAIFMT_I2S:
  314. iface |= 0x2;
  315. break;
  316. case SND_SOC_DAIFMT_RIGHT_J:
  317. break;
  318. case SND_SOC_DAIFMT_LEFT_J:
  319. iface |= 0x1;
  320. break;
  321. default:
  322. return -EINVAL;
  323. }
  324. switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
  325. case SND_SOC_DAIFMT_NB_NF:
  326. break;
  327. case SND_SOC_DAIFMT_IB_IF:
  328. iface |= 0xc;
  329. break;
  330. case SND_SOC_DAIFMT_IB_NF:
  331. iface |= 0x8;
  332. break;
  333. case SND_SOC_DAIFMT_NB_IF:
  334. iface |= 0x4;
  335. break;
  336. default:
  337. return -EINVAL;
  338. }
  339. snd_soc_component_update_bits(component, WM8770_IFACECTRL, 0xf, iface);
  340. snd_soc_component_update_bits(component, WM8770_MSTRCTRL, 0x100, master);
  341. return 0;
  342. }
  343. static const int mclk_ratios[] = {
  344. 128,
  345. 192,
  346. 256,
  347. 384,
  348. 512,
  349. 768
  350. };
  351. static int wm8770_hw_params(struct snd_pcm_substream *substream,
  352. struct snd_pcm_hw_params *params,
  353. struct snd_soc_dai *dai)
  354. {
  355. struct snd_soc_component *component;
  356. struct wm8770_priv *wm8770;
  357. int i;
  358. int iface;
  359. int shift;
  360. int ratio;
  361. component = dai->component;
  362. wm8770 = snd_soc_component_get_drvdata(component);
  363. iface = 0;
  364. switch (params_width(params)) {
  365. case 16:
  366. break;
  367. case 20:
  368. iface |= 0x10;
  369. break;
  370. case 24:
  371. iface |= 0x20;
  372. break;
  373. case 32:
  374. iface |= 0x30;
  375. break;
  376. }
  377. switch (substream->stream) {
  378. case SNDRV_PCM_STREAM_PLAYBACK:
  379. i = 0;
  380. shift = 4;
  381. break;
  382. case SNDRV_PCM_STREAM_CAPTURE:
  383. i = 2;
  384. shift = 0;
  385. break;
  386. default:
  387. return -EINVAL;
  388. }
  389. /* Only need to set MCLK/LRCLK ratio if we're master */
  390. if (snd_soc_component_read(component, WM8770_MSTRCTRL) & 0x100) {
  391. for (; i < ARRAY_SIZE(mclk_ratios); ++i) {
  392. ratio = wm8770->sysclk / params_rate(params);
  393. if (ratio == mclk_ratios[i])
  394. break;
  395. }
  396. if (i == ARRAY_SIZE(mclk_ratios)) {
  397. dev_err(component->dev,
  398. "Unable to configure MCLK ratio %d/%d\n",
  399. wm8770->sysclk, params_rate(params));
  400. return -EINVAL;
  401. }
  402. dev_dbg(component->dev, "MCLK is %dfs\n", mclk_ratios[i]);
  403. snd_soc_component_update_bits(component, WM8770_MSTRCTRL, 0x7 << shift,
  404. i << shift);
  405. }
  406. snd_soc_component_update_bits(component, WM8770_IFACECTRL, 0x30, iface);
  407. return 0;
  408. }
  409. static int wm8770_mute(struct snd_soc_dai *dai, int mute, int direction)
  410. {
  411. struct snd_soc_component *component;
  412. component = dai->component;
  413. return snd_soc_component_update_bits(component, WM8770_DACMUTE, 0x10,
  414. !!mute << 4);
  415. }
  416. static int wm8770_set_sysclk(struct snd_soc_dai *dai,
  417. int clk_id, unsigned int freq, int dir)
  418. {
  419. struct snd_soc_component *component;
  420. struct wm8770_priv *wm8770;
  421. component = dai->component;
  422. wm8770 = snd_soc_component_get_drvdata(component);
  423. wm8770->sysclk = freq;
  424. return 0;
  425. }
  426. static int wm8770_set_bias_level(struct snd_soc_component *component,
  427. enum snd_soc_bias_level level)
  428. {
  429. int ret;
  430. struct wm8770_priv *wm8770;
  431. wm8770 = snd_soc_component_get_drvdata(component);
  432. switch (level) {
  433. case SND_SOC_BIAS_ON:
  434. break;
  435. case SND_SOC_BIAS_PREPARE:
  436. break;
  437. case SND_SOC_BIAS_STANDBY:
  438. if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
  439. ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies),
  440. wm8770->supplies);
  441. if (ret) {
  442. dev_err(component->dev,
  443. "Failed to enable supplies: %d\n",
  444. ret);
  445. return ret;
  446. }
  447. regcache_sync(wm8770->regmap);
  448. /* global powerup */
  449. snd_soc_component_write(component, WM8770_PWDNCTRL, 0);
  450. }
  451. break;
  452. case SND_SOC_BIAS_OFF:
  453. /* global powerdown */
  454. snd_soc_component_write(component, WM8770_PWDNCTRL, 1);
  455. regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies),
  456. wm8770->supplies);
  457. break;
  458. }
  459. return 0;
  460. }
  461. #define WM8770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
  462. SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
  463. static const struct snd_soc_dai_ops wm8770_dai_ops = {
  464. .mute_stream = wm8770_mute,
  465. .hw_params = wm8770_hw_params,
  466. .set_fmt = wm8770_set_fmt,
  467. .set_sysclk = wm8770_set_sysclk,
  468. .no_capture_mute = 1,
  469. };
  470. static struct snd_soc_dai_driver wm8770_dai = {
  471. .name = "wm8770-hifi",
  472. .playback = {
  473. .stream_name = "Playback",
  474. .channels_min = 2,
  475. .channels_max = 2,
  476. .rates = SNDRV_PCM_RATE_8000_192000,
  477. .formats = WM8770_FORMATS
  478. },
  479. .capture = {
  480. .stream_name = "Capture",
  481. .channels_min = 2,
  482. .channels_max = 2,
  483. .rates = SNDRV_PCM_RATE_8000_96000,
  484. .formats = WM8770_FORMATS
  485. },
  486. .ops = &wm8770_dai_ops,
  487. .symmetric_rates = 1
  488. };
  489. static int wm8770_probe(struct snd_soc_component *component)
  490. {
  491. struct wm8770_priv *wm8770;
  492. int ret;
  493. wm8770 = snd_soc_component_get_drvdata(component);
  494. wm8770->component = component;
  495. ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies),
  496. wm8770->supplies);
  497. if (ret) {
  498. dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
  499. return ret;
  500. }
  501. ret = wm8770_reset(component);
  502. if (ret < 0) {
  503. dev_err(component->dev, "Failed to issue reset: %d\n", ret);
  504. goto err_reg_enable;
  505. }
  506. /* latch the volume update bits */
  507. snd_soc_component_update_bits(component, WM8770_MSDIGVOL, 0x100, 0x100);
  508. snd_soc_component_update_bits(component, WM8770_MSALGVOL, 0x100, 0x100);
  509. snd_soc_component_update_bits(component, WM8770_VOUT1RVOL, 0x100, 0x100);
  510. snd_soc_component_update_bits(component, WM8770_VOUT2RVOL, 0x100, 0x100);
  511. snd_soc_component_update_bits(component, WM8770_VOUT3RVOL, 0x100, 0x100);
  512. snd_soc_component_update_bits(component, WM8770_VOUT4RVOL, 0x100, 0x100);
  513. snd_soc_component_update_bits(component, WM8770_DAC1RVOL, 0x100, 0x100);
  514. snd_soc_component_update_bits(component, WM8770_DAC2RVOL, 0x100, 0x100);
  515. snd_soc_component_update_bits(component, WM8770_DAC3RVOL, 0x100, 0x100);
  516. snd_soc_component_update_bits(component, WM8770_DAC4RVOL, 0x100, 0x100);
  517. /* mute all DACs */
  518. snd_soc_component_update_bits(component, WM8770_DACMUTE, 0x10, 0x10);
  519. err_reg_enable:
  520. regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies);
  521. return ret;
  522. }
  523. static const struct snd_soc_component_driver soc_component_dev_wm8770 = {
  524. .probe = wm8770_probe,
  525. .set_bias_level = wm8770_set_bias_level,
  526. .controls = wm8770_snd_controls,
  527. .num_controls = ARRAY_SIZE(wm8770_snd_controls),
  528. .dapm_widgets = wm8770_dapm_widgets,
  529. .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets),
  530. .dapm_routes = wm8770_intercon,
  531. .num_dapm_routes = ARRAY_SIZE(wm8770_intercon),
  532. .use_pmdown_time = 1,
  533. .endianness = 1,
  534. .non_legacy_dai_naming = 1,
  535. };
  536. static const struct of_device_id wm8770_of_match[] = {
  537. { .compatible = "wlf,wm8770", },
  538. { }
  539. };
  540. MODULE_DEVICE_TABLE(of, wm8770_of_match);
  541. static const struct regmap_config wm8770_regmap = {
  542. .reg_bits = 7,
  543. .val_bits = 9,
  544. .max_register = WM8770_RESET,
  545. .reg_defaults = wm8770_reg_defaults,
  546. .num_reg_defaults = ARRAY_SIZE(wm8770_reg_defaults),
  547. .cache_type = REGCACHE_RBTREE,
  548. .volatile_reg = wm8770_volatile_reg,
  549. };
  550. static int wm8770_spi_probe(struct spi_device *spi)
  551. {
  552. struct wm8770_priv *wm8770;
  553. int ret, i;
  554. wm8770 = devm_kzalloc(&spi->dev, sizeof(struct wm8770_priv),
  555. GFP_KERNEL);
  556. if (!wm8770)
  557. return -ENOMEM;
  558. for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++)
  559. wm8770->supplies[i].supply = wm8770_supply_names[i];
  560. ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8770->supplies),
  561. wm8770->supplies);
  562. if (ret) {
  563. dev_err(&spi->dev, "Failed to request supplies: %d\n", ret);
  564. return ret;
  565. }
  566. wm8770->disable_nb[0].notifier_call = wm8770_regulator_event_0;
  567. wm8770->disable_nb[1].notifier_call = wm8770_regulator_event_1;
  568. wm8770->disable_nb[2].notifier_call = wm8770_regulator_event_2;
  569. /* This should really be moved into the regulator core */
  570. for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) {
  571. ret = devm_regulator_register_notifier(
  572. wm8770->supplies[i].consumer,
  573. &wm8770->disable_nb[i]);
  574. if (ret) {
  575. dev_err(&spi->dev,
  576. "Failed to register regulator notifier: %d\n",
  577. ret);
  578. }
  579. }
  580. wm8770->regmap = devm_regmap_init_spi(spi, &wm8770_regmap);
  581. if (IS_ERR(wm8770->regmap))
  582. return PTR_ERR(wm8770->regmap);
  583. spi_set_drvdata(spi, wm8770);
  584. ret = devm_snd_soc_register_component(&spi->dev,
  585. &soc_component_dev_wm8770, &wm8770_dai, 1);
  586. return ret;
  587. }
  588. static struct spi_driver wm8770_spi_driver = {
  589. .driver = {
  590. .name = "wm8770",
  591. .of_match_table = wm8770_of_match,
  592. },
  593. .probe = wm8770_spi_probe,
  594. };
  595. module_spi_driver(wm8770_spi_driver);
  596. MODULE_DESCRIPTION("ASoC WM8770 driver");
  597. MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
  598. MODULE_LICENSE("GPL");