1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun // Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
3*4882a593Smuzhiyun // Copyright (c) 2017-2018, Linaro Limited
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun #include <linux/slab.h>
6*4882a593Smuzhiyun #include <sound/soc.h>
7*4882a593Smuzhiyun #include <linux/kernel.h>
8*4882a593Smuzhiyun #include <linux/delay.h>
9*4882a593Smuzhiyun #include "wcd9335.h"
10*4882a593Smuzhiyun #include "wcd-clsh-v2.h"
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun struct wcd_clsh_ctrl {
13*4882a593Smuzhiyun int state;
14*4882a593Smuzhiyun int mode;
15*4882a593Smuzhiyun int flyback_users;
16*4882a593Smuzhiyun int buck_users;
17*4882a593Smuzhiyun int clsh_users;
18*4882a593Smuzhiyun int codec_version;
19*4882a593Smuzhiyun struct snd_soc_component *comp;
20*4882a593Smuzhiyun };
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun /* Class-H registers for codecs from and above WCD9335 */
23*4882a593Smuzhiyun #define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0xB, 0x42)
24*4882a593Smuzhiyun #define WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK BIT(6)
25*4882a593Smuzhiyun #define WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE BIT(6)
26*4882a593Smuzhiyun #define WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE 0
27*4882a593Smuzhiyun #define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0xB, 0x56)
28*4882a593Smuzhiyun #define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0xB, 0x6A)
29*4882a593Smuzhiyun #define WCD9XXX_A_CDC_CLSH_K1_MSB WCD9335_REG(0xC, 0x08)
30*4882a593Smuzhiyun #define WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK GENMASK(3, 0)
31*4882a593Smuzhiyun #define WCD9XXX_A_CDC_CLSH_K1_LSB WCD9335_REG(0xC, 0x09)
32*4882a593Smuzhiyun #define WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK GENMASK(7, 0)
33*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_SUPPLIES WCD9335_REG(0x6, 0x08)
34*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK BIT(1)
35*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H 0
36*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB BIT(1)
37*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK BIT(2)
38*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA BIT(2)
39*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT 0
40*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK BIT(3)
41*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA BIT(3)
42*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT 0
43*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VNEG_EN_MASK BIT(6)
44*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT 6
45*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VNEG_ENABLE BIT(6)
46*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VNEG_DISABLE 0
47*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VPOS_EN_MASK BIT(7)
48*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT 7
49*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VPOS_ENABLE BIT(7)
50*4882a593Smuzhiyun #define WCD9XXX_A_ANA_RX_VPOS_DISABLE 0
51*4882a593Smuzhiyun #define WCD9XXX_A_ANA_HPH WCD9335_REG(0x6, 0x09)
52*4882a593Smuzhiyun #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK GENMASK(3, 2)
53*4882a593Smuzhiyun #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA 0x08
54*4882a593Smuzhiyun #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP 0x04
55*4882a593Smuzhiyun #define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL 0x0
56*4882a593Smuzhiyun #define WCD9XXX_A_CDC_CLSH_CRC WCD9335_REG(0xC, 0x01)
57*4882a593Smuzhiyun #define WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK BIT(0)
58*4882a593Smuzhiyun #define WCD9XXX_A_CDC_CLSH_CRC_CLK_ENABLE BIT(0)
59*4882a593Smuzhiyun #define WCD9XXX_A_CDC_CLSH_CRC_CLK_DISABLE 0
60*4882a593Smuzhiyun #define WCD9XXX_FLYBACK_EN WCD9335_REG(0x6, 0xA4)
61*4882a593Smuzhiyun #define WCD9XXX_FLYBACK_EN_DELAY_SEL_MASK GENMASK(6, 5)
62*4882a593Smuzhiyun #define WCD9XXX_FLYBACK_EN_DELAY_26P25_US 0x40
63*4882a593Smuzhiyun #define WCD9XXX_FLYBACK_EN_RESET_BY_EXT_MASK BIT(4)
64*4882a593Smuzhiyun #define WCD9XXX_FLYBACK_EN_PWDN_WITHOUT_DELAY BIT(4)
65*4882a593Smuzhiyun #define WCD9XXX_FLYBACK_EN_PWDN_WITH_DELAY 0
66*4882a593Smuzhiyun #define WCD9XXX_RX_BIAS_FLYB_BUFF WCD9335_REG(0x6, 0xC7)
67*4882a593Smuzhiyun #define WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4)
68*4882a593Smuzhiyun #define WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(3, 0)
69*4882a593Smuzhiyun #define WCD9XXX_HPH_L_EN WCD9335_REG(0x6, 0xD3)
70*4882a593Smuzhiyun #define WCD9XXX_HPH_CONST_SEL_L_MASK GENMASK(7, 3)
71*4882a593Smuzhiyun #define WCD9XXX_HPH_CONST_SEL_BYPASS 0
72*4882a593Smuzhiyun #define WCD9XXX_HPH_CONST_SEL_LP_PATH 0x40
73*4882a593Smuzhiyun #define WCD9XXX_HPH_CONST_SEL_HQ_PATH 0x80
74*4882a593Smuzhiyun #define WCD9XXX_HPH_R_EN WCD9335_REG(0x6, 0xD6)
75*4882a593Smuzhiyun #define WCD9XXX_HPH_REFBUFF_UHQA_CTL WCD9335_REG(0x6, 0xDD)
76*4882a593Smuzhiyun #define WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK GENMASK(2, 0)
77*4882a593Smuzhiyun #define WCD9XXX_CLASSH_CTRL_VCL_2 WCD9335_REG(0x6, 0x9B)
78*4882a593Smuzhiyun #define WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK GENMASK(5, 4)
79*4882a593Smuzhiyun #define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM 0x20
80*4882a593Smuzhiyun #define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM 0x0
81*4882a593Smuzhiyun #define WCD9XXX_CDC_RX1_RX_PATH_CTL WCD9335_REG(0xB, 0x55)
82*4882a593Smuzhiyun #define WCD9XXX_CDC_RX2_RX_PATH_CTL WCD9335_REG(0xB, 0x69)
83*4882a593Smuzhiyun #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0xD, 0x41)
84*4882a593Smuzhiyun #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0)
85*4882a593Smuzhiyun #define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_11P3_EN_MASK BIT(1)
86*4882a593Smuzhiyun #define WCD9XXX_CLASSH_CTRL_CCL_1 WCD9335_REG(0x6, 0x9C)
87*4882a593Smuzhiyun #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK GENMASK(7, 4)
88*4882a593Smuzhiyun #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA 0x50
89*4882a593Smuzhiyun #define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA 0x30
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun #define CLSH_REQ_ENABLE true
92*4882a593Smuzhiyun #define CLSH_REQ_DISABLE false
93*4882a593Smuzhiyun #define WCD_USLEEP_RANGE 50
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun enum {
96*4882a593Smuzhiyun DAC_GAIN_0DB = 0,
97*4882a593Smuzhiyun DAC_GAIN_0P2DB,
98*4882a593Smuzhiyun DAC_GAIN_0P4DB,
99*4882a593Smuzhiyun DAC_GAIN_0P6DB,
100*4882a593Smuzhiyun DAC_GAIN_0P8DB,
101*4882a593Smuzhiyun DAC_GAIN_M0P2DB,
102*4882a593Smuzhiyun DAC_GAIN_M0P4DB,
103*4882a593Smuzhiyun DAC_GAIN_M0P6DB,
104*4882a593Smuzhiyun };
105*4882a593Smuzhiyun
wcd_enable_clsh_block(struct wcd_clsh_ctrl * ctrl,bool enable)106*4882a593Smuzhiyun static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl,
107*4882a593Smuzhiyun bool enable)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if ((enable && ++ctrl->clsh_users == 1) ||
112*4882a593Smuzhiyun (!enable && --ctrl->clsh_users == 0))
113*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC,
114*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK,
115*4882a593Smuzhiyun enable);
116*4882a593Smuzhiyun if (ctrl->clsh_users < 0)
117*4882a593Smuzhiyun ctrl->clsh_users = 0;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
wcd_clsh_enable_status(struct snd_soc_component * comp)120*4882a593Smuzhiyun static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun return snd_soc_component_read(comp, WCD9XXX_A_CDC_CLSH_CRC) &
123*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK;
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun
wcd_clsh_set_buck_mode(struct snd_soc_component * comp,int mode)126*4882a593Smuzhiyun static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp,
127*4882a593Smuzhiyun int mode)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun /* set to HIFI */
130*4882a593Smuzhiyun if (mode == CLS_H_HIFI)
131*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
132*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,
133*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA);
134*4882a593Smuzhiyun else
135*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
136*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,
137*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
wcd_clsh_set_flyback_mode(struct snd_soc_component * comp,int mode)140*4882a593Smuzhiyun static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp,
141*4882a593Smuzhiyun int mode)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun /* set to HIFI */
144*4882a593Smuzhiyun if (mode == CLS_H_HIFI)
145*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
146*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,
147*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA);
148*4882a593Smuzhiyun else
149*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
150*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,
151*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT);
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl * ctrl,int mode,bool enable)154*4882a593Smuzhiyun static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl,
155*4882a593Smuzhiyun int mode,
156*4882a593Smuzhiyun bool enable)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun /* enable/disable buck */
161*4882a593Smuzhiyun if ((enable && (++ctrl->buck_users == 1)) ||
162*4882a593Smuzhiyun (!enable && (--ctrl->buck_users == 0)))
163*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
164*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VPOS_EN_MASK,
165*4882a593Smuzhiyun enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT);
166*4882a593Smuzhiyun /*
167*4882a593Smuzhiyun * 500us sleep is required after buck enable/disable
168*4882a593Smuzhiyun * as per HW requirement
169*4882a593Smuzhiyun */
170*4882a593Smuzhiyun usleep_range(500, 500 + WCD_USLEEP_RANGE);
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl * ctrl,int mode,bool enable)173*4882a593Smuzhiyun static void wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl *ctrl,
174*4882a593Smuzhiyun int mode,
175*4882a593Smuzhiyun bool enable)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun /* enable/disable flyback */
180*4882a593Smuzhiyun if ((enable && (++ctrl->flyback_users == 1)) ||
181*4882a593Smuzhiyun (!enable && (--ctrl->flyback_users == 0))) {
182*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
183*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_VNEG_EN_MASK,
184*4882a593Smuzhiyun enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT);
185*4882a593Smuzhiyun /* 100usec delay is needed as per HW requirement */
186*4882a593Smuzhiyun usleep_range(100, 110);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun /*
189*4882a593Smuzhiyun * 500us sleep is required after flyback enable/disable
190*4882a593Smuzhiyun * as per HW requirement
191*4882a593Smuzhiyun */
192*4882a593Smuzhiyun usleep_range(500, 500 + WCD_USLEEP_RANGE);
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun
wcd_clsh_set_gain_path(struct wcd_clsh_ctrl * ctrl,int mode)195*4882a593Smuzhiyun static void wcd_clsh_set_gain_path(struct wcd_clsh_ctrl *ctrl, int mode)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
198*4882a593Smuzhiyun int val = 0;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun switch (mode) {
201*4882a593Smuzhiyun case CLS_H_NORMAL:
202*4882a593Smuzhiyun case CLS_AB:
203*4882a593Smuzhiyun val = WCD9XXX_HPH_CONST_SEL_BYPASS;
204*4882a593Smuzhiyun break;
205*4882a593Smuzhiyun case CLS_H_HIFI:
206*4882a593Smuzhiyun val = WCD9XXX_HPH_CONST_SEL_HQ_PATH;
207*4882a593Smuzhiyun break;
208*4882a593Smuzhiyun case CLS_H_LP:
209*4882a593Smuzhiyun val = WCD9XXX_HPH_CONST_SEL_LP_PATH;
210*4882a593Smuzhiyun break;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN,
214*4882a593Smuzhiyun WCD9XXX_HPH_CONST_SEL_L_MASK,
215*4882a593Smuzhiyun val);
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN,
218*4882a593Smuzhiyun WCD9XXX_HPH_CONST_SEL_L_MASK,
219*4882a593Smuzhiyun val);
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
wcd_clsh_set_hph_mode(struct snd_soc_component * comp,int mode)222*4882a593Smuzhiyun static void wcd_clsh_set_hph_mode(struct snd_soc_component *comp,
223*4882a593Smuzhiyun int mode)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun int val = 0, gain = 0, res_val;
226*4882a593Smuzhiyun int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM;
229*4882a593Smuzhiyun switch (mode) {
230*4882a593Smuzhiyun case CLS_H_NORMAL:
231*4882a593Smuzhiyun res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM;
232*4882a593Smuzhiyun val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;
233*4882a593Smuzhiyun gain = DAC_GAIN_0DB;
234*4882a593Smuzhiyun ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
235*4882a593Smuzhiyun break;
236*4882a593Smuzhiyun case CLS_AB:
237*4882a593Smuzhiyun val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;
238*4882a593Smuzhiyun gain = DAC_GAIN_0DB;
239*4882a593Smuzhiyun ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
240*4882a593Smuzhiyun break;
241*4882a593Smuzhiyun case CLS_H_HIFI:
242*4882a593Smuzhiyun val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA;
243*4882a593Smuzhiyun gain = DAC_GAIN_M0P2DB;
244*4882a593Smuzhiyun ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;
245*4882a593Smuzhiyun break;
246*4882a593Smuzhiyun case CLS_H_LP:
247*4882a593Smuzhiyun val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP;
248*4882a593Smuzhiyun ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA;
249*4882a593Smuzhiyun break;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH,
253*4882a593Smuzhiyun WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val);
254*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2,
255*4882a593Smuzhiyun WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK,
256*4882a593Smuzhiyun res_val);
257*4882a593Smuzhiyun if (mode != CLS_H_LP)
258*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
259*4882a593Smuzhiyun WCD9XXX_HPH_REFBUFF_UHQA_CTL,
260*4882a593Smuzhiyun WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK,
261*4882a593Smuzhiyun gain);
262*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1,
263*4882a593Smuzhiyun WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK,
264*4882a593Smuzhiyun ipeak);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
wcd_clsh_set_flyback_current(struct snd_soc_component * comp,int mode)267*4882a593Smuzhiyun static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp,
268*4882a593Smuzhiyun int mode)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,
272*4882a593Smuzhiyun WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A);
273*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,
274*4882a593Smuzhiyun WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A);
275*4882a593Smuzhiyun /* Sleep needed to avoid click and pop as per HW requirement */
276*4882a593Smuzhiyun usleep_range(100, 110);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
wcd_clsh_set_buck_regulator_mode(struct snd_soc_component * comp,int mode)279*4882a593Smuzhiyun static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_component *comp,
280*4882a593Smuzhiyun int mode)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun if (mode == CLS_AB)
283*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
284*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,
285*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB);
286*4882a593Smuzhiyun else
287*4882a593Smuzhiyun snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,
288*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,
289*4882a593Smuzhiyun WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H);
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun
wcd_clsh_state_lo(struct wcd_clsh_ctrl * ctrl,int req_state,bool is_enable,int mode)292*4882a593Smuzhiyun static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state,
293*4882a593Smuzhiyun bool is_enable, int mode)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun if (mode != CLS_AB) {
298*4882a593Smuzhiyun dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n",
299*4882a593Smuzhiyun __func__, mode);
300*4882a593Smuzhiyun return;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun if (is_enable) {
304*4882a593Smuzhiyun wcd_clsh_set_buck_regulator_mode(comp, mode);
305*4882a593Smuzhiyun wcd_clsh_set_buck_mode(comp, mode);
306*4882a593Smuzhiyun wcd_clsh_set_flyback_mode(comp, mode);
307*4882a593Smuzhiyun wcd_clsh_flyback_ctrl(ctrl, mode, true);
308*4882a593Smuzhiyun wcd_clsh_set_flyback_current(comp, mode);
309*4882a593Smuzhiyun wcd_clsh_buck_ctrl(ctrl, mode, true);
310*4882a593Smuzhiyun } else {
311*4882a593Smuzhiyun wcd_clsh_buck_ctrl(ctrl, mode, false);
312*4882a593Smuzhiyun wcd_clsh_flyback_ctrl(ctrl, mode, false);
313*4882a593Smuzhiyun wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
314*4882a593Smuzhiyun wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
315*4882a593Smuzhiyun wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun
wcd_clsh_state_hph_r(struct wcd_clsh_ctrl * ctrl,int req_state,bool is_enable,int mode)319*4882a593Smuzhiyun static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,
320*4882a593Smuzhiyun bool is_enable, int mode)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun if (mode == CLS_H_NORMAL) {
325*4882a593Smuzhiyun dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n",
326*4882a593Smuzhiyun __func__);
327*4882a593Smuzhiyun return;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun if (is_enable) {
331*4882a593Smuzhiyun if (mode != CLS_AB) {
332*4882a593Smuzhiyun wcd_enable_clsh_block(ctrl, true);
333*4882a593Smuzhiyun /*
334*4882a593Smuzhiyun * These K1 values depend on the Headphone Impedance
335*4882a593Smuzhiyun * For now it is assumed to be 16 ohm
336*4882a593Smuzhiyun */
337*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
338*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_K1_MSB,
339*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,
340*4882a593Smuzhiyun 0x00);
341*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
342*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_K1_LSB,
343*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,
344*4882a593Smuzhiyun 0xC0);
345*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
346*4882a593Smuzhiyun WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
347*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
348*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun wcd_clsh_set_buck_regulator_mode(comp, mode);
351*4882a593Smuzhiyun wcd_clsh_set_flyback_mode(comp, mode);
352*4882a593Smuzhiyun wcd_clsh_flyback_ctrl(ctrl, mode, true);
353*4882a593Smuzhiyun wcd_clsh_set_flyback_current(comp, mode);
354*4882a593Smuzhiyun wcd_clsh_set_buck_mode(comp, mode);
355*4882a593Smuzhiyun wcd_clsh_buck_ctrl(ctrl, mode, true);
356*4882a593Smuzhiyun wcd_clsh_set_hph_mode(comp, mode);
357*4882a593Smuzhiyun wcd_clsh_set_gain_path(ctrl, mode);
358*4882a593Smuzhiyun } else {
359*4882a593Smuzhiyun wcd_clsh_set_hph_mode(comp, CLS_H_NORMAL);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun if (mode != CLS_AB) {
362*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
363*4882a593Smuzhiyun WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
364*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
365*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
366*4882a593Smuzhiyun wcd_enable_clsh_block(ctrl, false);
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun /* buck and flyback set to default mode and disable */
369*4882a593Smuzhiyun wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);
370*4882a593Smuzhiyun wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);
371*4882a593Smuzhiyun wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
372*4882a593Smuzhiyun wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
373*4882a593Smuzhiyun wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
wcd_clsh_state_hph_l(struct wcd_clsh_ctrl * ctrl,int req_state,bool is_enable,int mode)377*4882a593Smuzhiyun static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,
378*4882a593Smuzhiyun bool is_enable, int mode)
379*4882a593Smuzhiyun {
380*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun if (mode == CLS_H_NORMAL) {
383*4882a593Smuzhiyun dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n",
384*4882a593Smuzhiyun __func__);
385*4882a593Smuzhiyun return;
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun if (is_enable) {
389*4882a593Smuzhiyun if (mode != CLS_AB) {
390*4882a593Smuzhiyun wcd_enable_clsh_block(ctrl, true);
391*4882a593Smuzhiyun /*
392*4882a593Smuzhiyun * These K1 values depend on the Headphone Impedance
393*4882a593Smuzhiyun * For now it is assumed to be 16 ohm
394*4882a593Smuzhiyun */
395*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
396*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_K1_MSB,
397*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,
398*4882a593Smuzhiyun 0x00);
399*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
400*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_K1_LSB,
401*4882a593Smuzhiyun WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,
402*4882a593Smuzhiyun 0xC0);
403*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
404*4882a593Smuzhiyun WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
405*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
406*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun wcd_clsh_set_buck_regulator_mode(comp, mode);
409*4882a593Smuzhiyun wcd_clsh_set_flyback_mode(comp, mode);
410*4882a593Smuzhiyun wcd_clsh_flyback_ctrl(ctrl, mode, true);
411*4882a593Smuzhiyun wcd_clsh_set_flyback_current(comp, mode);
412*4882a593Smuzhiyun wcd_clsh_set_buck_mode(comp, mode);
413*4882a593Smuzhiyun wcd_clsh_buck_ctrl(ctrl, mode, true);
414*4882a593Smuzhiyun wcd_clsh_set_hph_mode(comp, mode);
415*4882a593Smuzhiyun wcd_clsh_set_gain_path(ctrl, mode);
416*4882a593Smuzhiyun } else {
417*4882a593Smuzhiyun wcd_clsh_set_hph_mode(comp, CLS_H_NORMAL);
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun if (mode != CLS_AB) {
420*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
421*4882a593Smuzhiyun WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
422*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
423*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
424*4882a593Smuzhiyun wcd_enable_clsh_block(ctrl, false);
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun /* set buck and flyback to Default Mode */
427*4882a593Smuzhiyun wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);
428*4882a593Smuzhiyun wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);
429*4882a593Smuzhiyun wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
430*4882a593Smuzhiyun wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
431*4882a593Smuzhiyun wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun
wcd_clsh_state_ear(struct wcd_clsh_ctrl * ctrl,int req_state,bool is_enable,int mode)435*4882a593Smuzhiyun static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,
436*4882a593Smuzhiyun bool is_enable, int mode)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun if (mode != CLS_H_NORMAL) {
441*4882a593Smuzhiyun dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n",
442*4882a593Smuzhiyun __func__, mode);
443*4882a593Smuzhiyun return;
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun if (is_enable) {
447*4882a593Smuzhiyun wcd_enable_clsh_block(ctrl, true);
448*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
449*4882a593Smuzhiyun WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
450*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
451*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);
452*4882a593Smuzhiyun wcd_clsh_set_buck_mode(comp, mode);
453*4882a593Smuzhiyun wcd_clsh_set_flyback_mode(comp, mode);
454*4882a593Smuzhiyun wcd_clsh_flyback_ctrl(ctrl, mode, true);
455*4882a593Smuzhiyun wcd_clsh_set_flyback_current(comp, mode);
456*4882a593Smuzhiyun wcd_clsh_buck_ctrl(ctrl, mode, true);
457*4882a593Smuzhiyun } else {
458*4882a593Smuzhiyun snd_soc_component_update_bits(comp,
459*4882a593Smuzhiyun WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
460*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,
461*4882a593Smuzhiyun WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);
462*4882a593Smuzhiyun wcd_enable_clsh_block(ctrl, false);
463*4882a593Smuzhiyun wcd_clsh_buck_ctrl(ctrl, mode, false);
464*4882a593Smuzhiyun wcd_clsh_flyback_ctrl(ctrl, mode, false);
465*4882a593Smuzhiyun wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);
466*4882a593Smuzhiyun wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun
_wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl * ctrl,int req_state,bool is_enable,int mode)470*4882a593Smuzhiyun static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state,
471*4882a593Smuzhiyun bool is_enable, int mode)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun switch (req_state) {
474*4882a593Smuzhiyun case WCD_CLSH_STATE_EAR:
475*4882a593Smuzhiyun wcd_clsh_state_ear(ctrl, req_state, is_enable, mode);
476*4882a593Smuzhiyun break;
477*4882a593Smuzhiyun case WCD_CLSH_STATE_HPHL:
478*4882a593Smuzhiyun wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode);
479*4882a593Smuzhiyun break;
480*4882a593Smuzhiyun case WCD_CLSH_STATE_HPHR:
481*4882a593Smuzhiyun wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode);
482*4882a593Smuzhiyun break;
483*4882a593Smuzhiyun break;
484*4882a593Smuzhiyun case WCD_CLSH_STATE_LO:
485*4882a593Smuzhiyun wcd_clsh_state_lo(ctrl, req_state, is_enable, mode);
486*4882a593Smuzhiyun break;
487*4882a593Smuzhiyun default:
488*4882a593Smuzhiyun break;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun return 0;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun /*
495*4882a593Smuzhiyun * Function: wcd_clsh_is_state_valid
496*4882a593Smuzhiyun * Params: state
497*4882a593Smuzhiyun * Description:
498*4882a593Smuzhiyun * Provides information on valid states of Class H configuration
499*4882a593Smuzhiyun */
wcd_clsh_is_state_valid(int state)500*4882a593Smuzhiyun static bool wcd_clsh_is_state_valid(int state)
501*4882a593Smuzhiyun {
502*4882a593Smuzhiyun switch (state) {
503*4882a593Smuzhiyun case WCD_CLSH_STATE_IDLE:
504*4882a593Smuzhiyun case WCD_CLSH_STATE_EAR:
505*4882a593Smuzhiyun case WCD_CLSH_STATE_HPHL:
506*4882a593Smuzhiyun case WCD_CLSH_STATE_HPHR:
507*4882a593Smuzhiyun case WCD_CLSH_STATE_LO:
508*4882a593Smuzhiyun return true;
509*4882a593Smuzhiyun default:
510*4882a593Smuzhiyun return false;
511*4882a593Smuzhiyun };
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun /*
515*4882a593Smuzhiyun * Function: wcd_clsh_fsm
516*4882a593Smuzhiyun * Params: ctrl, req_state, req_type, clsh_event
517*4882a593Smuzhiyun * Description:
518*4882a593Smuzhiyun * This function handles PRE DAC and POST DAC conditions of different devices
519*4882a593Smuzhiyun * and updates class H configuration of different combination of devices
520*4882a593Smuzhiyun * based on validity of their states. ctrl will contain current
521*4882a593Smuzhiyun * class h state information
522*4882a593Smuzhiyun */
wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl * ctrl,enum wcd_clsh_event clsh_event,int nstate,enum wcd_clsh_mode mode)523*4882a593Smuzhiyun int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl,
524*4882a593Smuzhiyun enum wcd_clsh_event clsh_event,
525*4882a593Smuzhiyun int nstate,
526*4882a593Smuzhiyun enum wcd_clsh_mode mode)
527*4882a593Smuzhiyun {
528*4882a593Smuzhiyun struct snd_soc_component *comp = ctrl->comp;
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun if (nstate == ctrl->state)
531*4882a593Smuzhiyun return 0;
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun if (!wcd_clsh_is_state_valid(nstate)) {
534*4882a593Smuzhiyun dev_err(comp->dev, "Class-H not a valid new state:\n");
535*4882a593Smuzhiyun return -EINVAL;
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun switch (clsh_event) {
539*4882a593Smuzhiyun case WCD_CLSH_EVENT_PRE_DAC:
540*4882a593Smuzhiyun _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode);
541*4882a593Smuzhiyun break;
542*4882a593Smuzhiyun case WCD_CLSH_EVENT_POST_PA:
543*4882a593Smuzhiyun _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode);
544*4882a593Smuzhiyun break;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun ctrl->state = nstate;
548*4882a593Smuzhiyun ctrl->mode = mode;
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun return 0;
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun
wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl * ctrl)553*4882a593Smuzhiyun int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl)
554*4882a593Smuzhiyun {
555*4882a593Smuzhiyun return ctrl->state;
556*4882a593Smuzhiyun }
557*4882a593Smuzhiyun
wcd_clsh_ctrl_alloc(struct snd_soc_component * comp,int version)558*4882a593Smuzhiyun struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp,
559*4882a593Smuzhiyun int version)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun struct wcd_clsh_ctrl *ctrl;
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
564*4882a593Smuzhiyun if (!ctrl)
565*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun ctrl->state = WCD_CLSH_STATE_IDLE;
568*4882a593Smuzhiyun ctrl->comp = comp;
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun return ctrl;
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun
wcd_clsh_ctrl_free(struct wcd_clsh_ctrl * ctrl)573*4882a593Smuzhiyun void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl)
574*4882a593Smuzhiyun {
575*4882a593Smuzhiyun kfree(ctrl);
576*4882a593Smuzhiyun }
577