wcd-clsh-v2.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
  3. // Copyright (c) 2017-2018, Linaro Limited
  4. #include <linux/slab.h>
  5. #include <sound/soc.h>
  6. #include <linux/kernel.h>
  7. #include <linux/delay.h>
  8. #include "wcd9335.h"
  9. #include "wcd-clsh-v2.h"
  10. struct wcd_clsh_ctrl {
  11. int state;
  12. int mode;
  13. int flyback_users;
  14. int buck_users;
  15. int clsh_users;
  16. int codec_version;
  17. struct snd_soc_component *comp;
  18. };
  19. /* Class-H registers for codecs from and above WCD9335 */
  20. #define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0xB, 0x42)
  21. #define WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK BIT(6)
  22. #define WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE BIT(6)
  23. #define WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE 0
  24. #define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0xB, 0x56)
  25. #define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0xB, 0x6A)
  26. #define WCD9XXX_A_CDC_CLSH_K1_MSB WCD9335_REG(0xC, 0x08)
  27. #define WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK GENMASK(3, 0)
  28. #define WCD9XXX_A_CDC_CLSH_K1_LSB WCD9335_REG(0xC, 0x09)
  29. #define WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK GENMASK(7, 0)
  30. #define WCD9XXX_A_ANA_RX_SUPPLIES WCD9335_REG(0x6, 0x08)
  31. #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK BIT(1)
  32. #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H 0
  33. #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB BIT(1)
  34. #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK BIT(2)
  35. #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA BIT(2)
  36. #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT 0
  37. #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK BIT(3)
  38. #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA BIT(3)
  39. #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT 0
  40. #define WCD9XXX_A_ANA_RX_VNEG_EN_MASK BIT(6)
  41. #define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT 6
  42. #define WCD9XXX_A_ANA_RX_VNEG_ENABLE BIT(6)
  43. #define WCD9XXX_A_ANA_RX_VNEG_DISABLE 0
  44. #define WCD9XXX_A_ANA_RX_VPOS_EN_MASK BIT(7)
  45. #define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT 7
  46. #define WCD9XXX_A_ANA_RX_VPOS_ENABLE BIT(7)
  47. #define WCD9XXX_A_ANA_RX_VPOS_DISABLE 0
  48. #define WCD9XXX_A_ANA_HPH WCD9335_REG(0x6, 0x09)
  49. #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK GENMASK(3, 2)
  50. #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA 0x08
  51. #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP 0x04
  52. #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL 0x0
  53. #define WCD9XXX_A_CDC_CLSH_CRC WCD9335_REG(0xC, 0x01)
  54. #define WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK BIT(0)
  55. #define WCD9XXX_A_CDC_CLSH_CRC_CLK_ENABLE BIT(0)
  56. #define WCD9XXX_A_CDC_CLSH_CRC_CLK_DISABLE 0
  57. #define WCD9XXX_FLYBACK_EN WCD9335_REG(0x6, 0xA4)
  58. #define WCD9XXX_FLYBACK_EN_DELAY_SEL_MASK GENMASK(6, 5)
  59. #define WCD9XXX_FLYBACK_EN_DELAY_26P25_US 0x40
  60. #define WCD9XXX_FLYBACK_EN_RESET_BY_EXT_MASK BIT(4)
  61. #define WCD9XXX_FLYBACK_EN_PWDN_WITHOUT_DELAY BIT(4)
  62. #define WCD9XXX_FLYBACK_EN_PWDN_WITH_DELAY 0
  63. #define WCD9XXX_RX_BIAS_FLYB_BUFF WCD9335_REG(0x6, 0xC7)
  64. #define WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4)
  65. #define WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(3, 0)
  66. #define WCD9XXX_HPH_L_EN WCD9335_REG(0x6, 0xD3)
  67. #define WCD9XXX_HPH_CONST_SEL_L_MASK GENMASK(7, 3)
  68. #define WCD9XXX_HPH_CONST_SEL_BYPASS 0
  69. #define WCD9XXX_HPH_CONST_SEL_LP_PATH 0x40
  70. #define WCD9XXX_HPH_CONST_SEL_HQ_PATH 0x80
  71. #define WCD9XXX_HPH_R_EN WCD9335_REG(0x6, 0xD6)
  72. #define WCD9XXX_HPH_REFBUFF_UHQA_CTL WCD9335_REG(0x6, 0xDD)
  73. #define WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK GENMASK(2, 0)
  74. #define WCD9XXX_CLASSH_CTRL_VCL_2 WCD9335_REG(0x6, 0x9B)
  75. #define WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK GENMASK(5, 4)
  76. #define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM 0x20
  77. #define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM 0x0
  78. #define WCD9XXX_CDC_RX1_RX_PATH_CTL WCD9335_REG(0xB, 0x55)
  79. #define WCD9XXX_CDC_RX2_RX_PATH_CTL WCD9335_REG(0xB, 0x69)
  80. #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0xD, 0x41)
  81. #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0)
  82. #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_11P3_EN_MASK BIT(1)
  83. #define WCD9XXX_CLASSH_CTRL_CCL_1 WCD9335_REG(0x6, 0x9C)
  84. #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK GENMASK(7, 4)
  85. #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA 0x50
  86. #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA 0x30
  87. #define WCD9XXX_BASE_ADDRESS 0x3000
  88. #define WCD9XXX_ANA_RX_SUPPLIES (WCD9XXX_BASE_ADDRESS+0x008)
  89. #define WCD9XXX_ANA_HPH (WCD9XXX_BASE_ADDRESS+0x009)
  90. #define WCD9XXX_CLASSH_MODE_2 (WCD9XXX_BASE_ADDRESS+0x098)
  91. #define WCD9XXX_CLASSH_MODE_3 (WCD9XXX_BASE_ADDRESS+0x099)
  92. #define WCD9XXX_FLYBACK_VNEG_CTRL_1 (WCD9XXX_BASE_ADDRESS+0x0A5)
  93. #define WCD9XXX_FLYBACK_VNEG_CTRL_4 (WCD9XXX_BASE_ADDRESS+0x0A8)
  94. #define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (WCD9XXX_BASE_ADDRESS+0x0AF)
  95. #define WCD9XXX_RX_BIAS_HPH_LOWPOWER (WCD9XXX_BASE_ADDRESS+0x0BF)
  96. #define WCD9XXX_V3_RX_BIAS_FLYB_BUFF (WCD9XXX_BASE_ADDRESS+0x0C7)
  97. #define WCD9XXX_HPH_PA_CTL1 (WCD9XXX_BASE_ADDRESS+0x0D1)
  98. #define WCD9XXX_HPH_NEW_INT_PA_MISC2 (WCD9XXX_BASE_ADDRESS+0x138)
  99. #define CLSH_REQ_ENABLE true
  100. #define CLSH_REQ_DISABLE false
  101. #define WCD_USLEEP_RANGE 50
  102. enum {
  103. DAC_GAIN_0DB = 0,
  104. DAC_GAIN_0P2DB,
  105. DAC_GAIN_0P4DB,
  106. DAC_GAIN_0P6DB,
  107. DAC_GAIN_0P8DB,
  108. DAC_GAIN_M0P2DB,
  109. DAC_GAIN_M0P4DB,
  110. DAC_GAIN_M0P6DB,
  111. };
  112. static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl,
  113. bool enable)
  114. {
  115. struct snd_soc_component *comp = ctrl->comp;
  116. if ((enable && ++ctrl->clsh_users == 1) ||
  117. (!enable && --ctrl->clsh_users == 0))
  118. snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC,
  119. WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK,
  120. enable);
  121. if (ctrl->clsh_users < 0)
  122. ctrl->clsh_users = 0;
  123. }
  124. static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp)
  125. {
  126. return snd_soc_component_read(comp, WCD9XXX_A_CDC_CLSH_CRC) &
  127. WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK;
  128. }
  129. static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp,
  130. int mode)
  131. {
  132. /* set to HIFI */
  133. if (mode == CLS_H_HIFI)
  134. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
  135. WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,
  136. WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA);
  137. else
  138. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
  139. WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,
  140. WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT);
  141. }
  142. static void wcd_clsh_v3_set_buck_mode(struct snd_soc_component *component,
  143. int mode)
  144. {
  145. if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
  146. mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI)
  147. snd_soc_component_update_bits(component,
  148. WCD9XXX_ANA_RX_SUPPLIES,
  149. 0x08, 0x08); /* set to HIFI */
  150. else
  151. snd_soc_component_update_bits(component,
  152. WCD9XXX_ANA_RX_SUPPLIES,
  153. 0x08, 0x00); /* set to default */
  154. }
  155. static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp,
  156. int mode)
  157. {
  158. /* set to HIFI */
  159. if (mode == CLS_H_HIFI)
  160. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
  161. WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,
  162. WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA);
  163. else
  164. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
  165. WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,
  166. WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT);
  167. }
  168. static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl,
  169. int mode,
  170. bool enable)
  171. {
  172. struct snd_soc_component *comp = ctrl->comp;
  173. /* enable/disable buck */
  174. if ((enable && (++ctrl->buck_users == 1)) ||
  175. (!enable && (--ctrl->buck_users == 0)))
  176. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
  177. WCD9XXX_A_ANA_RX_VPOS_EN_MASK,
  178. enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT);
  179. /*
  180. * 500us sleep is required after buck enable/disable
  181. * as per HW requirement
  182. */
  183. usleep_range(500, 500 + WCD_USLEEP_RANGE);
  184. }
  185. static void wcd_clsh_v3_buck_ctrl(struct snd_soc_component *component,
  186. struct wcd_clsh_ctrl *ctrl,
  187. int mode,
  188. bool enable)
  189. {
  190. /* enable/disable buck */
  191. if ((enable && (++ctrl->buck_users == 1)) ||
  192. (!enable && (--ctrl->buck_users == 0))) {
  193. snd_soc_component_update_bits(component,
  194. WCD9XXX_ANA_RX_SUPPLIES,
  195. (1 << 7), (enable << 7));
  196. /*
  197. * 500us sleep is required after buck enable/disable
  198. * as per HW requirement
  199. */
  200. usleep_range(500, 510);
  201. if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
  202. mode == CLS_H_HIFI || mode == CLS_H_LP)
  203. snd_soc_component_update_bits(component,
  204. WCD9XXX_CLASSH_MODE_3,
  205. 0x02, 0x00);
  206. snd_soc_component_update_bits(component,
  207. WCD9XXX_CLASSH_MODE_2,
  208. 0xFF, 0x3A);
  209. /* 500usec delay is needed as per HW requirement */
  210. usleep_range(500, 500 + WCD_USLEEP_RANGE);
  211. }
  212. }
  213. static void wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl *ctrl,
  214. int mode,
  215. bool enable)
  216. {
  217. struct snd_soc_component *comp = ctrl->comp;
  218. /* enable/disable flyback */
  219. if ((enable && (++ctrl->flyback_users == 1)) ||
  220. (!enable && (--ctrl->flyback_users == 0))) {
  221. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
  222. WCD9XXX_A_ANA_RX_VNEG_EN_MASK,
  223. enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT);
  224. /* 100usec delay is needed as per HW requirement */
  225. usleep_range(100, 110);
  226. }
  227. /*
  228. * 500us sleep is required after flyback enable/disable
  229. * as per HW requirement
  230. */
  231. usleep_range(500, 500 + WCD_USLEEP_RANGE);
  232. }
  233. static void wcd_clsh_set_gain_path(struct wcd_clsh_ctrl *ctrl, int mode)
  234. {
  235. struct snd_soc_component *comp = ctrl->comp;
  236. int val = 0;
  237. switch (mode) {
  238. case CLS_H_NORMAL:
  239. case CLS_AB:
  240. val = WCD9XXX_HPH_CONST_SEL_BYPASS;
  241. break;
  242. case CLS_H_HIFI:
  243. val = WCD9XXX_HPH_CONST_SEL_HQ_PATH;
  244. break;
  245. case CLS_H_LP:
  246. val = WCD9XXX_HPH_CONST_SEL_LP_PATH;
  247. break;
  248. }
  249. snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN,
  250. WCD9XXX_HPH_CONST_SEL_L_MASK,
  251. val);
  252. snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN,
  253. WCD9XXX_HPH_CONST_SEL_L_MASK,
  254. val);
  255. }
  256. static void wcd_clsh_v2_set_hph_mode(struct snd_soc_component *comp, int mode)
  257. {
  258. int val = 0, gain = 0, res_val;
  259. int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
  260. res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM;
  261. switch (mode) {
  262. case CLS_H_NORMAL:
  263. res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM;
  264. val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;
  265. gain = DAC_GAIN_0DB;
  266. ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
  267. break;
  268. case CLS_AB:
  269. val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;
  270. gain = DAC_GAIN_0DB;
  271. ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
  272. break;
  273. case CLS_H_HIFI:
  274. val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA;
  275. gain = DAC_GAIN_M0P2DB;
  276. ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
  277. break;
  278. case CLS_H_LP:
  279. val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP;
  280. ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA;
  281. break;
  282. }
  283. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH,
  284. WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val);
  285. snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2,
  286. WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK,
  287. res_val);
  288. if (mode != CLS_H_LP)
  289. snd_soc_component_update_bits(comp,
  290. WCD9XXX_HPH_REFBUFF_UHQA_CTL,
  291. WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK,
  292. gain);
  293. snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1,
  294. WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK,
  295. ipeak);
  296. }
  297. static void wcd_clsh_v3_set_hph_mode(struct snd_soc_component *component,
  298. int mode)
  299. {
  300. u8 val;
  301. switch (mode) {
  302. case CLS_H_NORMAL:
  303. val = 0x00;
  304. break;
  305. case CLS_AB:
  306. case CLS_H_ULP:
  307. val = 0x0C;
  308. break;
  309. case CLS_AB_HIFI:
  310. case CLS_H_HIFI:
  311. val = 0x08;
  312. break;
  313. case CLS_H_LP:
  314. case CLS_H_LOHIFI:
  315. case CLS_AB_LP:
  316. case CLS_AB_LOHIFI:
  317. val = 0x04;
  318. break;
  319. default:
  320. dev_err(component->dev, "%s:Invalid mode %d\n", __func__, mode);
  321. return;
  322. }
  323. snd_soc_component_update_bits(component, WCD9XXX_ANA_HPH, 0x0C, val);
  324. }
  325. void wcd_clsh_set_hph_mode(struct wcd_clsh_ctrl *ctrl, int mode)
  326. {
  327. struct snd_soc_component *comp = ctrl->comp;
  328. if (ctrl->codec_version >= WCD937X)
  329. wcd_clsh_v3_set_hph_mode(comp, mode);
  330. else
  331. wcd_clsh_v2_set_hph_mode(comp, mode);
  332. }
  333. static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp,
  334. int mode)
  335. {
  336. snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,
  337. WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A);
  338. snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,
  339. WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A);
  340. /* Sleep needed to avoid click and pop as per HW requirement */
  341. usleep_range(100, 110);
  342. }
  343. static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_component *comp,
  344. int mode)
  345. {
  346. if (mode == CLS_AB)
  347. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
  348. WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,
  349. WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB);
  350. else
  351. snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
  352. WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,
  353. WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H);
  354. }
  355. static void wcd_clsh_v3_set_buck_regulator_mode(struct snd_soc_component *component,
  356. int mode)
  357. {
  358. snd_soc_component_update_bits(component, WCD9XXX_ANA_RX_SUPPLIES,
  359. 0x02, 0x00);
  360. }
  361. static void wcd_clsh_v3_set_flyback_mode(struct snd_soc_component *component,
  362. int mode)
  363. {
  364. if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
  365. mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI) {
  366. snd_soc_component_update_bits(component,
  367. WCD9XXX_ANA_RX_SUPPLIES,
  368. 0x04, 0x04);
  369. snd_soc_component_update_bits(component,
  370. WCD9XXX_FLYBACK_VNEG_CTRL_4,
  371. 0xF0, 0x80);
  372. } else {
  373. snd_soc_component_update_bits(component,
  374. WCD9XXX_ANA_RX_SUPPLIES,
  375. 0x04, 0x00); /* set to Default */
  376. snd_soc_component_update_bits(component,
  377. WCD9XXX_FLYBACK_VNEG_CTRL_4,
  378. 0xF0, 0x70);
  379. }
  380. }
  381. static void wcd_clsh_v3_force_iq_ctl(struct snd_soc_component *component,
  382. int mode, bool enable)
  383. {
  384. if (enable) {
  385. snd_soc_component_update_bits(component,
  386. WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
  387. 0xE0, 0xA0);
  388. /* 100usec delay is needed as per HW requirement */
  389. usleep_range(100, 110);
  390. snd_soc_component_update_bits(component,
  391. WCD9XXX_CLASSH_MODE_3,
  392. 0x02, 0x02);
  393. snd_soc_component_update_bits(component,
  394. WCD9XXX_CLASSH_MODE_2,
  395. 0xFF, 0x1C);
  396. if (mode == CLS_H_LOHIFI || mode == CLS_AB_LOHIFI) {
  397. snd_soc_component_update_bits(component,
  398. WCD9XXX_HPH_NEW_INT_PA_MISC2,
  399. 0x20, 0x20);
  400. snd_soc_component_update_bits(component,
  401. WCD9XXX_RX_BIAS_HPH_LOWPOWER,
  402. 0xF0, 0xC0);
  403. snd_soc_component_update_bits(component,
  404. WCD9XXX_HPH_PA_CTL1,
  405. 0x0E, 0x02);
  406. }
  407. } else {
  408. snd_soc_component_update_bits(component,
  409. WCD9XXX_HPH_NEW_INT_PA_MISC2,
  410. 0x20, 0x00);
  411. snd_soc_component_update_bits(component,
  412. WCD9XXX_RX_BIAS_HPH_LOWPOWER,
  413. 0xF0, 0x80);
  414. snd_soc_component_update_bits(component,
  415. WCD9XXX_HPH_PA_CTL1,
  416. 0x0E, 0x06);
  417. }
  418. }
  419. static void wcd_clsh_v3_flyback_ctrl(struct snd_soc_component *component,
  420. struct wcd_clsh_ctrl *ctrl,
  421. int mode,
  422. bool enable)
  423. {
  424. /* enable/disable flyback */
  425. if ((enable && (++ctrl->flyback_users == 1)) ||
  426. (!enable && (--ctrl->flyback_users == 0))) {
  427. snd_soc_component_update_bits(component,
  428. WCD9XXX_FLYBACK_VNEG_CTRL_1,
  429. 0xE0, 0xE0);
  430. snd_soc_component_update_bits(component,
  431. WCD9XXX_ANA_RX_SUPPLIES,
  432. (1 << 6), (enable << 6));
  433. /*
  434. * 100us sleep is required after flyback enable/disable
  435. * as per HW requirement
  436. */
  437. usleep_range(100, 110);
  438. snd_soc_component_update_bits(component,
  439. WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
  440. 0xE0, 0xE0);
  441. /* 500usec delay is needed as per HW requirement */
  442. usleep_range(500, 500 + WCD_USLEEP_RANGE);
  443. }
  444. }
  445. static void wcd_clsh_v3_set_flyback_current(struct snd_soc_component *component,
  446. int mode)
  447. {
  448. snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF,
  449. 0x0F, 0x0A);
  450. snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF,
  451. 0xF0, 0xA0);
  452. /* Sleep needed to avoid click and pop as per HW requirement */
  453. usleep_range(100, 110);
  454. }
  455. static void wcd_clsh_v3_state_aux(struct wcd_clsh_ctrl *ctrl, int req_state,
  456. bool is_enable, int mode)
  457. {
  458. struct snd_soc_component *component = ctrl->comp;
  459. if (is_enable) {
  460. wcd_clsh_v3_set_buck_mode(component, mode);
  461. wcd_clsh_v3_set_flyback_mode(component, mode);
  462. wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
  463. wcd_clsh_v3_set_flyback_current(component, mode);
  464. wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
  465. } else {
  466. wcd_clsh_v3_buck_ctrl(component, ctrl, mode, false);
  467. wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, false);
  468. wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
  469. wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
  470. }
  471. }
  472. static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state,
  473. bool is_enable, int mode)
  474. {
  475. struct snd_soc_component *comp = ctrl->comp;
  476. if (mode != CLS_AB) {
  477. dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n",
  478. __func__, mode);
  479. return;
  480. }
  481. if (is_enable) {
  482. wcd_clsh_set_buck_regulator_mode(comp, mode);
  483. wcd_clsh_set_buck_mode(comp, mode);
  484. wcd_clsh_set_flyback_mode(comp, mode);
  485. wcd_clsh_flyback_ctrl(ctrl, mode, true);
  486. wcd_clsh_set_flyback_current(comp, mode);
  487. wcd_clsh_buck_ctrl(ctrl, mode, true);
  488. } else {
  489. wcd_clsh_buck_ctrl(ctrl, mode, false);
  490. wcd_clsh_flyback_ctrl(ctrl, mode, false);
  491. wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
  492. wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
  493. wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
  494. }
  495. }
  496. static void wcd_clsh_v3_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,
  497. bool is_enable, int mode)
  498. {
  499. struct snd_soc_component *component = ctrl->comp;
  500. if (mode == CLS_H_NORMAL) {
  501. dev_dbg(component->dev, "%s: Normal mode not applicable for hph_r\n",
  502. __func__);
  503. return;
  504. }
  505. if (is_enable) {
  506. wcd_clsh_v3_set_buck_regulator_mode(component, mode);
  507. wcd_clsh_v3_set_flyback_mode(component, mode);
  508. wcd_clsh_v3_force_iq_ctl(component, mode, true);
  509. wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
  510. wcd_clsh_v3_set_flyback_current(component, mode);
  511. wcd_clsh_v3_set_buck_mode(component, mode);
  512. wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
  513. wcd_clsh_v3_set_hph_mode(component, mode);
  514. } else {
  515. wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
  516. /* buck and flyback set to default mode and disable */
  517. wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
  518. wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
  519. wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
  520. wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
  521. wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
  522. }
  523. }
  524. static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,
  525. bool is_enable, int mode)
  526. {
  527. struct snd_soc_component *comp = ctrl->comp;
  528. if (mode == CLS_H_NORMAL) {
  529. dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n",
  530. __func__);
  531. return;
  532. }
  533. if (is_enable) {
  534. if (mode != CLS_AB) {
  535. wcd_enable_clsh_block(ctrl, true);
  536. /*
  537. * These K1 values depend on the Headphone Impedance
  538. * For now it is assumed to be 16 ohm
  539. */
  540. snd_soc_component_update_bits(comp,
  541. WCD9XXX_A_CDC_CLSH_K1_MSB,
  542. WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,
  543. 0x00);
  544. snd_soc_component_update_bits(comp,
  545. WCD9XXX_A_CDC_CLSH_K1_LSB,
  546. WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,
  547. 0xC0);
  548. snd_soc_component_update_bits(comp,
  549. WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
  550. WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
  551. WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
  552. }
  553. wcd_clsh_set_buck_regulator_mode(comp, mode);
  554. wcd_clsh_set_flyback_mode(comp, mode);
  555. wcd_clsh_flyback_ctrl(ctrl, mode, true);
  556. wcd_clsh_set_flyback_current(comp, mode);
  557. wcd_clsh_set_buck_mode(comp, mode);
  558. wcd_clsh_buck_ctrl(ctrl, mode, true);
  559. wcd_clsh_v2_set_hph_mode(comp, mode);
  560. wcd_clsh_set_gain_path(ctrl, mode);
  561. } else {
  562. wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL);
  563. if (mode != CLS_AB) {
  564. snd_soc_component_update_bits(comp,
  565. WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
  566. WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
  567. WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
  568. wcd_enable_clsh_block(ctrl, false);
  569. }
  570. /* buck and flyback set to default mode and disable */
  571. wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);
  572. wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);
  573. wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
  574. wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
  575. wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
  576. }
  577. }
  578. static void wcd_clsh_v3_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,
  579. bool is_enable, int mode)
  580. {
  581. struct snd_soc_component *component = ctrl->comp;
  582. if (mode == CLS_H_NORMAL) {
  583. dev_dbg(component->dev, "%s: Normal mode not applicable for hph_l\n",
  584. __func__);
  585. return;
  586. }
  587. if (is_enable) {
  588. wcd_clsh_v3_set_buck_regulator_mode(component, mode);
  589. wcd_clsh_v3_set_flyback_mode(component, mode);
  590. wcd_clsh_v3_force_iq_ctl(component, mode, true);
  591. wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
  592. wcd_clsh_v3_set_flyback_current(component, mode);
  593. wcd_clsh_v3_set_buck_mode(component, mode);
  594. wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
  595. wcd_clsh_v3_set_hph_mode(component, mode);
  596. } else {
  597. wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
  598. /* set buck and flyback to Default Mode */
  599. wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
  600. wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
  601. wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
  602. wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
  603. wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
  604. }
  605. }
  606. static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,
  607. bool is_enable, int mode)
  608. {
  609. struct snd_soc_component *comp = ctrl->comp;
  610. if (mode == CLS_H_NORMAL) {
  611. dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n",
  612. __func__);
  613. return;
  614. }
  615. if (is_enable) {
  616. if (mode != CLS_AB) {
  617. wcd_enable_clsh_block(ctrl, true);
  618. /*
  619. * These K1 values depend on the Headphone Impedance
  620. * For now it is assumed to be 16 ohm
  621. */
  622. snd_soc_component_update_bits(comp,
  623. WCD9XXX_A_CDC_CLSH_K1_MSB,
  624. WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,
  625. 0x00);
  626. snd_soc_component_update_bits(comp,
  627. WCD9XXX_A_CDC_CLSH_K1_LSB,
  628. WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,
  629. 0xC0);
  630. snd_soc_component_update_bits(comp,
  631. WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
  632. WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
  633. WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
  634. }
  635. wcd_clsh_set_buck_regulator_mode(comp, mode);
  636. wcd_clsh_set_flyback_mode(comp, mode);
  637. wcd_clsh_flyback_ctrl(ctrl, mode, true);
  638. wcd_clsh_set_flyback_current(comp, mode);
  639. wcd_clsh_set_buck_mode(comp, mode);
  640. wcd_clsh_buck_ctrl(ctrl, mode, true);
  641. wcd_clsh_v2_set_hph_mode(comp, mode);
  642. wcd_clsh_set_gain_path(ctrl, mode);
  643. } else {
  644. wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL);
  645. if (mode != CLS_AB) {
  646. snd_soc_component_update_bits(comp,
  647. WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
  648. WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
  649. WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
  650. wcd_enable_clsh_block(ctrl, false);
  651. }
  652. /* set buck and flyback to Default Mode */
  653. wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);
  654. wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);
  655. wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
  656. wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
  657. wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
  658. }
  659. }
  660. static void wcd_clsh_v3_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,
  661. bool is_enable, int mode)
  662. {
  663. struct snd_soc_component *component = ctrl->comp;
  664. if (is_enable) {
  665. wcd_clsh_v3_set_buck_regulator_mode(component, mode);
  666. wcd_clsh_v3_set_flyback_mode(component, mode);
  667. wcd_clsh_v3_force_iq_ctl(component, mode, true);
  668. wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
  669. wcd_clsh_v3_set_flyback_current(component, mode);
  670. wcd_clsh_v3_set_buck_mode(component, mode);
  671. wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
  672. wcd_clsh_v3_set_hph_mode(component, mode);
  673. } else {
  674. wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
  675. /* set buck and flyback to Default Mode */
  676. wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
  677. wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
  678. wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
  679. wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
  680. wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
  681. }
  682. }
  683. static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,
  684. bool is_enable, int mode)
  685. {
  686. struct snd_soc_component *comp = ctrl->comp;
  687. if (mode != CLS_H_NORMAL) {
  688. dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n",
  689. __func__, mode);
  690. return;
  691. }
  692. if (is_enable) {
  693. wcd_enable_clsh_block(ctrl, true);
  694. snd_soc_component_update_bits(comp,
  695. WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
  696. WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
  697. WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
  698. wcd_clsh_set_buck_mode(comp, mode);
  699. wcd_clsh_set_flyback_mode(comp, mode);
  700. wcd_clsh_flyback_ctrl(ctrl, mode, true);
  701. wcd_clsh_set_flyback_current(comp, mode);
  702. wcd_clsh_buck_ctrl(ctrl, mode, true);
  703. } else {
  704. snd_soc_component_update_bits(comp,
  705. WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
  706. WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
  707. WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
  708. wcd_enable_clsh_block(ctrl, false);
  709. wcd_clsh_buck_ctrl(ctrl, mode, false);
  710. wcd_clsh_flyback_ctrl(ctrl, mode, false);
  711. wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
  712. wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
  713. }
  714. }
  715. static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state,
  716. bool is_enable, int mode)
  717. {
  718. switch (req_state) {
  719. case WCD_CLSH_STATE_EAR:
  720. if (ctrl->codec_version >= WCD937X)
  721. wcd_clsh_v3_state_ear(ctrl, req_state, is_enable, mode);
  722. else
  723. wcd_clsh_state_ear(ctrl, req_state, is_enable, mode);
  724. break;
  725. case WCD_CLSH_STATE_HPHL:
  726. if (ctrl->codec_version >= WCD937X)
  727. wcd_clsh_v3_state_hph_l(ctrl, req_state, is_enable, mode);
  728. else
  729. wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode);
  730. break;
  731. case WCD_CLSH_STATE_HPHR:
  732. if (ctrl->codec_version >= WCD937X)
  733. wcd_clsh_v3_state_hph_r(ctrl, req_state, is_enable, mode);
  734. else
  735. wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode);
  736. break;
  737. case WCD_CLSH_STATE_LO:
  738. if (ctrl->codec_version < WCD937X)
  739. wcd_clsh_state_lo(ctrl, req_state, is_enable, mode);
  740. break;
  741. case WCD_CLSH_STATE_AUX:
  742. if (ctrl->codec_version >= WCD937X)
  743. wcd_clsh_v3_state_aux(ctrl, req_state, is_enable, mode);
  744. break;
  745. default:
  746. break;
  747. }
  748. return 0;
  749. }
  750. /*
  751. * Function: wcd_clsh_is_state_valid
  752. * Params: state
  753. * Description:
  754. * Provides information on valid states of Class H configuration
  755. */
  756. static bool wcd_clsh_is_state_valid(int state)
  757. {
  758. switch (state) {
  759. case WCD_CLSH_STATE_IDLE:
  760. case WCD_CLSH_STATE_EAR:
  761. case WCD_CLSH_STATE_HPHL:
  762. case WCD_CLSH_STATE_HPHR:
  763. case WCD_CLSH_STATE_LO:
  764. case WCD_CLSH_STATE_AUX:
  765. return true;
  766. default:
  767. return false;
  768. };
  769. }
  770. /*
  771. * Function: wcd_clsh_fsm
  772. * Params: ctrl, req_state, req_type, clsh_event
  773. * Description:
  774. * This function handles PRE DAC and POST DAC conditions of different devices
  775. * and updates class H configuration of different combination of devices
  776. * based on validity of their states. ctrl will contain current
  777. * class h state information
  778. */
  779. int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl,
  780. enum wcd_clsh_event clsh_event,
  781. int nstate,
  782. enum wcd_clsh_mode mode)
  783. {
  784. struct snd_soc_component *comp = ctrl->comp;
  785. if (nstate == ctrl->state)
  786. return 0;
  787. if (!wcd_clsh_is_state_valid(nstate)) {
  788. dev_err(comp->dev, "Class-H not a valid new state:\n");
  789. return -EINVAL;
  790. }
  791. switch (clsh_event) {
  792. case WCD_CLSH_EVENT_PRE_DAC:
  793. _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode);
  794. break;
  795. case WCD_CLSH_EVENT_POST_PA:
  796. _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode);
  797. break;
  798. }
  799. ctrl->state = nstate;
  800. ctrl->mode = mode;
  801. return 0;
  802. }
  803. int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl)
  804. {
  805. return ctrl->state;
  806. }
  807. struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp,
  808. int version)
  809. {
  810. struct wcd_clsh_ctrl *ctrl;
  811. ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
  812. if (!ctrl)
  813. return ERR_PTR(-ENOMEM);
  814. ctrl->state = WCD_CLSH_STATE_IDLE;
  815. ctrl->comp = comp;
  816. ctrl->codec_version = version;
  817. return ctrl;
  818. }
  819. void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl)
  820. {
  821. kfree(ctrl);
  822. }