1
0

hpm_wm8960.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. /*
  2. * Copyright (c) 2015, Freescale Semiconductor, Inc.
  3. * Copyright 2016-2021 NXP
  4. * Copyright (c) 2022 HPMicro
  5. *
  6. * SPDX-License-Identifier: BSD-3-Clause
  7. *
  8. */
  9. #include <stdio.h>
  10. #include "hpm_wm8960.h"
  11. #ifndef HPM_WM8960_MCLK_TOLERANCE
  12. #define HPM_WM8960_MCLK_TOLERANCE (4U)
  13. #endif
  14. /* wm8960 register default value */
  15. static const uint16_t wm8960_default_reg_val[WM8960_REG_NUM] = {
  16. 0x0097, 0x0097, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x000a, 0x01c0, 0x0000, 0x00ff, 0x00ff, 0x0000, 0x0000,
  17. 0x0000, 0x0000, 0x0000, 0x007b, 0x0100, 0x0032, 0x0000, 0x00c3, 0x00c3, 0x01c0, 0x0000, 0x0000, 0x0000, 0x0000,
  18. 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0100, 0x0050, 0x0050, 0x0050, 0x0050, 0x0000, 0x0000, 0x0000, 0x0000,
  19. 0x0040, 0x0000, 0x0000, 0x0050, 0x0050, 0x0000, 0x0002, 0x0037, 0x004d, 0x0080, 0x0008, 0x0031, 0x0026, 0x00e9,
  20. };
  21. /* store reg value */
  22. static uint16_t wm8960_reg_val[WM8960_REG_NUM];
  23. hpm_stat_t wm8960_init(wm8960_control_t *control, wm8960_config_t *config)
  24. {
  25. assert(control != NULL);
  26. assert(config != NULL);
  27. hpm_stat_t stat = status_success;
  28. (void)memcpy(wm8960_reg_val, wm8960_default_reg_val, sizeof(wm8960_default_reg_val));
  29. /* Reset */
  30. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RESET, 0x00));
  31. /* Power on input modules */
  32. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, 0xFE));
  33. /* Power on output modules */
  34. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER2, 0x1F8));
  35. /* Power on PGA and mixer */
  36. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x3C));
  37. /* ADC and DAC uses same clock */
  38. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_IFACE2, 0x40));
  39. /* set data protocol */
  40. HPM_CHECK_RET(wm8960_set_protocol(control, config->bus));
  41. /* set wm8960 as slave */
  42. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS_SET(0)));
  43. /* invert LRCLK */
  44. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_LRP_MASK, WM8960_IFACE1_LRP_SET(1)));
  45. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ADDCTL1, 0xC0));
  46. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ADDCTL4, 0x40));
  47. /* ADC volume, 8dB */
  48. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LADC, 0x1D3));
  49. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RADC, 0x1D3));
  50. /* Digital DAC volume, 0dB */
  51. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LDAC, 0x1E0));
  52. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RDAC, 0x1E0));
  53. /* Headphone volume, LOUT1 and ROUT1, 6dB */
  54. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT1, 0x17F));
  55. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT1, 0x17F));
  56. /* speaker volume 6dB */
  57. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT2, 0x1ff));
  58. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT2, 0x1ff));
  59. /* enable class D output */
  60. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_CLASSD1, 0xf7));
  61. /* Unmute DAC. */
  62. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_DACCTL1, 0x0000));
  63. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, 0x117));
  64. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, 0x117));
  65. HPM_CHECK_RET(wm8960_set_data_format(control, config->format.mclk_hz, config->format.sample_rate, config->format.bit_width));
  66. /* set data route */
  67. HPM_CHECK_RET(wm8960_set_data_route(control, config));
  68. return status_success;
  69. }
  70. hpm_stat_t wm8960_deinit(wm8960_control_t *control)
  71. {
  72. hpm_stat_t stat = status_success;
  73. /* power off all modules */
  74. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, 0x00U));
  75. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER2, 0x00U));
  76. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x00U));
  77. return status_success;
  78. }
  79. hpm_stat_t wm8960_set_protocol(wm8960_control_t *control, wm8960_protocol_t protocol)
  80. {
  81. return wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK, (uint16_t)protocol);
  82. }
  83. hpm_stat_t wm8960_set_module(wm8960_control_t *control, wm8960_module_t module, bool enable)
  84. {
  85. hpm_stat_t stat = status_success;
  86. switch (module) {
  87. case wm8960_module_adc:
  88. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_ADCL_MASK,
  89. ((uint16_t)enable << WM8960_POWER1_ADCL_SHIFT)));
  90. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_ADCR_MASK,
  91. ((uint16_t)enable << WM8960_POWER1_ADCR_SHIFT)));
  92. break;
  93. case wm8960_module_dac:
  94. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_DACL_MASK,
  95. ((uint16_t)enable << WM8960_POWER2_DACL_SHIFT)));
  96. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_DACR_MASK,
  97. ((uint16_t)enable << WM8960_POWER2_DACR_SHIFT)));
  98. break;
  99. case wm8960_module_vref:
  100. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_VREF_MASK,
  101. ((uint16_t)enable << WM8960_POWER1_VREF_SHIFT)));
  102. break;
  103. case wm8960_module_ana_in:
  104. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_AINL_MASK,
  105. ((uint16_t)enable << WM8960_POWER1_AINL_SHIFT)));
  106. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_AINR_MASK,
  107. ((uint16_t)enable << WM8960_POWER1_AINR_SHIFT)));
  108. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER3, WM8960_POWER3_LMIC_MASK,
  109. ((uint16_t)enable << WM8960_POWER3_LMIC_SHIFT)));
  110. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER3, WM8960_POWER3_RMIC_MASK,
  111. ((uint16_t)enable << WM8960_POWER3_RMIC_SHIFT)));
  112. break;
  113. case wm8960_module_lineout:
  114. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_LOUT1_MASK,
  115. ((uint16_t)enable << WM8960_POWER2_LOUT1_SHIFT)));
  116. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_ROUT1_MASK,
  117. ((uint16_t)enable << WM8960_POWER2_ROUT1_SHIFT)));
  118. break;
  119. case wm8960_module_micbais:
  120. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_MICB_MASK,
  121. ((uint16_t)enable << WM8960_POWER1_MICB_SHIFT)));
  122. break;
  123. case wm8960_module_speaker:
  124. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_SPKL_MASK,
  125. ((uint16_t)enable << WM8960_POWER2_SPKL_SHIFT)));
  126. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_SPKR_MASK,
  127. ((uint16_t)enable << WM8960_POWER2_SPKR_SHIFT)));
  128. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_CLASSD1, 0xF7));
  129. break;
  130. case wm8960_module_output_mixer:
  131. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER3, WM8960_POWER3_LOMIX_MASK,
  132. ((uint16_t)enable << WM8960_POWER3_LOMIX_SHIFT)));
  133. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER3, WM8960_POWER3_ROMIX_MASK,
  134. ((uint16_t)enable << WM8960_POWER3_ROMIX_SHIFT)));
  135. break;
  136. default:
  137. stat = status_invalid_argument;
  138. break;
  139. }
  140. return stat;
  141. }
  142. hpm_stat_t wm8960_set_data_route(wm8960_control_t *control, wm8960_config_t *config)
  143. {
  144. hpm_stat_t stat = status_success;
  145. /* select left input */
  146. HPM_CHECK_RET(wm8960_set_left_input(control, config->left_input));
  147. /* select right input */
  148. HPM_CHECK_RET(wm8960_set_right_input(control, config->right_input));
  149. /* select source to output mixer */
  150. HPM_CHECK_RET(wm8960_config_input_to_output_mixer(control, config->play_source));
  151. switch (config->route) {
  152. case wm8960_route_bypass:
  153. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_micbais, true));
  154. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_ana_in, true));
  155. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_output_mixer, true));
  156. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_lineout, true));
  157. break;
  158. case wm8960_route_playback:
  159. /* I2S_IN-> DAC-> HP */
  160. /* Set power for DAC */
  161. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x0C));
  162. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_dac, true));
  163. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_output_mixer, true));
  164. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_lineout, true));
  165. break;
  166. case wm8960_route_playback_and_record:
  167. /* Set power */
  168. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x3C));
  169. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_adc, true));
  170. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_micbais, true));
  171. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_ana_in, true));
  172. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_dac, true));
  173. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_output_mixer, true));
  174. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_lineout, true));
  175. break;
  176. case wm8960_route_record:
  177. /* ANA_IN->ADC->I2S_OUT */
  178. /* Power up ADC and AIN */
  179. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x30));
  180. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_micbais, true));
  181. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_ana_in, true));
  182. HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_adc, true));
  183. break;
  184. default:
  185. stat = status_invalid_argument;
  186. break;
  187. }
  188. return stat;
  189. }
  190. hpm_stat_t wm8960_set_left_input(wm8960_control_t *control, wm8960_input_t input)
  191. {
  192. hpm_stat_t stat = status_success;
  193. uint16_t val = 0;
  194. switch (input) {
  195. case wm8960_input_closed:
  196. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  197. val &= (uint16_t) ~(WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
  198. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  199. break;
  200. case wm8960_input_single_ended_mic:
  201. /* Only LMN1 enabled, LMICBOOST to 13db, LMIC2B enabled */
  202. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  203. val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
  204. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  205. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINPATH, 0x138));
  206. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, 0x117));
  207. break;
  208. case wm8960_input_differential_mic_input2:
  209. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  210. val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
  211. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  212. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINPATH, 0x178));
  213. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, 0x117));
  214. break;
  215. case wm8960_input_differential_mic_input3:
  216. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  217. val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
  218. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  219. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINPATH, 0x1B8));
  220. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, 0x117));
  221. break;
  222. case wm8960_input_line_input2:
  223. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  224. val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
  225. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  226. HPM_CHECK_RET(wm8960_read_reg(WM8960_INBMIX1, &val));
  227. val |= 0xEU;
  228. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_INBMIX1, val));
  229. break;
  230. case wm8960_input_line_input3:
  231. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  232. val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
  233. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  234. HPM_CHECK_RET(wm8960_read_reg(WM8960_INBMIX1, &val));
  235. val |= 0x70U;
  236. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_INBMIX1, val));
  237. break;
  238. default:
  239. stat = status_invalid_argument;
  240. break;
  241. }
  242. return stat;
  243. }
  244. hpm_stat_t wm8960_set_right_input(wm8960_control_t *control, wm8960_input_t input)
  245. {
  246. hpm_stat_t stat = status_success;
  247. uint16_t val = 0;
  248. switch (input) {
  249. case wm8960_input_closed:
  250. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  251. val &= (uint16_t) ~(WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
  252. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  253. break;
  254. case wm8960_input_single_ended_mic:
  255. /* Only LMN1 enabled, LMICBOOST to 13db, LMIC2B enabled */
  256. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  257. val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
  258. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  259. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINPATH, 0x138));
  260. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, 0x117));
  261. break;
  262. case wm8960_input_differential_mic_input2:
  263. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  264. val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
  265. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  266. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINPATH, 0x178));
  267. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, 0x117));
  268. break;
  269. case wm8960_input_differential_mic_input3:
  270. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  271. val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
  272. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  273. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINPATH, 0x1B8));
  274. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, 0x117));
  275. break;
  276. case wm8960_input_line_input2:
  277. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  278. val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
  279. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  280. HPM_CHECK_RET(wm8960_read_reg(WM8960_INBMIX2, &val));
  281. val |= 0xEU;
  282. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_INBMIX2, val));
  283. break;
  284. case wm8960_input_line_input3:
  285. HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
  286. val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
  287. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
  288. HPM_CHECK_RET(wm8960_read_reg(WM8960_INBMIX2, &val));
  289. val |= 0x70U;
  290. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_INBMIX2, val));
  291. break;
  292. default:
  293. stat = status_invalid_argument;
  294. break;
  295. }
  296. return stat;
  297. }
  298. hpm_stat_t wm8960_set_volume(wm8960_control_t *control, wm8960_module_t module, uint32_t volume)
  299. {
  300. uint16_t vol = 0;
  301. hpm_stat_t stat = status_success;
  302. switch (module) {
  303. case wm8960_module_adc:
  304. if (volume > 255U) {
  305. stat = status_invalid_argument;
  306. } else {
  307. vol = (uint16_t)volume;
  308. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LADC, vol));
  309. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RADC, vol));
  310. /* Update volume */
  311. vol = (uint16_t)(0x100U | volume);
  312. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LADC, vol));
  313. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RADC, vol));
  314. }
  315. break;
  316. case wm8960_module_dac:
  317. if (volume > 255U) {
  318. stat = status_invalid_argument;
  319. } else {
  320. vol = (uint16_t)volume;
  321. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LDAC, vol));
  322. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RDAC, vol));
  323. vol = 0x100U | (uint16_t)volume;
  324. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LDAC, vol));
  325. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RDAC, vol));
  326. }
  327. break;
  328. case wm8960_module_headphone:
  329. if (volume > 0x7FU) {
  330. stat = status_invalid_argument;
  331. } else {
  332. vol = (uint16_t)volume;
  333. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT1, vol));
  334. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT1, vol));
  335. vol = 0x100U | (uint16_t)volume;
  336. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT1, vol));
  337. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT1, vol));
  338. }
  339. break;
  340. case wm8960_module_ana_in:
  341. if (volume > 0x3FU) {
  342. stat = status_invalid_argument;
  343. } else {
  344. vol = (uint16_t)volume;
  345. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, vol));
  346. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, vol));
  347. vol = 0x100U | (uint16_t)volume;
  348. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, vol));
  349. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, vol));
  350. }
  351. break;
  352. case wm8960_module_speaker:
  353. if (volume > 0x7FU) {
  354. stat = status_invalid_argument;
  355. } else {
  356. vol = (uint16_t)volume;
  357. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT2, vol));
  358. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT2, vol));
  359. vol = 0x100U | (uint16_t)volume;
  360. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT2, vol));
  361. HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT2, vol));
  362. }
  363. break;
  364. default:
  365. stat = status_invalid_argument;
  366. break;
  367. }
  368. return stat;
  369. }
  370. static bool wm8960_check_clock_tolerance(uint32_t source, uint32_t target)
  371. {
  372. uint32_t delta = (source >= target) ? (source - target) : (target - source);
  373. if (delta * 100 <= HPM_WM8960_MCLK_TOLERANCE * target) {
  374. return true;
  375. }
  376. return false;
  377. }
  378. hpm_stat_t wm8960_set_data_format(wm8960_control_t *control, uint32_t sysclk, uint32_t sample_rate, uint32_t bits)
  379. {
  380. hpm_stat_t stat = status_success;
  381. uint16_t val = 0;
  382. uint32_t ratio[7] = {256, 256 * 1.5, 256 * 2, 256 * 3, 256 * 4, 256 * 5.5, 256 * 6};
  383. bool clock_meet_requirement = false;
  384. if (sysclk / sample_rate > 256 * 6) {
  385. sysclk = sysclk / 2;
  386. val = WM8960_CLOCK1_SYSCLKDIV_SET(2U); /* SYSCLK Pre-divider */
  387. }
  388. for (uint8_t i = 0; i < 7; i++) {
  389. if (wm8960_check_clock_tolerance(sysclk, sample_rate * ratio[i])) {
  390. val |= ((i << WM8960_CLOCK1_ADCDIV_SHIFT) | (i << WM8960_CLOCK1_DACDIV_SHIFT));
  391. clock_meet_requirement = true;
  392. break;
  393. }
  394. }
  395. if (!clock_meet_requirement) {
  396. return status_invalid_argument;
  397. }
  398. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_CLOCK1, 0x1FEU, val));
  399. /* set sample bit */
  400. switch (bits) {
  401. case 16:
  402. stat = wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL_SET(0U));
  403. break;
  404. case 20:
  405. stat = wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL_SET(1U));
  406. break;
  407. case 24:
  408. stat = wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL_SET(2U));
  409. break;
  410. case 32:
  411. stat = wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL_SET(3U));
  412. break;
  413. default:
  414. stat = status_invalid_argument;
  415. break;
  416. }
  417. return stat;
  418. }
  419. hpm_stat_t wm8960_config_input_to_output_mixer(wm8960_control_t *control, uint32_t play_source)
  420. {
  421. hpm_stat_t stat = status_success;
  422. if ((play_source & (uint32_t)wm8960_play_source_input_mixer) != 0U) {
  423. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS1, 0x80U, 0x80U));
  424. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS2, 0x80U, 0x80U));
  425. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_LOUTMIX, 0x180U, 0U));
  426. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_ROUTMIX, 0x180U, 0U));
  427. }
  428. if ((play_source & (uint32_t)wm8960_play_source_dac) != 0U) {
  429. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS1, 0x80U, 0x00U));
  430. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS2, 0x80U, 0x00U));
  431. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_LOUTMIX, 0x180U, 0x100U));
  432. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_ROUTMIX, 0x180U, 0x100U));
  433. }
  434. if ((play_source & (uint32_t)wm8960_play_source_input3) != 0U) {
  435. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS1, 0x80U, 0x0U));
  436. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS2, 0x80U, 0x0U));
  437. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_LOUTMIX, 0x180U, 0x80U));
  438. HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_ROUTMIX, 0x180U, 0x80U));
  439. }
  440. return stat;
  441. }
  442. hpm_stat_t wm8960_write_reg(wm8960_control_t *control, uint8_t reg, uint16_t val)
  443. {
  444. /* The first 7 bits (B15 to B9) are address bits that select which control register */
  445. /* is accessed. The remaining 9 bits (B8 to B0) are data bits */
  446. rt_size_t size;
  447. rt_uint8_t data[2];
  448. data[0] = (reg << 1) | (uint8_t)((val >> 8U) & 0x0001U);
  449. data[1] = (uint8_t)(val & 0xFFU);
  450. size = rt_i2c_master_send(control->i2c_bus, control->slave_address, RT_I2C_WR, data, 2U);
  451. if (size != 2) {
  452. return status_fail;
  453. }
  454. wm8960_reg_val[reg] = val;
  455. return status_success;
  456. }
  457. hpm_stat_t wm8960_read_reg(uint8_t reg, uint16_t *val)
  458. {
  459. if (reg >= WM8960_REG_NUM) {
  460. return status_invalid_argument;
  461. }
  462. *val = wm8960_reg_val[reg];
  463. return status_success;
  464. }
  465. hpm_stat_t wm8960_modify_reg(wm8960_control_t *control, uint8_t reg, uint16_t mask, uint16_t val)
  466. {
  467. hpm_stat_t stat = 0;
  468. uint16_t reg_val;
  469. /* Read the register value out */
  470. stat = wm8960_read_reg(reg, &reg_val);
  471. if (stat != status_success) {
  472. return status_fail;
  473. }
  474. /* Modify the value */
  475. reg_val &= (uint16_t)~mask;
  476. reg_val |= val;
  477. /* Write the data to register */
  478. stat = wm8960_write_reg(control, reg, reg_val);
  479. if (stat != status_success) {
  480. return status_fail;
  481. }
  482. return status_success;
  483. }