1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * 88pm860x-codec.c -- 88PM860x ALSA SoC Audio Driver |
4 | * |
5 | * Copyright 2010 Marvell International Ltd. |
6 | * Author: Haojian Zhuang <haojian.zhuang@marvell.com> |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/mfd/88pm860x.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/regmap.h> |
17 | #include <sound/core.h> |
18 | #include <sound/pcm.h> |
19 | #include <sound/pcm_params.h> |
20 | #include <sound/soc.h> |
21 | #include <sound/tlv.h> |
22 | #include <sound/initval.h> |
23 | #include <sound/jack.h> |
24 | #include <trace/events/asoc.h> |
25 | |
26 | #include "88pm860x-codec.h" |
27 | |
28 | #define MAX_NAME_LEN 20 |
29 | #define REG_CACHE_SIZE 0x40 |
30 | #define REG_CACHE_BASE 0xb0 |
31 | |
32 | /* Status Register 1 (0x01) */ |
33 | #define REG_STATUS_1 0x01 |
34 | #define MIC_STATUS (1 << 7) |
35 | #define HOOK_STATUS (1 << 6) |
36 | #define HEADSET_STATUS (1 << 5) |
37 | |
38 | /* Mic Detection Register (0x37) */ |
39 | #define REG_MIC_DET 0x37 |
40 | #define CONTINUOUS_POLLING (3 << 1) |
41 | #define EN_MIC_DET (1 << 0) |
42 | #define MICDET_MASK 0x07 |
43 | |
44 | /* Headset Detection Register (0x38) */ |
45 | #define REG_HS_DET 0x38 |
46 | #define EN_HS_DET (1 << 0) |
47 | |
48 | /* Misc2 Register (0x42) */ |
49 | #define REG_MISC2 0x42 |
50 | #define AUDIO_PLL (1 << 5) |
51 | #define AUDIO_SECTION_RESET (1 << 4) |
52 | #define AUDIO_SECTION_ON (1 << 3) |
53 | |
54 | /* PCM Interface Register 2 (0xb1) */ |
55 | #define PCM_INF2_BCLK (1 << 6) /* Bit clock polarity */ |
56 | #define PCM_INF2_FS (1 << 5) /* Frame Sync polarity */ |
57 | #define PCM_INF2_MASTER (1 << 4) /* Master / Slave */ |
58 | #define PCM_INF2_18WL (1 << 3) /* 18 / 16 bits */ |
59 | #define PCM_GENERAL_I2S 0 |
60 | #define PCM_EXACT_I2S 1 |
61 | #define PCM_LEFT_I2S 2 |
62 | #define PCM_RIGHT_I2S 3 |
63 | #define PCM_SHORT_FS 4 |
64 | #define PCM_LONG_FS 5 |
65 | #define PCM_MODE_MASK 7 |
66 | |
67 | /* I2S Interface Register 4 (0xbe) */ |
68 | #define I2S_EQU_BYP (1 << 6) |
69 | |
70 | /* DAC Offset Register (0xcb) */ |
71 | #define DAC_MUTE (1 << 7) |
72 | #define MUTE_LEFT (1 << 6) |
73 | #define MUTE_RIGHT (1 << 2) |
74 | |
75 | /* ADC Analog Register 1 (0xd0) */ |
76 | #define REG_ADC_ANA_1 0xd0 |
77 | #define MIC1BIAS_MASK 0x60 |
78 | |
79 | /* Earpiece/Speaker Control Register 2 (0xda) */ |
80 | #define REG_EAR2 0xda |
81 | #define RSYNC_CHANGE (1 << 2) |
82 | |
83 | /* Audio Supplies Register 2 (0xdc) */ |
84 | #define REG_SUPPLIES2 0xdc |
85 | #define LDO15_READY (1 << 4) |
86 | #define LDO15_EN (1 << 3) |
87 | #define CPUMP_READY (1 << 2) |
88 | #define CPUMP_EN (1 << 1) |
89 | #define AUDIO_EN (1 << 0) |
90 | #define SUPPLY_MASK (LDO15_EN | CPUMP_EN | AUDIO_EN) |
91 | |
92 | /* Audio Enable Register 1 (0xdd) */ |
93 | #define ADC_MOD_RIGHT (1 << 1) |
94 | #define ADC_MOD_LEFT (1 << 0) |
95 | |
96 | /* Audio Enable Register 2 (0xde) */ |
97 | #define ADC_LEFT (1 << 5) |
98 | #define ADC_RIGHT (1 << 4) |
99 | |
100 | /* DAC Enable Register 2 (0xe1) */ |
101 | #define DAC_LEFT (1 << 5) |
102 | #define DAC_RIGHT (1 << 4) |
103 | #define MODULATOR (1 << 3) |
104 | |
105 | /* Shorts Register (0xeb) */ |
106 | #define REG_SHORTS 0xeb |
107 | #define CLR_SHORT_LO2 (1 << 7) |
108 | #define SHORT_LO2 (1 << 6) |
109 | #define CLR_SHORT_LO1 (1 << 5) |
110 | #define SHORT_LO1 (1 << 4) |
111 | #define CLR_SHORT_HS2 (1 << 3) |
112 | #define SHORT_HS2 (1 << 2) |
113 | #define CLR_SHORT_HS1 (1 << 1) |
114 | #define SHORT_HS1 (1 << 0) |
115 | |
116 | /* |
117 | * This widget should be just after DAC & PGA in DAPM power-on sequence and |
118 | * before DAC & PGA in DAPM power-off sequence. |
119 | */ |
120 | #define PM860X_DAPM_OUTPUT(wname, wevent) \ |
121 | SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, 0, 0, NULL, 0, wevent, \ |
122 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD) |
123 | |
124 | struct pm860x_det { |
125 | struct snd_soc_jack *hp_jack; |
126 | struct snd_soc_jack *mic_jack; |
127 | int hp_det; |
128 | int mic_det; |
129 | int hook_det; |
130 | int hs_shrt; |
131 | int lo_shrt; |
132 | }; |
133 | |
134 | struct pm860x_priv { |
135 | unsigned int sysclk; |
136 | unsigned int pcmclk; |
137 | unsigned int dir; |
138 | unsigned int filter; |
139 | struct snd_soc_component *component; |
140 | struct i2c_client *i2c; |
141 | struct regmap *regmap; |
142 | struct pm860x_chip *chip; |
143 | struct pm860x_det det; |
144 | |
145 | int irq[4]; |
146 | unsigned char name[4][MAX_NAME_LEN]; |
147 | }; |
148 | |
149 | /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ |
150 | static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1); |
151 | |
152 | /* -9dB to 0db in 3dB steps */ |
153 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0); |
154 | |
155 | /* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */ |
156 | static const DECLARE_TLV_DB_RANGE(mic_tlv, |
157 | 0, 0, TLV_DB_SCALE_ITEM(-2300, 0, 0), |
158 | 1, 1, TLV_DB_SCALE_ITEM(-1700, 0, 0), |
159 | 2, 2, TLV_DB_SCALE_ITEM(-1350, 0, 0), |
160 | 3, 3, TLV_DB_SCALE_ITEM(-1100, 0, 0), |
161 | 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0) |
162 | ); |
163 | |
164 | /* {0, 0, 0, -6, 0, 6, 12, 18}dB */ |
165 | static const DECLARE_TLV_DB_RANGE(aux_tlv, |
166 | 0, 2, TLV_DB_SCALE_ITEM(0, 0, 0), |
167 | 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0) |
168 | ); |
169 | |
170 | /* {-16, -13, -10, -7, -5.2, -3,3, -2.2, 0}dB, mute instead of -16dB */ |
171 | static const DECLARE_TLV_DB_RANGE(out_tlv, |
172 | 0, 3, TLV_DB_SCALE_ITEM(-1600, 300, 1), |
173 | 4, 4, TLV_DB_SCALE_ITEM(-520, 0, 0), |
174 | 5, 5, TLV_DB_SCALE_ITEM(-330, 0, 0), |
175 | 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0) |
176 | ); |
177 | |
178 | static const DECLARE_TLV_DB_RANGE(st_tlv, |
179 | 0, 1, TLV_DB_SCALE_ITEM(-12041, 602, 0), |
180 | 2, 3, TLV_DB_SCALE_ITEM(-11087, 250, 0), |
181 | 4, 5, TLV_DB_SCALE_ITEM(-10643, 158, 0), |
182 | 6, 7, TLV_DB_SCALE_ITEM(-10351, 116, 0), |
183 | 8, 9, TLV_DB_SCALE_ITEM(-10133, 92, 0), |
184 | 10, 13, TLV_DB_SCALE_ITEM(-9958, 70, 0), |
185 | 14, 17, TLV_DB_SCALE_ITEM(-9689, 53, 0), |
186 | 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0) |
187 | ); |
188 | |
189 | /* Sidetone Gain = M * 2^(-5-N) */ |
190 | struct st_gain { |
191 | unsigned int db; |
192 | unsigned int m; |
193 | unsigned int n; |
194 | }; |
195 | |
196 | static struct st_gain st_table[] = { |
197 | {-12041, 1, 15}, {-11439, 1, 14}, {-11087, 3, 15}, {-10837, 1, 13}, |
198 | {-10643, 5, 15}, {-10485, 3, 14}, {-10351, 7, 15}, {-10235, 1, 12}, |
199 | {-10133, 9, 15}, {-10041, 5, 14}, { -9958, 11, 15}, { -9883, 3, 13}, |
200 | { -9813, 13, 15}, { -9749, 7, 14}, { -9689, 15, 15}, { -9633, 1, 11}, |
201 | { -9580, 17, 15}, { -9531, 9, 14}, { -9484, 19, 15}, { -9439, 5, 13}, |
202 | { -9397, 21, 15}, { -9356, 11, 14}, { -9318, 23, 15}, { -9281, 3, 12}, |
203 | { -9245, 25, 15}, { -9211, 13, 14}, { -9178, 27, 15}, { -9147, 7, 13}, |
204 | { -9116, 29, 15}, { -9087, 15, 14}, { -9058, 31, 15}, { -9031, 1, 10}, |
205 | { -8978, 17, 14}, { -8929, 9, 13}, { -8882, 19, 14}, { -8837, 5, 12}, |
206 | { -8795, 21, 14}, { -8754, 11, 13}, { -8716, 23, 14}, { -8679, 3, 11}, |
207 | { -8643, 25, 14}, { -8609, 13, 13}, { -8576, 27, 14}, { -8545, 7, 12}, |
208 | { -8514, 29, 14}, { -8485, 15, 13}, { -8456, 31, 14}, { -8429, 1, 9}, |
209 | { -8376, 17, 13}, { -8327, 9, 12}, { -8280, 19, 13}, { -8235, 5, 11}, |
210 | { -8193, 21, 13}, { -8152, 11, 12}, { -8114, 23, 13}, { -8077, 3, 10}, |
211 | { -8041, 25, 13}, { -8007, 13, 12}, { -7974, 27, 13}, { -7943, 7, 11}, |
212 | { -7912, 29, 13}, { -7883, 15, 12}, { -7854, 31, 13}, { -7827, 1, 8}, |
213 | { -7774, 17, 12}, { -7724, 9, 11}, { -7678, 19, 12}, { -7633, 5, 10}, |
214 | { -7591, 21, 12}, { -7550, 11, 11}, { -7512, 23, 12}, { -7475, 3, 9}, |
215 | { -7439, 25, 12}, { -7405, 13, 11}, { -7372, 27, 12}, { -7341, 7, 10}, |
216 | { -7310, 29, 12}, { -7281, 15, 11}, { -7252, 31, 12}, { -7225, 1, 7}, |
217 | { -7172, 17, 11}, { -7122, 9, 10}, { -7075, 19, 11}, { -7031, 5, 9}, |
218 | { -6989, 21, 11}, { -6948, 11, 10}, { -6910, 23, 11}, { -6873, 3, 8}, |
219 | { -6837, 25, 11}, { -6803, 13, 10}, { -6770, 27, 11}, { -6739, 7, 9}, |
220 | { -6708, 29, 11}, { -6679, 15, 10}, { -6650, 31, 11}, { -6623, 1, 6}, |
221 | { -6570, 17, 10}, { -6520, 9, 9}, { -6473, 19, 10}, { -6429, 5, 8}, |
222 | { -6386, 21, 10}, { -6346, 11, 9}, { -6307, 23, 10}, { -6270, 3, 7}, |
223 | { -6235, 25, 10}, { -6201, 13, 9}, { -6168, 27, 10}, { -6137, 7, 8}, |
224 | { -6106, 29, 10}, { -6077, 15, 9}, { -6048, 31, 10}, { -6021, 1, 5}, |
225 | { -5968, 17, 9}, { -5918, 9, 8}, { -5871, 19, 9}, { -5827, 5, 7}, |
226 | { -5784, 21, 9}, { -5744, 11, 8}, { -5705, 23, 9}, { -5668, 3, 6}, |
227 | { -5633, 25, 9}, { -5599, 13, 8}, { -5566, 27, 9}, { -5535, 7, 7}, |
228 | { -5504, 29, 9}, { -5475, 15, 8}, { -5446, 31, 9}, { -5419, 1, 4}, |
229 | { -5366, 17, 8}, { -5316, 9, 7}, { -5269, 19, 8}, { -5225, 5, 6}, |
230 | { -5182, 21, 8}, { -5142, 11, 7}, { -5103, 23, 8}, { -5066, 3, 5}, |
231 | { -5031, 25, 8}, { -4997, 13, 7}, { -4964, 27, 8}, { -4932, 7, 6}, |
232 | { -4902, 29, 8}, { -4873, 15, 7}, { -4844, 31, 8}, { -4816, 1, 3}, |
233 | { -4764, 17, 7}, { -4714, 9, 6}, { -4667, 19, 7}, { -4623, 5, 5}, |
234 | { -4580, 21, 7}, { -4540, 11, 6}, { -4501, 23, 7}, { -4464, 3, 4}, |
235 | { -4429, 25, 7}, { -4395, 13, 6}, { -4362, 27, 7}, { -4330, 7, 5}, |
236 | { -4300, 29, 7}, { -4270, 15, 6}, { -4242, 31, 7}, { -4214, 1, 2}, |
237 | { -4162, 17, 6}, { -4112, 9, 5}, { -4065, 19, 6}, { -4021, 5, 4}, |
238 | { -3978, 21, 6}, { -3938, 11, 5}, { -3899, 23, 6}, { -3862, 3, 3}, |
239 | { -3827, 25, 6}, { -3793, 13, 5}, { -3760, 27, 6}, { -3728, 7, 4}, |
240 | { -3698, 29, 6}, { -3668, 15, 5}, { -3640, 31, 6}, { -3612, 1, 1}, |
241 | { -3560, 17, 5}, { -3510, 9, 4}, { -3463, 19, 5}, { -3419, 5, 3}, |
242 | { -3376, 21, 5}, { -3336, 11, 4}, { -3297, 23, 5}, { -3260, 3, 2}, |
243 | { -3225, 25, 5}, { -3191, 13, 4}, { -3158, 27, 5}, { -3126, 7, 3}, |
244 | { -3096, 29, 5}, { -3066, 15, 4}, { -3038, 31, 5}, { -3010, 1, 0}, |
245 | { -2958, 17, 4}, { -2908, 9, 3}, { -2861, 19, 4}, { -2816, 5, 2}, |
246 | { -2774, 21, 4}, { -2734, 11, 3}, { -2695, 23, 4}, { -2658, 3, 1}, |
247 | { -2623, 25, 4}, { -2589, 13, 3}, { -2556, 27, 4}, { -2524, 7, 2}, |
248 | { -2494, 29, 4}, { -2464, 15, 3}, { -2436, 31, 4}, { -2408, 2, 0}, |
249 | { -2356, 17, 3}, { -2306, 9, 2}, { -2259, 19, 3}, { -2214, 5, 1}, |
250 | { -2172, 21, 3}, { -2132, 11, 2}, { -2093, 23, 3}, { -2056, 3, 0}, |
251 | { -2021, 25, 3}, { -1987, 13, 2}, { -1954, 27, 3}, { -1922, 7, 1}, |
252 | { -1892, 29, 3}, { -1862, 15, 2}, { -1834, 31, 3}, { -1806, 4, 0}, |
253 | { -1754, 17, 2}, { -1704, 9, 1}, { -1657, 19, 2}, { -1612, 5, 0}, |
254 | { -1570, 21, 2}, { -1530, 11, 1}, { -1491, 23, 2}, { -1454, 6, 0}, |
255 | { -1419, 25, 2}, { -1384, 13, 1}, { -1352, 27, 2}, { -1320, 7, 0}, |
256 | { -1290, 29, 2}, { -1260, 15, 1}, { -1232, 31, 2}, { -1204, 8, 0}, |
257 | { -1151, 17, 1}, { -1102, 9, 0}, { -1055, 19, 1}, { -1010, 10, 0}, |
258 | { -968, 21, 1}, { -928, 11, 0}, { -889, 23, 1}, { -852, 12, 0}, |
259 | { -816, 25, 1}, { -782, 13, 0}, { -750, 27, 1}, { -718, 14, 0}, |
260 | { -688, 29, 1}, { -658, 15, 0}, { -630, 31, 1}, { -602, 16, 0}, |
261 | { -549, 17, 0}, { -500, 18, 0}, { -453, 19, 0}, { -408, 20, 0}, |
262 | { -366, 21, 0}, { -325, 22, 0}, { -287, 23, 0}, { -250, 24, 0}, |
263 | { -214, 25, 0}, { -180, 26, 0}, { -148, 27, 0}, { -116, 28, 0}, |
264 | { -86, 29, 0}, { -56, 30, 0}, { -28, 31, 0}, { 0, 0, 0}, |
265 | }; |
266 | |
267 | static int snd_soc_get_volsw_2r_st(struct snd_kcontrol *kcontrol, |
268 | struct snd_ctl_elem_value *ucontrol) |
269 | { |
270 | struct soc_mixer_control *mc = |
271 | (struct soc_mixer_control *)kcontrol->private_value; |
272 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
273 | unsigned int reg = mc->reg; |
274 | unsigned int reg2 = mc->rreg; |
275 | int val[2], val2[2], i; |
276 | |
277 | val[0] = snd_soc_component_read(component, reg) & 0x3f; |
278 | val[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT) >> 4) & 0xf; |
279 | val2[0] = snd_soc_component_read(component, reg: reg2) & 0x3f; |
280 | val2[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT)) & 0xf; |
281 | |
282 | for (i = 0; i < ARRAY_SIZE(st_table); i++) { |
283 | if ((st_table[i].m == val[0]) && (st_table[i].n == val[1])) |
284 | ucontrol->value.integer.value[0] = i; |
285 | if ((st_table[i].m == val2[0]) && (st_table[i].n == val2[1])) |
286 | ucontrol->value.integer.value[1] = i; |
287 | } |
288 | return 0; |
289 | } |
290 | |
291 | static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol, |
292 | struct snd_ctl_elem_value *ucontrol) |
293 | { |
294 | struct soc_mixer_control *mc = |
295 | (struct soc_mixer_control *)kcontrol->private_value; |
296 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
297 | unsigned int reg = mc->reg; |
298 | unsigned int reg2 = mc->rreg; |
299 | int err; |
300 | unsigned int val, val2; |
301 | |
302 | val = ucontrol->value.integer.value[0]; |
303 | val2 = ucontrol->value.integer.value[1]; |
304 | |
305 | if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table)) |
306 | return -EINVAL; |
307 | |
308 | err = snd_soc_component_update_bits(component, reg, mask: 0x3f, val: st_table[val].m); |
309 | if (err < 0) |
310 | return err; |
311 | err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, mask: 0xf0, |
312 | val: st_table[val].n << 4); |
313 | if (err < 0) |
314 | return err; |
315 | |
316 | err = snd_soc_component_update_bits(component, reg: reg2, mask: 0x3f, val: st_table[val2].m); |
317 | if (err < 0) |
318 | return err; |
319 | err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, mask: 0x0f, |
320 | val: st_table[val2].n); |
321 | return err; |
322 | } |
323 | |
324 | static int snd_soc_get_volsw_2r_out(struct snd_kcontrol *kcontrol, |
325 | struct snd_ctl_elem_value *ucontrol) |
326 | { |
327 | struct soc_mixer_control *mc = |
328 | (struct soc_mixer_control *)kcontrol->private_value; |
329 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
330 | unsigned int reg = mc->reg; |
331 | unsigned int reg2 = mc->rreg; |
332 | unsigned int shift = mc->shift; |
333 | int max = mc->max, val, val2; |
334 | unsigned int mask = (1 << fls(x: max)) - 1; |
335 | |
336 | val = snd_soc_component_read(component, reg) >> shift; |
337 | val2 = snd_soc_component_read(component, reg: reg2) >> shift; |
338 | ucontrol->value.integer.value[0] = (max - val) & mask; |
339 | ucontrol->value.integer.value[1] = (max - val2) & mask; |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol, |
345 | struct snd_ctl_elem_value *ucontrol) |
346 | { |
347 | struct soc_mixer_control *mc = |
348 | (struct soc_mixer_control *)kcontrol->private_value; |
349 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
350 | unsigned int reg = mc->reg; |
351 | unsigned int reg2 = mc->rreg; |
352 | unsigned int shift = mc->shift; |
353 | int max = mc->max; |
354 | unsigned int mask = (1 << fls(x: max)) - 1; |
355 | int err; |
356 | unsigned int val, val2, val_mask; |
357 | |
358 | val_mask = mask << shift; |
359 | val = ((max - ucontrol->value.integer.value[0]) & mask); |
360 | val2 = ((max - ucontrol->value.integer.value[1]) & mask); |
361 | |
362 | val = val << shift; |
363 | val2 = val2 << shift; |
364 | |
365 | err = snd_soc_component_update_bits(component, reg, mask: val_mask, val); |
366 | if (err < 0) |
367 | return err; |
368 | |
369 | err = snd_soc_component_update_bits(component, reg: reg2, mask: val_mask, val: val2); |
370 | return err; |
371 | } |
372 | |
373 | /* DAPM Widget Events */ |
374 | /* |
375 | * A lot registers are belong to RSYNC domain. It requires enabling RSYNC bit |
376 | * after updating these registers. Otherwise, these updated registers won't |
377 | * be effective. |
378 | */ |
379 | static int pm860x_rsync_event(struct snd_soc_dapm_widget *w, |
380 | struct snd_kcontrol *kcontrol, int event) |
381 | { |
382 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
383 | |
384 | /* |
385 | * In order to avoid current on the load, mute power-on and power-off |
386 | * should be transients. |
387 | * Unmute by DAC_MUTE. It should be unmuted when DAPM sequence is |
388 | * finished. |
389 | */ |
390 | snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, DAC_MUTE, val: 0); |
391 | snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, |
392 | RSYNC_CHANGE, RSYNC_CHANGE); |
393 | return 0; |
394 | } |
395 | |
396 | static int pm860x_dac_event(struct snd_soc_dapm_widget *w, |
397 | struct snd_kcontrol *kcontrol, int event) |
398 | { |
399 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
400 | unsigned int dac = 0; |
401 | int data; |
402 | |
403 | if (!snd_soc_dapm_widget_name_cmp(widget: w, s: "Left DAC" )) |
404 | dac = DAC_LEFT; |
405 | if (!snd_soc_dapm_widget_name_cmp(widget: w, s: "Right DAC" )) |
406 | dac = DAC_RIGHT; |
407 | switch (event) { |
408 | case SND_SOC_DAPM_PRE_PMU: |
409 | if (dac) { |
410 | /* Auto mute in power-on sequence. */ |
411 | dac |= MODULATOR; |
412 | snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, |
413 | DAC_MUTE, DAC_MUTE); |
414 | snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, |
415 | RSYNC_CHANGE, RSYNC_CHANGE); |
416 | /* update dac */ |
417 | snd_soc_component_update_bits(component, PM860X_DAC_EN_2, |
418 | mask: dac, val: dac); |
419 | } |
420 | break; |
421 | case SND_SOC_DAPM_PRE_PMD: |
422 | if (dac) { |
423 | /* Auto mute in power-off sequence. */ |
424 | snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, |
425 | DAC_MUTE, DAC_MUTE); |
426 | snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, |
427 | RSYNC_CHANGE, RSYNC_CHANGE); |
428 | /* update dac */ |
429 | data = snd_soc_component_read(component, PM860X_DAC_EN_2); |
430 | data &= ~dac; |
431 | if (!(data & (DAC_LEFT | DAC_RIGHT))) |
432 | data &= ~MODULATOR; |
433 | snd_soc_component_write(component, PM860X_DAC_EN_2, val: data); |
434 | } |
435 | break; |
436 | } |
437 | return 0; |
438 | } |
439 | |
440 | static const char *pm860x_opamp_texts[] = {"-50%" , "-25%" , "0%" , "75%" }; |
441 | |
442 | static const char *pm860x_pa_texts[] = {"-33%" , "0%" , "33%" , "66%" }; |
443 | |
444 | static SOC_ENUM_SINGLE_DECL(pm860x_hs1_opamp_enum, |
445 | PM860X_HS1_CTRL, 5, pm860x_opamp_texts); |
446 | |
447 | static SOC_ENUM_SINGLE_DECL(pm860x_hs2_opamp_enum, |
448 | PM860X_HS2_CTRL, 5, pm860x_opamp_texts); |
449 | |
450 | static SOC_ENUM_SINGLE_DECL(pm860x_hs1_pa_enum, |
451 | PM860X_HS1_CTRL, 3, pm860x_pa_texts); |
452 | |
453 | static SOC_ENUM_SINGLE_DECL(pm860x_hs2_pa_enum, |
454 | PM860X_HS2_CTRL, 3, pm860x_pa_texts); |
455 | |
456 | static SOC_ENUM_SINGLE_DECL(pm860x_lo1_opamp_enum, |
457 | PM860X_LO1_CTRL, 5, pm860x_opamp_texts); |
458 | |
459 | static SOC_ENUM_SINGLE_DECL(pm860x_lo2_opamp_enum, |
460 | PM860X_LO2_CTRL, 5, pm860x_opamp_texts); |
461 | |
462 | static SOC_ENUM_SINGLE_DECL(pm860x_lo1_pa_enum, |
463 | PM860X_LO1_CTRL, 3, pm860x_pa_texts); |
464 | |
465 | static SOC_ENUM_SINGLE_DECL(pm860x_lo2_pa_enum, |
466 | PM860X_LO2_CTRL, 3, pm860x_pa_texts); |
467 | |
468 | static SOC_ENUM_SINGLE_DECL(pm860x_spk_pa_enum, |
469 | PM860X_EAR_CTRL_1, 5, pm860x_pa_texts); |
470 | |
471 | static SOC_ENUM_SINGLE_DECL(pm860x_ear_pa_enum, |
472 | PM860X_EAR_CTRL_2, 0, pm860x_pa_texts); |
473 | |
474 | static SOC_ENUM_SINGLE_DECL(pm860x_spk_ear_opamp_enum, |
475 | PM860X_EAR_CTRL_1, 3, pm860x_opamp_texts); |
476 | |
477 | static const struct snd_kcontrol_new pm860x_snd_controls[] = { |
478 | SOC_DOUBLE_R_TLV("ADC Capture Volume" , PM860X_ADC_ANA_2, |
479 | PM860X_ADC_ANA_3, 6, 3, 0, adc_tlv), |
480 | SOC_DOUBLE_TLV("AUX Capture Volume" , PM860X_ADC_ANA_3, 0, 3, 7, 0, |
481 | aux_tlv), |
482 | SOC_SINGLE_TLV("MIC1 Capture Volume" , PM860X_ADC_ANA_2, 0, 7, 0, |
483 | mic_tlv), |
484 | SOC_SINGLE_TLV("MIC3 Capture Volume" , PM860X_ADC_ANA_2, 3, 7, 0, |
485 | mic_tlv), |
486 | SOC_DOUBLE_R_EXT_TLV("Sidetone Volume" , PM860X_SIDETONE_L_GAIN, |
487 | PM860X_SIDETONE_R_GAIN, 0, ARRAY_SIZE(st_table)-1, |
488 | 0, snd_soc_get_volsw_2r_st, |
489 | snd_soc_put_volsw_2r_st, st_tlv), |
490 | SOC_SINGLE_TLV("Speaker Playback Volume" , PM860X_EAR_CTRL_1, |
491 | 0, 7, 0, out_tlv), |
492 | SOC_DOUBLE_R_TLV("Line Playback Volume" , PM860X_LO1_CTRL, |
493 | PM860X_LO2_CTRL, 0, 7, 0, out_tlv), |
494 | SOC_DOUBLE_R_TLV("Headset Playback Volume" , PM860X_HS1_CTRL, |
495 | PM860X_HS2_CTRL, 0, 7, 0, out_tlv), |
496 | SOC_DOUBLE_R_EXT_TLV("Hifi Left Playback Volume" , |
497 | PM860X_HIFIL_GAIN_LEFT, |
498 | PM860X_HIFIL_GAIN_RIGHT, 0, 63, 0, |
499 | snd_soc_get_volsw_2r_out, |
500 | snd_soc_put_volsw_2r_out, dpga_tlv), |
501 | SOC_DOUBLE_R_EXT_TLV("Hifi Right Playback Volume" , |
502 | PM860X_HIFIR_GAIN_LEFT, |
503 | PM860X_HIFIR_GAIN_RIGHT, 0, 63, 0, |
504 | snd_soc_get_volsw_2r_out, |
505 | snd_soc_put_volsw_2r_out, dpga_tlv), |
506 | SOC_DOUBLE_R_EXT_TLV("Lofi Playback Volume" , PM860X_LOFI_GAIN_LEFT, |
507 | PM860X_LOFI_GAIN_RIGHT, 0, 63, 0, |
508 | snd_soc_get_volsw_2r_out, |
509 | snd_soc_put_volsw_2r_out, dpga_tlv), |
510 | SOC_ENUM("Headset1 Operational Amplifier Current" , |
511 | pm860x_hs1_opamp_enum), |
512 | SOC_ENUM("Headset2 Operational Amplifier Current" , |
513 | pm860x_hs2_opamp_enum), |
514 | SOC_ENUM("Headset1 Amplifier Current" , pm860x_hs1_pa_enum), |
515 | SOC_ENUM("Headset2 Amplifier Current" , pm860x_hs2_pa_enum), |
516 | SOC_ENUM("Lineout1 Operational Amplifier Current" , |
517 | pm860x_lo1_opamp_enum), |
518 | SOC_ENUM("Lineout2 Operational Amplifier Current" , |
519 | pm860x_lo2_opamp_enum), |
520 | SOC_ENUM("Lineout1 Amplifier Current" , pm860x_lo1_pa_enum), |
521 | SOC_ENUM("Lineout2 Amplifier Current" , pm860x_lo2_pa_enum), |
522 | SOC_ENUM("Speaker Operational Amplifier Current" , |
523 | pm860x_spk_ear_opamp_enum), |
524 | SOC_ENUM("Speaker Amplifier Current" , pm860x_spk_pa_enum), |
525 | SOC_ENUM("Earpiece Amplifier Current" , pm860x_ear_pa_enum), |
526 | }; |
527 | |
528 | /* |
529 | * DAPM Controls |
530 | */ |
531 | |
532 | /* AUX1 Switch */ |
533 | static const struct snd_kcontrol_new aux1_switch_controls = |
534 | SOC_DAPM_SINGLE("Switch" , PM860X_ANA_TO_ANA, 4, 1, 0); |
535 | |
536 | /* AUX2 Switch */ |
537 | static const struct snd_kcontrol_new aux2_switch_controls = |
538 | SOC_DAPM_SINGLE("Switch" , PM860X_ANA_TO_ANA, 5, 1, 0); |
539 | |
540 | /* Left Ex. PA Switch */ |
541 | static const struct snd_kcontrol_new lepa_switch_controls = |
542 | SOC_DAPM_SINGLE("Switch" , PM860X_DAC_EN_2, 2, 1, 0); |
543 | |
544 | /* Right Ex. PA Switch */ |
545 | static const struct snd_kcontrol_new repa_switch_controls = |
546 | SOC_DAPM_SINGLE("Switch" , PM860X_DAC_EN_2, 1, 1, 0); |
547 | |
548 | /* I2S Mux / Mux9 */ |
549 | static const char *i2s_din_text[] = { |
550 | "DIN" , "DIN1" , |
551 | }; |
552 | |
553 | static SOC_ENUM_SINGLE_DECL(i2s_din_enum, |
554 | PM860X_I2S_IFACE_3, 1, i2s_din_text); |
555 | |
556 | static const struct snd_kcontrol_new i2s_din_mux = |
557 | SOC_DAPM_ENUM("I2S DIN Mux" , i2s_din_enum); |
558 | |
559 | /* I2S Mic Mux / Mux8 */ |
560 | static const char *i2s_mic_text[] = { |
561 | "Ex PA" , "ADC" , |
562 | }; |
563 | |
564 | static SOC_ENUM_SINGLE_DECL(i2s_mic_enum, |
565 | PM860X_I2S_IFACE_3, 4, i2s_mic_text); |
566 | |
567 | static const struct snd_kcontrol_new i2s_mic_mux = |
568 | SOC_DAPM_ENUM("I2S Mic Mux" , i2s_mic_enum); |
569 | |
570 | /* ADCL Mux / Mux2 */ |
571 | static const char *adcl_text[] = { |
572 | "ADCR" , "ADCL" , |
573 | }; |
574 | |
575 | static SOC_ENUM_SINGLE_DECL(adcl_enum, |
576 | PM860X_PCM_IFACE_3, 4, adcl_text); |
577 | |
578 | static const struct snd_kcontrol_new adcl_mux = |
579 | SOC_DAPM_ENUM("ADC Left Mux" , adcl_enum); |
580 | |
581 | /* ADCR Mux / Mux3 */ |
582 | static const char *adcr_text[] = { |
583 | "ADCL" , "ADCR" , |
584 | }; |
585 | |
586 | static SOC_ENUM_SINGLE_DECL(adcr_enum, |
587 | PM860X_PCM_IFACE_3, 2, adcr_text); |
588 | |
589 | static const struct snd_kcontrol_new adcr_mux = |
590 | SOC_DAPM_ENUM("ADC Right Mux" , adcr_enum); |
591 | |
592 | /* ADCR EC Mux / Mux6 */ |
593 | static const char *adcr_ec_text[] = { |
594 | "ADCR" , "EC" , |
595 | }; |
596 | |
597 | static SOC_ENUM_SINGLE_DECL(adcr_ec_enum, |
598 | PM860X_ADC_EN_2, 3, adcr_ec_text); |
599 | |
600 | static const struct snd_kcontrol_new adcr_ec_mux = |
601 | SOC_DAPM_ENUM("ADCR EC Mux" , adcr_ec_enum); |
602 | |
603 | /* EC Mux / Mux4 */ |
604 | static const char *ec_text[] = { |
605 | "Left" , "Right" , "Left + Right" , |
606 | }; |
607 | |
608 | static SOC_ENUM_SINGLE_DECL(ec_enum, |
609 | PM860X_EC_PATH, 1, ec_text); |
610 | |
611 | static const struct snd_kcontrol_new ec_mux = |
612 | SOC_DAPM_ENUM("EC Mux" , ec_enum); |
613 | |
614 | static const char *dac_text[] = { |
615 | "No input" , "Right" , "Left" , "No input" , |
616 | }; |
617 | |
618 | /* DAC Headset 1 Mux / Mux10 */ |
619 | static SOC_ENUM_SINGLE_DECL(dac_hs1_enum, |
620 | PM860X_ANA_INPUT_SEL_1, 0, dac_text); |
621 | |
622 | static const struct snd_kcontrol_new dac_hs1_mux = |
623 | SOC_DAPM_ENUM("DAC HS1 Mux" , dac_hs1_enum); |
624 | |
625 | /* DAC Headset 2 Mux / Mux11 */ |
626 | static SOC_ENUM_SINGLE_DECL(dac_hs2_enum, |
627 | PM860X_ANA_INPUT_SEL_1, 2, dac_text); |
628 | |
629 | static const struct snd_kcontrol_new dac_hs2_mux = |
630 | SOC_DAPM_ENUM("DAC HS2 Mux" , dac_hs2_enum); |
631 | |
632 | /* DAC Lineout 1 Mux / Mux12 */ |
633 | static SOC_ENUM_SINGLE_DECL(dac_lo1_enum, |
634 | PM860X_ANA_INPUT_SEL_1, 4, dac_text); |
635 | |
636 | static const struct snd_kcontrol_new dac_lo1_mux = |
637 | SOC_DAPM_ENUM("DAC LO1 Mux" , dac_lo1_enum); |
638 | |
639 | /* DAC Lineout 2 Mux / Mux13 */ |
640 | static SOC_ENUM_SINGLE_DECL(dac_lo2_enum, |
641 | PM860X_ANA_INPUT_SEL_1, 6, dac_text); |
642 | |
643 | static const struct snd_kcontrol_new dac_lo2_mux = |
644 | SOC_DAPM_ENUM("DAC LO2 Mux" , dac_lo2_enum); |
645 | |
646 | /* DAC Spearker Earphone Mux / Mux14 */ |
647 | static SOC_ENUM_SINGLE_DECL(dac_spk_ear_enum, |
648 | PM860X_ANA_INPUT_SEL_2, 0, dac_text); |
649 | |
650 | static const struct snd_kcontrol_new dac_spk_ear_mux = |
651 | SOC_DAPM_ENUM("DAC SP Mux" , dac_spk_ear_enum); |
652 | |
653 | /* Headset 1 Mux / Mux15 */ |
654 | static const char *in_text[] = { |
655 | "Digital" , "Analog" , |
656 | }; |
657 | |
658 | static SOC_ENUM_SINGLE_DECL(hs1_enum, |
659 | PM860X_ANA_TO_ANA, 0, in_text); |
660 | |
661 | static const struct snd_kcontrol_new hs1_mux = |
662 | SOC_DAPM_ENUM("Headset1 Mux" , hs1_enum); |
663 | |
664 | /* Headset 2 Mux / Mux16 */ |
665 | static SOC_ENUM_SINGLE_DECL(hs2_enum, |
666 | PM860X_ANA_TO_ANA, 1, in_text); |
667 | |
668 | static const struct snd_kcontrol_new hs2_mux = |
669 | SOC_DAPM_ENUM("Headset2 Mux" , hs2_enum); |
670 | |
671 | /* Lineout 1 Mux / Mux17 */ |
672 | static SOC_ENUM_SINGLE_DECL(lo1_enum, |
673 | PM860X_ANA_TO_ANA, 2, in_text); |
674 | |
675 | static const struct snd_kcontrol_new lo1_mux = |
676 | SOC_DAPM_ENUM("Lineout1 Mux" , lo1_enum); |
677 | |
678 | /* Lineout 2 Mux / Mux18 */ |
679 | static SOC_ENUM_SINGLE_DECL(lo2_enum, |
680 | PM860X_ANA_TO_ANA, 3, in_text); |
681 | |
682 | static const struct snd_kcontrol_new lo2_mux = |
683 | SOC_DAPM_ENUM("Lineout2 Mux" , lo2_enum); |
684 | |
685 | /* Speaker Earpiece Demux */ |
686 | static const char *spk_text[] = { |
687 | "Earpiece" , "Speaker" , |
688 | }; |
689 | |
690 | static SOC_ENUM_SINGLE_DECL(spk_enum, |
691 | PM860X_ANA_TO_ANA, 6, spk_text); |
692 | |
693 | static const struct snd_kcontrol_new spk_demux = |
694 | SOC_DAPM_ENUM("Speaker Earpiece Demux" , spk_enum); |
695 | |
696 | /* MIC Mux / Mux1 */ |
697 | static const char *mic_text[] = { |
698 | "Mic 1" , "Mic 2" , |
699 | }; |
700 | |
701 | static SOC_ENUM_SINGLE_DECL(mic_enum, |
702 | PM860X_ADC_ANA_4, 4, mic_text); |
703 | |
704 | static const struct snd_kcontrol_new mic_mux = |
705 | SOC_DAPM_ENUM("MIC Mux" , mic_enum); |
706 | |
707 | static const struct snd_soc_dapm_widget pm860x_dapm_widgets[] = { |
708 | SND_SOC_DAPM_AIF_IN("PCM SDI" , "PCM Playback" , 0, |
709 | PM860X_ADC_EN_2, 0, 0), |
710 | SND_SOC_DAPM_AIF_OUT("PCM SDO" , "PCM Capture" , 0, |
711 | PM860X_PCM_IFACE_3, 1, 1), |
712 | |
713 | |
714 | SND_SOC_DAPM_AIF_IN("I2S DIN" , "I2S Playback" , 0, |
715 | SND_SOC_NOPM, 0, 0), |
716 | SND_SOC_DAPM_AIF_IN("I2S DIN1" , "I2S Playback" , 0, |
717 | SND_SOC_NOPM, 0, 0), |
718 | SND_SOC_DAPM_AIF_OUT("I2S DOUT" , "I2S Capture" , 0, |
719 | PM860X_I2S_IFACE_3, 5, 1), |
720 | SND_SOC_DAPM_SUPPLY("I2S CLK" , PM860X_DAC_EN_2, 0, 0, NULL, 0), |
721 | SND_SOC_DAPM_MUX("I2S Mic Mux" , SND_SOC_NOPM, 0, 0, &i2s_mic_mux), |
722 | SND_SOC_DAPM_MUX("ADC Left Mux" , SND_SOC_NOPM, 0, 0, &adcl_mux), |
723 | SND_SOC_DAPM_MUX("ADC Right Mux" , SND_SOC_NOPM, 0, 0, &adcr_mux), |
724 | SND_SOC_DAPM_MUX("EC Mux" , SND_SOC_NOPM, 0, 0, &ec_mux), |
725 | SND_SOC_DAPM_MUX("ADCR EC Mux" , SND_SOC_NOPM, 0, 0, &adcr_ec_mux), |
726 | SND_SOC_DAPM_SWITCH("Left EPA" , SND_SOC_NOPM, 0, 0, |
727 | &lepa_switch_controls), |
728 | SND_SOC_DAPM_SWITCH("Right EPA" , SND_SOC_NOPM, 0, 0, |
729 | &repa_switch_controls), |
730 | |
731 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Left ADC MOD" , PM860X_ADC_EN_1, |
732 | 0, 1, 1, 0), |
733 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Right ADC MOD" , PM860X_ADC_EN_1, |
734 | 1, 1, 1, 0), |
735 | SND_SOC_DAPM_ADC("Left ADC" , NULL, PM860X_ADC_EN_2, 5, 0), |
736 | SND_SOC_DAPM_ADC("Right ADC" , NULL, PM860X_ADC_EN_2, 4, 0), |
737 | |
738 | SND_SOC_DAPM_SWITCH("AUX1 Switch" , SND_SOC_NOPM, 0, 0, |
739 | &aux1_switch_controls), |
740 | SND_SOC_DAPM_SWITCH("AUX2 Switch" , SND_SOC_NOPM, 0, 0, |
741 | &aux2_switch_controls), |
742 | |
743 | SND_SOC_DAPM_MUX("MIC Mux" , SND_SOC_NOPM, 0, 0, &mic_mux), |
744 | SND_SOC_DAPM_MICBIAS("Mic1 Bias" , PM860X_ADC_ANA_1, 2, 0), |
745 | SND_SOC_DAPM_MICBIAS("Mic3 Bias" , PM860X_ADC_ANA_1, 7, 0), |
746 | SND_SOC_DAPM_PGA("MIC1 Volume" , PM860X_ADC_EN_1, 2, 0, NULL, 0), |
747 | SND_SOC_DAPM_PGA("MIC3 Volume" , PM860X_ADC_EN_1, 3, 0, NULL, 0), |
748 | SND_SOC_DAPM_PGA("AUX1 Volume" , PM860X_ADC_EN_1, 4, 0, NULL, 0), |
749 | SND_SOC_DAPM_PGA("AUX2 Volume" , PM860X_ADC_EN_1, 5, 0, NULL, 0), |
750 | SND_SOC_DAPM_PGA("Sidetone PGA" , PM860X_ADC_EN_2, 1, 0, NULL, 0), |
751 | SND_SOC_DAPM_PGA("Lofi PGA" , PM860X_ADC_EN_2, 2, 0, NULL, 0), |
752 | |
753 | SND_SOC_DAPM_INPUT("AUX1" ), |
754 | SND_SOC_DAPM_INPUT("AUX2" ), |
755 | SND_SOC_DAPM_INPUT("MIC1P" ), |
756 | SND_SOC_DAPM_INPUT("MIC1N" ), |
757 | SND_SOC_DAPM_INPUT("MIC2P" ), |
758 | SND_SOC_DAPM_INPUT("MIC2N" ), |
759 | SND_SOC_DAPM_INPUT("MIC3P" ), |
760 | SND_SOC_DAPM_INPUT("MIC3N" ), |
761 | |
762 | SND_SOC_DAPM_DAC_E("Left DAC" , NULL, SND_SOC_NOPM, 0, 0, |
763 | pm860x_dac_event, |
764 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), |
765 | SND_SOC_DAPM_DAC_E("Right DAC" , NULL, SND_SOC_NOPM, 0, 0, |
766 | pm860x_dac_event, |
767 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), |
768 | |
769 | SND_SOC_DAPM_MUX("I2S DIN Mux" , SND_SOC_NOPM, 0, 0, &i2s_din_mux), |
770 | SND_SOC_DAPM_MUX("DAC HS1 Mux" , SND_SOC_NOPM, 0, 0, &dac_hs1_mux), |
771 | SND_SOC_DAPM_MUX("DAC HS2 Mux" , SND_SOC_NOPM, 0, 0, &dac_hs2_mux), |
772 | SND_SOC_DAPM_MUX("DAC LO1 Mux" , SND_SOC_NOPM, 0, 0, &dac_lo1_mux), |
773 | SND_SOC_DAPM_MUX("DAC LO2 Mux" , SND_SOC_NOPM, 0, 0, &dac_lo2_mux), |
774 | SND_SOC_DAPM_MUX("DAC SP Mux" , SND_SOC_NOPM, 0, 0, &dac_spk_ear_mux), |
775 | SND_SOC_DAPM_MUX("Headset1 Mux" , SND_SOC_NOPM, 0, 0, &hs1_mux), |
776 | SND_SOC_DAPM_MUX("Headset2 Mux" , SND_SOC_NOPM, 0, 0, &hs2_mux), |
777 | SND_SOC_DAPM_MUX("Lineout1 Mux" , SND_SOC_NOPM, 0, 0, &lo1_mux), |
778 | SND_SOC_DAPM_MUX("Lineout2 Mux" , SND_SOC_NOPM, 0, 0, &lo2_mux), |
779 | SND_SOC_DAPM_MUX("Speaker Earpiece Demux" , SND_SOC_NOPM, 0, 0, |
780 | &spk_demux), |
781 | |
782 | |
783 | SND_SOC_DAPM_PGA("Headset1 PGA" , PM860X_DAC_EN_1, 0, 0, NULL, 0), |
784 | SND_SOC_DAPM_PGA("Headset2 PGA" , PM860X_DAC_EN_1, 1, 0, NULL, 0), |
785 | SND_SOC_DAPM_OUTPUT("HS1" ), |
786 | SND_SOC_DAPM_OUTPUT("HS2" ), |
787 | SND_SOC_DAPM_PGA("Lineout1 PGA" , PM860X_DAC_EN_1, 2, 0, NULL, 0), |
788 | SND_SOC_DAPM_PGA("Lineout2 PGA" , PM860X_DAC_EN_1, 3, 0, NULL, 0), |
789 | SND_SOC_DAPM_OUTPUT("LINEOUT1" ), |
790 | SND_SOC_DAPM_OUTPUT("LINEOUT2" ), |
791 | SND_SOC_DAPM_PGA("Earpiece PGA" , PM860X_DAC_EN_1, 4, 0, NULL, 0), |
792 | SND_SOC_DAPM_OUTPUT("EARP" ), |
793 | SND_SOC_DAPM_OUTPUT("EARN" ), |
794 | SND_SOC_DAPM_PGA("Speaker PGA" , PM860X_DAC_EN_1, 5, 0, NULL, 0), |
795 | SND_SOC_DAPM_OUTPUT("LSP" ), |
796 | SND_SOC_DAPM_OUTPUT("LSN" ), |
797 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "VCODEC" , PM860X_AUDIO_SUPPLIES_2, |
798 | 0, SUPPLY_MASK, SUPPLY_MASK, 0), |
799 | |
800 | PM860X_DAPM_OUTPUT("RSYNC" , pm860x_rsync_event), |
801 | }; |
802 | |
803 | static const struct snd_soc_dapm_route pm860x_dapm_routes[] = { |
804 | /* supply */ |
805 | {"Left DAC" , NULL, "VCODEC" }, |
806 | {"Right DAC" , NULL, "VCODEC" }, |
807 | {"Left ADC" , NULL, "VCODEC" }, |
808 | {"Right ADC" , NULL, "VCODEC" }, |
809 | {"Left ADC" , NULL, "Left ADC MOD" }, |
810 | {"Right ADC" , NULL, "Right ADC MOD" }, |
811 | |
812 | /* I2S Clock */ |
813 | {"I2S DIN" , NULL, "I2S CLK" }, |
814 | {"I2S DIN1" , NULL, "I2S CLK" }, |
815 | {"I2S DOUT" , NULL, "I2S CLK" }, |
816 | |
817 | /* PCM/AIF1 Inputs */ |
818 | {"PCM SDO" , NULL, "ADC Left Mux" }, |
819 | {"PCM SDO" , NULL, "ADCR EC Mux" }, |
820 | |
821 | /* PCM/AFI2 Outputs */ |
822 | {"Lofi PGA" , NULL, "PCM SDI" }, |
823 | {"Lofi PGA" , NULL, "Sidetone PGA" }, |
824 | {"Left DAC" , NULL, "Lofi PGA" }, |
825 | {"Right DAC" , NULL, "Lofi PGA" }, |
826 | |
827 | /* I2S/AIF2 Inputs */ |
828 | {"MIC Mux" , "Mic 1" , "MIC1P" }, |
829 | {"MIC Mux" , "Mic 1" , "MIC1N" }, |
830 | {"MIC Mux" , "Mic 2" , "MIC2P" }, |
831 | {"MIC Mux" , "Mic 2" , "MIC2N" }, |
832 | {"MIC1 Volume" , NULL, "MIC Mux" }, |
833 | {"MIC3 Volume" , NULL, "MIC3P" }, |
834 | {"MIC3 Volume" , NULL, "MIC3N" }, |
835 | {"Left ADC" , NULL, "MIC1 Volume" }, |
836 | {"Right ADC" , NULL, "MIC3 Volume" }, |
837 | {"ADC Left Mux" , "ADCR" , "Right ADC" }, |
838 | {"ADC Left Mux" , "ADCL" , "Left ADC" }, |
839 | {"ADC Right Mux" , "ADCL" , "Left ADC" }, |
840 | {"ADC Right Mux" , "ADCR" , "Right ADC" }, |
841 | {"Left EPA" , "Switch" , "Left DAC" }, |
842 | {"Right EPA" , "Switch" , "Right DAC" }, |
843 | {"EC Mux" , "Left" , "Left DAC" }, |
844 | {"EC Mux" , "Right" , "Right DAC" }, |
845 | {"EC Mux" , "Left + Right" , "Left DAC" }, |
846 | {"EC Mux" , "Left + Right" , "Right DAC" }, |
847 | {"ADCR EC Mux" , "ADCR" , "ADC Right Mux" }, |
848 | {"ADCR EC Mux" , "EC" , "EC Mux" }, |
849 | {"I2S Mic Mux" , "Ex PA" , "Left EPA" }, |
850 | {"I2S Mic Mux" , "Ex PA" , "Right EPA" }, |
851 | {"I2S Mic Mux" , "ADC" , "ADC Left Mux" }, |
852 | {"I2S Mic Mux" , "ADC" , "ADCR EC Mux" }, |
853 | {"I2S DOUT" , NULL, "I2S Mic Mux" }, |
854 | |
855 | /* I2S/AIF2 Outputs */ |
856 | {"I2S DIN Mux" , "DIN" , "I2S DIN" }, |
857 | {"I2S DIN Mux" , "DIN1" , "I2S DIN1" }, |
858 | {"Left DAC" , NULL, "I2S DIN Mux" }, |
859 | {"Right DAC" , NULL, "I2S DIN Mux" }, |
860 | {"DAC HS1 Mux" , "Left" , "Left DAC" }, |
861 | {"DAC HS1 Mux" , "Right" , "Right DAC" }, |
862 | {"DAC HS2 Mux" , "Left" , "Left DAC" }, |
863 | {"DAC HS2 Mux" , "Right" , "Right DAC" }, |
864 | {"DAC LO1 Mux" , "Left" , "Left DAC" }, |
865 | {"DAC LO1 Mux" , "Right" , "Right DAC" }, |
866 | {"DAC LO2 Mux" , "Left" , "Left DAC" }, |
867 | {"DAC LO2 Mux" , "Right" , "Right DAC" }, |
868 | {"Headset1 Mux" , "Digital" , "DAC HS1 Mux" }, |
869 | {"Headset2 Mux" , "Digital" , "DAC HS2 Mux" }, |
870 | {"Lineout1 Mux" , "Digital" , "DAC LO1 Mux" }, |
871 | {"Lineout2 Mux" , "Digital" , "DAC LO2 Mux" }, |
872 | {"Headset1 PGA" , NULL, "Headset1 Mux" }, |
873 | {"Headset2 PGA" , NULL, "Headset2 Mux" }, |
874 | {"Lineout1 PGA" , NULL, "Lineout1 Mux" }, |
875 | {"Lineout2 PGA" , NULL, "Lineout2 Mux" }, |
876 | {"DAC SP Mux" , "Left" , "Left DAC" }, |
877 | {"DAC SP Mux" , "Right" , "Right DAC" }, |
878 | {"Speaker Earpiece Demux" , "Speaker" , "DAC SP Mux" }, |
879 | {"Speaker PGA" , NULL, "Speaker Earpiece Demux" }, |
880 | {"Earpiece PGA" , NULL, "Speaker Earpiece Demux" }, |
881 | |
882 | {"RSYNC" , NULL, "Headset1 PGA" }, |
883 | {"RSYNC" , NULL, "Headset2 PGA" }, |
884 | {"RSYNC" , NULL, "Lineout1 PGA" }, |
885 | {"RSYNC" , NULL, "Lineout2 PGA" }, |
886 | {"RSYNC" , NULL, "Speaker PGA" }, |
887 | {"RSYNC" , NULL, "Speaker PGA" }, |
888 | {"RSYNC" , NULL, "Earpiece PGA" }, |
889 | {"RSYNC" , NULL, "Earpiece PGA" }, |
890 | |
891 | {"HS1" , NULL, "RSYNC" }, |
892 | {"HS2" , NULL, "RSYNC" }, |
893 | {"LINEOUT1" , NULL, "RSYNC" }, |
894 | {"LINEOUT2" , NULL, "RSYNC" }, |
895 | {"LSP" , NULL, "RSYNC" }, |
896 | {"LSN" , NULL, "RSYNC" }, |
897 | {"EARP" , NULL, "RSYNC" }, |
898 | {"EARN" , NULL, "RSYNC" }, |
899 | }; |
900 | |
901 | /* |
902 | * Use MUTE_LEFT & MUTE_RIGHT to implement digital mute. |
903 | * These bits can also be used to mute. |
904 | */ |
905 | static int pm860x_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction) |
906 | { |
907 | struct snd_soc_component *component = codec_dai->component; |
908 | int data = 0, mask = MUTE_LEFT | MUTE_RIGHT; |
909 | |
910 | if (mute) |
911 | data = mask; |
912 | snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, mask, val: data); |
913 | snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, |
914 | RSYNC_CHANGE, RSYNC_CHANGE); |
915 | return 0; |
916 | } |
917 | |
918 | static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream, |
919 | struct snd_pcm_hw_params *params, |
920 | struct snd_soc_dai *dai) |
921 | { |
922 | struct snd_soc_component *component = dai->component; |
923 | unsigned char inf = 0, mask = 0; |
924 | |
925 | /* bit size */ |
926 | switch (params_width(p: params)) { |
927 | case 16: |
928 | inf &= ~PCM_INF2_18WL; |
929 | break; |
930 | case 18: |
931 | inf |= PCM_INF2_18WL; |
932 | break; |
933 | default: |
934 | return -EINVAL; |
935 | } |
936 | mask |= PCM_INF2_18WL; |
937 | snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, val: inf); |
938 | |
939 | /* sample rate */ |
940 | switch (params_rate(p: params)) { |
941 | case 8000: |
942 | inf = 0; |
943 | break; |
944 | case 16000: |
945 | inf = 3; |
946 | break; |
947 | case 32000: |
948 | inf = 6; |
949 | break; |
950 | case 48000: |
951 | inf = 8; |
952 | break; |
953 | default: |
954 | return -EINVAL; |
955 | } |
956 | snd_soc_component_update_bits(component, PM860X_PCM_RATE, mask: 0x0f, val: inf); |
957 | |
958 | return 0; |
959 | } |
960 | |
961 | static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, |
962 | unsigned int fmt) |
963 | { |
964 | struct snd_soc_component *component = codec_dai->component; |
965 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(c: component); |
966 | unsigned char inf = 0, mask = 0; |
967 | int ret = -EINVAL; |
968 | |
969 | mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; |
970 | |
971 | /* set audio interface clocking */ |
972 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
973 | case SND_SOC_DAIFMT_CBP_CFP: |
974 | case SND_SOC_DAIFMT_CBP_CFC: |
975 | if (pm860x->dir == PM860X_CLK_DIR_OUT) { |
976 | inf |= PCM_INF2_MASTER; |
977 | ret = 0; |
978 | } |
979 | break; |
980 | case SND_SOC_DAIFMT_CBC_CFC: |
981 | if (pm860x->dir == PM860X_CLK_DIR_IN) { |
982 | inf &= ~PCM_INF2_MASTER; |
983 | ret = 0; |
984 | } |
985 | break; |
986 | } |
987 | |
988 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
989 | case SND_SOC_DAIFMT_I2S: |
990 | inf |= PCM_EXACT_I2S; |
991 | ret = 0; |
992 | break; |
993 | } |
994 | mask |= PCM_MODE_MASK; |
995 | if (ret) |
996 | return ret; |
997 | snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, val: inf); |
998 | return 0; |
999 | } |
1000 | |
1001 | static int pm860x_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
1002 | int clk_id, unsigned int freq, int dir) |
1003 | { |
1004 | struct snd_soc_component *component = codec_dai->component; |
1005 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(c: component); |
1006 | |
1007 | if (dir == PM860X_CLK_DIR_OUT) |
1008 | pm860x->dir = PM860X_CLK_DIR_OUT; |
1009 | else /* Slave mode is not supported */ |
1010 | return -EINVAL; |
1011 | |
1012 | return 0; |
1013 | } |
1014 | |
1015 | static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream, |
1016 | struct snd_pcm_hw_params *params, |
1017 | struct snd_soc_dai *dai) |
1018 | { |
1019 | struct snd_soc_component *component = dai->component; |
1020 | unsigned char inf; |
1021 | |
1022 | /* bit size */ |
1023 | switch (params_width(p: params)) { |
1024 | case 16: |
1025 | inf = 0; |
1026 | break; |
1027 | case 18: |
1028 | inf = PCM_INF2_18WL; |
1029 | break; |
1030 | default: |
1031 | return -EINVAL; |
1032 | } |
1033 | snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, PCM_INF2_18WL, val: inf); |
1034 | |
1035 | /* sample rate */ |
1036 | switch (params_rate(p: params)) { |
1037 | case 8000: |
1038 | inf = 0; |
1039 | break; |
1040 | case 11025: |
1041 | inf = 1; |
1042 | break; |
1043 | case 16000: |
1044 | inf = 3; |
1045 | break; |
1046 | case 22050: |
1047 | inf = 4; |
1048 | break; |
1049 | case 32000: |
1050 | inf = 6; |
1051 | break; |
1052 | case 44100: |
1053 | inf = 7; |
1054 | break; |
1055 | case 48000: |
1056 | inf = 8; |
1057 | break; |
1058 | default: |
1059 | return -EINVAL; |
1060 | } |
1061 | snd_soc_component_update_bits(component, PM860X_I2S_IFACE_4, mask: 0xf, val: inf); |
1062 | |
1063 | return 0; |
1064 | } |
1065 | |
1066 | static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, |
1067 | unsigned int fmt) |
1068 | { |
1069 | struct snd_soc_component *component = codec_dai->component; |
1070 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(c: component); |
1071 | unsigned char inf = 0, mask = 0; |
1072 | |
1073 | mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; |
1074 | |
1075 | /* set audio interface clocking */ |
1076 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
1077 | case SND_SOC_DAIFMT_CBP_CFP: |
1078 | if (pm860x->dir == PM860X_CLK_DIR_OUT) |
1079 | inf |= PCM_INF2_MASTER; |
1080 | else |
1081 | return -EINVAL; |
1082 | break; |
1083 | case SND_SOC_DAIFMT_CBC_CFC: |
1084 | if (pm860x->dir == PM860X_CLK_DIR_IN) |
1085 | inf &= ~PCM_INF2_MASTER; |
1086 | else |
1087 | return -EINVAL; |
1088 | break; |
1089 | default: |
1090 | return -EINVAL; |
1091 | } |
1092 | |
1093 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
1094 | case SND_SOC_DAIFMT_I2S: |
1095 | inf |= PCM_EXACT_I2S; |
1096 | break; |
1097 | default: |
1098 | return -EINVAL; |
1099 | } |
1100 | mask |= PCM_MODE_MASK; |
1101 | snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, mask, val: inf); |
1102 | return 0; |
1103 | } |
1104 | |
1105 | static int pm860x_set_bias_level(struct snd_soc_component *component, |
1106 | enum snd_soc_bias_level level) |
1107 | { |
1108 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(c: component); |
1109 | int data; |
1110 | |
1111 | switch (level) { |
1112 | case SND_SOC_BIAS_ON: |
1113 | break; |
1114 | |
1115 | case SND_SOC_BIAS_PREPARE: |
1116 | break; |
1117 | |
1118 | case SND_SOC_BIAS_STANDBY: |
1119 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { |
1120 | /* Enable Audio PLL & Audio section */ |
1121 | data = AUDIO_PLL | AUDIO_SECTION_ON; |
1122 | pm860x_reg_write(pm860x->i2c, REG_MISC2, data); |
1123 | udelay(300); |
1124 | data = AUDIO_PLL | AUDIO_SECTION_RESET |
1125 | | AUDIO_SECTION_ON; |
1126 | pm860x_reg_write(pm860x->i2c, REG_MISC2, data); |
1127 | } |
1128 | break; |
1129 | |
1130 | case SND_SOC_BIAS_OFF: |
1131 | data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; |
1132 | pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0); |
1133 | break; |
1134 | } |
1135 | return 0; |
1136 | } |
1137 | |
1138 | static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = { |
1139 | .mute_stream = pm860x_mute_stream, |
1140 | .hw_params = pm860x_pcm_hw_params, |
1141 | .set_fmt = pm860x_pcm_set_dai_fmt, |
1142 | .set_sysclk = pm860x_set_dai_sysclk, |
1143 | .no_capture_mute = 1, |
1144 | }; |
1145 | |
1146 | static const struct snd_soc_dai_ops pm860x_i2s_dai_ops = { |
1147 | .mute_stream = pm860x_mute_stream, |
1148 | .hw_params = pm860x_i2s_hw_params, |
1149 | .set_fmt = pm860x_i2s_set_dai_fmt, |
1150 | .set_sysclk = pm860x_set_dai_sysclk, |
1151 | .no_capture_mute = 1, |
1152 | }; |
1153 | |
1154 | #define PM860X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ |
1155 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) |
1156 | |
1157 | static struct snd_soc_dai_driver pm860x_dai[] = { |
1158 | { |
1159 | /* DAI PCM */ |
1160 | .name = "88pm860x-pcm" , |
1161 | .id = 1, |
1162 | .playback = { |
1163 | .stream_name = "PCM Playback" , |
1164 | .channels_min = 2, |
1165 | .channels_max = 2, |
1166 | .rates = PM860X_RATES, |
1167 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ |
1168 | SNDRV_PCM_FMTBIT_S18_3LE, |
1169 | }, |
1170 | .capture = { |
1171 | .stream_name = "PCM Capture" , |
1172 | .channels_min = 2, |
1173 | .channels_max = 2, |
1174 | .rates = PM860X_RATES, |
1175 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ |
1176 | SNDRV_PCM_FMTBIT_S18_3LE, |
1177 | }, |
1178 | .ops = &pm860x_pcm_dai_ops, |
1179 | }, { |
1180 | /* DAI I2S */ |
1181 | .name = "88pm860x-i2s" , |
1182 | .id = 2, |
1183 | .playback = { |
1184 | .stream_name = "I2S Playback" , |
1185 | .channels_min = 2, |
1186 | .channels_max = 2, |
1187 | .rates = SNDRV_PCM_RATE_8000_48000, |
1188 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ |
1189 | SNDRV_PCM_FMTBIT_S18_3LE, |
1190 | }, |
1191 | .capture = { |
1192 | .stream_name = "I2S Capture" , |
1193 | .channels_min = 2, |
1194 | .channels_max = 2, |
1195 | .rates = SNDRV_PCM_RATE_8000_48000, |
1196 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ |
1197 | SNDRV_PCM_FMTBIT_S18_3LE, |
1198 | }, |
1199 | .ops = &pm860x_i2s_dai_ops, |
1200 | }, |
1201 | }; |
1202 | |
1203 | static irqreturn_t pm860x_component_handler(int irq, void *data) |
1204 | { |
1205 | struct pm860x_priv *pm860x = data; |
1206 | int status, shrt, report = 0, mic_report = 0; |
1207 | int mask; |
1208 | |
1209 | status = pm860x_reg_read(pm860x->i2c, REG_STATUS_1); |
1210 | shrt = pm860x_reg_read(pm860x->i2c, REG_SHORTS); |
1211 | mask = pm860x->det.hs_shrt | pm860x->det.hook_det | pm860x->det.lo_shrt |
1212 | | pm860x->det.hp_det; |
1213 | |
1214 | #ifndef CONFIG_SND_SOC_88PM860X_MODULE |
1215 | if (status & (HEADSET_STATUS | MIC_STATUS | SHORT_HS1 | SHORT_HS2 | |
1216 | SHORT_LO1 | SHORT_LO2)) |
1217 | trace_snd_soc_jack_irq(name: dev_name(dev: pm860x->component->dev)); |
1218 | #endif |
1219 | |
1220 | if ((pm860x->det.hp_det & SND_JACK_HEADPHONE) |
1221 | && (status & HEADSET_STATUS)) |
1222 | report |= SND_JACK_HEADPHONE; |
1223 | |
1224 | if ((pm860x->det.mic_det & SND_JACK_MICROPHONE) |
1225 | && (status & MIC_STATUS)) |
1226 | mic_report |= SND_JACK_MICROPHONE; |
1227 | |
1228 | if (pm860x->det.hs_shrt && (shrt & (SHORT_HS1 | SHORT_HS2))) |
1229 | report |= pm860x->det.hs_shrt; |
1230 | |
1231 | if (pm860x->det.hook_det && (status & HOOK_STATUS)) |
1232 | report |= pm860x->det.hook_det; |
1233 | |
1234 | if (pm860x->det.lo_shrt && (shrt & (SHORT_LO1 | SHORT_LO2))) |
1235 | report |= pm860x->det.lo_shrt; |
1236 | |
1237 | if (report) |
1238 | snd_soc_jack_report(jack: pm860x->det.hp_jack, status: report, mask); |
1239 | if (mic_report) |
1240 | snd_soc_jack_report(jack: pm860x->det.mic_jack, status: SND_JACK_MICROPHONE, |
1241 | mask: SND_JACK_MICROPHONE); |
1242 | |
1243 | dev_dbg(pm860x->component->dev, "headphone report:0x%x, mask:%x\n" , |
1244 | report, mask); |
1245 | dev_dbg(pm860x->component->dev, "microphone report:0x%x\n" , mic_report); |
1246 | return IRQ_HANDLED; |
1247 | } |
1248 | |
1249 | int pm860x_hs_jack_detect(struct snd_soc_component *component, |
1250 | struct snd_soc_jack *jack, |
1251 | int det, int hook, int hs_shrt, int lo_shrt) |
1252 | { |
1253 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(c: component); |
1254 | int data; |
1255 | |
1256 | pm860x->det.hp_jack = jack; |
1257 | pm860x->det.hp_det = det; |
1258 | pm860x->det.hook_det = hook; |
1259 | pm860x->det.hs_shrt = hs_shrt; |
1260 | pm860x->det.lo_shrt = lo_shrt; |
1261 | |
1262 | if (det & SND_JACK_HEADPHONE) |
1263 | pm860x_set_bits(pm860x->i2c, REG_HS_DET, |
1264 | EN_HS_DET, EN_HS_DET); |
1265 | /* headset short detect */ |
1266 | if (hs_shrt) { |
1267 | data = CLR_SHORT_HS2 | CLR_SHORT_HS1; |
1268 | pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data); |
1269 | } |
1270 | /* Lineout short detect */ |
1271 | if (lo_shrt) { |
1272 | data = CLR_SHORT_LO2 | CLR_SHORT_LO1; |
1273 | pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data); |
1274 | } |
1275 | |
1276 | /* sync status */ |
1277 | pm860x_component_handler(irq: 0, data: pm860x); |
1278 | return 0; |
1279 | } |
1280 | EXPORT_SYMBOL_GPL(pm860x_hs_jack_detect); |
1281 | |
1282 | int pm860x_mic_jack_detect(struct snd_soc_component *component, |
1283 | struct snd_soc_jack *jack, int det) |
1284 | { |
1285 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(c: component); |
1286 | |
1287 | pm860x->det.mic_jack = jack; |
1288 | pm860x->det.mic_det = det; |
1289 | |
1290 | if (det & SND_JACK_MICROPHONE) |
1291 | pm860x_set_bits(pm860x->i2c, REG_MIC_DET, |
1292 | MICDET_MASK, MICDET_MASK); |
1293 | |
1294 | /* sync status */ |
1295 | pm860x_component_handler(irq: 0, data: pm860x); |
1296 | return 0; |
1297 | } |
1298 | EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect); |
1299 | |
1300 | static int pm860x_probe(struct snd_soc_component *component) |
1301 | { |
1302 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(c: component); |
1303 | int i, ret; |
1304 | |
1305 | pm860x->component = component; |
1306 | snd_soc_component_init_regmap(component, regmap: pm860x->regmap); |
1307 | |
1308 | for (i = 0; i < 4; i++) { |
1309 | ret = request_threaded_irq(irq: pm860x->irq[i], NULL, |
1310 | thread_fn: pm860x_component_handler, IRQF_ONESHOT, |
1311 | name: pm860x->name[i], dev: pm860x); |
1312 | if (ret < 0) { |
1313 | dev_err(component->dev, "Failed to request IRQ!\n" ); |
1314 | goto out; |
1315 | } |
1316 | } |
1317 | |
1318 | return 0; |
1319 | |
1320 | out: |
1321 | while (--i >= 0) |
1322 | free_irq(pm860x->irq[i], pm860x); |
1323 | return ret; |
1324 | } |
1325 | |
1326 | static void pm860x_remove(struct snd_soc_component *component) |
1327 | { |
1328 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(c: component); |
1329 | int i; |
1330 | |
1331 | for (i = 3; i >= 0; i--) |
1332 | free_irq(pm860x->irq[i], pm860x); |
1333 | } |
1334 | |
1335 | static const struct snd_soc_component_driver soc_component_dev_pm860x = { |
1336 | .probe = pm860x_probe, |
1337 | .remove = pm860x_remove, |
1338 | .set_bias_level = pm860x_set_bias_level, |
1339 | .controls = pm860x_snd_controls, |
1340 | .num_controls = ARRAY_SIZE(pm860x_snd_controls), |
1341 | .dapm_widgets = pm860x_dapm_widgets, |
1342 | .num_dapm_widgets = ARRAY_SIZE(pm860x_dapm_widgets), |
1343 | .dapm_routes = pm860x_dapm_routes, |
1344 | .num_dapm_routes = ARRAY_SIZE(pm860x_dapm_routes), |
1345 | .idle_bias_on = 1, |
1346 | .use_pmdown_time = 1, |
1347 | .endianness = 1, |
1348 | }; |
1349 | |
1350 | static int pm860x_codec_probe(struct platform_device *pdev) |
1351 | { |
1352 | struct pm860x_chip *chip = dev_get_drvdata(dev: pdev->dev.parent); |
1353 | struct pm860x_priv *pm860x; |
1354 | struct resource *res; |
1355 | int i, ret; |
1356 | |
1357 | pm860x = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct pm860x_priv), |
1358 | GFP_KERNEL); |
1359 | if (pm860x == NULL) |
1360 | return -ENOMEM; |
1361 | |
1362 | pm860x->chip = chip; |
1363 | pm860x->i2c = (chip->id == CHIP_PM8607) ? chip->client |
1364 | : chip->companion; |
1365 | pm860x->regmap = (chip->id == CHIP_PM8607) ? chip->regmap |
1366 | : chip->regmap_companion; |
1367 | platform_set_drvdata(pdev, data: pm860x); |
1368 | |
1369 | for (i = 0; i < 4; i++) { |
1370 | res = platform_get_resource(pdev, IORESOURCE_IRQ, i); |
1371 | if (!res) { |
1372 | dev_err(&pdev->dev, "Failed to get IRQ resources\n" ); |
1373 | return -EINVAL; |
1374 | } |
1375 | pm860x->irq[i] = res->start + chip->irq_base; |
1376 | strscpy(pm860x->name[i], res->name, MAX_NAME_LEN); |
1377 | } |
1378 | |
1379 | ret = devm_snd_soc_register_component(dev: &pdev->dev, |
1380 | component_driver: &soc_component_dev_pm860x, |
1381 | dai_drv: pm860x_dai, ARRAY_SIZE(pm860x_dai)); |
1382 | if (ret) { |
1383 | dev_err(&pdev->dev, "Failed to register component\n" ); |
1384 | return -EINVAL; |
1385 | } |
1386 | return ret; |
1387 | } |
1388 | |
1389 | static struct platform_driver pm860x_codec_driver = { |
1390 | .driver = { |
1391 | .name = "88pm860x-codec" , |
1392 | }, |
1393 | .probe = pm860x_codec_probe, |
1394 | }; |
1395 | |
1396 | module_platform_driver(pm860x_codec_driver); |
1397 | |
1398 | MODULE_DESCRIPTION("ASoC 88PM860x driver" ); |
1399 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>" ); |
1400 | MODULE_LICENSE("GPL" ); |
1401 | MODULE_ALIAS("platform:88pm860x-codec" ); |
1402 | |
1403 | |