hpm_wm8960.c 23 KB

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