blob: dbf2e397da5b97331f77e037b62d6f5ebb9c4036 [file] [log] [blame]
Bhalchandra Gajare87fef4c2013-02-19 14:57:03 -08001/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <sound/soc.h>
15#include <linux/kernel.h>
16#include <linux/delay.h>
17#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
18#include "wcd9xxx-common.h"
19
20#define CLSH_COMPUTE_EAR 0x01
21#define CLSH_COMPUTE_HPH_L 0x02
22#define CLSH_COMPUTE_HPH_R 0x03
23
24#define BUCK_VREF_2V 0xFF
25#define BUCK_VREF_1P8V 0xE6
26
27#define NCP_FCLK_LEVEL_8 0x08
28#define NCP_FCLK_LEVEL_5 0x05
29
30#define BUCK_SETTLE_TIME_US 50
31#define NCP_SETTLE_TIME_US 50
32
33static inline void wcd9xxx_enable_clsh_block(
34 struct snd_soc_codec *codec,
35 bool on)
36{
37 snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
38 0x01, on ? 0x01 : 0x00);
39}
40
41static inline void wcd9xxx_enable_anc_delay(
42 struct snd_soc_codec *codec,
43 bool on)
44{
45 snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
46 0x02, on ? 0x02 : 0x00);
47}
48
49static inline void wcd9xxx_enable_ncp(
50 struct snd_soc_codec *codec,
51 bool on)
52{
53 snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN,
54 0x01, on ? 0x01 : 0x00);
55}
56
57static inline void wcd9xxx_enable_buck(
58 struct snd_soc_codec *codec,
59 bool on)
60{
61 snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
62 0x80, on ? 0x80 : 0x00);
63}
64
65static int cdc_lo_count;
66
67static void (*clsh_state_fp[NUM_CLSH_STATES])
68 (struct snd_soc_codec *,
69 struct wcd9xxx_clsh_cdc_data *,
70 u8 req_state, bool req_type);
71
72static const char *state_to_str(u8 state)
73{
74 if (state == WCD9XXX_CLSH_STATE_IDLE)
75 return "STATE_IDLE";
76 else if (state == WCD9XXX_CLSH_STATE_EAR)
77 return "STATE_EAR";
78 else if (state == WCD9XXX_CLSH_STATE_HPHL)
79 return "STATE_HPH_L";
80 else if (state == WCD9XXX_CLSH_STATE_HPHR)
81 return "STATE_HPH_R";
82 else if (state == (WCD9XXX_CLSH_STATE_HPHL
83 | WCD9XXX_CLSH_STATE_HPHR))
84 return "STATE_HPH_L_R";
85 else if (state == WCD9XXX_CLSH_STATE_LO)
86 return "STATE_LO";
87
88 return "UNKNOWN_STATE";
89}
90
91static void wcd9xxx_cfg_clsh_buck(
92 struct snd_soc_codec *codec)
93{
94 int i;
95 const struct wcd9xxx_reg_mask_val reg_set[] = {
96 {WCD9XXX_A_BUCK_CTRL_CCL_4, 0x0B, 0x00},
97 {WCD9XXX_A_BUCK_CTRL_CCL_1, 0xF0, 0x50},
98 {WCD9XXX_A_BUCK_CTRL_CCL_3, 0x03, 0x00},
99 {WCD9XXX_A_BUCK_CTRL_CCL_3, 0x0B, 0x00},
100 };
101
102 for (i = 0; i < ARRAY_SIZE(reg_set); i++)
103 snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
104 reg_set[i].val);
105
106 dev_dbg(codec->dev, "%s: Programmed buck parameters", __func__);
107}
108
109static void wcd9xxx_cfg_clsh_param_common(
110 struct snd_soc_codec *codec)
111{
112 int i;
113 const struct wcd9xxx_reg_mask_val reg_set[] = {
114 {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 0, 0},
115 {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 2, 1 << 2},
116 {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, (0x1 << 4), 0},
117 {WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 0), 0x01},
118 {WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 2), (0x01 << 2)},
119 {WCD9XXX_A_CDC_CLSH_B2_CTL, (0xf << 4), (0x03 << 4)},
120 {WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 4), (0x03 << 4)},
121 {WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 0), (0x0B)},
122 {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 5), (0x01 << 5)},
123 {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 1), (0x01 << 1)},
124 };
125
126 for (i = 0; i < ARRAY_SIZE(reg_set); i++)
127 snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
128 reg_set[i].val);
129
130 dev_dbg(codec->dev, "%s: Programmed class H controller common parameters",
131 __func__);
132}
133
134static void wcd9xxx_chargepump_request(
135 struct snd_soc_codec *codec, bool on)
136{
137 static int cp_count;
138
139 if (on && (++cp_count == 1)) {
140 snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
141 0x01, 0x01);
142 dev_info(codec->dev, "%s: Charge Pump enabled, count = %d\n",
143 __func__, cp_count);
144 }
145
146 else if (!on) {
147 if (--cp_count < 0) {
148 dev_dbg(codec->dev, "%s: Unbalanced disable for charge pump\n",
149 __func__);
150 if (snd_soc_read(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL)
151 & 0x01) {
152 dev_info(codec->dev, "%s: Actual chargepump is ON\n",
153 __func__);
154 }
155 cp_count = 0;
156 WARN_ON(1);
157 }
158
159 if (cp_count == 0) {
160 snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
161 0x01, 0x00);
162 dev_dbg(codec->dev, "%s: Charge pump disabled, count = %d\n",
163 __func__, cp_count);
164 }
165 }
166}
167
168static inline void wcd9xxx_clsh_computation_request(
169 struct snd_soc_codec *codec, int compute_pa, bool on)
170{
171 u8 reg_val, reg_mask;
172
173 switch (compute_pa) {
174 case CLSH_COMPUTE_EAR:
175 reg_mask = 0x10;
176 reg_val = (on ? 0x10 : 0x00);
177 break;
178 case CLSH_COMPUTE_HPH_L:
179 reg_mask = 0x08;
180 reg_val = (on ? 0x08 : 0x00);
181 break;
182 case CLSH_COMPUTE_HPH_R:
183 reg_mask = 0x04;
184 reg_val = (on ? 0x04 : 0x00);
185 break;
186 default:
187 dev_dbg(codec->dev, "%s: class h computation PA request incorrect\n",
188 __func__);
189 return;
190 }
191
192 snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
193 reg_mask, reg_val);
194
195}
196
197static void wcd9xxx_enable_buck_mode(struct snd_soc_codec *codec,
198 u8 buck_vref)
199{
200 int i;
201 const struct wcd9xxx_reg_mask_val reg_set[] = {
202 {WCD9XXX_A_BUCK_MODE_5, 0x02, 0x03},
203 {WCD9XXX_A_BUCK_MODE_4, 0xFF, buck_vref},
204 {WCD9XXX_A_BUCK_MODE_1, 0x04, 0x04},
205 {WCD9XXX_A_BUCK_MODE_1, 0x08, 0x00},
206 {WCD9XXX_A_BUCK_MODE_3, 0x04, 0x00},
207 {WCD9XXX_A_BUCK_MODE_3, 0x08, 0x00},
208 {WCD9XXX_A_BUCK_MODE_1, 0x80, 0x80},
209 };
210
211 for (i = 0; i < ARRAY_SIZE(reg_set); i++)
212 snd_soc_update_bits(codec, reg_set[i].reg,
213 reg_set[i].mask, reg_set[i].val);
214
215 dev_dbg(codec->dev, "%s: Done\n", __func__);
216 usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US);
217}
218
219static void wcd9xxx_clsh_enable_post_pa(struct snd_soc_codec *codec)
220{
221 int i;
222 const struct wcd9xxx_reg_mask_val reg_set[] = {
223 {WCD9XXX_A_BUCK_MODE_5, 0x02, 0x00},
224 {WCD9XXX_A_NCP_STATIC, 0x20, 0x00},
225 {WCD9XXX_A_BUCK_MODE_3, 0x04, 0x04},
226 {WCD9XXX_A_BUCK_MODE_3, 0x08, 0x08},
227 };
228
229 for (i = 0; i < ARRAY_SIZE(reg_set); i++)
230 snd_soc_update_bits(codec, reg_set[i].reg,
231 reg_set[i].mask, reg_set[i].val);
232
233 dev_dbg(codec->dev, "%s: completed clsh mode settings after PA enable\n",
234 __func__);
235
236}
237
238static void wcd9xxx_set_fclk_enable_ncp(struct snd_soc_codec *codec,
239 u8 fclk_level)
240{
241 int i;
242 const struct wcd9xxx_reg_mask_val reg_set[] = {
243 {WCD9XXX_A_NCP_STATIC, 0x20, 0x20},
244 {WCD9XXX_A_NCP_EN, 0x01, 0x01},
245 };
246 snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
247 0x010, 0x00);
248 snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
249 0x0F, fclk_level);
250 for (i = 0; i < ARRAY_SIZE(reg_set); i++)
251 snd_soc_update_bits(codec, reg_set[i].reg,
252 reg_set[i].mask, reg_set[i].val);
253
254 usleep_range(NCP_SETTLE_TIME_US, NCP_SETTLE_TIME_US);
255
256 dev_dbg(codec->dev, "%s: set ncp done\n", __func__);
257}
258
259static void wcd9xxx_cfg_clsh_param_ear(struct snd_soc_codec *codec)
260{
261 int i;
262 const struct wcd9xxx_reg_mask_val reg_set[] = {
263 {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 7), 0},
264 {WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR, (0x3f << 0), 0x0D},
265 {WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR, (0x3f << 0), 0x3A},
266
267 /* Under assumption that EAR load is 10.7ohm */
268 {WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD, (0x3f << 0), 0x26},
269 {WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD, (0x3f << 0), 0x2C},
270 {WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L, 0xff, 0xA9},
271 {WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U, 0xff, 0x07},
272 {WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0},
273 {WCD9XXX_A_CDC_CLSH_K_ADDR, (0xf << 0), 0x08},
274 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1b},
275 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
276 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2d},
277 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
278 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x36},
279 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
280 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37},
281 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
282 };
283
284 for (i = 0; i < ARRAY_SIZE(reg_set); i++)
285 snd_soc_update_bits(codec, reg_set[i].reg,
286 reg_set[i].mask, reg_set[i].val);
287
288 dev_dbg(codec->dev, "%s: Programmed Class H controller EAR specific params\n",
289 __func__);
290}
291
292static void wcd9xxx_cfg_clsh_param_hph(struct snd_soc_codec *codec)
293{
294 int i;
295 const struct wcd9xxx_reg_mask_val reg_set[] = {
296 {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 6), 0},
297 {WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH, 0x3f, 0x0D},
298 {WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH, 0x3f, 0x1D},
299
300 /* Under assumption that HPH load is 16ohm per channel */
301 {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0x3f, 0x13},
302 {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0x1f, 0x19},
303 {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97},
304 {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
305 {WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0},
306 {WCD9XXX_A_CDC_CLSH_K_ADDR, 0x0f, 0},
307 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
308 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
309 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
310 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
311 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
312 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
313 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
314 {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
315 };
316
317 for (i = 0; i < ARRAY_SIZE(reg_set); i++)
318 snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
319 reg_set[i].val);
320 dev_dbg(codec->dev, "%s: Programmed Class H controller HPH specific params\n",
321 __func__);
322}
323
324static void wcd9xxx_clsh_turnoff_postpa
325 (struct snd_soc_codec *codec)
326{
327
328 int i;
329
330 const struct wcd9xxx_reg_mask_val reg_set[] = {
331 {WCD9XXX_A_NCP_EN, 0x01, 0x00},
332 {WCD9XXX_A_BUCK_MODE_1, 0x80, 0x00},
333 {WCD9XXX_A_CDC_CLSH_B1_CTL, 0x10, 0x00},
334 };
335
336 wcd9xxx_chargepump_request(codec, false);
337
338 for (i = 0; i < ARRAY_SIZE(reg_set); i++)
339 snd_soc_update_bits(codec, reg_set[i].reg,
340 reg_set[i].mask, reg_set[i].val);
341
342 wcd9xxx_enable_clsh_block(codec, false);
343
344 dev_dbg(codec->dev, "%s: Done\n", __func__);
345}
346
347static void wcd9xxx_clsh_state_idle(struct snd_soc_codec *codec,
348 struct wcd9xxx_clsh_cdc_data *clsh_d,
349 u8 req_state, bool is_enable)
350{
351 if (is_enable) {
352 dev_dbg(codec->dev, "%s: wrong transition, cannot enable IDLE state\n",
353 __func__);
354 } else {
355 if (req_state == WCD9XXX_CLSH_STATE_EAR) {
356 wcd9xxx_clsh_turnoff_postpa(codec);
357 } else if (req_state == WCD9XXX_CLSH_STATE_HPHL) {
358 wcd9xxx_clsh_computation_request(codec,
359 CLSH_COMPUTE_HPH_L, false);
360 wcd9xxx_clsh_turnoff_postpa(codec);
361 } else if (req_state == WCD9XXX_CLSH_STATE_HPHR) {
362 wcd9xxx_clsh_computation_request(codec,
363 CLSH_COMPUTE_HPH_R, false);
364 wcd9xxx_clsh_turnoff_postpa(codec);
365 } else if (req_state == WCD9XXX_CLSH_STATE_LO) {
366 wcd9xxx_enable_ncp(codec, false);
367 wcd9xxx_enable_buck(codec, false);
368 }
369 }
370}
371
372static void wcd9xxx_clsh_state_ear(struct snd_soc_codec *codec,
373 struct wcd9xxx_clsh_cdc_data *clsh_d,
374 u8 req_state, bool is_enable)
375{
376 if (is_enable) {
377 wcd9xxx_cfg_clsh_buck(codec);
378 wcd9xxx_cfg_clsh_param_common(codec);
379 wcd9xxx_cfg_clsh_param_ear(codec);
380 wcd9xxx_enable_clsh_block(codec, true);
381 wcd9xxx_chargepump_request(codec, true);
382 wcd9xxx_enable_anc_delay(codec, true);
383 wcd9xxx_clsh_computation_request(codec,
384 CLSH_COMPUTE_EAR, true);
385 wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
386 wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
387
388 dev_dbg(codec->dev, "%s: Enabled ear mode class h\n", __func__);
389 } else {
390 dev_dbg(codec->dev, "%s: stub fallback to ear\n", __func__);
391 }
392}
393
394static void wcd9xxx_clsh_state_hph_l(struct snd_soc_codec *codec,
395 struct wcd9xxx_clsh_cdc_data *clsh_d,
396 u8 req_state, bool is_enable)
397{
398 if (is_enable) {
399 wcd9xxx_cfg_clsh_buck(codec);
400 wcd9xxx_cfg_clsh_param_common(codec);
401 wcd9xxx_cfg_clsh_param_hph(codec);
402 wcd9xxx_enable_clsh_block(codec, true);
403 wcd9xxx_chargepump_request(codec, true);
404 wcd9xxx_enable_anc_delay(codec, true);
405 wcd9xxx_clsh_computation_request(codec,
406 CLSH_COMPUTE_HPH_L, true);
407 wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
408 wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
409
410 dev_dbg(codec->dev, "%s: Done\n", __func__);
411 } else {
412 if (req_state == WCD9XXX_CLSH_STATE_HPHR) {
413 wcd9xxx_clsh_computation_request(codec,
414 CLSH_COMPUTE_HPH_R, false);
415 } else {
416 dev_dbg(codec->dev, "%s: stub fallback to hph_l\n",
417 __func__);
418 }
419 }
420}
421
422static void wcd9xxx_clsh_state_hph_r(struct snd_soc_codec *codec,
423 struct wcd9xxx_clsh_cdc_data *clsh_d,
424 u8 req_state, bool is_enable)
425{
426 if (is_enable) {
427
428 wcd9xxx_cfg_clsh_buck(codec);
429 wcd9xxx_cfg_clsh_param_common(codec);
430 wcd9xxx_cfg_clsh_param_hph(codec);
431 wcd9xxx_enable_clsh_block(codec, true);
432 wcd9xxx_chargepump_request(codec, true);
433 wcd9xxx_enable_anc_delay(codec, true);
434 wcd9xxx_clsh_computation_request(codec,
435 CLSH_COMPUTE_HPH_R, true);
436 wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
437 wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
438
439 dev_dbg(codec->dev, "%s: Done\n", __func__);
440 } else {
441 if (req_state == WCD9XXX_CLSH_STATE_HPHL) {
442 wcd9xxx_clsh_computation_request(codec,
443 CLSH_COMPUTE_HPH_L, false);
444 } else {
445 dev_dbg(codec->dev, "%s: stub fallback to hph_r\n",
446 __func__);
447 }
448 }
449}
450
451static void wcd9xxx_clsh_state_hph_st(struct snd_soc_codec *codec,
452 struct wcd9xxx_clsh_cdc_data *clsh_d,
453 u8 req_state, bool is_enable)
454{
455 if (is_enable) {
456 wcd9xxx_clsh_computation_request(codec,
457 CLSH_COMPUTE_HPH_L, true);
458 wcd9xxx_clsh_computation_request(codec,
459 CLSH_COMPUTE_HPH_R, true);
460 } else {
461 dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
462 }
463}
464
465static void wcd9xxx_clsh_state_lo(struct snd_soc_codec *codec,
466 struct wcd9xxx_clsh_cdc_data *clsh_d,
467 u8 req_state, bool is_enable)
468{
469 if (is_enable) {
470 if (++cdc_lo_count > 1)
471 return;
472
473 wcd9xxx_enable_buck_mode(codec, BUCK_VREF_1P8V);
474 wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_5);
475
476 if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
477 wcd9xxx_enable_buck(codec, false);
478 snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
479 0x20, 0x01);
480 wcd9xxx_enable_ncp(codec, true);
481 msleep(NCP_SETTLE_TIME_US);
482
483 } else {
484 snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN,
485 0x40, 0x00);
486 wcd9xxx_enable_ncp(codec, true);
487 msleep(NCP_SETTLE_TIME_US);
488 snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
489 0x01, 0x01);
490 snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
491 0xFB, (0x02 << 2));
492 }
493 snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
494 0x04, 0x00);
495 } else {
496 dev_dbg(codec->dev, "%s: stub fallback to lineout\n", __func__);
497 }
498}
499
500static void wcd9xxx_clsh_state_err(struct snd_soc_codec *codec,
501 struct wcd9xxx_clsh_cdc_data *clsh_d,
502 u8 req_state, bool is_enable)
503{
504 dev_dbg(codec->dev, "%s Wrong request for class H state machine requested to %s %s"
505 , __func__, is_enable ? "enable" : "disable",
506 state_to_str(req_state));
507 WARN_ON(1);
508}
509
510void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
511 struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
512 u8 req_state, bool req_type, u8 clsh_event)
513{
514 u8 old_state, new_state;
515
516 switch (clsh_event) {
517
518 case WCD9XXX_CLSH_EVENT_PRE_DAC:
519
520 /* PRE_DAC event should be used only for Enable */
521 BUG_ON(req_type != WCD9XXX_CLSH_REQ_ENABLE);
522
523 old_state = cdc_clsh_d->state;
524 new_state = old_state | req_state;
525
526 (*clsh_state_fp[new_state]) (codec, cdc_clsh_d,
527 req_state, req_type);
528 cdc_clsh_d->state = new_state;
529 dev_info(codec->dev, "%s: ClassH state transition from %s to %s\n",
530 __func__, state_to_str(old_state),
531 state_to_str(cdc_clsh_d->state));
532
533 break;
534
535 case WCD9XXX_CLSH_EVENT_POST_PA:
536
537 if (req_type == WCD9XXX_CLSH_REQ_DISABLE) {
538 if (req_state == WCD9XXX_CLSH_STATE_LO
539 && --cdc_lo_count > 0)
540 break;
541
542 old_state = cdc_clsh_d->state;
543 new_state = old_state & (~req_state);
544
545 if (new_state < NUM_CLSH_STATES) {
546 (*clsh_state_fp[new_state]) (codec, cdc_clsh_d,
547 req_state, req_type);
548 cdc_clsh_d->state = new_state;
549 dev_info(codec->dev, "%s: ClassH state transition from %s to %s\n",
550 __func__, state_to_str(old_state),
551 state_to_str(cdc_clsh_d->state));
552
553 } else {
554 dev_dbg(codec->dev, "%s: wrong new state = %x\n",
555 __func__, new_state);
556 }
557
558
559 } else if (req_state != WCD9XXX_CLSH_STATE_LO) {
560 wcd9xxx_clsh_enable_post_pa(codec);
561 }
562
563 break;
564 }
565
566}
567EXPORT_SYMBOL_GPL(wcd9xxx_clsh_fsm);
568
569void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh)
570{
571 int i;
572 clsh->state = WCD9XXX_CLSH_STATE_IDLE;
573
574 for (i = 0; i < NUM_CLSH_STATES; i++)
575 clsh_state_fp[i] = wcd9xxx_clsh_state_err;
576
577 clsh_state_fp[WCD9XXX_CLSH_STATE_IDLE] = wcd9xxx_clsh_state_idle;
578 clsh_state_fp[WCD9XXX_CLSH_STATE_EAR] = wcd9xxx_clsh_state_ear;
579 clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL] =
580 wcd9xxx_clsh_state_hph_l;
581 clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR] =
582 wcd9xxx_clsh_state_hph_r;
583 clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST] =
584 wcd9xxx_clsh_state_hph_st;
585 clsh_state_fp[WCD9XXX_CLSH_STATE_LO] = wcd9xxx_clsh_state_lo;
586
587}
588EXPORT_SYMBOL_GPL(wcd9xxx_clsh_init);
589
590MODULE_DESCRIPTION("WCD9XXX Common");
591MODULE_LICENSE("GPL v2");