Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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 WCD9XXX_BASE_ADDRESS				0x3000
 92#define WCD9XXX_ANA_RX_SUPPLIES				(WCD9XXX_BASE_ADDRESS+0x008)
 93#define WCD9XXX_ANA_HPH					(WCD9XXX_BASE_ADDRESS+0x009)
 94#define WCD9XXX_CLASSH_MODE_2				(WCD9XXX_BASE_ADDRESS+0x098)
 95#define WCD9XXX_CLASSH_MODE_3				(WCD9XXX_BASE_ADDRESS+0x099)
 96#define WCD9XXX_FLYBACK_VNEG_CTRL_1			(WCD9XXX_BASE_ADDRESS+0x0A5)
 97#define WCD9XXX_FLYBACK_VNEG_CTRL_4			(WCD9XXX_BASE_ADDRESS+0x0A8)
 98#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2			(WCD9XXX_BASE_ADDRESS+0x0AF)
 99#define WCD9XXX_RX_BIAS_HPH_LOWPOWER			(WCD9XXX_BASE_ADDRESS+0x0BF)
100#define WCD9XXX_V3_RX_BIAS_FLYB_BUFF			(WCD9XXX_BASE_ADDRESS+0x0C7)
101#define WCD9XXX_HPH_PA_CTL1				(WCD9XXX_BASE_ADDRESS+0x0D1)
102#define WCD9XXX_HPH_NEW_INT_PA_MISC2			(WCD9XXX_BASE_ADDRESS+0x138)
103
104#define CLSH_REQ_ENABLE		true
105#define CLSH_REQ_DISABLE	false
106#define WCD_USLEEP_RANGE	50
107
108enum {
109	DAC_GAIN_0DB = 0,
110	DAC_GAIN_0P2DB,
111	DAC_GAIN_0P4DB,
112	DAC_GAIN_0P6DB,
113	DAC_GAIN_0P8DB,
114	DAC_GAIN_M0P2DB,
115	DAC_GAIN_M0P4DB,
116	DAC_GAIN_M0P6DB,
117};
118
119static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl,
120					 bool enable)
121{
122	struct snd_soc_component *comp = ctrl->comp;
123
124	if ((enable && ++ctrl->clsh_users == 1) ||
125	    (!enable && --ctrl->clsh_users == 0))
126		snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC,
127				      WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK,
128				      enable);
129	if (ctrl->clsh_users < 0)
130		ctrl->clsh_users = 0;
131}
132
133static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp,
134					  int mode)
135{
136	/* set to HIFI */
137	if (mode == CLS_H_HIFI)
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_UHQA);
141	else
142		snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
143					WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,
144					WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT);
145}
146
147static void wcd_clsh_v3_set_buck_mode(struct snd_soc_component *component,
148					  int mode)
149{
150	if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
151	    mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI)
152		snd_soc_component_update_bits(component,
153				WCD9XXX_ANA_RX_SUPPLIES,
154				0x08, 0x08); /* set to HIFI */
155	else
156		snd_soc_component_update_bits(component,
157				WCD9XXX_ANA_RX_SUPPLIES,
158				0x08, 0x00); /* set to default */
159}
160
161static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp,
162					     int mode)
163{
164	/* set to HIFI */
165	if (mode == CLS_H_HIFI)
166		snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
167					WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,
168					WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA);
169	else
170		snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
171					WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,
172					WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT);
173}
174
175static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl,
176			       int mode,
177			       bool enable)
178{
179	struct snd_soc_component *comp = ctrl->comp;
180
181	/* enable/disable buck */
182	if ((enable && (++ctrl->buck_users == 1)) ||
183	   (!enable && (--ctrl->buck_users == 0)))
184		snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
185				WCD9XXX_A_ANA_RX_VPOS_EN_MASK,
186				enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT);
187	/*
188	 * 500us sleep is required after buck enable/disable
189	 * as per HW requirement
190	 */
191	usleep_range(500, 500 + WCD_USLEEP_RANGE);
192}
193
194static void wcd_clsh_v3_buck_ctrl(struct snd_soc_component *component,
195			       struct wcd_clsh_ctrl *ctrl,
196			       int mode,
197			       bool enable)
198{
199	/* enable/disable buck */
200	if ((enable && (++ctrl->buck_users == 1)) ||
201	   (!enable && (--ctrl->buck_users == 0))) {
202		snd_soc_component_update_bits(component,
203				WCD9XXX_ANA_RX_SUPPLIES,
204				(1 << 7), (enable << 7));
205		/*
206		 * 500us sleep is required after buck enable/disable
207		 * as per HW requirement
208		 */
209		usleep_range(500, 510);
210		if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
211			mode == CLS_H_HIFI || mode == CLS_H_LP)
212			snd_soc_component_update_bits(component,
213					WCD9XXX_CLASSH_MODE_3,
214					0x02, 0x00);
215
216		snd_soc_component_update_bits(component,
217					WCD9XXX_CLASSH_MODE_2,
218					0xFF, 0x3A);
219		/* 500usec delay is needed as per HW requirement */
220		usleep_range(500, 500 + WCD_USLEEP_RANGE);
221	}
222}
223
224static void wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl *ctrl,
225				  int mode,
226				  bool enable)
227{
228	struct snd_soc_component *comp = ctrl->comp;
229
230	/* enable/disable flyback */
231	if ((enable && (++ctrl->flyback_users == 1)) ||
232	   (!enable && (--ctrl->flyback_users == 0))) {
233		snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
234				WCD9XXX_A_ANA_RX_VNEG_EN_MASK,
235				enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT);
236		/* 100usec delay is needed as per HW requirement */
237		usleep_range(100, 110);
238	}
239	/*
240	 * 500us sleep is required after flyback enable/disable
241	 * as per HW requirement
242	 */
243	usleep_range(500, 500 + WCD_USLEEP_RANGE);
244}
245
246static void wcd_clsh_set_gain_path(struct wcd_clsh_ctrl *ctrl, int mode)
247{
248	struct snd_soc_component *comp = ctrl->comp;
249	int val = 0;
250
251	switch (mode) {
252	case CLS_H_NORMAL:
253	case CLS_AB:
254		val = WCD9XXX_HPH_CONST_SEL_BYPASS;
255		break;
256	case CLS_H_HIFI:
257		val = WCD9XXX_HPH_CONST_SEL_HQ_PATH;
258		break;
259	case CLS_H_LP:
260		val = WCD9XXX_HPH_CONST_SEL_LP_PATH;
261		break;
262	}
263
264	snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN,
265					WCD9XXX_HPH_CONST_SEL_L_MASK,
266					val);
267
268	snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN,
269					WCD9XXX_HPH_CONST_SEL_L_MASK,
270					val);
271}
272
273static void wcd_clsh_v2_set_hph_mode(struct snd_soc_component *comp, int mode)
274{
275	int val = 0, gain = 0, res_val;
276	int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
277
278	res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM;
279	switch (mode) {
280	case CLS_H_NORMAL:
281		res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM;
282		val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;
283		gain = DAC_GAIN_0DB;
284		ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
285		break;
286	case CLS_AB:
287		val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;
288		gain = DAC_GAIN_0DB;
289		ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
290		break;
291	case CLS_H_HIFI:
292		val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA;
293		gain = DAC_GAIN_M0P2DB;
294		ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
295		break;
296	case CLS_H_LP:
297		val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP;
298		ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA;
299		break;
300	}
301
302	snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH,
303					WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val);
304	snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2,
305				WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK,
306				res_val);
307	if (mode != CLS_H_LP)
308		snd_soc_component_update_bits(comp,
309					WCD9XXX_HPH_REFBUFF_UHQA_CTL,
310					WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK,
311					gain);
312	snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1,
313				WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK,
314				ipeak);
315}
316
317static void wcd_clsh_v3_set_hph_mode(struct snd_soc_component *component,
318				  int mode)
319{
320	u8 val;
321
322	switch (mode) {
323	case CLS_H_NORMAL:
324		val = 0x00;
325		break;
326	case CLS_AB:
327	case CLS_H_ULP:
328		val = 0x0C;
329		break;
330	case CLS_AB_HIFI:
331	case CLS_H_HIFI:
332		val = 0x08;
333		break;
334	case CLS_H_LP:
335	case CLS_H_LOHIFI:
336	case CLS_AB_LP:
337	case CLS_AB_LOHIFI:
338		val = 0x04;
339		break;
340	default:
341		dev_err(component->dev, "%s:Invalid mode %d\n", __func__, mode);
342		return;
343	}
344
345	snd_soc_component_update_bits(component, WCD9XXX_ANA_HPH, 0x0C, val);
346}
347
348void wcd_clsh_set_hph_mode(struct wcd_clsh_ctrl *ctrl, int mode)
349{
350	struct snd_soc_component *comp = ctrl->comp;
351
352	if (ctrl->codec_version >= WCD937X)
353		wcd_clsh_v3_set_hph_mode(comp, mode);
354	else
355		wcd_clsh_v2_set_hph_mode(comp, mode);
356
357}
358EXPORT_SYMBOL_GPL(wcd_clsh_set_hph_mode);
359
360static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp,
361					 int mode)
362{
363
364	snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,
365				WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A);
366	snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,
367				WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A);
368	/* Sleep needed to avoid click and pop as per HW requirement */
369	usleep_range(100, 110);
370}
371
372static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_component *comp,
373					     int mode)
374{
375	if (mode == CLS_AB)
376		snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
377					WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,
378					WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB);
379	else
380		snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
381					WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,
382					WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H);
383}
384
385static void wcd_clsh_v3_set_buck_regulator_mode(struct snd_soc_component *component,
386						int mode)
387{
388	snd_soc_component_update_bits(component, WCD9XXX_ANA_RX_SUPPLIES,
389			    0x02, 0x00);
390}
391
392static void wcd_clsh_v3_set_flyback_mode(struct snd_soc_component *component,
393						int mode)
394{
395	if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
396	    mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI) {
397		snd_soc_component_update_bits(component,
398				WCD9XXX_ANA_RX_SUPPLIES,
399				0x04, 0x04);
400		snd_soc_component_update_bits(component,
401				WCD9XXX_FLYBACK_VNEG_CTRL_4,
402				0xF0, 0x80);
403	} else {
404		snd_soc_component_update_bits(component,
405				WCD9XXX_ANA_RX_SUPPLIES,
406				0x04, 0x00); /* set to Default */
407		snd_soc_component_update_bits(component,
408				WCD9XXX_FLYBACK_VNEG_CTRL_4,
409				0xF0, 0x70);
410	}
411}
412
413static void wcd_clsh_v3_force_iq_ctl(struct snd_soc_component *component,
414					 int mode, bool enable)
415{
416	if (enable) {
417		snd_soc_component_update_bits(component,
418				WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
419				0xE0, 0xA0);
420		/* 100usec delay is needed as per HW requirement */
421		usleep_range(100, 110);
422		snd_soc_component_update_bits(component,
423				WCD9XXX_CLASSH_MODE_3,
424				0x02, 0x02);
425		snd_soc_component_update_bits(component,
426				WCD9XXX_CLASSH_MODE_2,
427				0xFF, 0x1C);
428		if (mode == CLS_H_LOHIFI || mode == CLS_AB_LOHIFI) {
429			snd_soc_component_update_bits(component,
430					WCD9XXX_HPH_NEW_INT_PA_MISC2,
431					0x20, 0x20);
432			snd_soc_component_update_bits(component,
433					WCD9XXX_RX_BIAS_HPH_LOWPOWER,
434					0xF0, 0xC0);
435			snd_soc_component_update_bits(component,
436					WCD9XXX_HPH_PA_CTL1,
437					0x0E, 0x02);
438		}
439	} else {
440		snd_soc_component_update_bits(component,
441				WCD9XXX_HPH_NEW_INT_PA_MISC2,
442				0x20, 0x00);
443		snd_soc_component_update_bits(component,
444				WCD9XXX_RX_BIAS_HPH_LOWPOWER,
445				0xF0, 0x80);
446		snd_soc_component_update_bits(component,
447				WCD9XXX_HPH_PA_CTL1,
448				0x0E, 0x06);
449	}
450}
451
452static void wcd_clsh_v3_flyback_ctrl(struct snd_soc_component *component,
453				  struct wcd_clsh_ctrl *ctrl,
454				  int mode,
455				  bool enable)
456{
457	/* enable/disable flyback */
458	if ((enable && (++ctrl->flyback_users == 1)) ||
459	   (!enable && (--ctrl->flyback_users == 0))) {
460		snd_soc_component_update_bits(component,
461				WCD9XXX_FLYBACK_VNEG_CTRL_1,
462				0xE0, 0xE0);
463		snd_soc_component_update_bits(component,
464				WCD9XXX_ANA_RX_SUPPLIES,
465				(1 << 6), (enable << 6));
466		/*
467		 * 100us sleep is required after flyback enable/disable
468		 * as per HW requirement
469		 */
470		usleep_range(100, 110);
471		snd_soc_component_update_bits(component,
472				WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
473				0xE0, 0xE0);
474		/* 500usec delay is needed as per HW requirement */
475		usleep_range(500, 500 + WCD_USLEEP_RANGE);
476	}
477}
478
479static void wcd_clsh_v3_set_flyback_current(struct snd_soc_component *component,
480				int mode)
481{
482	snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF,
483				0x0F, 0x0A);
484	snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF,
485				0xF0, 0xA0);
486	/* Sleep needed to avoid click and pop as per HW requirement */
487	usleep_range(100, 110);
488}
489
490static void wcd_clsh_v3_state_aux(struct wcd_clsh_ctrl *ctrl, int req_state,
491			      bool is_enable, int mode)
492{
493	struct snd_soc_component *component = ctrl->comp;
494
495	if (is_enable) {
496		wcd_clsh_v3_set_buck_mode(component, mode);
497		wcd_clsh_v3_set_flyback_mode(component, mode);
498		wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
499		wcd_clsh_v3_set_flyback_current(component, mode);
500		wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
501	} else {
502		wcd_clsh_v3_buck_ctrl(component, ctrl, mode, false);
503		wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, false);
504		wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
505		wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
506	}
507}
508
509static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state,
510			      bool is_enable, int mode)
511{
512	struct snd_soc_component *comp = ctrl->comp;
513
514	if (mode != CLS_AB) {
515		dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n",
516			__func__, mode);
517		return;
518	}
519
520	if (is_enable) {
521		wcd_clsh_set_buck_regulator_mode(comp, mode);
522		wcd_clsh_set_buck_mode(comp, mode);
523		wcd_clsh_set_flyback_mode(comp, mode);
524		wcd_clsh_flyback_ctrl(ctrl, mode, true);
525		wcd_clsh_set_flyback_current(comp, mode);
526		wcd_clsh_buck_ctrl(ctrl, mode, true);
527	} else {
528		wcd_clsh_buck_ctrl(ctrl, mode, false);
529		wcd_clsh_flyback_ctrl(ctrl, mode, false);
530		wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
531		wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
532		wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
533	}
534}
535
536static void wcd_clsh_v3_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,
537				 bool is_enable, int mode)
538{
539	struct snd_soc_component *component = ctrl->comp;
540
541	if (mode == CLS_H_NORMAL) {
542		dev_dbg(component->dev, "%s: Normal mode not applicable for hph_r\n",
543			__func__);
544		return;
545	}
546
547	if (is_enable) {
548		wcd_clsh_v3_set_buck_regulator_mode(component, mode);
549		wcd_clsh_v3_set_flyback_mode(component, mode);
550		wcd_clsh_v3_force_iq_ctl(component, mode, true);
551		wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
552		wcd_clsh_v3_set_flyback_current(component, mode);
553		wcd_clsh_v3_set_buck_mode(component, mode);
554		wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
555		wcd_clsh_v3_set_hph_mode(component, mode);
556	} else {
557		wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
558
559		/* buck and flyback set to default mode and disable */
560		wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
561		wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
562		wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
563		wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
564		wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
565	}
566}
567
568static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,
569				 bool is_enable, int mode)
570{
571	struct snd_soc_component *comp = ctrl->comp;
572
573	if (mode == CLS_H_NORMAL) {
574		dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n",
575			__func__);
576		return;
577	}
578
579	if (is_enable) {
580		if (mode != CLS_AB) {
581			wcd_enable_clsh_block(ctrl, true);
582			/*
583			 * These K1 values depend on the Headphone Impedance
584			 * For now it is assumed to be 16 ohm
585			 */
586			snd_soc_component_update_bits(comp,
587					WCD9XXX_A_CDC_CLSH_K1_MSB,
588					WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,
589					0x00);
590			snd_soc_component_update_bits(comp,
591					WCD9XXX_A_CDC_CLSH_K1_LSB,
592					WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,
593					0xC0);
594			snd_soc_component_update_bits(comp,
595					    WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
596					    WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
597					    WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
598		}
599		wcd_clsh_set_buck_regulator_mode(comp, mode);
600		wcd_clsh_set_flyback_mode(comp, mode);
601		wcd_clsh_flyback_ctrl(ctrl, mode, true);
602		wcd_clsh_set_flyback_current(comp, mode);
603		wcd_clsh_set_buck_mode(comp, mode);
604		wcd_clsh_buck_ctrl(ctrl, mode, true);
605		wcd_clsh_v2_set_hph_mode(comp, mode);
606		wcd_clsh_set_gain_path(ctrl, mode);
607	} else {
608		wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL);
609
610		if (mode != CLS_AB) {
611			snd_soc_component_update_bits(comp,
612					    WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
613					    WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
614					    WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
615			wcd_enable_clsh_block(ctrl, false);
616		}
617		/* buck and flyback set to default mode and disable */
618		wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);
619		wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);
620		wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
621		wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
622		wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
623	}
624}
625
626static void wcd_clsh_v3_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,
627				 bool is_enable, int mode)
628{
629	struct snd_soc_component *component = ctrl->comp;
630
631	if (mode == CLS_H_NORMAL) {
632		dev_dbg(component->dev, "%s: Normal mode not applicable for hph_l\n",
633			__func__);
634		return;
635	}
636
637	if (is_enable) {
638		wcd_clsh_v3_set_buck_regulator_mode(component, mode);
639		wcd_clsh_v3_set_flyback_mode(component, mode);
640		wcd_clsh_v3_force_iq_ctl(component, mode, true);
641		wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
642		wcd_clsh_v3_set_flyback_current(component, mode);
643		wcd_clsh_v3_set_buck_mode(component, mode);
644		wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
645		wcd_clsh_v3_set_hph_mode(component, mode);
646	} else {
647		wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
648
649		/* set buck and flyback to Default Mode */
650		wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
651		wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
652		wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
653		wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
654		wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
655	}
656}
657
658static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,
659				 bool is_enable, int mode)
660{
661	struct snd_soc_component *comp = ctrl->comp;
662
663	if (mode == CLS_H_NORMAL) {
664		dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n",
665			__func__);
666		return;
667	}
668
669	if (is_enable) {
670		if (mode != CLS_AB) {
671			wcd_enable_clsh_block(ctrl, true);
672			/*
673			 * These K1 values depend on the Headphone Impedance
674			 * For now it is assumed to be 16 ohm
675			 */
676			snd_soc_component_update_bits(comp,
677					WCD9XXX_A_CDC_CLSH_K1_MSB,
678					WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,
679					0x00);
680			snd_soc_component_update_bits(comp,
681					WCD9XXX_A_CDC_CLSH_K1_LSB,
682					WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,
683					0xC0);
684			snd_soc_component_update_bits(comp,
685					    WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
686					    WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
687					    WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
688		}
689		wcd_clsh_set_buck_regulator_mode(comp, mode);
690		wcd_clsh_set_flyback_mode(comp, mode);
691		wcd_clsh_flyback_ctrl(ctrl, mode, true);
692		wcd_clsh_set_flyback_current(comp, mode);
693		wcd_clsh_set_buck_mode(comp, mode);
694		wcd_clsh_buck_ctrl(ctrl, mode, true);
695		wcd_clsh_v2_set_hph_mode(comp, mode);
696		wcd_clsh_set_gain_path(ctrl, mode);
697	} else {
698		wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL);
699
700		if (mode != CLS_AB) {
701			snd_soc_component_update_bits(comp,
702					    WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
703					    WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
704					    WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
705			wcd_enable_clsh_block(ctrl, false);
706		}
707		/* set buck and flyback to Default Mode */
708		wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);
709		wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);
710		wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
711		wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
712		wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
713	}
714}
715
716static void wcd_clsh_v3_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,
717			       bool is_enable, int mode)
718{
719	struct snd_soc_component *component = ctrl->comp;
720
721	if (is_enable) {
722		wcd_clsh_v3_set_buck_regulator_mode(component, mode);
723		wcd_clsh_v3_set_flyback_mode(component, mode);
724		wcd_clsh_v3_force_iq_ctl(component, mode, true);
725		wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);
726		wcd_clsh_v3_set_flyback_current(component, mode);
727		wcd_clsh_v3_set_buck_mode(component, mode);
728		wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);
729		wcd_clsh_v3_set_hph_mode(component, mode);
730	} else {
731		wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);
732
733		/* set buck and flyback to Default Mode */
734		wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);
735		wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);
736		wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);
737		wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);
738		wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);
739	}
740}
741
742static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,
743			       bool is_enable, int mode)
744{
745	struct snd_soc_component *comp = ctrl->comp;
746
747	if (mode != CLS_H_NORMAL) {
748		dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n",
749			__func__, mode);
750		return;
751	}
752
753	if (is_enable) {
754		wcd_enable_clsh_block(ctrl, true);
755		snd_soc_component_update_bits(comp,
756					WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
757					WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
758					WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
759		wcd_clsh_set_buck_mode(comp, mode);
760		wcd_clsh_set_flyback_mode(comp, mode);
761		wcd_clsh_flyback_ctrl(ctrl, mode, true);
762		wcd_clsh_set_flyback_current(comp, mode);
763		wcd_clsh_buck_ctrl(ctrl, mode, true);
764	} else {
765		snd_soc_component_update_bits(comp,
766					WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
767					WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
768					WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
769		wcd_enable_clsh_block(ctrl, false);
770		wcd_clsh_buck_ctrl(ctrl, mode, false);
771		wcd_clsh_flyback_ctrl(ctrl, mode, false);
772		wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
773		wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
774	}
775}
776
777static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state,
778				    bool is_enable, int mode)
779{
780	switch (req_state) {
781	case WCD_CLSH_STATE_EAR:
782		if (ctrl->codec_version >= WCD937X)
783			wcd_clsh_v3_state_ear(ctrl, req_state, is_enable, mode);
784		else
785			wcd_clsh_state_ear(ctrl, req_state, is_enable, mode);
786		break;
787	case WCD_CLSH_STATE_HPHL:
788		if (ctrl->codec_version >= WCD937X)
789			wcd_clsh_v3_state_hph_l(ctrl, req_state, is_enable, mode);
790		else
791			wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode);
792		break;
793	case WCD_CLSH_STATE_HPHR:
794		if (ctrl->codec_version >= WCD937X)
795			wcd_clsh_v3_state_hph_r(ctrl, req_state, is_enable, mode);
796		else
797			wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode);
798		break;
799	case WCD_CLSH_STATE_LO:
800		if (ctrl->codec_version < WCD937X)
801			wcd_clsh_state_lo(ctrl, req_state, is_enable, mode);
802		break;
803	case WCD_CLSH_STATE_AUX:
804		if (ctrl->codec_version >= WCD937X)
805			wcd_clsh_v3_state_aux(ctrl, req_state, is_enable, mode);
806		break;
807	default:
808		break;
809	}
810
811	return 0;
812}
813
814/*
815 * Function: wcd_clsh_is_state_valid
816 * Params: state
817 * Description:
818 * Provides information on valid states of Class H configuration
819 */
820static bool wcd_clsh_is_state_valid(int state)
821{
822	switch (state) {
823	case WCD_CLSH_STATE_IDLE:
824	case WCD_CLSH_STATE_EAR:
825	case WCD_CLSH_STATE_HPHL:
826	case WCD_CLSH_STATE_HPHR:
827	case WCD_CLSH_STATE_LO:
828	case WCD_CLSH_STATE_AUX:
829		return true;
830	default:
831		return false;
832	};
833}
834
835/*
836 * Function: wcd_clsh_fsm
837 * Params: ctrl, req_state, req_type, clsh_event
838 * Description:
839 * This function handles PRE DAC and POST DAC conditions of different devices
840 * and updates class H configuration of different combination of devices
841 * based on validity of their states. ctrl will contain current
842 * class h state information
843 */
844int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl,
845			    enum wcd_clsh_event clsh_event,
846			    int nstate,
847			    enum wcd_clsh_mode mode)
848{
849	struct snd_soc_component *comp = ctrl->comp;
850
851	if (nstate == ctrl->state)
852		return 0;
853
854	if (!wcd_clsh_is_state_valid(nstate)) {
855		dev_err(comp->dev, "Class-H not a valid new state:\n");
856		return -EINVAL;
857	}
858
859	switch (clsh_event) {
860	case WCD_CLSH_EVENT_PRE_DAC:
861		_wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode);
862		break;
863	case WCD_CLSH_EVENT_POST_PA:
864		_wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode);
865		break;
866	}
867
868	ctrl->state = nstate;
869	ctrl->mode = mode;
870
871	return 0;
872}
873EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_set_state);
874
875int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl)
876{
877	return ctrl->state;
878}
879EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_get_state);
880
881struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp,
882					  int version)
883{
884	struct wcd_clsh_ctrl *ctrl;
885
886	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
887	if (!ctrl)
888		return ERR_PTR(-ENOMEM);
889
890	ctrl->state = WCD_CLSH_STATE_IDLE;
891	ctrl->comp = comp;
892	ctrl->codec_version = version;
893
894	return ctrl;
895}
896EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_alloc);
897
898void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl)
899{
900	kfree(ctrl);
901}
902EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_free);
903
904MODULE_DESCRIPTION("WCD93XX Class-H driver");
905MODULE_LICENSE("GPL");