wcd9xxx-resmgr-v2.c 19 KB

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