Commit | Line | Data |
---|---|---|
cc2e324d SK |
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 | ||
5 | #include <linux/slab.h> | |
6 | #include <sound/soc.h> | |
7 | #include <linux/kernel.h> | |
8 | #include <linux/delay.h> | |
9 | #include "wcd9335.h" | |
10 | #include "wcd-clsh-v2.h" | |
11 | ||
12 | struct wcd_clsh_ctrl { | |
13 | int state; | |
14 | int mode; | |
15 | int flyback_users; | |
16 | int buck_users; | |
17 | int clsh_users; | |
18 | int codec_version; | |
19 | struct snd_soc_component *comp; | |
20 | }; | |
21 | ||
22 | /* Class-H registers for codecs from and above WCD9335 */ | |
23 | #define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0xB, 0x42) | |
24 | #define WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK BIT(6) | |
25 | #define WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE BIT(6) | |
26 | #define WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE 0 | |
27 | #define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0xB, 0x56) | |
28 | #define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0xB, 0x6A) | |
29 | #define WCD9XXX_A_CDC_CLSH_K1_MSB WCD9335_REG(0xC, 0x08) | |
30 | #define WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK GENMASK(3, 0) | |
31 | #define WCD9XXX_A_CDC_CLSH_K1_LSB WCD9335_REG(0xC, 0x09) | |
32 | #define WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK GENMASK(7, 0) | |
33 | #define WCD9XXX_A_ANA_RX_SUPPLIES WCD9335_REG(0x6, 0x08) | |
34 | #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK BIT(1) | |
35 | #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H 0 | |
36 | #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB BIT(1) | |
37 | #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK BIT(2) | |
38 | #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA BIT(2) | |
39 | #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT 0 | |
40 | #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK BIT(3) | |
41 | #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA BIT(3) | |
42 | #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT 0 | |
43 | #define WCD9XXX_A_ANA_RX_VNEG_EN_MASK BIT(6) | |
44 | #define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT 6 | |
45 | #define WCD9XXX_A_ANA_RX_VNEG_ENABLE BIT(6) | |
46 | #define WCD9XXX_A_ANA_RX_VNEG_DISABLE 0 | |
47 | #define WCD9XXX_A_ANA_RX_VPOS_EN_MASK BIT(7) | |
48 | #define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT 7 | |
49 | #define WCD9XXX_A_ANA_RX_VPOS_ENABLE BIT(7) | |
50 | #define WCD9XXX_A_ANA_RX_VPOS_DISABLE 0 | |
51 | #define WCD9XXX_A_ANA_HPH WCD9335_REG(0x6, 0x09) | |
52 | #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK GENMASK(3, 2) | |
53 | #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA 0x08 | |
54 | #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP 0x04 | |
55 | #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL 0x0 | |
56 | #define WCD9XXX_A_CDC_CLSH_CRC WCD9335_REG(0xC, 0x01) | |
57 | #define WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK BIT(0) | |
58 | #define WCD9XXX_A_CDC_CLSH_CRC_CLK_ENABLE BIT(0) | |
59 | #define WCD9XXX_A_CDC_CLSH_CRC_CLK_DISABLE 0 | |
60 | #define WCD9XXX_FLYBACK_EN WCD9335_REG(0x6, 0xA4) | |
61 | #define WCD9XXX_FLYBACK_EN_DELAY_SEL_MASK GENMASK(6, 5) | |
62 | #define WCD9XXX_FLYBACK_EN_DELAY_26P25_US 0x40 | |
63 | #define WCD9XXX_FLYBACK_EN_RESET_BY_EXT_MASK BIT(4) | |
64 | #define WCD9XXX_FLYBACK_EN_PWDN_WITHOUT_DELAY BIT(4) | |
65 | #define WCD9XXX_FLYBACK_EN_PWDN_WITH_DELAY 0 | |
66 | #define WCD9XXX_RX_BIAS_FLYB_BUFF WCD9335_REG(0x6, 0xC7) | |
67 | #define WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4) | |
68 | #define WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(0, 3) | |
69 | #define WCD9XXX_HPH_L_EN WCD9335_REG(0x6, 0xD3) | |
70 | #define WCD9XXX_HPH_CONST_SEL_L_MASK GENMASK(7, 3) | |
71 | #define WCD9XXX_HPH_CONST_SEL_BYPASS 0 | |
72 | #define WCD9XXX_HPH_CONST_SEL_LP_PATH 0x40 | |
73 | #define WCD9XXX_HPH_CONST_SEL_HQ_PATH 0x80 | |
74 | #define WCD9XXX_HPH_R_EN WCD9335_REG(0x6, 0xD6) | |
75 | #define WCD9XXX_HPH_REFBUFF_UHQA_CTL WCD9335_REG(0x6, 0xDD) | |
76 | #define WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK GENMASK(2, 0) | |
77 | #define WCD9XXX_CLASSH_CTRL_VCL_2 WCD9335_REG(0x6, 0x9B) | |
78 | #define WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK GENMASK(5, 4) | |
79 | #define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM 0x20 | |
80 | #define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM 0x0 | |
81 | #define WCD9XXX_CDC_RX1_RX_PATH_CTL WCD9335_REG(0xB, 0x55) | |
82 | #define WCD9XXX_CDC_RX2_RX_PATH_CTL WCD9335_REG(0xB, 0x69) | |
83 | #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0xD, 0x41) | |
84 | #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0) | |
85 | #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_11P3_EN_MASK BIT(1) | |
86 | #define WCD9XXX_CLASSH_CTRL_CCL_1 WCD9335_REG(0x6, 0x9C) | |
87 | #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK GENMASK(7, 4) | |
88 | #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA 0x50 | |
89 | #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA 0x30 | |
90 | ||
91 | #define CLSH_REQ_ENABLE true | |
92 | #define CLSH_REQ_DISABLE false | |
93 | #define WCD_USLEEP_RANGE 50 | |
94 | ||
95 | enum { | |
96 | DAC_GAIN_0DB = 0, | |
97 | DAC_GAIN_0P2DB, | |
98 | DAC_GAIN_0P4DB, | |
99 | DAC_GAIN_0P6DB, | |
100 | DAC_GAIN_0P8DB, | |
101 | DAC_GAIN_M0P2DB, | |
102 | DAC_GAIN_M0P4DB, | |
103 | DAC_GAIN_M0P6DB, | |
104 | }; | |
105 | ||
106 | static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl, | |
107 | bool enable) | |
108 | { | |
109 | struct snd_soc_component *comp = ctrl->comp; | |
110 | ||
111 | if ((enable && ++ctrl->clsh_users == 1) || | |
112 | (!enable && --ctrl->clsh_users == 0)) | |
113 | snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC, | |
114 | WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK, | |
115 | enable); | |
116 | if (ctrl->clsh_users < 0) | |
117 | ctrl->clsh_users = 0; | |
118 | } | |
119 | ||
120 | static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp) | |
121 | { | |
122 | return snd_soc_component_read32(comp, WCD9XXX_A_CDC_CLSH_CRC) & | |
123 | WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK; | |
124 | } | |
125 | ||
126 | static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp, | |
127 | int mode) | |
128 | { | |
129 | /* set to HIFI */ | |
130 | if (mode == CLS_H_HIFI) | |
131 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, | |
132 | WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK, | |
133 | WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA); | |
134 | else | |
135 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, | |
136 | WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK, | |
137 | WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT); | |
138 | } | |
139 | ||
140 | static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp, | |
141 | int mode) | |
142 | { | |
143 | /* set to HIFI */ | |
144 | if (mode == CLS_H_HIFI) | |
145 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, | |
146 | WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK, | |
147 | WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA); | |
148 | else | |
149 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, | |
150 | WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK, | |
151 | WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT); | |
152 | } | |
153 | ||
154 | static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl, | |
155 | int mode, | |
156 | bool enable) | |
157 | { | |
158 | struct snd_soc_component *comp = ctrl->comp; | |
159 | ||
160 | /* enable/disable buck */ | |
161 | if ((enable && (++ctrl->buck_users == 1)) || | |
162 | (!enable && (--ctrl->buck_users == 0))) | |
163 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, | |
164 | WCD9XXX_A_ANA_RX_VPOS_EN_MASK, | |
165 | enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT); | |
166 | /* | |
167 | * 500us sleep is required after buck enable/disable | |
168 | * as per HW requirement | |
169 | */ | |
170 | usleep_range(500, 500 + WCD_USLEEP_RANGE); | |
171 | } | |
172 | ||
173 | static void wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl *ctrl, | |
174 | int mode, | |
175 | bool enable) | |
176 | { | |
177 | struct snd_soc_component *comp = ctrl->comp; | |
178 | ||
179 | /* enable/disable flyback */ | |
180 | if ((enable && (++ctrl->flyback_users == 1)) || | |
181 | (!enable && (--ctrl->flyback_users == 0))) { | |
182 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, | |
183 | WCD9XXX_A_ANA_RX_VNEG_EN_MASK, | |
184 | enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT); | |
185 | /* 100usec delay is needed as per HW requirement */ | |
186 | usleep_range(100, 110); | |
187 | } | |
188 | /* | |
189 | * 500us sleep is required after flyback enable/disable | |
190 | * as per HW requirement | |
191 | */ | |
192 | usleep_range(500, 500 + WCD_USLEEP_RANGE); | |
193 | } | |
194 | ||
195 | static void wcd_clsh_set_gain_path(struct wcd_clsh_ctrl *ctrl, int mode) | |
196 | { | |
197 | struct snd_soc_component *comp = ctrl->comp; | |
198 | int val = 0; | |
199 | ||
200 | switch (mode) { | |
201 | case CLS_H_NORMAL: | |
202 | case CLS_AB: | |
203 | val = WCD9XXX_HPH_CONST_SEL_BYPASS; | |
204 | break; | |
205 | case CLS_H_HIFI: | |
206 | val = WCD9XXX_HPH_CONST_SEL_HQ_PATH; | |
207 | break; | |
208 | case CLS_H_LP: | |
209 | val = WCD9XXX_HPH_CONST_SEL_LP_PATH; | |
210 | break; | |
afb86626 | 211 | } |
cc2e324d SK |
212 | |
213 | snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN, | |
214 | WCD9XXX_HPH_CONST_SEL_L_MASK, | |
215 | val); | |
216 | ||
217 | snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN, | |
218 | WCD9XXX_HPH_CONST_SEL_L_MASK, | |
219 | val); | |
220 | } | |
221 | ||
222 | static void wcd_clsh_set_hph_mode(struct snd_soc_component *comp, | |
223 | int mode) | |
224 | { | |
225 | int val = 0, gain = 0, res_val; | |
226 | int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; | |
227 | ||
228 | res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM; | |
229 | switch (mode) { | |
230 | case CLS_H_NORMAL: | |
231 | res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM; | |
232 | val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL; | |
233 | gain = DAC_GAIN_0DB; | |
234 | ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; | |
235 | break; | |
236 | case CLS_AB: | |
237 | val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL; | |
238 | gain = DAC_GAIN_0DB; | |
239 | ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; | |
240 | break; | |
241 | case CLS_H_HIFI: | |
242 | val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA; | |
243 | gain = DAC_GAIN_M0P2DB; | |
244 | ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; | |
245 | break; | |
246 | case CLS_H_LP: | |
247 | val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP; | |
248 | ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA; | |
249 | break; | |
afb86626 | 250 | } |
cc2e324d SK |
251 | |
252 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH, | |
253 | WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val); | |
254 | snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2, | |
255 | WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK, | |
256 | res_val); | |
257 | if (mode != CLS_H_LP) | |
258 | snd_soc_component_update_bits(comp, | |
259 | WCD9XXX_HPH_REFBUFF_UHQA_CTL, | |
260 | WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK, | |
261 | gain); | |
262 | snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1, | |
263 | WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK, | |
264 | ipeak); | |
265 | } | |
266 | ||
267 | static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp, | |
268 | int mode) | |
269 | { | |
270 | ||
271 | snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF, | |
272 | WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A); | |
273 | snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF, | |
274 | WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A); | |
275 | /* Sleep needed to avoid click and pop as per HW requirement */ | |
276 | usleep_range(100, 110); | |
277 | } | |
278 | ||
279 | static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_component *comp, | |
280 | int mode) | |
281 | { | |
282 | if (mode == CLS_AB) | |
283 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, | |
284 | WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK, | |
285 | WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB); | |
286 | else | |
287 | snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, | |
288 | WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK, | |
289 | WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H); | |
290 | } | |
291 | ||
292 | static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state, | |
293 | bool is_enable, int mode) | |
294 | { | |
295 | struct snd_soc_component *comp = ctrl->comp; | |
296 | ||
297 | if (mode != CLS_AB) { | |
298 | dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n", | |
299 | __func__, mode); | |
300 | return; | |
301 | } | |
302 | ||
303 | if (is_enable) { | |
304 | wcd_clsh_set_buck_regulator_mode(comp, mode); | |
305 | wcd_clsh_set_buck_mode(comp, mode); | |
306 | wcd_clsh_set_flyback_mode(comp, mode); | |
307 | wcd_clsh_flyback_ctrl(ctrl, mode, true); | |
308 | wcd_clsh_set_flyback_current(comp, mode); | |
309 | wcd_clsh_buck_ctrl(ctrl, mode, true); | |
310 | } else { | |
311 | wcd_clsh_buck_ctrl(ctrl, mode, false); | |
312 | wcd_clsh_flyback_ctrl(ctrl, mode, false); | |
313 | wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); | |
314 | wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); | |
315 | wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); | |
316 | } | |
317 | } | |
318 | ||
319 | static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state, | |
320 | bool is_enable, int mode) | |
321 | { | |
322 | struct snd_soc_component *comp = ctrl->comp; | |
323 | ||
324 | if (mode == CLS_H_NORMAL) { | |
325 | dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n", | |
326 | __func__); | |
327 | return; | |
328 | } | |
329 | ||
330 | if (is_enable) { | |
331 | if (mode != CLS_AB) { | |
332 | wcd_enable_clsh_block(ctrl, true); | |
333 | /* | |
334 | * These K1 values depend on the Headphone Impedance | |
335 | * For now it is assumed to be 16 ohm | |
336 | */ | |
337 | snd_soc_component_update_bits(comp, | |
338 | WCD9XXX_A_CDC_CLSH_K1_MSB, | |
339 | WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK, | |
340 | 0x00); | |
341 | snd_soc_component_update_bits(comp, | |
342 | WCD9XXX_A_CDC_CLSH_K1_LSB, | |
343 | WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK, | |
344 | 0xC0); | |
345 | snd_soc_component_update_bits(comp, | |
346 | WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, | |
347 | WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, | |
348 | WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); | |
349 | } | |
350 | wcd_clsh_set_buck_regulator_mode(comp, mode); | |
351 | wcd_clsh_set_flyback_mode(comp, mode); | |
352 | wcd_clsh_flyback_ctrl(ctrl, mode, true); | |
353 | wcd_clsh_set_flyback_current(comp, mode); | |
354 | wcd_clsh_set_buck_mode(comp, mode); | |
355 | wcd_clsh_buck_ctrl(ctrl, mode, true); | |
356 | wcd_clsh_set_hph_mode(comp, mode); | |
357 | wcd_clsh_set_gain_path(ctrl, mode); | |
358 | } else { | |
359 | wcd_clsh_set_hph_mode(comp, CLS_H_NORMAL); | |
360 | ||
361 | if (mode != CLS_AB) { | |
362 | snd_soc_component_update_bits(comp, | |
363 | WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, | |
364 | WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, | |
365 | WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); | |
366 | wcd_enable_clsh_block(ctrl, false); | |
367 | } | |
368 | /* buck and flyback set to default mode and disable */ | |
369 | wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false); | |
370 | wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false); | |
371 | wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); | |
372 | wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); | |
373 | wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); | |
374 | } | |
375 | } | |
376 | ||
377 | static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state, | |
378 | bool is_enable, int mode) | |
379 | { | |
380 | struct snd_soc_component *comp = ctrl->comp; | |
381 | ||
382 | if (mode == CLS_H_NORMAL) { | |
383 | dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n", | |
384 | __func__); | |
385 | return; | |
386 | } | |
387 | ||
388 | if (is_enable) { | |
389 | if (mode != CLS_AB) { | |
390 | wcd_enable_clsh_block(ctrl, true); | |
391 | /* | |
392 | * These K1 values depend on the Headphone Impedance | |
393 | * For now it is assumed to be 16 ohm | |
394 | */ | |
395 | snd_soc_component_update_bits(comp, | |
396 | WCD9XXX_A_CDC_CLSH_K1_MSB, | |
397 | WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK, | |
398 | 0x00); | |
399 | snd_soc_component_update_bits(comp, | |
400 | WCD9XXX_A_CDC_CLSH_K1_LSB, | |
401 | WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK, | |
402 | 0xC0); | |
403 | snd_soc_component_update_bits(comp, | |
404 | WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, | |
405 | WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, | |
406 | WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); | |
407 | } | |
408 | wcd_clsh_set_buck_regulator_mode(comp, mode); | |
409 | wcd_clsh_set_flyback_mode(comp, mode); | |
410 | wcd_clsh_flyback_ctrl(ctrl, mode, true); | |
411 | wcd_clsh_set_flyback_current(comp, mode); | |
412 | wcd_clsh_set_buck_mode(comp, mode); | |
413 | wcd_clsh_buck_ctrl(ctrl, mode, true); | |
414 | wcd_clsh_set_hph_mode(comp, mode); | |
415 | wcd_clsh_set_gain_path(ctrl, mode); | |
416 | } else { | |
417 | wcd_clsh_set_hph_mode(comp, CLS_H_NORMAL); | |
418 | ||
419 | if (mode != CLS_AB) { | |
420 | snd_soc_component_update_bits(comp, | |
421 | WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, | |
422 | WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, | |
423 | WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); | |
424 | wcd_enable_clsh_block(ctrl, false); | |
425 | } | |
426 | /* set buck and flyback to Default Mode */ | |
427 | wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false); | |
428 | wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false); | |
429 | wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); | |
430 | wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); | |
431 | wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); | |
432 | } | |
433 | } | |
434 | ||
435 | static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state, | |
436 | bool is_enable, int mode) | |
437 | { | |
438 | struct snd_soc_component *comp = ctrl->comp; | |
439 | ||
440 | if (mode != CLS_H_NORMAL) { | |
441 | dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n", | |
442 | __func__, mode); | |
443 | return; | |
444 | } | |
445 | ||
446 | if (is_enable) { | |
447 | wcd_enable_clsh_block(ctrl, true); | |
448 | snd_soc_component_update_bits(comp, | |
449 | WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, | |
450 | WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, | |
451 | WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); | |
452 | wcd_clsh_set_buck_mode(comp, mode); | |
453 | wcd_clsh_set_flyback_mode(comp, mode); | |
454 | wcd_clsh_flyback_ctrl(ctrl, mode, true); | |
455 | wcd_clsh_set_flyback_current(comp, mode); | |
456 | wcd_clsh_buck_ctrl(ctrl, mode, true); | |
457 | } else { | |
458 | snd_soc_component_update_bits(comp, | |
459 | WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, | |
460 | WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, | |
461 | WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); | |
462 | wcd_enable_clsh_block(ctrl, false); | |
463 | wcd_clsh_buck_ctrl(ctrl, mode, false); | |
464 | wcd_clsh_flyback_ctrl(ctrl, mode, false); | |
465 | wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); | |
466 | wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); | |
467 | } | |
468 | } | |
469 | ||
470 | static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state, | |
471 | bool is_enable, int mode) | |
472 | { | |
473 | switch (req_state) { | |
474 | case WCD_CLSH_STATE_EAR: | |
475 | wcd_clsh_state_ear(ctrl, req_state, is_enable, mode); | |
476 | break; | |
477 | case WCD_CLSH_STATE_HPHL: | |
478 | wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode); | |
479 | break; | |
480 | case WCD_CLSH_STATE_HPHR: | |
481 | wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode); | |
482 | break; | |
483 | break; | |
484 | case WCD_CLSH_STATE_LO: | |
485 | wcd_clsh_state_lo(ctrl, req_state, is_enable, mode); | |
486 | break; | |
487 | default: | |
488 | break; | |
489 | } | |
490 | ||
491 | return 0; | |
492 | } | |
493 | ||
494 | /* | |
495 | * Function: wcd_clsh_is_state_valid | |
496 | * Params: state | |
497 | * Description: | |
498 | * Provides information on valid states of Class H configuration | |
499 | */ | |
500 | static bool wcd_clsh_is_state_valid(int state) | |
501 | { | |
502 | switch (state) { | |
503 | case WCD_CLSH_STATE_IDLE: | |
504 | case WCD_CLSH_STATE_EAR: | |
505 | case WCD_CLSH_STATE_HPHL: | |
506 | case WCD_CLSH_STATE_HPHR: | |
507 | case WCD_CLSH_STATE_LO: | |
508 | return true; | |
509 | default: | |
510 | return false; | |
511 | }; | |
512 | } | |
513 | ||
514 | /* | |
515 | * Function: wcd_clsh_fsm | |
516 | * Params: ctrl, req_state, req_type, clsh_event | |
517 | * Description: | |
518 | * This function handles PRE DAC and POST DAC conditions of different devices | |
519 | * and updates class H configuration of different combination of devices | |
520 | * based on validity of their states. ctrl will contain current | |
521 | * class h state information | |
522 | */ | |
523 | int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, | |
524 | enum wcd_clsh_event clsh_event, | |
525 | int nstate, | |
526 | enum wcd_clsh_mode mode) | |
527 | { | |
528 | struct snd_soc_component *comp = ctrl->comp; | |
529 | ||
530 | if (nstate == ctrl->state) | |
531 | return 0; | |
532 | ||
533 | if (!wcd_clsh_is_state_valid(nstate)) { | |
534 | dev_err(comp->dev, "Class-H not a valid new state:\n"); | |
535 | return -EINVAL; | |
536 | } | |
537 | ||
538 | switch (clsh_event) { | |
539 | case WCD_CLSH_EVENT_PRE_DAC: | |
540 | _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode); | |
541 | break; | |
542 | case WCD_CLSH_EVENT_POST_PA: | |
543 | _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode); | |
544 | break; | |
afb86626 | 545 | } |
cc2e324d SK |
546 | |
547 | ctrl->state = nstate; | |
548 | ctrl->mode = mode; | |
549 | ||
550 | return 0; | |
551 | } | |
552 | ||
553 | int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl) | |
554 | { | |
555 | return ctrl->state; | |
556 | } | |
557 | ||
558 | struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp, | |
559 | int version) | |
560 | { | |
561 | struct wcd_clsh_ctrl *ctrl; | |
562 | ||
563 | ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); | |
564 | if (!ctrl) | |
565 | return ERR_PTR(-ENOMEM); | |
566 | ||
567 | ctrl->state = WCD_CLSH_STATE_IDLE; | |
568 | ctrl->comp = comp; | |
569 | ||
570 | return ctrl; | |
571 | } | |
572 | ||
573 | void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl) | |
574 | { | |
575 | kfree(ctrl); | |
576 | } |