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
12struct 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(3, 0)
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
95enum {
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
106static 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
120static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp)
121{
122	return snd_soc_component_read(comp, WCD9XXX_A_CDC_CLSH_CRC) &
123					WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK;
124}
125
126static 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
140static 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
154static 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
173static 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
195static 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;
211	}
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
222static 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;
250	}
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
267static 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
279static 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
292static 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
319static 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
377static 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
435static 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
470static 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 */
500static 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 */
523int 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;
545	}
546
547	ctrl->state = nstate;
548	ctrl->mode = mode;
549
550	return 0;
551}
552
553int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl)
554{
555	return ctrl->state;
556}
557
558struct 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
573void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl)
574{
575	kfree(ctrl);
576}
577