brintos

brintos / linux-shallow public Read only

0
0
Text · 27.6 KiB · d96e23e Raw
906 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.3// Copyright (c) 2017-2018, Linaro Limited4 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		027#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		036#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		040#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		043#define WCD9XXX_A_ANA_RX_VNEG_EN_MASK			BIT(6)44#define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT			645#define WCD9XXX_A_ANA_RX_VNEG_ENABLE			BIT(6)46#define WCD9XXX_A_ANA_RX_VNEG_DISABLE			047#define WCD9XXX_A_ANA_RX_VPOS_EN_MASK			BIT(7)48#define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT			749#define WCD9XXX_A_ANA_RX_VPOS_ENABLE			BIT(7)50#define WCD9XXX_A_ANA_RX_VPOS_DISABLE			051#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		0x0854#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP			0x0455#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL		0x056#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		060#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		0x4063#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			066#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			072#define WCD9XXX_HPH_CONST_SEL_LP_PATH			0x4073#define WCD9XXX_HPH_CONST_SEL_HQ_PATH			0x8074#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	0x2080#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM	0x081#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	0x5089#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA	0x3090 91#define WCD9XXX_BASE_ADDRESS				0x300092#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		true105#define CLSH_REQ_DISABLE	false106#define WCD_USLEEP_RANGE	50107 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	else142		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	else156		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	else170		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/disable189	 * as per HW requirement190	 */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/disable207		 * as per HW requirement208		 */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/disable241	 * as per HW requirement242	 */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	else355		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	else380		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/disable468		 * as per HW requirement469		 */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 Impedance584			 * For now it is assumed to be 16 ohm585			 */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 Impedance674			 * For now it is assumed to be 16 ohm675			 */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		else785			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		else791			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		else797			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_valid816 * Params: state817 * Description:818 * Provides information on valid states of Class H configuration819 */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_fsm837 * Params: ctrl, req_state, req_type, clsh_event838 * Description:839 * This function handles PRE DAC and POST DAC conditions of different devices840 * and updates class H configuration of different combination of devices841 * based on validity of their states. ctrl will contain current842 * class h state information843 */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");906