wcd9xxx-resmgr-v2.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/module.h>
  6. #include <linux/init.h>
  7. #include <linux/slab.h>
  8. #include <linux/delay.h>
  9. #include <sound/soc.h>
  10. #include "wcd9xxx-resmgr-v2.h"
  11. #include "core.h"
  12. #define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 5000
  13. /* This register is valid only for WCD9335 */
  14. #define WCD93XX_ANA_CLK_TOP 0x0602
  15. #define WCD93XX_ANA_BIAS 0x0601
  16. #define WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41
  17. #define WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42
  18. #define WCD93XX_CLK_SYS_MCLK_PRG 0x711
  19. #define WCD93XX_CODEC_RPM_CLK_GATE 0x002
  20. #define WCD93XX_ANA_RCO 0x603
  21. #define WCD93XX_ANA_BUCK_CTL 0x606
  22. static const char *wcd_resmgr_clk_type_to_str(enum wcd_clock_type clk_type)
  23. {
  24. if (clk_type == WCD_CLK_OFF)
  25. return "WCD_CLK_OFF";
  26. else if (clk_type == WCD_CLK_RCO)
  27. return "WCD_CLK_RCO";
  28. else if (clk_type == WCD_CLK_MCLK)
  29. return "WCD_CLK_MCLK";
  30. else
  31. return "WCD_CLK_UNDEFINED";
  32. }
  33. static int wcd_resmgr_codec_reg_update_bits(struct wcd9xxx_resmgr_v2 *resmgr,
  34. u16 reg, u8 mask, u8 val)
  35. {
  36. bool change;
  37. int ret;
  38. if (resmgr->codec_type != WCD9335) {
  39. /* Tavil and Pahu does not support ANA_CLK_TOP register */
  40. if (reg == WCD93XX_ANA_CLK_TOP)
  41. return 0;
  42. } else {
  43. /* Tasha does not support CLK_SYS_MCLK_PRG register */
  44. if (reg == WCD93XX_CLK_SYS_MCLK_PRG)
  45. return 0;
  46. }
  47. if (resmgr->codec) {
  48. ret = snd_soc_update_bits(resmgr->codec, reg, mask, val);
  49. } else if (resmgr->core_res->wcd_core_regmap) {
  50. ret = regmap_update_bits_check(
  51. resmgr->core_res->wcd_core_regmap,
  52. reg, mask, val, &change);
  53. if (!ret)
  54. ret = change;
  55. } else {
  56. pr_err("%s: codec/regmap not defined\n", __func__);
  57. ret = -EINVAL;
  58. }
  59. return ret;
  60. }
  61. static int wcd_resmgr_codec_reg_read(struct wcd9xxx_resmgr_v2 *resmgr,
  62. unsigned int reg)
  63. {
  64. int val, ret;
  65. if (resmgr->codec_type != WCD9335) {
  66. if (reg == WCD93XX_ANA_CLK_TOP)
  67. return 0;
  68. } else {
  69. if (reg == WCD93XX_CLK_SYS_MCLK_PRG)
  70. return 0;
  71. }
  72. if (resmgr->codec) {
  73. val = snd_soc_read(resmgr->codec, reg);
  74. } else if (resmgr->core_res->wcd_core_regmap) {
  75. ret = regmap_read(resmgr->core_res->wcd_core_regmap,
  76. reg, &val);
  77. if (ret)
  78. val = ret;
  79. } else {
  80. pr_err("%s: wcd regmap is null\n", __func__);
  81. return -EINVAL;
  82. }
  83. return val;
  84. }
  85. /*
  86. * wcd_resmgr_get_clk_type()
  87. * Returns clk type that is currently enabled
  88. */
  89. int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr)
  90. {
  91. if (!resmgr) {
  92. pr_err("%s: resmgr not initialized\n", __func__);
  93. return -EINVAL;
  94. }
  95. return resmgr->clk_type;
  96. }
  97. EXPORT_SYMBOL(wcd_resmgr_get_clk_type);
  98. static void wcd_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr_v2 *resmgr,
  99. int clk_users)
  100. {
  101. /* Caller of this function should have acquired BG_CLK lock */
  102. if (clk_users) {
  103. if (resmgr->resmgr_cb &&
  104. resmgr->resmgr_cb->cdc_rco_ctrl) {
  105. while (clk_users--)
  106. resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec,
  107. true);
  108. }
  109. }
  110. }
  111. /*
  112. * wcd_resmgr_post_ssr_v2
  113. * @resmgr: handle to struct wcd9xxx_resmgr_v2
  114. */
  115. void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr)
  116. {
  117. int old_bg_audio_users;
  118. int old_clk_rco_users, old_clk_mclk_users;
  119. WCD9XXX_V2_BG_CLK_LOCK(resmgr);
  120. old_bg_audio_users = resmgr->master_bias_users;
  121. old_clk_mclk_users = resmgr->clk_mclk_users;
  122. old_clk_rco_users = resmgr->clk_rco_users;
  123. resmgr->master_bias_users = 0;
  124. resmgr->clk_mclk_users = 0;
  125. resmgr->clk_rco_users = 0;
  126. resmgr->clk_type = WCD_CLK_OFF;
  127. pr_debug("%s: old_bg_audio_users=%d old_clk_mclk_users=%d old_clk_rco_users=%d\n",
  128. __func__, old_bg_audio_users,
  129. old_clk_mclk_users, old_clk_rco_users);
  130. if (old_bg_audio_users) {
  131. while (old_bg_audio_users--)
  132. wcd_resmgr_enable_master_bias(resmgr);
  133. }
  134. if (old_clk_mclk_users) {
  135. while (old_clk_mclk_users--)
  136. wcd_resmgr_enable_clk_block(resmgr, WCD_CLK_MCLK);
  137. }
  138. if (old_clk_rco_users)
  139. wcd_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users);
  140. WCD9XXX_V2_BG_CLK_UNLOCK(resmgr);
  141. }
  142. EXPORT_SYMBOL(wcd_resmgr_post_ssr_v2);
  143. /*
  144. * wcd_resmgr_enable_master_bias: enable codec master bias
  145. * @resmgr: handle to struct wcd9xxx_resmgr_v2
  146. */
  147. int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr)
  148. {
  149. mutex_lock(&resmgr->master_bias_lock);
  150. resmgr->master_bias_users++;
  151. if (resmgr->master_bias_users == 1) {
  152. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
  153. 0x80, 0x80);
  154. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
  155. 0x40, 0x40);
  156. /*
  157. * 1ms delay is required after pre-charge is enabled
  158. * as per HW requirement
  159. */
  160. usleep_range(1000, 1100);
  161. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
  162. 0x40, 0x00);
  163. wcd_resmgr_codec_reg_update_bits(resmgr,
  164. WCD93XX_ANA_BIAS, 0x20, 0x00);
  165. }
  166. pr_debug("%s: current master bias users: %d\n", __func__,
  167. resmgr->master_bias_users);
  168. mutex_unlock(&resmgr->master_bias_lock);
  169. return 0;
  170. }
  171. EXPORT_SYMBOL(wcd_resmgr_enable_master_bias);
  172. /*
  173. * wcd_resmgr_disable_master_bias: disable codec master bias
  174. * @resmgr: handle to struct wcd9xxx_resmgr_v2
  175. */
  176. int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr)
  177. {
  178. mutex_lock(&resmgr->master_bias_lock);
  179. if (resmgr->master_bias_users <= 0) {
  180. mutex_unlock(&resmgr->master_bias_lock);
  181. return -EINVAL;
  182. }
  183. resmgr->master_bias_users--;
  184. if (resmgr->master_bias_users == 0) {
  185. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
  186. 0x80, 0x00);
  187. wcd_resmgr_codec_reg_update_bits(resmgr,
  188. WCD93XX_ANA_BIAS, 0x20, 0x00);
  189. }
  190. mutex_unlock(&resmgr->master_bias_lock);
  191. return 0;
  192. }
  193. EXPORT_SYMBOL(wcd_resmgr_disable_master_bias);
  194. static int wcd_resmgr_enable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr)
  195. {
  196. /* Enable mclk requires master bias to be enabled first */
  197. if (resmgr->master_bias_users <= 0) {
  198. pr_err("%s: Cannot turn on MCLK, BG is not enabled\n",
  199. __func__);
  200. return -EINVAL;
  201. }
  202. if (((resmgr->clk_mclk_users == 0) &&
  203. (resmgr->clk_type == WCD_CLK_MCLK)) ||
  204. ((resmgr->clk_mclk_users > 0) &&
  205. (resmgr->clk_type != WCD_CLK_MCLK))) {
  206. pr_err("%s: Error enabling MCLK, clk_type: %s\n",
  207. __func__,
  208. wcd_resmgr_clk_type_to_str(resmgr->clk_type));
  209. return -EINVAL;
  210. }
  211. if (++resmgr->clk_mclk_users == 1) {
  212. wcd_resmgr_codec_reg_update_bits(resmgr,
  213. WCD93XX_ANA_CLK_TOP, 0x80, 0x80);
  214. wcd_resmgr_codec_reg_update_bits(resmgr,
  215. WCD93XX_ANA_CLK_TOP, 0x08, 0x00);
  216. wcd_resmgr_codec_reg_update_bits(resmgr,
  217. WCD93XX_ANA_CLK_TOP, 0x04, 0x04);
  218. if (resmgr->codec_type != WCD9335) {
  219. /*
  220. * In tavil clock contrl register is changed
  221. * to CLK_SYS_MCLK_PRG
  222. */
  223. wcd_resmgr_codec_reg_update_bits(resmgr,
  224. WCD93XX_CLK_SYS_MCLK_PRG, 0x80, 0x80);
  225. wcd_resmgr_codec_reg_update_bits(resmgr,
  226. WCD93XX_CLK_SYS_MCLK_PRG, 0x30, 0x10);
  227. wcd_resmgr_codec_reg_update_bits(resmgr,
  228. WCD93XX_CLK_SYS_MCLK_PRG, 0x02, 0x00);
  229. wcd_resmgr_codec_reg_update_bits(resmgr,
  230. WCD93XX_CLK_SYS_MCLK_PRG, 0x01, 0x01);
  231. wcd_resmgr_codec_reg_update_bits(resmgr,
  232. WCD93XX_CLK_SYS_MCLK_PRG, 0x02, 0x00);
  233. wcd_resmgr_codec_reg_update_bits(resmgr,
  234. WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
  235. 0x01, 0x01);
  236. wcd_resmgr_codec_reg_update_bits(resmgr,
  237. WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL,
  238. 0x01, 0x01);
  239. wcd_resmgr_codec_reg_update_bits(resmgr,
  240. WCD93XX_CODEC_RPM_CLK_GATE, 0x03, 0x00);
  241. } else {
  242. wcd_resmgr_codec_reg_update_bits(resmgr,
  243. WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
  244. 0x01, 0x01);
  245. wcd_resmgr_codec_reg_update_bits(resmgr,
  246. WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL,
  247. 0x01, 0x01);
  248. }
  249. /*
  250. * 10us sleep is required after clock is enabled
  251. * as per HW requirement
  252. */
  253. usleep_range(10, 15);
  254. }
  255. resmgr->clk_type = WCD_CLK_MCLK;
  256. pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__,
  257. resmgr->clk_mclk_users,
  258. wcd_resmgr_clk_type_to_str(resmgr->clk_type));
  259. return 0;
  260. }
  261. static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr)
  262. {
  263. if (resmgr->clk_mclk_users <= 0) {
  264. pr_err("%s: No mclk users, cannot disable mclk\n", __func__);
  265. return -EINVAL;
  266. }
  267. if (--resmgr->clk_mclk_users == 0) {
  268. if (resmgr->clk_rco_users > 0) {
  269. /* MCLK to RCO switch */
  270. wcd_resmgr_codec_reg_update_bits(resmgr,
  271. WCD93XX_ANA_CLK_TOP,
  272. 0x08, 0x08);
  273. wcd_resmgr_codec_reg_update_bits(resmgr,
  274. WCD93XX_CLK_SYS_MCLK_PRG, 0x02, 0x02);
  275. /* Disable clock buffer */
  276. wcd_resmgr_codec_reg_update_bits(resmgr,
  277. WCD93XX_CLK_SYS_MCLK_PRG, 0x80, 0x00);
  278. resmgr->clk_type = WCD_CLK_RCO;
  279. } else {
  280. wcd_resmgr_codec_reg_update_bits(resmgr,
  281. WCD93XX_ANA_CLK_TOP,
  282. 0x04, 0x00);
  283. wcd_resmgr_codec_reg_update_bits(resmgr,
  284. WCD93XX_CLK_SYS_MCLK_PRG, 0x81, 0x00);
  285. resmgr->clk_type = WCD_CLK_OFF;
  286. }
  287. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_CLK_TOP,
  288. 0x80, 0x00);
  289. }
  290. if ((resmgr->codec_type != WCD9335) &&
  291. (resmgr->clk_type == WCD_CLK_OFF))
  292. wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL);
  293. pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__,
  294. resmgr->clk_mclk_users,
  295. wcd_resmgr_clk_type_to_str(resmgr->clk_type));
  296. return 0;
  297. }
  298. static void wcd_resmgr_set_buck_accuracy(struct wcd9xxx_resmgr_v2 *resmgr)
  299. {
  300. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL,
  301. 0x02, 0x02);
  302. /* 100us sleep needed after HIGH_ACCURACY_PRE_EN1 */
  303. usleep_range(100, 110);
  304. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL,
  305. 0x01, 0x01);
  306. /* 100us sleep needed after HIGH_ACCURACY_PRE_EN2 */
  307. usleep_range(100, 110);
  308. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL,
  309. 0x04, 0x04);
  310. /* 100us sleep needed after HIGH_ACCURACY_EN */
  311. usleep_range(100, 110);
  312. }
  313. static int wcd_resmgr_enable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr)
  314. {
  315. bool rco_cal_done = true;
  316. resmgr->clk_rco_users++;
  317. if ((resmgr->clk_rco_users == 1) &&
  318. ((resmgr->clk_type == WCD_CLK_OFF) ||
  319. (resmgr->clk_mclk_users == 0))) {
  320. pr_warn("%s: RCO enable requires MCLK to be ON first\n",
  321. __func__);
  322. resmgr->clk_rco_users--;
  323. return -EINVAL;
  324. } else if ((resmgr->clk_rco_users == 1) &&
  325. (resmgr->clk_mclk_users)) {
  326. /* RCO Enable */
  327. if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) {
  328. wcd_resmgr_codec_reg_update_bits(resmgr,
  329. WCD93XX_ANA_RCO,
  330. 0x80, 0x80);
  331. if (resmgr->codec_type != WCD9335)
  332. wcd_resmgr_set_buck_accuracy(resmgr);
  333. }
  334. /*
  335. * 20us required after RCO BG is enabled as per HW
  336. * requirements
  337. */
  338. usleep_range(20, 25);
  339. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO,
  340. 0x40, 0x40);
  341. /*
  342. * 20us required after RCO is enabled as per HW
  343. * requirements
  344. */
  345. usleep_range(20, 25);
  346. /* RCO Calibration */
  347. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO,
  348. 0x04, 0x04);
  349. if (resmgr->codec_type != WCD9335)
  350. /*
  351. * For wcd934x and wcd936x codecs, 20us sleep is needed
  352. * after enabling RCO calibration
  353. */
  354. usleep_range(20, 25);
  355. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO,
  356. 0x04, 0x00);
  357. if (resmgr->codec_type != WCD9335)
  358. /*
  359. * For wcd934x and wcd936x codecs, 20us sleep is needed
  360. * after disabling RCO calibration
  361. */
  362. usleep_range(20, 25);
  363. /* RCO calibration takes app. 5ms to complete */
  364. usleep_range(WCD9XXX_RCO_CALIBRATION_DELAY_INC_US,
  365. WCD9XXX_RCO_CALIBRATION_DELAY_INC_US + 100);
  366. if (wcd_resmgr_codec_reg_read(resmgr, WCD93XX_ANA_RCO) & 0x02)
  367. rco_cal_done = false;
  368. WARN((!rco_cal_done), "RCO Calibration failed\n");
  369. /* Switch MUX to RCO */
  370. if (resmgr->clk_mclk_users == 1) {
  371. wcd_resmgr_codec_reg_update_bits(resmgr,
  372. WCD93XX_ANA_CLK_TOP,
  373. 0x08, 0x08);
  374. wcd_resmgr_codec_reg_update_bits(resmgr,
  375. WCD93XX_CLK_SYS_MCLK_PRG,
  376. 0x02, 0x02);
  377. resmgr->clk_type = WCD_CLK_RCO;
  378. }
  379. }
  380. pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__,
  381. resmgr->clk_rco_users,
  382. wcd_resmgr_clk_type_to_str(resmgr->clk_type));
  383. return 0;
  384. }
  385. static int wcd_resmgr_disable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr)
  386. {
  387. if ((resmgr->clk_rco_users <= 0) ||
  388. (resmgr->clk_type == WCD_CLK_OFF)) {
  389. pr_err("%s: rco_clk_users = %d, clk_type = %d, cannot disable\n",
  390. __func__, resmgr->clk_rco_users, resmgr->clk_type);
  391. return -EINVAL;
  392. }
  393. resmgr->clk_rco_users--;
  394. if ((resmgr->clk_rco_users == 0) &&
  395. (resmgr->clk_type == WCD_CLK_RCO)) {
  396. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_CLK_TOP,
  397. 0x08, 0x00);
  398. wcd_resmgr_codec_reg_update_bits(resmgr,
  399. WCD93XX_CLK_SYS_MCLK_PRG,
  400. 0x02, 0x00);
  401. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_CLK_TOP,
  402. 0x04, 0x00);
  403. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO,
  404. 0x40, 0x00);
  405. if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL)
  406. wcd_resmgr_codec_reg_update_bits(resmgr,
  407. WCD93XX_ANA_RCO,
  408. 0x80, 0x00);
  409. wcd_resmgr_codec_reg_update_bits(resmgr,
  410. WCD93XX_CLK_SYS_MCLK_PRG,
  411. 0x01, 0x00);
  412. resmgr->clk_type = WCD_CLK_OFF;
  413. } else if ((resmgr->clk_rco_users == 0) &&
  414. (resmgr->clk_mclk_users)) {
  415. /* Disable RCO while MCLK is ON */
  416. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO,
  417. 0x40, 0x00);
  418. if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL)
  419. wcd_resmgr_codec_reg_update_bits(resmgr,
  420. WCD93XX_ANA_RCO,
  421. 0x80, 0x00);
  422. }
  423. if ((resmgr->codec_type != WCD9335) &&
  424. (resmgr->clk_type == WCD_CLK_OFF))
  425. wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL);
  426. pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__,
  427. resmgr->clk_rco_users,
  428. wcd_resmgr_clk_type_to_str(resmgr->clk_type));
  429. return 0;
  430. }
  431. /*
  432. * wcd_resmgr_enable_clk_block: enable MCLK or RCO
  433. * @resmgr: handle to struct wcd9xxx_resmgr_v2
  434. * @type: Clock type to enable
  435. */
  436. int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
  437. enum wcd_clock_type type)
  438. {
  439. int ret;
  440. switch (type) {
  441. case WCD_CLK_MCLK:
  442. ret = wcd_resmgr_enable_clk_mclk(resmgr);
  443. break;
  444. case WCD_CLK_RCO:
  445. ret = wcd_resmgr_enable_clk_rco(resmgr);
  446. break;
  447. default:
  448. pr_err("%s: Unknown Clock type: %s\n", __func__,
  449. wcd_resmgr_clk_type_to_str(type));
  450. ret = -EINVAL;
  451. break;
  452. };
  453. if (ret)
  454. pr_err("%s: Enable clock %s failed\n", __func__,
  455. wcd_resmgr_clk_type_to_str(type));
  456. return ret;
  457. }
  458. EXPORT_SYMBOL(wcd_resmgr_enable_clk_block);
  459. void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr,
  460. int sido_src)
  461. {
  462. if (!resmgr)
  463. return;
  464. if (sido_src == resmgr->sido_input_src)
  465. return;
  466. if (sido_src == SIDO_SOURCE_INTERNAL) {
  467. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL,
  468. 0x04, 0x00);
  469. usleep_range(100, 110);
  470. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL,
  471. 0x03, 0x00);
  472. usleep_range(100, 110);
  473. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO,
  474. 0x80, 0x00);
  475. usleep_range(100, 110);
  476. resmgr->sido_input_src = SIDO_SOURCE_INTERNAL;
  477. pr_debug("%s: sido input src to internal\n", __func__);
  478. } else if (sido_src == SIDO_SOURCE_RCO_BG) {
  479. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO,
  480. 0x80, 0x80);
  481. usleep_range(100, 110);
  482. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL,
  483. 0x02, 0x02);
  484. usleep_range(100, 110);
  485. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL,
  486. 0x01, 0x01);
  487. usleep_range(100, 110);
  488. wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL,
  489. 0x04, 0x04);
  490. usleep_range(100, 110);
  491. resmgr->sido_input_src = SIDO_SOURCE_RCO_BG;
  492. pr_debug("%s: sido input src to external\n", __func__);
  493. }
  494. }
  495. EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src);
  496. /*
  497. * wcd_resmgr_set_sido_input_src_locked:
  498. * Set SIDO input in BG_CLK locked context
  499. *
  500. * @resmgr: handle to struct wcd9xxx_resmgr_v2
  501. * @sido_src: Select the SIDO input source
  502. */
  503. void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr,
  504. int sido_src)
  505. {
  506. if (!resmgr)
  507. return;
  508. WCD9XXX_V2_BG_CLK_LOCK(resmgr);
  509. wcd_resmgr_set_sido_input_src(resmgr, sido_src);
  510. WCD9XXX_V2_BG_CLK_UNLOCK(resmgr);
  511. }
  512. EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src_locked);
  513. /*
  514. * wcd_resmgr_disable_clk_block: disable MCLK or RCO
  515. * @resmgr: handle to struct wcd9xxx_resmgr_v2
  516. * @type: Clock type to disable
  517. */
  518. int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
  519. enum wcd_clock_type type)
  520. {
  521. int ret;
  522. switch (type) {
  523. case WCD_CLK_MCLK:
  524. ret = wcd_resmgr_disable_clk_mclk(resmgr);
  525. break;
  526. case WCD_CLK_RCO:
  527. ret = wcd_resmgr_disable_clk_rco(resmgr);
  528. break;
  529. default:
  530. pr_err("%s: Unknown Clock type: %s\n", __func__,
  531. wcd_resmgr_clk_type_to_str(type));
  532. ret = -EINVAL;
  533. break;
  534. };
  535. if (ret)
  536. pr_err("%s: Disable clock %s failed\n", __func__,
  537. wcd_resmgr_clk_type_to_str(type));
  538. return ret;
  539. }
  540. EXPORT_SYMBOL(wcd_resmgr_disable_clk_block);
  541. /*
  542. * wcd_resmgr_init: initialize wcd resource manager
  543. * @core_res: handle to struct wcd9xxx_core_resource
  544. *
  545. * Early init call without a handle to snd_soc_codec *
  546. */
  547. struct wcd9xxx_resmgr_v2 *wcd_resmgr_init(
  548. struct wcd9xxx_core_resource *core_res,
  549. struct snd_soc_codec *codec)
  550. {
  551. struct wcd9xxx_resmgr_v2 *resmgr;
  552. struct wcd9xxx *wcd9xxx;
  553. resmgr = kzalloc(sizeof(struct wcd9xxx_resmgr_v2), GFP_KERNEL);
  554. if (!resmgr)
  555. return ERR_PTR(-ENOMEM);
  556. wcd9xxx = container_of(core_res, struct wcd9xxx, core_res);
  557. if (!wcd9xxx) {
  558. kfree(resmgr);
  559. pr_err("%s: Cannot get wcd9xx pointer\n", __func__);
  560. return ERR_PTR(-EINVAL);
  561. }
  562. mutex_init(&resmgr->codec_bg_clk_lock);
  563. mutex_init(&resmgr->master_bias_lock);
  564. resmgr->master_bias_users = 0;
  565. resmgr->clk_mclk_users = 0;
  566. resmgr->clk_rco_users = 0;
  567. resmgr->master_bias_users = 0;
  568. resmgr->codec = codec;
  569. resmgr->core_res = core_res;
  570. resmgr->sido_input_src = SIDO_SOURCE_INTERNAL;
  571. resmgr->codec_type = wcd9xxx->type;
  572. return resmgr;
  573. }
  574. EXPORT_SYMBOL(wcd_resmgr_init);
  575. /*
  576. * wcd_resmgr_remove: Clean-up wcd resource manager
  577. * @resmgr: handle to struct wcd9xxx_resmgr_v2
  578. */
  579. void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr)
  580. {
  581. mutex_destroy(&resmgr->master_bias_lock);
  582. kfree(resmgr);
  583. }
  584. EXPORT_SYMBOL(wcd_resmgr_remove);
  585. /*
  586. * wcd_resmgr_post_init: post init call to assign codec handle
  587. * @resmgr: handle to struct wcd9xxx_resmgr_v2 created during early init
  588. * @resmgr_cb: codec callback function for resmgr
  589. * @codec: handle to struct snd_soc_codec
  590. */
  591. int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr,
  592. const struct wcd_resmgr_cb *resmgr_cb,
  593. struct snd_soc_codec *codec)
  594. {
  595. if (!resmgr) {
  596. pr_err("%s: resmgr not allocated\n", __func__);
  597. return -EINVAL;
  598. }
  599. if (!codec) {
  600. pr_err("%s: Codec memory is NULL, nothing to post init\n",
  601. __func__);
  602. return -EINVAL;
  603. }
  604. resmgr->codec = codec;
  605. resmgr->resmgr_cb = resmgr_cb;
  606. return 0;
  607. }
  608. EXPORT_SYMBOL(wcd_resmgr_post_init);
  609. MODULE_DESCRIPTION("wcd9xxx resmgr v2 module");
  610. MODULE_LICENSE("GPL v2");