blob: 7aec959474aa01418e8e995a746283fba20c730f [file] [log] [blame]
Meng Wang61af6842018-09-10 17:47:55 +08001// SPDX-License-Identifier: GPL-2.0
Laxminath Kasam7adc34e2018-11-09 11:24:38 +05302/*
3 * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
Laxminath Kasam7adc34e2018-11-09 11:24:38 +05304 */
5
6#include <linux/module.h>
7#include <linux/slab.h>
8#include <sound/soc.h>
9#include <linux/kernel.h>
10#include <linux/delay.h>
11#include <asoc/wcd9xxx_registers.h>
12#include "wcd-clsh.h"
13
14#define WCD_USLEEP_RANGE 50
15
Meng Wang15c825d2018-09-06 10:49:18 +080016static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_component *,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +053017 struct wcd_clsh_cdc_info *,
18 u8 req_state, bool en, int mode);
19
20static const char *mode_to_str(int mode)
21{
22 switch (mode) {
23 case CLS_H_NORMAL:
24 return WCD_CLSH_STRINGIFY(CLS_H_NORMAL);
25 case CLS_H_HIFI:
26 return WCD_CLSH_STRINGIFY(CLS_H_HIFI);
27 case CLS_H_LOHIFI:
28 return WCD_CLSH_STRINGIFY(CLS_H_LOHIFI);
29 case CLS_H_LP:
30 return WCD_CLSH_STRINGIFY(CLS_H_LP);
31 case CLS_H_ULP:
32 return WCD_CLSH_STRINGIFY(CLS_H_ULP);
33 case CLS_AB:
34 return WCD_CLSH_STRINGIFY(CLS_AB);
35 case CLS_AB_HIFI:
36 return WCD_CLSH_STRINGIFY(CLS_AB_HIFI);
37 default:
38 return WCD_CLSH_STRINGIFY(CLS_H_INVALID);
39 };
40}
41
42static const char *state_to_str(u8 state, char *buf, size_t buflen)
43{
44 int i;
45 int cnt = 0;
46 /*
47 * This array of strings should match with enum wcd_clsh_state_bit.
48 */
49 static const char *const states[] = {
50 "STATE_EAR",
51 "STATE_HPH_L",
52 "STATE_HPH_R",
53 "STATE_AUX",
54 };
55
56 if (state == WCD_CLSH_STATE_IDLE) {
57 snprintf(buf, buflen, "[STATE_IDLE]");
58 goto done;
59 }
60
61 buf[0] = '\0';
62 for (i = 0; i < ARRAY_SIZE(states); i++) {
63 if (!(state & (1 << i)))
64 continue;
65 cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf,
66 buf[0] == '\0' ? "[" : "|",
67 states[i]);
68 }
69 if (cnt > 0)
70 strlcat(buf + cnt, "]", buflen);
71
72done:
73 if (buf[0] == '\0')
74 snprintf(buf, buflen, "[STATE_UNKNOWN]");
75 return buf;
76}
77
78static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_info *clsh_d,
79 int clsh_state)
80{
81 int mode;
82
83 if ((clsh_state != WCD_CLSH_STATE_EAR) &&
84 (clsh_state != WCD_CLSH_STATE_HPHL) &&
85 (clsh_state != WCD_CLSH_STATE_HPHR) &&
86 (clsh_state != WCD_CLSH_STATE_AUX))
87 mode = CLS_NONE;
88 else
89 mode = clsh_d->interpolator_modes[ffs(clsh_state)];
90
91 return mode;
92}
93
94static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_info *clsh_d,
95 int clsh_state, int mode)
96{
97 if ((clsh_state != WCD_CLSH_STATE_EAR) &&
98 (clsh_state != WCD_CLSH_STATE_HPHL) &&
99 (clsh_state != WCD_CLSH_STATE_HPHR) &&
100 (clsh_state != WCD_CLSH_STATE_AUX))
101 return;
102
103 clsh_d->interpolator_modes[ffs(clsh_state)] = mode;
104}
105
Meng Wang15c825d2018-09-06 10:49:18 +0800106static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530107 int mode)
108{
109 if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
110 mode == CLS_AB_HIFI)
Meng Wang15c825d2018-09-06 10:49:18 +0800111 snd_soc_component_update_bits(component,
112 WCD9XXX_ANA_RX_SUPPLIES,
113 0x08, 0x08); /* set to HIFI */
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530114 else
Meng Wang15c825d2018-09-06 10:49:18 +0800115 snd_soc_component_update_bits(component,
116 WCD9XXX_ANA_RX_SUPPLIES,
117 0x08, 0x00); /* set to default */
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530118}
119
Meng Wang15c825d2018-09-06 10:49:18 +0800120static inline void wcd_clsh_set_flyback_mode(
121 struct snd_soc_component *component,
122 int mode)
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530123{
124 if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
125 mode == CLS_AB_HIFI) {
Meng Wang15c825d2018-09-06 10:49:18 +0800126 snd_soc_component_update_bits(component,
127 WCD9XXX_ANA_RX_SUPPLIES,
128 0x04, 0x04);
129 snd_soc_component_update_bits(component,
130 WCD9XXX_FLYBACK_VNEG_CTRL_4,
131 0xF0, 0x80);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530132 } else {
Meng Wang15c825d2018-09-06 10:49:18 +0800133 snd_soc_component_update_bits(component,
134 WCD9XXX_ANA_RX_SUPPLIES,
135 0x04, 0x00); /* set to Default */
136 snd_soc_component_update_bits(component,
137 WCD9XXX_FLYBACK_VNEG_CTRL_4,
138 0xF0, 0x70);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530139 }
140}
141
Meng Wang15c825d2018-09-06 10:49:18 +0800142static inline void wcd_clsh_force_iq_ctl(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530143 int mode, bool enable)
144{
145 if (enable) {
Meng Wang15c825d2018-09-06 10:49:18 +0800146 snd_soc_component_update_bits(component,
147 WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
148 0xE0, 0xA0);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530149 /* 100usec delay is needed as per HW requirement */
150 usleep_range(100, 110);
Meng Wang15c825d2018-09-06 10:49:18 +0800151 snd_soc_component_update_bits(component,
152 WCD9XXX_CLASSH_MODE_3,
153 0x02, 0x02);
154 snd_soc_component_update_bits(component,
155 WCD9XXX_CLASSH_MODE_2,
156 0xFF, 0x1C);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530157 if (mode == CLS_H_LOHIFI) {
Meng Wang15c825d2018-09-06 10:49:18 +0800158 snd_soc_component_update_bits(component,
159 WCD9XXX_HPH_NEW_INT_PA_MISC2,
160 0x20, 0x20);
161 snd_soc_component_update_bits(component,
162 WCD9XXX_RX_BIAS_HPH_LOWPOWER,
163 0xF0, 0xC0);
164 snd_soc_component_update_bits(component,
165 WCD9XXX_HPH_PA_CTL1,
166 0x0E, 0x02);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530167 }
168 } else {
Meng Wang15c825d2018-09-06 10:49:18 +0800169 snd_soc_component_update_bits(component,
170 WCD9XXX_HPH_NEW_INT_PA_MISC2,
171 0x20, 0x00);
172 snd_soc_component_update_bits(component,
173 WCD9XXX_RX_BIAS_HPH_LOWPOWER,
174 0xF0, 0x80);
175 snd_soc_component_update_bits(component,
176 WCD9XXX_HPH_PA_CTL1,
177 0x0E, 0x06);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530178 }
179}
180
Meng Wang15c825d2018-09-06 10:49:18 +0800181static void wcd_clsh_buck_ctrl(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530182 struct wcd_clsh_cdc_info *clsh_d,
183 int mode,
184 bool enable)
185{
186 /* enable/disable buck */
187 if ((enable && (++clsh_d->buck_users == 1)) ||
188 (!enable && (--clsh_d->buck_users == 0))) {
Meng Wang15c825d2018-09-06 10:49:18 +0800189 snd_soc_component_update_bits(component,
190 WCD9XXX_ANA_RX_SUPPLIES,
191 (1 << 7), (enable << 7));
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530192 /*
193 * 500us sleep is required after buck enable/disable
194 * as per HW requirement
195 */
196 usleep_range(500, 510);
197 if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
198 mode == CLS_H_HIFI || mode == CLS_H_LP)
Meng Wang15c825d2018-09-06 10:49:18 +0800199 snd_soc_component_update_bits(component,
200 WCD9XXX_CLASSH_MODE_3,
201 0x02, 0x00);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530202
Meng Wang15c825d2018-09-06 10:49:18 +0800203 snd_soc_component_update_bits(component,
204 WCD9XXX_CLASSH_MODE_2,
205 0xFF, 0x3A);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530206 /* 500usec delay is needed as per HW requirement */
207 usleep_range(500, 500 + WCD_USLEEP_RANGE);
208 }
Meng Wang15c825d2018-09-06 10:49:18 +0800209 dev_dbg(component->dev, "%s: buck_users %d, enable %d, mode: %s\n",
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530210 __func__, clsh_d->buck_users, enable, mode_to_str(mode));
211}
212
Meng Wang15c825d2018-09-06 10:49:18 +0800213static void wcd_clsh_flyback_ctrl(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530214 struct wcd_clsh_cdc_info *clsh_d,
215 int mode,
216 bool enable)
217{
218 /* enable/disable flyback */
219 if ((enable && (++clsh_d->flyback_users == 1)) ||
220 (!enable && (--clsh_d->flyback_users == 0))) {
Meng Wang15c825d2018-09-06 10:49:18 +0800221 snd_soc_component_update_bits(component,
222 WCD9XXX_ANA_RX_SUPPLIES,
223 (1 << 6), (enable << 6));
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530224 /*
225 * 100us sleep is required after flyback enable/disable
226 * as per HW requirement
227 */
228 usleep_range(100, 110);
Meng Wang15c825d2018-09-06 10:49:18 +0800229 snd_soc_component_update_bits(component,
230 WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
231 0xE0, 0xE0);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530232 /* 500usec delay is needed as per HW requirement */
233 usleep_range(500, 500 + WCD_USLEEP_RANGE);
234 }
Meng Wang15c825d2018-09-06 10:49:18 +0800235 dev_dbg(component->dev, "%s: flyback_users %d, enable %d, mode: %s\n",
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530236 __func__, clsh_d->flyback_users, enable, mode_to_str(mode));
237}
238
Meng Wang15c825d2018-09-06 10:49:18 +0800239static void wcd_clsh_set_hph_mode(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530240 int mode)
241{
242 u8 val = 0;
243
244 switch (mode) {
245 case CLS_H_NORMAL:
246 case CLS_H_LOHIFI:
247 val = 0x00;
248 break;
249 case CLS_AB:
250 case CLS_H_ULP:
251 val = 0x0C;
252 break;
253 case CLS_AB_HIFI:
254 case CLS_H_HIFI:
255 val = 0x08;
256 break;
257 case CLS_H_LP:
258 val = 0x04;
259 break;
260 default:
Meng Wang15c825d2018-09-06 10:49:18 +0800261 dev_err(component->dev, "%s:Invalid mode %d\n", __func__, mode);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530262 return;
263 };
264
Meng Wang15c825d2018-09-06 10:49:18 +0800265 snd_soc_component_update_bits(component, WCD9XXX_ANA_HPH, 0x0C, val);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530266}
267
Meng Wang15c825d2018-09-06 10:49:18 +0800268static void wcd_clsh_set_flyback_current(struct snd_soc_component *component,
269 int mode)
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530270{
Meng Wang15c825d2018-09-06 10:49:18 +0800271 snd_soc_component_update_bits(component, WCD9XXX_RX_BIAS_FLYB_BUFF,
272 0x0F, 0x0A);
273 snd_soc_component_update_bits(component, WCD9XXX_RX_BIAS_FLYB_BUFF,
274 0xF0, 0xA0);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530275 /* Sleep needed to avoid click and pop as per HW requirement */
276 usleep_range(100, 110);
277}
278
Meng Wang15c825d2018-09-06 10:49:18 +0800279static void wcd_clsh_set_buck_regulator_mode(
280 struct snd_soc_component *component,
281 int mode)
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530282{
Meng Wang15c825d2018-09-06 10:49:18 +0800283 snd_soc_component_update_bits(component, WCD9XXX_ANA_RX_SUPPLIES,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530284 0x02, 0x00);
285}
286
Meng Wang15c825d2018-09-06 10:49:18 +0800287static void wcd_clsh_state_ear_aux(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530288 struct wcd_clsh_cdc_info *clsh_d,
289 u8 req_state, bool is_enable, int mode)
290{
Meng Wang15c825d2018-09-06 10:49:18 +0800291 dev_dbg(component->dev, "%s: mode: %s, %s\n", __func__,
292 mode_to_str(mode), is_enable ? "enable" : "disable");
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530293}
294
Meng Wang15c825d2018-09-06 10:49:18 +0800295static void wcd_clsh_state_hph_aux(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530296 struct wcd_clsh_cdc_info *clsh_d,
297 u8 req_state, bool is_enable, int mode)
298{
Meng Wang15c825d2018-09-06 10:49:18 +0800299 dev_dbg(component->dev, "%s: mode: %s, %s\n", __func__,
300 mode_to_str(mode), is_enable ? "enable" : "disable");
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530301}
302
Meng Wang15c825d2018-09-06 10:49:18 +0800303static void wcd_clsh_state_hph_ear(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530304 struct wcd_clsh_cdc_info *clsh_d,
305 u8 req_state, bool is_enable, int mode)
306{
Meng Wang15c825d2018-09-06 10:49:18 +0800307 dev_dbg(component->dev, "%s: mode: %s, %s\n", __func__,
308 mode_to_str(mode), is_enable ? "enable" : "disable");
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530309}
310
Meng Wang15c825d2018-09-06 10:49:18 +0800311static void wcd_clsh_state_hph_st(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530312 struct wcd_clsh_cdc_info *clsh_d,
313 u8 req_state, bool is_enable, int mode)
314{
Meng Wang15c825d2018-09-06 10:49:18 +0800315 dev_dbg(component->dev, "%s: mode: %s, %s\n", __func__,
316 mode_to_str(mode), is_enable ? "enable" : "disable");
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530317}
318
Meng Wang15c825d2018-09-06 10:49:18 +0800319static void wcd_clsh_state_hph_r(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530320 struct wcd_clsh_cdc_info *clsh_d,
321 u8 req_state, bool is_enable, int mode)
322{
Meng Wang15c825d2018-09-06 10:49:18 +0800323 dev_dbg(component->dev, "%s: mode: %s, %s\n", __func__,
324 mode_to_str(mode), is_enable ? "enable" : "disable");
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530325
326 if (mode == CLS_H_NORMAL) {
Meng Wang15c825d2018-09-06 10:49:18 +0800327 dev_dbg(component->dev, "%s: Normal mode not applicable for hph_r\n",
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530328 __func__);
329 return;
330 }
331
332 if (is_enable) {
Meng Wang15c825d2018-09-06 10:49:18 +0800333 wcd_clsh_set_buck_regulator_mode(component, mode);
334 wcd_clsh_set_flyback_mode(component, mode);
335 wcd_clsh_force_iq_ctl(component, mode, true);
336 wcd_clsh_flyback_ctrl(component, clsh_d, mode, true);
337 wcd_clsh_set_flyback_current(component, mode);
338 wcd_clsh_set_buck_mode(component, mode);
339 wcd_clsh_buck_ctrl(component, clsh_d, mode, true);
340 wcd_clsh_set_hph_mode(component, mode);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530341 } else {
Meng Wang15c825d2018-09-06 10:49:18 +0800342 wcd_clsh_set_hph_mode(component, CLS_H_NORMAL);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530343
344 /* buck and flyback set to default mode and disable */
Meng Wang15c825d2018-09-06 10:49:18 +0800345 wcd_clsh_flyback_ctrl(component, clsh_d, CLS_H_NORMAL, false);
346 wcd_clsh_buck_ctrl(component, clsh_d, CLS_H_NORMAL, false);
347 wcd_clsh_force_iq_ctl(component, CLS_H_NORMAL, false);
348 wcd_clsh_set_flyback_mode(component, CLS_H_NORMAL);
349 wcd_clsh_set_buck_mode(component, CLS_H_NORMAL);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530350 }
351}
352
Meng Wang15c825d2018-09-06 10:49:18 +0800353static void wcd_clsh_state_hph_l(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530354 struct wcd_clsh_cdc_info *clsh_d,
355 u8 req_state, bool is_enable, int mode)
356{
Meng Wang15c825d2018-09-06 10:49:18 +0800357 dev_dbg(component->dev, "%s: mode: %s, %s\n", __func__,
358 mode_to_str(mode), is_enable ? "enable" : "disable");
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530359
360 if (mode == CLS_H_NORMAL) {
Meng Wang15c825d2018-09-06 10:49:18 +0800361 dev_dbg(component->dev, "%s: Normal mode not applicable for hph_l\n",
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530362 __func__);
363 return;
364 }
365
366 if (is_enable) {
Meng Wang15c825d2018-09-06 10:49:18 +0800367 wcd_clsh_set_buck_regulator_mode(component, mode);
368 wcd_clsh_set_flyback_mode(component, mode);
369 wcd_clsh_force_iq_ctl(component, mode, true);
370 wcd_clsh_flyback_ctrl(component, clsh_d, mode, true);
371 wcd_clsh_set_flyback_current(component, mode);
372 wcd_clsh_set_buck_mode(component, mode);
373 wcd_clsh_buck_ctrl(component, clsh_d, mode, true);
374 wcd_clsh_set_hph_mode(component, mode);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530375 } else {
Meng Wang15c825d2018-09-06 10:49:18 +0800376 wcd_clsh_set_hph_mode(component, CLS_H_NORMAL);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530377
378 /* set buck and flyback to Default Mode */
Meng Wang15c825d2018-09-06 10:49:18 +0800379 wcd_clsh_flyback_ctrl(component, clsh_d, CLS_H_NORMAL, false);
380 wcd_clsh_buck_ctrl(component, clsh_d, CLS_H_NORMAL, false);
381 wcd_clsh_force_iq_ctl(component, CLS_H_NORMAL, false);
382 wcd_clsh_set_flyback_mode(component, CLS_H_NORMAL);
383 wcd_clsh_set_buck_mode(component, CLS_H_NORMAL);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530384 }
385}
386
Meng Wang15c825d2018-09-06 10:49:18 +0800387static void wcd_clsh_state_aux(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530388 struct wcd_clsh_cdc_info *clsh_d,
389 u8 req_state, bool is_enable, int mode)
390{
Meng Wang15c825d2018-09-06 10:49:18 +0800391 dev_dbg(component->dev, "%s: mode: %s, %s\n", __func__,
392 mode_to_str(mode), is_enable ? "enable" : "disable");
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530393
394 if (is_enable) {
Meng Wang15c825d2018-09-06 10:49:18 +0800395 wcd_clsh_set_buck_mode(component, mode);
396 wcd_clsh_set_flyback_mode(component, mode);
397 wcd_clsh_flyback_ctrl(component, clsh_d, mode, true);
398 wcd_clsh_set_flyback_current(component, mode);
399 wcd_clsh_buck_ctrl(component, clsh_d, mode, true);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530400 } else {
Meng Wang15c825d2018-09-06 10:49:18 +0800401 wcd_clsh_buck_ctrl(component, clsh_d, mode, false);
402 wcd_clsh_flyback_ctrl(component, clsh_d, mode, false);
403 wcd_clsh_set_flyback_mode(component, CLS_H_NORMAL);
404 wcd_clsh_set_buck_mode(component, CLS_H_NORMAL);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530405 }
406}
407
Meng Wang15c825d2018-09-06 10:49:18 +0800408static void wcd_clsh_state_ear(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530409 struct wcd_clsh_cdc_info *clsh_d,
410 u8 req_state, bool is_enable, int mode)
411{
Meng Wang15c825d2018-09-06 10:49:18 +0800412 dev_dbg(component->dev, "%s: mode: %s, %s\n", __func__,
413 mode_to_str(mode),
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530414 is_enable ? "enable" : "disable");
415
416 if (is_enable) {
Meng Wang15c825d2018-09-06 10:49:18 +0800417 wcd_clsh_set_buck_regulator_mode(component, mode);
418 wcd_clsh_set_flyback_mode(component, mode);
419 wcd_clsh_force_iq_ctl(component, mode, true);
420 wcd_clsh_flyback_ctrl(component, clsh_d, mode, true);
421 wcd_clsh_set_flyback_current(component, mode);
422 wcd_clsh_set_buck_mode(component, mode);
423 wcd_clsh_buck_ctrl(component, clsh_d, mode, true);
424 wcd_clsh_set_hph_mode(component, mode);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530425 } else {
Meng Wang15c825d2018-09-06 10:49:18 +0800426 wcd_clsh_set_hph_mode(component, CLS_H_NORMAL);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530427
428 /* set buck and flyback to Default Mode */
Meng Wang15c825d2018-09-06 10:49:18 +0800429 wcd_clsh_flyback_ctrl(component, clsh_d, CLS_H_NORMAL, false);
430 wcd_clsh_buck_ctrl(component, clsh_d, CLS_H_NORMAL, false);
431 wcd_clsh_force_iq_ctl(component, CLS_H_NORMAL, false);
432 wcd_clsh_set_flyback_mode(component, CLS_H_NORMAL);
433 wcd_clsh_set_buck_mode(component, CLS_H_NORMAL);
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530434 }
435}
436
Meng Wang15c825d2018-09-06 10:49:18 +0800437static void wcd_clsh_state_err(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530438 struct wcd_clsh_cdc_info *clsh_d,
439 u8 req_state, bool is_enable, int mode)
440{
441 char msg[128];
442
Meng Wang15c825d2018-09-06 10:49:18 +0800443 dev_err(component->dev,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530444 "%s Wrong request for class H state machine requested to %s %s\n",
445 __func__, is_enable ? "enable" : "disable",
446 state_to_str(req_state, msg, sizeof(msg)));
447}
448
449/*
450 * Function: wcd_clsh_is_state_valid
451 * Params: state
452 * Description:
453 * Provides information on valid states of Class H configuration
454 */
455static bool wcd_clsh_is_state_valid(u8 state)
456{
457 switch (state) {
458 case WCD_CLSH_STATE_IDLE:
459 case WCD_CLSH_STATE_EAR:
460 case WCD_CLSH_STATE_HPHL:
461 case WCD_CLSH_STATE_HPHR:
462 case WCD_CLSH_STATE_HPH_ST:
463 case WCD_CLSH_STATE_AUX:
464 case WCD_CLSH_STATE_HPHL_AUX:
465 case WCD_CLSH_STATE_HPHR_AUX:
466 case WCD_CLSH_STATE_HPH_ST_AUX:
467 case WCD_CLSH_STATE_EAR_AUX:
468 return true;
469 default:
470 return false;
471 };
472}
473
474/*
475 * Function: wcd_cls_h_fsm
Meng Wang15c825d2018-09-06 10:49:18 +0800476 * Params: component, cdc_clsh_d, req_state, req_type, clsh_event
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530477 * Description:
478 * This function handles PRE DAC and POST DAC conditions of different devices
479 * and updates class H configuration of different combination of devices
480 * based on validity of their states. cdc_clsh_d will contain current
481 * class h state information
482 */
Meng Wang15c825d2018-09-06 10:49:18 +0800483void wcd_cls_h_fsm(struct snd_soc_component *component,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530484 struct wcd_clsh_cdc_info *cdc_clsh_d,
485 u8 clsh_event, u8 req_state,
486 int int_mode)
487{
488 u8 old_state, new_state;
489 char msg0[128], msg1[128];
490
491 switch (clsh_event) {
492 case WCD_CLSH_EVENT_PRE_DAC:
493 old_state = cdc_clsh_d->state;
494 new_state = old_state | req_state;
495
496 if (!wcd_clsh_is_state_valid(new_state)) {
Meng Wang15c825d2018-09-06 10:49:18 +0800497 dev_err(component->dev,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530498 "%s: Class-H not a valid new state: %s\n",
499 __func__,
500 state_to_str(new_state, msg0, sizeof(msg0)));
501 return;
502 }
503 if (new_state == old_state) {
Meng Wang15c825d2018-09-06 10:49:18 +0800504 dev_err(component->dev,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530505 "%s: Class-H already in requested state: %s\n",
506 __func__,
507 state_to_str(new_state, msg0, sizeof(msg0)));
508 return;
509 }
510 cdc_clsh_d->state = new_state;
511 wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode);
Meng Wang15c825d2018-09-06 10:49:18 +0800512 (*clsh_state_fp[new_state]) (component, cdc_clsh_d, req_state,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530513 CLSH_REQ_ENABLE, int_mode);
Meng Wang15c825d2018-09-06 10:49:18 +0800514 dev_dbg(component->dev,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530515 "%s: ClassH state transition from %s to %s\n",
516 __func__, state_to_str(old_state, msg0, sizeof(msg0)),
517 state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
518 break;
519 case WCD_CLSH_EVENT_POST_PA:
520 old_state = cdc_clsh_d->state;
521 new_state = old_state & (~req_state);
522 if (new_state < NUM_CLSH_STATES) {
523 if (!wcd_clsh_is_state_valid(old_state)) {
Meng Wang15c825d2018-09-06 10:49:18 +0800524 dev_err(component->dev,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530525 "%s:Invalid old state:%s\n",
526 __func__,
527 state_to_str(old_state, msg0,
528 sizeof(msg0)));
529 return;
530 }
531 if (new_state == old_state) {
Meng Wang15c825d2018-09-06 10:49:18 +0800532 dev_err(component->dev,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530533 "%s: Class-H already in requested state: %s\n",
534 __func__,
535 state_to_str(new_state, msg0,
536 sizeof(msg0)));
537 return;
538 }
Meng Wang15c825d2018-09-06 10:49:18 +0800539 (*clsh_state_fp[old_state]) (component, cdc_clsh_d,
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530540 req_state, CLSH_REQ_DISABLE,
541 int_mode);
542 cdc_clsh_d->state = new_state;
543 wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE);
Meng Wang15c825d2018-09-06 10:49:18 +0800544 dev_dbg(component->dev, "%s: ClassH state transition from %s to %s\n",
Laxminath Kasam7adc34e2018-11-09 11:24:38 +0530545 __func__, state_to_str(old_state, msg0,
546 sizeof(msg0)),
547 state_to_str(cdc_clsh_d->state, msg1,
548 sizeof(msg1)));
549 }
550 break;
551 };
552}
553EXPORT_SYMBOL(wcd_cls_h_fsm);
554
555/*
556 * wcd_cls_h_init: Called to init clsh info
557 *
558 * @clsh: pointer for clsh state information.
559 */
560void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh)
561{
562 int i;
563
564 clsh->state = WCD_CLSH_STATE_IDLE;
565
566 for (i = 0; i < NUM_CLSH_STATES; i++)
567 clsh_state_fp[i] = wcd_clsh_state_err;
568
569 clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear;
570 clsh_state_fp[WCD_CLSH_STATE_HPHL] = wcd_clsh_state_hph_l;
571 clsh_state_fp[WCD_CLSH_STATE_HPHR] = wcd_clsh_state_hph_r;
572 clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = wcd_clsh_state_hph_st;
573 clsh_state_fp[WCD_CLSH_STATE_AUX] = wcd_clsh_state_aux;
574 clsh_state_fp[WCD_CLSH_STATE_HPHL_AUX] = wcd_clsh_state_hph_aux;
575 clsh_state_fp[WCD_CLSH_STATE_HPHR_AUX] = wcd_clsh_state_hph_aux;
576 clsh_state_fp[WCD_CLSH_STATE_HPH_ST_AUX] =
577 wcd_clsh_state_hph_aux;
578 clsh_state_fp[WCD_CLSH_STATE_EAR_AUX] = wcd_clsh_state_ear_aux;
579 clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = wcd_clsh_state_hph_ear;
580 clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = wcd_clsh_state_hph_ear;
581 clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = wcd_clsh_state_hph_ear;
582 /* Set interpolaotr modes to NONE */
583 wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE);
584 wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE);
585 wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE);
586 wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_AUX, CLS_NONE);
587 clsh->flyback_users = 0;
588 clsh->buck_users = 0;
589}
590EXPORT_SYMBOL(wcd_cls_h_init);
591
592MODULE_DESCRIPTION("WCD Class-H Driver");
593MODULE_LICENSE("GPL v2");