wcd9xxx-resmgr-v2.c 18 KB

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