wcd9xxx-resmgr-v2.c 19 KB

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