wcd-clsh.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  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/slab.h>
  15. #include <sound/soc.h>
  16. #include <linux/kernel.h>
  17. #include <linux/delay.h>
  18. #include <asoc/wcd9xxx_registers.h>
  19. #include "wcd-clsh.h"
  20. #define WCD_USLEEP_RANGE 50
  21. static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *,
  22. struct wcd_clsh_cdc_info *,
  23. u8 req_state, bool en, int mode);
  24. static const char *mode_to_str(int mode)
  25. {
  26. switch (mode) {
  27. case CLS_H_NORMAL:
  28. return WCD_CLSH_STRINGIFY(CLS_H_NORMAL);
  29. case CLS_H_HIFI:
  30. return WCD_CLSH_STRINGIFY(CLS_H_HIFI);
  31. case CLS_H_LOHIFI:
  32. return WCD_CLSH_STRINGIFY(CLS_H_LOHIFI);
  33. case CLS_H_LP:
  34. return WCD_CLSH_STRINGIFY(CLS_H_LP);
  35. case CLS_H_ULP:
  36. return WCD_CLSH_STRINGIFY(CLS_H_ULP);
  37. case CLS_AB:
  38. return WCD_CLSH_STRINGIFY(CLS_AB);
  39. case CLS_AB_HIFI:
  40. return WCD_CLSH_STRINGIFY(CLS_AB_HIFI);
  41. default:
  42. return WCD_CLSH_STRINGIFY(CLS_H_INVALID);
  43. };
  44. }
  45. static const char *state_to_str(u8 state, char *buf, size_t buflen)
  46. {
  47. int i;
  48. int cnt = 0;
  49. /*
  50. * This array of strings should match with enum wcd_clsh_state_bit.
  51. */
  52. static const char *const states[] = {
  53. "STATE_EAR",
  54. "STATE_HPH_L",
  55. "STATE_HPH_R",
  56. "STATE_AUX",
  57. };
  58. if (state == WCD_CLSH_STATE_IDLE) {
  59. snprintf(buf, buflen, "[STATE_IDLE]");
  60. goto done;
  61. }
  62. buf[0] = '\0';
  63. for (i = 0; i < ARRAY_SIZE(states); i++) {
  64. if (!(state & (1 << i)))
  65. continue;
  66. cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf,
  67. buf[0] == '\0' ? "[" : "|",
  68. states[i]);
  69. }
  70. if (cnt > 0)
  71. strlcat(buf + cnt, "]", buflen);
  72. done:
  73. if (buf[0] == '\0')
  74. snprintf(buf, buflen, "[STATE_UNKNOWN]");
  75. return buf;
  76. }
  77. static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_info *clsh_d,
  78. int clsh_state)
  79. {
  80. int mode;
  81. if ((clsh_state != WCD_CLSH_STATE_EAR) &&
  82. (clsh_state != WCD_CLSH_STATE_HPHL) &&
  83. (clsh_state != WCD_CLSH_STATE_HPHR) &&
  84. (clsh_state != WCD_CLSH_STATE_AUX))
  85. mode = CLS_NONE;
  86. else
  87. mode = clsh_d->interpolator_modes[ffs(clsh_state)];
  88. return mode;
  89. }
  90. static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_info *clsh_d,
  91. int clsh_state, int mode)
  92. {
  93. if ((clsh_state != WCD_CLSH_STATE_EAR) &&
  94. (clsh_state != WCD_CLSH_STATE_HPHL) &&
  95. (clsh_state != WCD_CLSH_STATE_HPHR) &&
  96. (clsh_state != WCD_CLSH_STATE_AUX))
  97. return;
  98. clsh_d->interpolator_modes[ffs(clsh_state)] = mode;
  99. }
  100. static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec,
  101. int mode)
  102. {
  103. if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
  104. mode == CLS_AB_HIFI)
  105. snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
  106. 0x08, 0x08); /* set to HIFI */
  107. else
  108. snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
  109. 0x08, 0x00); /* set to default */
  110. }
  111. static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec,
  112. int mode)
  113. {
  114. if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
  115. mode == CLS_AB_HIFI) {
  116. snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
  117. 0x04, 0x04);
  118. snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
  119. 0xF0, 0x80);
  120. } else {
  121. snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
  122. 0x04, 0x00); /* set to Default */
  123. snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
  124. 0xF0, 0x70);
  125. }
  126. }
  127. static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec,
  128. int mode, bool enable)
  129. {
  130. if (enable) {
  131. snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
  132. 0xE0, 0xA0);
  133. /* 100usec delay is needed as per HW requirement */
  134. usleep_range(100, 110);
  135. snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3,
  136. 0x02, 0x02);
  137. snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2,
  138. 0xFF, 0x1C);
  139. if (mode == CLS_H_LOHIFI) {
  140. snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
  141. 0x20, 0x20);
  142. snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
  143. 0xF0, 0xC0);
  144. snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
  145. 0x0E, 0x02);
  146. }
  147. } else {
  148. snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
  149. 0x20, 0x00);
  150. snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
  151. 0xF0, 0x80);
  152. snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
  153. 0x0E, 0x06);
  154. }
  155. }
  156. static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec,
  157. struct wcd_clsh_cdc_info *clsh_d,
  158. int mode,
  159. bool enable)
  160. {
  161. /* enable/disable buck */
  162. if ((enable && (++clsh_d->buck_users == 1)) ||
  163. (!enable && (--clsh_d->buck_users == 0))) {
  164. snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
  165. (1 << 7), (enable << 7));
  166. /*
  167. * 500us sleep is required after buck enable/disable
  168. * as per HW requirement
  169. */
  170. usleep_range(500, 510);
  171. if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
  172. mode == CLS_H_HIFI || mode == CLS_H_LP)
  173. snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3,
  174. 0x02, 0x00);
  175. snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2, 0xFF, 0x3A);
  176. /* 500usec delay is needed as per HW requirement */
  177. usleep_range(500, 500 + WCD_USLEEP_RANGE);
  178. }
  179. dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s\n",
  180. __func__, clsh_d->buck_users, enable, mode_to_str(mode));
  181. }
  182. static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec,
  183. struct wcd_clsh_cdc_info *clsh_d,
  184. int mode,
  185. bool enable)
  186. {
  187. /* enable/disable flyback */
  188. if ((enable && (++clsh_d->flyback_users == 1)) ||
  189. (!enable && (--clsh_d->flyback_users == 0))) {
  190. snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
  191. (1 << 6), (enable << 6));
  192. /*
  193. * 100us sleep is required after flyback enable/disable
  194. * as per HW requirement
  195. */
  196. usleep_range(100, 110);
  197. snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
  198. 0xE0, 0xE0);
  199. /* 500usec delay is needed as per HW requirement */
  200. usleep_range(500, 500 + WCD_USLEEP_RANGE);
  201. }
  202. dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s\n",
  203. __func__, clsh_d->flyback_users, enable, mode_to_str(mode));
  204. }
  205. static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec,
  206. int mode)
  207. {
  208. u8 val = 0;
  209. switch (mode) {
  210. case CLS_H_NORMAL:
  211. case CLS_H_LOHIFI:
  212. val = 0x00;
  213. break;
  214. case CLS_AB:
  215. case CLS_H_ULP:
  216. val = 0x0C;
  217. break;
  218. case CLS_AB_HIFI:
  219. case CLS_H_HIFI:
  220. val = 0x08;
  221. break;
  222. case CLS_H_LP:
  223. val = 0x04;
  224. break;
  225. default:
  226. dev_err(codec->dev, "%s:Invalid mode %d\n", __func__, mode);
  227. return;
  228. };
  229. snd_soc_update_bits(codec, WCD9XXX_ANA_HPH, 0x0C, val);
  230. }
  231. static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode)
  232. {
  233. snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A);
  234. snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0);
  235. /* Sleep needed to avoid click and pop as per HW requirement */
  236. usleep_range(100, 110);
  237. }
  238. static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec,
  239. int mode)
  240. {
  241. snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
  242. 0x02, 0x00);
  243. }
  244. static void wcd_clsh_state_ear_aux(struct snd_soc_codec *codec,
  245. struct wcd_clsh_cdc_info *clsh_d,
  246. u8 req_state, bool is_enable, int mode)
  247. {
  248. dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
  249. is_enable ? "enable" : "disable");
  250. }
  251. static void wcd_clsh_state_hph_aux(struct snd_soc_codec *codec,
  252. struct wcd_clsh_cdc_info *clsh_d,
  253. u8 req_state, bool is_enable, int mode)
  254. {
  255. dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
  256. is_enable ? "enable" : "disable");
  257. }
  258. static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec,
  259. struct wcd_clsh_cdc_info *clsh_d,
  260. u8 req_state, bool is_enable, int mode)
  261. {
  262. dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
  263. is_enable ? "enable" : "disable");
  264. }
  265. static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec,
  266. struct wcd_clsh_cdc_info *clsh_d,
  267. u8 req_state, bool is_enable, int mode)
  268. {
  269. dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
  270. is_enable ? "enable" : "disable");
  271. }
  272. static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
  273. struct wcd_clsh_cdc_info *clsh_d,
  274. u8 req_state, bool is_enable, int mode)
  275. {
  276. dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
  277. is_enable ? "enable" : "disable");
  278. if (mode == CLS_H_NORMAL) {
  279. dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_r\n",
  280. __func__);
  281. return;
  282. }
  283. if (is_enable) {
  284. wcd_clsh_set_buck_regulator_mode(codec, mode);
  285. wcd_clsh_set_flyback_mode(codec, mode);
  286. wcd_clsh_force_iq_ctl(codec, mode, true);
  287. wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
  288. wcd_clsh_set_flyback_current(codec, mode);
  289. wcd_clsh_set_buck_mode(codec, mode);
  290. wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
  291. wcd_clsh_set_hph_mode(codec, mode);
  292. } else {
  293. wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
  294. /* buck and flyback set to default mode and disable */
  295. wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
  296. wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
  297. wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
  298. wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
  299. wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
  300. }
  301. }
  302. static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
  303. struct wcd_clsh_cdc_info *clsh_d,
  304. u8 req_state, bool is_enable, int mode)
  305. {
  306. dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
  307. is_enable ? "enable" : "disable");
  308. if (mode == CLS_H_NORMAL) {
  309. dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_l\n",
  310. __func__);
  311. return;
  312. }
  313. if (is_enable) {
  314. wcd_clsh_set_buck_regulator_mode(codec, mode);
  315. wcd_clsh_set_flyback_mode(codec, mode);
  316. wcd_clsh_force_iq_ctl(codec, mode, true);
  317. wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
  318. wcd_clsh_set_flyback_current(codec, mode);
  319. wcd_clsh_set_buck_mode(codec, mode);
  320. wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
  321. wcd_clsh_set_hph_mode(codec, mode);
  322. } else {
  323. wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
  324. /* set buck and flyback to Default Mode */
  325. wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
  326. wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
  327. wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
  328. wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
  329. wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
  330. }
  331. }
  332. static void wcd_clsh_state_aux(struct snd_soc_codec *codec,
  333. struct wcd_clsh_cdc_info *clsh_d,
  334. u8 req_state, bool is_enable, int mode)
  335. {
  336. dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
  337. is_enable ? "enable" : "disable");
  338. if (is_enable) {
  339. wcd_clsh_set_buck_mode(codec, mode);
  340. wcd_clsh_set_flyback_mode(codec, mode);
  341. wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
  342. wcd_clsh_set_flyback_current(codec, mode);
  343. wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
  344. } else {
  345. wcd_clsh_buck_ctrl(codec, clsh_d, mode, false);
  346. wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false);
  347. wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
  348. wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
  349. }
  350. }
  351. static void wcd_clsh_state_ear(struct snd_soc_codec *codec,
  352. struct wcd_clsh_cdc_info *clsh_d,
  353. u8 req_state, bool is_enable, int mode)
  354. {
  355. dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
  356. is_enable ? "enable" : "disable");
  357. if (is_enable) {
  358. wcd_clsh_set_buck_regulator_mode(codec, mode);
  359. wcd_clsh_set_flyback_mode(codec, mode);
  360. wcd_clsh_force_iq_ctl(codec, mode, true);
  361. wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
  362. wcd_clsh_set_flyback_current(codec, mode);
  363. wcd_clsh_set_buck_mode(codec, mode);
  364. wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
  365. wcd_clsh_set_hph_mode(codec, mode);
  366. } else {
  367. wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
  368. /* set buck and flyback to Default Mode */
  369. wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
  370. wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
  371. wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
  372. wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
  373. wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
  374. }
  375. }
  376. static void wcd_clsh_state_err(struct snd_soc_codec *codec,
  377. struct wcd_clsh_cdc_info *clsh_d,
  378. u8 req_state, bool is_enable, int mode)
  379. {
  380. char msg[128];
  381. dev_err(codec->dev,
  382. "%s Wrong request for class H state machine requested to %s %s\n",
  383. __func__, is_enable ? "enable" : "disable",
  384. state_to_str(req_state, msg, sizeof(msg)));
  385. }
  386. /*
  387. * Function: wcd_clsh_is_state_valid
  388. * Params: state
  389. * Description:
  390. * Provides information on valid states of Class H configuration
  391. */
  392. static bool wcd_clsh_is_state_valid(u8 state)
  393. {
  394. switch (state) {
  395. case WCD_CLSH_STATE_IDLE:
  396. case WCD_CLSH_STATE_EAR:
  397. case WCD_CLSH_STATE_HPHL:
  398. case WCD_CLSH_STATE_HPHR:
  399. case WCD_CLSH_STATE_HPH_ST:
  400. case WCD_CLSH_STATE_AUX:
  401. case WCD_CLSH_STATE_HPHL_AUX:
  402. case WCD_CLSH_STATE_HPHR_AUX:
  403. case WCD_CLSH_STATE_HPH_ST_AUX:
  404. case WCD_CLSH_STATE_EAR_AUX:
  405. return true;
  406. default:
  407. return false;
  408. };
  409. }
  410. /*
  411. * Function: wcd_cls_h_fsm
  412. * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
  413. * Description:
  414. * This function handles PRE DAC and POST DAC conditions of different devices
  415. * and updates class H configuration of different combination of devices
  416. * based on validity of their states. cdc_clsh_d will contain current
  417. * class h state information
  418. */
  419. void wcd_cls_h_fsm(struct snd_soc_codec *codec,
  420. struct wcd_clsh_cdc_info *cdc_clsh_d,
  421. u8 clsh_event, u8 req_state,
  422. int int_mode)
  423. {
  424. u8 old_state, new_state;
  425. char msg0[128], msg1[128];
  426. switch (clsh_event) {
  427. case WCD_CLSH_EVENT_PRE_DAC:
  428. old_state = cdc_clsh_d->state;
  429. new_state = old_state | req_state;
  430. if (!wcd_clsh_is_state_valid(new_state)) {
  431. dev_err(codec->dev,
  432. "%s: Class-H not a valid new state: %s\n",
  433. __func__,
  434. state_to_str(new_state, msg0, sizeof(msg0)));
  435. return;
  436. }
  437. if (new_state == old_state) {
  438. dev_err(codec->dev,
  439. "%s: Class-H already in requested state: %s\n",
  440. __func__,
  441. state_to_str(new_state, msg0, sizeof(msg0)));
  442. return;
  443. }
  444. cdc_clsh_d->state = new_state;
  445. wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode);
  446. (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
  447. CLSH_REQ_ENABLE, int_mode);
  448. dev_dbg(codec->dev,
  449. "%s: ClassH state transition from %s to %s\n",
  450. __func__, state_to_str(old_state, msg0, sizeof(msg0)),
  451. state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
  452. break;
  453. case WCD_CLSH_EVENT_POST_PA:
  454. old_state = cdc_clsh_d->state;
  455. new_state = old_state & (~req_state);
  456. if (new_state < NUM_CLSH_STATES) {
  457. if (!wcd_clsh_is_state_valid(old_state)) {
  458. dev_err(codec->dev,
  459. "%s:Invalid old state:%s\n",
  460. __func__,
  461. state_to_str(old_state, msg0,
  462. sizeof(msg0)));
  463. return;
  464. }
  465. if (new_state == old_state) {
  466. dev_err(codec->dev,
  467. "%s: Class-H already in requested state: %s\n",
  468. __func__,
  469. state_to_str(new_state, msg0,
  470. sizeof(msg0)));
  471. return;
  472. }
  473. (*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
  474. req_state, CLSH_REQ_DISABLE,
  475. int_mode);
  476. cdc_clsh_d->state = new_state;
  477. wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE);
  478. dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
  479. __func__, state_to_str(old_state, msg0,
  480. sizeof(msg0)),
  481. state_to_str(cdc_clsh_d->state, msg1,
  482. sizeof(msg1)));
  483. }
  484. break;
  485. };
  486. }
  487. EXPORT_SYMBOL(wcd_cls_h_fsm);
  488. /*
  489. * wcd_cls_h_init: Called to init clsh info
  490. *
  491. * @clsh: pointer for clsh state information.
  492. */
  493. void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh)
  494. {
  495. int i;
  496. clsh->state = WCD_CLSH_STATE_IDLE;
  497. for (i = 0; i < NUM_CLSH_STATES; i++)
  498. clsh_state_fp[i] = wcd_clsh_state_err;
  499. clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear;
  500. clsh_state_fp[WCD_CLSH_STATE_HPHL] = wcd_clsh_state_hph_l;
  501. clsh_state_fp[WCD_CLSH_STATE_HPHR] = wcd_clsh_state_hph_r;
  502. clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = wcd_clsh_state_hph_st;
  503. clsh_state_fp[WCD_CLSH_STATE_AUX] = wcd_clsh_state_aux;
  504. clsh_state_fp[WCD_CLSH_STATE_HPHL_AUX] = wcd_clsh_state_hph_aux;
  505. clsh_state_fp[WCD_CLSH_STATE_HPHR_AUX] = wcd_clsh_state_hph_aux;
  506. clsh_state_fp[WCD_CLSH_STATE_HPH_ST_AUX] =
  507. wcd_clsh_state_hph_aux;
  508. clsh_state_fp[WCD_CLSH_STATE_EAR_AUX] = wcd_clsh_state_ear_aux;
  509. clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = wcd_clsh_state_hph_ear;
  510. clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = wcd_clsh_state_hph_ear;
  511. clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = wcd_clsh_state_hph_ear;
  512. /* Set interpolaotr modes to NONE */
  513. wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE);
  514. wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE);
  515. wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE);
  516. wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_AUX, CLS_NONE);
  517. clsh->flyback_users = 0;
  518. clsh->buck_users = 0;
  519. }
  520. EXPORT_SYMBOL(wcd_cls_h_init);
  521. MODULE_DESCRIPTION("WCD Class-H Driver");
  522. MODULE_LICENSE("GPL v2");