Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
f213f4b5 HZ |
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> | |
f213f4b5 HZ |
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> | |
8af08945 | 15 | #include <linux/delay.h> |
f9ded3b2 | 16 | #include <linux/regmap.h> |
f213f4b5 HZ |
17 | #include <sound/core.h> |
18 | #include <sound/pcm.h> | |
19 | #include <sound/pcm_params.h> | |
20 | #include <sound/soc.h> | |
f213f4b5 HZ |
21 | #include <sound/tlv.h> |
22 | #include <sound/initval.h> | |
23 | #include <sound/jack.h> | |
1c9e9795 | 24 | #include <trace/events/asoc.h> |
f213f4b5 HZ |
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) \ | |
f0ddb219 LPC |
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) | |
f213f4b5 HZ |
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; | |
5783994b | 139 | struct snd_soc_component *component; |
f213f4b5 | 140 | struct i2c_client *i2c; |
f9ded3b2 | 141 | struct regmap *regmap; |
f213f4b5 HZ |
142 | struct pm860x_chip *chip; |
143 | struct pm860x_det det; | |
144 | ||
145 | int irq[4]; | |
a9a65b87 | 146 | unsigned char name[4][MAX_NAME_LEN]; |
f213f4b5 HZ |
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 */ | |
f17cbcfe | 156 | static const DECLARE_TLV_DB_RANGE(mic_tlv, |
f213f4b5 HZ |
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), | |
f17cbcfe LPC |
161 | 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0) |
162 | ); | |
f213f4b5 HZ |
163 | |
164 | /* {0, 0, 0, -6, 0, 6, 12, 18}dB */ | |
f17cbcfe | 165 | static const DECLARE_TLV_DB_RANGE(aux_tlv, |
f213f4b5 | 166 | 0, 2, TLV_DB_SCALE_ITEM(0, 0, 0), |
f17cbcfe LPC |
167 | 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0) |
168 | ); | |
f213f4b5 HZ |
169 | |
170 | /* {-16, -13, -10, -7, -5.2, -3,3, -2.2, 0}dB, mute instead of -16dB */ | |
f17cbcfe | 171 | static const DECLARE_TLV_DB_RANGE(out_tlv, |
f213f4b5 HZ |
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), | |
f17cbcfe LPC |
175 | 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0) |
176 | ); | |
f213f4b5 | 177 | |
f17cbcfe | 178 | static const DECLARE_TLV_DB_RANGE(st_tlv, |
f213f4b5 HZ |
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), | |
f17cbcfe LPC |
186 | 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0) |
187 | ); | |
f213f4b5 HZ |
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 | ||
f213f4b5 HZ |
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; | |
5783994b | 272 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
f213f4b5 HZ |
273 | unsigned int reg = mc->reg; |
274 | unsigned int reg2 = mc->rreg; | |
275 | int val[2], val2[2], i; | |
276 | ||
981abdfe KM |
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, reg2) & 0x3f; | |
280 | val2[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT)) & 0xf; | |
f213f4b5 HZ |
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; | |
5783994b | 296 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
f213f4b5 HZ |
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 | ||
d967967e DC |
305 | if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table)) |
306 | return -EINVAL; | |
307 | ||
5783994b | 308 | err = snd_soc_component_update_bits(component, reg, 0x3f, st_table[val].m); |
f213f4b5 HZ |
309 | if (err < 0) |
310 | return err; | |
5783994b | 311 | err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0xf0, |
f213f4b5 HZ |
312 | st_table[val].n << 4); |
313 | if (err < 0) | |
314 | return err; | |
315 | ||
5783994b | 316 | err = snd_soc_component_update_bits(component, reg2, 0x3f, st_table[val2].m); |
f213f4b5 HZ |
317 | if (err < 0) |
318 | return err; | |
5783994b | 319 | err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0x0f, |
f213f4b5 HZ |
320 | 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; | |
5783994b | 329 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
f213f4b5 HZ |
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(max)) - 1; | |
335 | ||
981abdfe KM |
336 | val = snd_soc_component_read(component, reg) >> shift; |
337 | val2 = snd_soc_component_read(component, reg2) >> shift; | |
f213f4b5 HZ |
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; | |
5783994b | 349 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
f213f4b5 HZ |
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(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 | ||
5783994b | 365 | err = snd_soc_component_update_bits(component, reg, val_mask, val); |
f213f4b5 HZ |
366 | if (err < 0) |
367 | return err; | |
368 | ||
5783994b | 369 | err = snd_soc_component_update_bits(component, reg2, val_mask, val2); |
f213f4b5 HZ |
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 | { | |
5783994b | 382 | struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); |
f213f4b5 HZ |
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 | */ | |
5783994b KM |
390 | snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, DAC_MUTE, 0); |
391 | snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, | |
f213f4b5 HZ |
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 | { | |
5783994b | 399 | struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); |
f213f4b5 HZ |
400 | unsigned int dac = 0; |
401 | int data; | |
402 | ||
ccd0c6c7 | 403 | if (!snd_soc_dapm_widget_name_cmp(w, "Left DAC")) |
f213f4b5 | 404 | dac = DAC_LEFT; |
ccd0c6c7 | 405 | if (!snd_soc_dapm_widget_name_cmp(w, "Right DAC")) |
f213f4b5 HZ |
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; | |
5783994b | 412 | snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, |
f213f4b5 | 413 | DAC_MUTE, DAC_MUTE); |
5783994b | 414 | snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, |
f213f4b5 HZ |
415 | RSYNC_CHANGE, RSYNC_CHANGE); |
416 | /* update dac */ | |
5783994b | 417 | snd_soc_component_update_bits(component, PM860X_DAC_EN_2, |
f213f4b5 HZ |
418 | dac, dac); |
419 | } | |
420 | break; | |
421 | case SND_SOC_DAPM_PRE_PMD: | |
422 | if (dac) { | |
423 | /* Auto mute in power-off sequence. */ | |
5783994b | 424 | snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, |
f213f4b5 | 425 | DAC_MUTE, DAC_MUTE); |
5783994b | 426 | snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, |
f213f4b5 HZ |
427 | RSYNC_CHANGE, RSYNC_CHANGE); |
428 | /* update dac */ | |
981abdfe | 429 | data = snd_soc_component_read(component, PM860X_DAC_EN_2); |
f213f4b5 HZ |
430 | data &= ~dac; |
431 | if (!(data & (DAC_LEFT | DAC_RIGHT))) | |
432 | data &= ~MODULATOR; | |
5783994b | 433 | snd_soc_component_write(component, PM860X_DAC_EN_2, data); |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
444 | static SOC_ENUM_SINGLE_DECL(pm860x_hs1_opamp_enum, |
445 | PM860X_HS1_CTRL, 5, pm860x_opamp_texts); | |
f213f4b5 | 446 | |
6eb0e8f9 TI |
447 | static SOC_ENUM_SINGLE_DECL(pm860x_hs2_opamp_enum, |
448 | PM860X_HS2_CTRL, 5, pm860x_opamp_texts); | |
f213f4b5 | 449 | |
6eb0e8f9 TI |
450 | static SOC_ENUM_SINGLE_DECL(pm860x_hs1_pa_enum, |
451 | PM860X_HS1_CTRL, 3, pm860x_pa_texts); | |
f213f4b5 | 452 | |
6eb0e8f9 TI |
453 | static SOC_ENUM_SINGLE_DECL(pm860x_hs2_pa_enum, |
454 | PM860X_HS2_CTRL, 3, pm860x_pa_texts); | |
f213f4b5 | 455 | |
6eb0e8f9 TI |
456 | static SOC_ENUM_SINGLE_DECL(pm860x_lo1_opamp_enum, |
457 | PM860X_LO1_CTRL, 5, pm860x_opamp_texts); | |
f213f4b5 | 458 | |
6eb0e8f9 TI |
459 | static SOC_ENUM_SINGLE_DECL(pm860x_lo2_opamp_enum, |
460 | PM860X_LO2_CTRL, 5, pm860x_opamp_texts); | |
f213f4b5 | 461 | |
6eb0e8f9 TI |
462 | static SOC_ENUM_SINGLE_DECL(pm860x_lo1_pa_enum, |
463 | PM860X_LO1_CTRL, 3, pm860x_pa_texts); | |
f213f4b5 | 464 | |
6eb0e8f9 TI |
465 | static SOC_ENUM_SINGLE_DECL(pm860x_lo2_pa_enum, |
466 | PM860X_LO2_CTRL, 3, pm860x_pa_texts); | |
f213f4b5 | 467 | |
6eb0e8f9 TI |
468 | static SOC_ENUM_SINGLE_DECL(pm860x_spk_pa_enum, |
469 | PM860X_EAR_CTRL_1, 5, pm860x_pa_texts); | |
f213f4b5 | 470 | |
6eb0e8f9 TI |
471 | static SOC_ENUM_SINGLE_DECL(pm860x_ear_pa_enum, |
472 | PM860X_EAR_CTRL_2, 0, pm860x_pa_texts); | |
f213f4b5 | 473 | |
6eb0e8f9 TI |
474 | static SOC_ENUM_SINGLE_DECL(pm860x_spk_ear_opamp_enum, |
475 | PM860X_EAR_CTRL_1, 3, pm860x_opamp_texts); | |
f213f4b5 HZ |
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 | ||
f213f4b5 HZ |
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 | ||
f213f4b5 HZ |
548 | /* I2S Mux / Mux9 */ |
549 | static const char *i2s_din_text[] = { | |
550 | "DIN", "DIN1", | |
551 | }; | |
552 | ||
6eb0e8f9 TI |
553 | static SOC_ENUM_SINGLE_DECL(i2s_din_enum, |
554 | PM860X_I2S_IFACE_3, 1, i2s_din_text); | |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
564 | static SOC_ENUM_SINGLE_DECL(i2s_mic_enum, |
565 | PM860X_I2S_IFACE_3, 4, i2s_mic_text); | |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
575 | static SOC_ENUM_SINGLE_DECL(adcl_enum, |
576 | PM860X_PCM_IFACE_3, 4, adcl_text); | |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
586 | static SOC_ENUM_SINGLE_DECL(adcr_enum, |
587 | PM860X_PCM_IFACE_3, 2, adcr_text); | |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
597 | static SOC_ENUM_SINGLE_DECL(adcr_ec_enum, |
598 | PM860X_ADC_EN_2, 3, adcr_ec_text); | |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
608 | static SOC_ENUM_SINGLE_DECL(ec_enum, |
609 | PM860X_EC_PATH, 1, ec_text); | |
f213f4b5 HZ |
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 */ | |
6eb0e8f9 TI |
619 | static SOC_ENUM_SINGLE_DECL(dac_hs1_enum, |
620 | PM860X_ANA_INPUT_SEL_1, 0, dac_text); | |
f213f4b5 HZ |
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 */ | |
6eb0e8f9 TI |
626 | static SOC_ENUM_SINGLE_DECL(dac_hs2_enum, |
627 | PM860X_ANA_INPUT_SEL_1, 2, dac_text); | |
f213f4b5 HZ |
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 */ | |
6eb0e8f9 TI |
633 | static SOC_ENUM_SINGLE_DECL(dac_lo1_enum, |
634 | PM860X_ANA_INPUT_SEL_1, 4, dac_text); | |
f213f4b5 HZ |
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 */ | |
6eb0e8f9 TI |
640 | static SOC_ENUM_SINGLE_DECL(dac_lo2_enum, |
641 | PM860X_ANA_INPUT_SEL_1, 6, dac_text); | |
f213f4b5 HZ |
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 */ | |
6eb0e8f9 TI |
647 | static SOC_ENUM_SINGLE_DECL(dac_spk_ear_enum, |
648 | PM860X_ANA_INPUT_SEL_2, 0, dac_text); | |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
658 | static SOC_ENUM_SINGLE_DECL(hs1_enum, |
659 | PM860X_ANA_TO_ANA, 0, in_text); | |
f213f4b5 HZ |
660 | |
661 | static const struct snd_kcontrol_new hs1_mux = | |
662 | SOC_DAPM_ENUM("Headset1 Mux", hs1_enum); | |
663 | ||
664 | /* Headset 2 Mux / Mux16 */ | |
6eb0e8f9 TI |
665 | static SOC_ENUM_SINGLE_DECL(hs2_enum, |
666 | PM860X_ANA_TO_ANA, 1, in_text); | |
f213f4b5 HZ |
667 | |
668 | static const struct snd_kcontrol_new hs2_mux = | |
669 | SOC_DAPM_ENUM("Headset2 Mux", hs2_enum); | |
670 | ||
671 | /* Lineout 1 Mux / Mux17 */ | |
6eb0e8f9 TI |
672 | static SOC_ENUM_SINGLE_DECL(lo1_enum, |
673 | PM860X_ANA_TO_ANA, 2, in_text); | |
f213f4b5 HZ |
674 | |
675 | static const struct snd_kcontrol_new lo1_mux = | |
676 | SOC_DAPM_ENUM("Lineout1 Mux", lo1_enum); | |
677 | ||
678 | /* Lineout 2 Mux / Mux18 */ | |
6eb0e8f9 TI |
679 | static SOC_ENUM_SINGLE_DECL(lo2_enum, |
680 | PM860X_ANA_TO_ANA, 3, in_text); | |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
690 | static SOC_ENUM_SINGLE_DECL(spk_enum, |
691 | PM860X_ANA_TO_ANA, 6, spk_text); | |
f213f4b5 HZ |
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 | ||
6eb0e8f9 TI |
701 | static SOC_ENUM_SINGLE_DECL(mic_enum, |
702 | PM860X_ADC_ANA_4, 4, mic_text); | |
f213f4b5 HZ |
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, | |
06c15baf | 715 | SND_SOC_NOPM, 0, 0), |
f213f4b5 | 716 | SND_SOC_DAPM_AIF_IN("I2S DIN1", "I2S Playback", 0, |
06c15baf | 717 | SND_SOC_NOPM, 0, 0), |
f213f4b5 HZ |
718 | SND_SOC_DAPM_AIF_OUT("I2S DOUT", "I2S Capture", 0, |
719 | PM860X_I2S_IFACE_3, 5, 1), | |
06c15baf | 720 | SND_SOC_DAPM_SUPPLY("I2S CLK", PM860X_DAC_EN_2, 0, 0, NULL, 0), |
f213f4b5 HZ |
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 | ||
f4f8e4c3 | 803 | static const struct snd_soc_dapm_route pm860x_dapm_routes[] = { |
f213f4b5 HZ |
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 | ||
06c15baf BV |
812 | /* I2S Clock */ |
813 | {"I2S DIN", NULL, "I2S CLK"}, | |
814 | {"I2S DIN1", NULL, "I2S CLK"}, | |
815 | {"I2S DOUT", NULL, "I2S CLK"}, | |
816 | ||
f213f4b5 HZ |
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 | */ | |
54b59270 | 905 | static int pm860x_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction) |
f213f4b5 | 906 | { |
5783994b | 907 | struct snd_soc_component *component = codec_dai->component; |
f213f4b5 HZ |
908 | int data = 0, mask = MUTE_LEFT | MUTE_RIGHT; |
909 | ||
910 | if (mute) | |
911 | data = mask; | |
5783994b KM |
912 | snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, mask, data); |
913 | snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, | |
f213f4b5 HZ |
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 | { | |
5783994b | 922 | struct snd_soc_component *component = dai->component; |
f213f4b5 HZ |
923 | unsigned char inf = 0, mask = 0; |
924 | ||
925 | /* bit size */ | |
0caf3eb7 MB |
926 | switch (params_width(params)) { |
927 | case 16: | |
f213f4b5 HZ |
928 | inf &= ~PCM_INF2_18WL; |
929 | break; | |
0caf3eb7 | 930 | case 18: |
f213f4b5 HZ |
931 | inf |= PCM_INF2_18WL; |
932 | break; | |
933 | default: | |
934 | return -EINVAL; | |
935 | } | |
936 | mask |= PCM_INF2_18WL; | |
5783994b | 937 | snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf); |
f213f4b5 HZ |
938 | |
939 | /* sample rate */ | |
940 | switch (params_rate(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 | } | |
5783994b | 956 | snd_soc_component_update_bits(component, PM860X_PCM_RATE, 0x0f, inf); |
f213f4b5 HZ |
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 | { | |
5783994b KM |
964 | struct snd_soc_component *component = codec_dai->component; |
965 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); | |
f213f4b5 HZ |
966 | unsigned char inf = 0, mask = 0; |
967 | int ret = -EINVAL; | |
968 | ||
969 | mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; | |
970 | ||
703ac1f2 MB |
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: | |
f213f4b5 HZ |
975 | if (pm860x->dir == PM860X_CLK_DIR_OUT) { |
976 | inf |= PCM_INF2_MASTER; | |
977 | ret = 0; | |
978 | } | |
979 | break; | |
703ac1f2 | 980 | case SND_SOC_DAIFMT_CBC_CFC: |
f213f4b5 HZ |
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; | |
5783994b | 997 | snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf); |
f213f4b5 HZ |
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 | { | |
5783994b KM |
1004 | struct snd_soc_component *component = codec_dai->component; |
1005 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); | |
f213f4b5 HZ |
1006 | |
1007 | if (dir == PM860X_CLK_DIR_OUT) | |
1008 | pm860x->dir = PM860X_CLK_DIR_OUT; | |
93ec3a1a | 1009 | else /* Slave mode is not supported */ |
f213f4b5 | 1010 | return -EINVAL; |
f213f4b5 HZ |
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 | { | |
5783994b | 1019 | struct snd_soc_component *component = dai->component; |
f213f4b5 HZ |
1020 | unsigned char inf; |
1021 | ||
1022 | /* bit size */ | |
0caf3eb7 MB |
1023 | switch (params_width(params)) { |
1024 | case 16: | |
f213f4b5 HZ |
1025 | inf = 0; |
1026 | break; | |
0caf3eb7 | 1027 | case 18: |
f213f4b5 HZ |
1028 | inf = PCM_INF2_18WL; |
1029 | break; | |
1030 | default: | |
1031 | return -EINVAL; | |
1032 | } | |
5783994b | 1033 | snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf); |
f213f4b5 HZ |
1034 | |
1035 | /* sample rate */ | |
1036 | switch (params_rate(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 | } | |
5783994b | 1061 | snd_soc_component_update_bits(component, PM860X_I2S_IFACE_4, 0xf, inf); |
f213f4b5 HZ |
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 | { | |
5783994b KM |
1069 | struct snd_soc_component *component = codec_dai->component; |
1070 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); | |
f213f4b5 HZ |
1071 | unsigned char inf = 0, mask = 0; |
1072 | ||
1073 | mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; | |
1074 | ||
703ac1f2 MB |
1075 | /* set audio interface clocking */ |
1076 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { | |
1077 | case SND_SOC_DAIFMT_CBP_CFP: | |
f213f4b5 HZ |
1078 | if (pm860x->dir == PM860X_CLK_DIR_OUT) |
1079 | inf |= PCM_INF2_MASTER; | |
1080 | else | |
1081 | return -EINVAL; | |
1082 | break; | |
703ac1f2 | 1083 | case SND_SOC_DAIFMT_CBC_CFC: |
f213f4b5 HZ |
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; | |
5783994b | 1101 | snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, mask, inf); |
f213f4b5 HZ |
1102 | return 0; |
1103 | } | |
1104 | ||
5783994b | 1105 | static int pm860x_set_bias_level(struct snd_soc_component *component, |
f213f4b5 HZ |
1106 | enum snd_soc_bias_level level) |
1107 | { | |
5783994b | 1108 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); |
f213f4b5 HZ |
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: | |
5783994b | 1119 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { |
f213f4b5 | 1120 | /* Enable Audio PLL & Audio section */ |
548aae8c | 1121 | data = AUDIO_PLL | AUDIO_SECTION_ON; |
fa129ebe | 1122 | pm860x_reg_write(pm860x->i2c, REG_MISC2, data); |
548aae8c | 1123 | udelay(300); |
f213f4b5 HZ |
1124 | data = AUDIO_PLL | AUDIO_SECTION_RESET |
1125 | | AUDIO_SECTION_ON; | |
fa129ebe | 1126 | pm860x_reg_write(pm860x->i2c, REG_MISC2, data); |
f213f4b5 HZ |
1127 | } |
1128 | break; | |
1129 | ||
1130 | case SND_SOC_BIAS_OFF: | |
1131 | data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; | |
fa129ebe | 1132 | pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0); |
f213f4b5 HZ |
1133 | break; |
1134 | } | |
f213f4b5 HZ |
1135 | return 0; |
1136 | } | |
1137 | ||
85e7652d | 1138 | static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = { |
54b59270 | 1139 | .mute_stream = pm860x_mute_stream, |
f213f4b5 HZ |
1140 | .hw_params = pm860x_pcm_hw_params, |
1141 | .set_fmt = pm860x_pcm_set_dai_fmt, | |
1142 | .set_sysclk = pm860x_set_dai_sysclk, | |
54b59270 | 1143 | .no_capture_mute = 1, |
f213f4b5 HZ |
1144 | }; |
1145 | ||
85e7652d | 1146 | static const struct snd_soc_dai_ops pm860x_i2s_dai_ops = { |
54b59270 | 1147 | .mute_stream = pm860x_mute_stream, |
f213f4b5 HZ |
1148 | .hw_params = pm860x_i2s_hw_params, |
1149 | .set_fmt = pm860x_i2s_set_dai_fmt, | |
1150 | .set_sysclk = pm860x_set_dai_sysclk, | |
54b59270 | 1151 | .no_capture_mute = 1, |
f213f4b5 HZ |
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, | |
e712bfca MS |
1167 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ |
1168 | SNDRV_PCM_FMTBIT_S18_3LE, | |
f213f4b5 HZ |
1169 | }, |
1170 | .capture = { | |
1171 | .stream_name = "PCM Capture", | |
1172 | .channels_min = 2, | |
1173 | .channels_max = 2, | |
1174 | .rates = PM860X_RATES, | |
e712bfca MS |
1175 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ |
1176 | SNDRV_PCM_FMTBIT_S18_3LE, | |
f213f4b5 HZ |
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, | |
e712bfca MS |
1188 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ |
1189 | SNDRV_PCM_FMTBIT_S18_3LE, | |
f213f4b5 HZ |
1190 | }, |
1191 | .capture = { | |
1192 | .stream_name = "I2S Capture", | |
1193 | .channels_min = 2, | |
1194 | .channels_max = 2, | |
1195 | .rates = SNDRV_PCM_RATE_8000_48000, | |
e712bfca MS |
1196 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ |
1197 | SNDRV_PCM_FMTBIT_S18_3LE, | |
f213f4b5 HZ |
1198 | }, |
1199 | .ops = &pm860x_i2s_dai_ops, | |
1200 | }, | |
1201 | }; | |
1202 | ||
5783994b | 1203 | static irqreturn_t pm860x_component_handler(int irq, void *data) |
f213f4b5 HZ |
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 | ||
7116f452 | 1214 | #ifndef CONFIG_SND_SOC_88PM860X_MODULE |
1c9e9795 MB |
1215 | if (status & (HEADSET_STATUS | MIC_STATUS | SHORT_HS1 | SHORT_HS2 | |
1216 | SHORT_LO1 | SHORT_LO2)) | |
5783994b | 1217 | trace_snd_soc_jack_irq(dev_name(pm860x->component->dev)); |
1435b940 | 1218 | #endif |
1c9e9795 | 1219 | |
f213f4b5 HZ |
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(pm860x->det.hp_jack, report, mask); | |
1239 | if (mic_report) | |
1240 | snd_soc_jack_report(pm860x->det.mic_jack, SND_JACK_MICROPHONE, | |
1241 | SND_JACK_MICROPHONE); | |
1242 | ||
5783994b | 1243 | dev_dbg(pm860x->component->dev, "headphone report:0x%x, mask:%x\n", |
f213f4b5 | 1244 | report, mask); |
5783994b | 1245 | dev_dbg(pm860x->component->dev, "microphone report:0x%x\n", mic_report); |
f213f4b5 HZ |
1246 | return IRQ_HANDLED; |
1247 | } | |
1248 | ||
5783994b | 1249 | int pm860x_hs_jack_detect(struct snd_soc_component *component, |
f213f4b5 HZ |
1250 | struct snd_soc_jack *jack, |
1251 | int det, int hook, int hs_shrt, int lo_shrt) | |
1252 | { | |
5783994b | 1253 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); |
f213f4b5 HZ |
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) | |
fa129ebe | 1263 | pm860x_set_bits(pm860x->i2c, REG_HS_DET, |
f213f4b5 HZ |
1264 | EN_HS_DET, EN_HS_DET); |
1265 | /* headset short detect */ | |
1266 | if (hs_shrt) { | |
1267 | data = CLR_SHORT_HS2 | CLR_SHORT_HS1; | |
fa129ebe | 1268 | pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data); |
f213f4b5 HZ |
1269 | } |
1270 | /* Lineout short detect */ | |
1271 | if (lo_shrt) { | |
1272 | data = CLR_SHORT_LO2 | CLR_SHORT_LO1; | |
fa129ebe | 1273 | pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data); |
f213f4b5 HZ |
1274 | } |
1275 | ||
1276 | /* sync status */ | |
5783994b | 1277 | pm860x_component_handler(0, pm860x); |
f213f4b5 HZ |
1278 | return 0; |
1279 | } | |
1280 | EXPORT_SYMBOL_GPL(pm860x_hs_jack_detect); | |
1281 | ||
5783994b | 1282 | int pm860x_mic_jack_detect(struct snd_soc_component *component, |
f213f4b5 HZ |
1283 | struct snd_soc_jack *jack, int det) |
1284 | { | |
5783994b | 1285 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); |
f213f4b5 HZ |
1286 | |
1287 | pm860x->det.mic_jack = jack; | |
1288 | pm860x->det.mic_det = det; | |
1289 | ||
1290 | if (det & SND_JACK_MICROPHONE) | |
fa129ebe | 1291 | pm860x_set_bits(pm860x->i2c, REG_MIC_DET, |
f213f4b5 HZ |
1292 | MICDET_MASK, MICDET_MASK); |
1293 | ||
1294 | /* sync status */ | |
5783994b | 1295 | pm860x_component_handler(0, pm860x); |
f213f4b5 HZ |
1296 | return 0; |
1297 | } | |
1298 | EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect); | |
1299 | ||
5783994b | 1300 | static int pm860x_probe(struct snd_soc_component *component) |
f213f4b5 | 1301 | { |
5783994b | 1302 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); |
f213f4b5 HZ |
1303 | int i, ret; |
1304 | ||
5783994b KM |
1305 | pm860x->component = component; |
1306 | snd_soc_component_init_regmap(component, pm860x->regmap); | |
f213f4b5 | 1307 | |
f213f4b5 HZ |
1308 | for (i = 0; i < 4; i++) { |
1309 | ret = request_threaded_irq(pm860x->irq[i], NULL, | |
5783994b | 1310 | pm860x_component_handler, IRQF_ONESHOT, |
f213f4b5 HZ |
1311 | pm860x->name[i], pm860x); |
1312 | if (ret < 0) { | |
5783994b | 1313 | dev_err(component->dev, "Failed to request IRQ!\n"); |
55110276 | 1314 | goto out; |
f213f4b5 HZ |
1315 | } |
1316 | } | |
1317 | ||
f213f4b5 HZ |
1318 | return 0; |
1319 | ||
55110276 AL |
1320 | out: |
1321 | while (--i >= 0) | |
f213f4b5 | 1322 | free_irq(pm860x->irq[i], pm860x); |
55110276 | 1323 | return ret; |
f213f4b5 HZ |
1324 | } |
1325 | ||
5783994b | 1326 | static void pm860x_remove(struct snd_soc_component *component) |
f213f4b5 | 1327 | { |
5783994b | 1328 | struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); |
f213f4b5 HZ |
1329 | int i; |
1330 | ||
1331 | for (i = 3; i >= 0; i--) | |
1332 | free_irq(pm860x->irq[i], pm860x); | |
f213f4b5 HZ |
1333 | } |
1334 | ||
5783994b KM |
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, | |
f213f4b5 HZ |
1348 | }; |
1349 | ||
7a79e94e | 1350 | static int pm860x_codec_probe(struct platform_device *pdev) |
f213f4b5 HZ |
1351 | { |
1352 | struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); | |
1353 | struct pm860x_priv *pm860x; | |
1354 | struct resource *res; | |
1355 | int i, ret; | |
1356 | ||
6ab7e71a AL |
1357 | pm860x = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_priv), |
1358 | GFP_KERNEL); | |
f213f4b5 HZ |
1359 | if (pm860x == NULL) |
1360 | return -ENOMEM; | |
1361 | ||
1362 | pm860x->chip = chip; | |
1363 | pm860x->i2c = (chip->id == CHIP_PM8607) ? chip->client | |
1364 | : chip->companion; | |
f9ded3b2 MB |
1365 | pm860x->regmap = (chip->id == CHIP_PM8607) ? chip->regmap |
1366 | : chip->regmap_companion; | |
f213f4b5 HZ |
1367 | platform_set_drvdata(pdev, 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"); | |
b1d9e66c | 1373 | return -EINVAL; |
f213f4b5 HZ |
1374 | } |
1375 | pm860x->irq[i] = res->start + chip->irq_base; | |
a9a65b87 | 1376 | strscpy(pm860x->name[i], res->name, MAX_NAME_LEN); |
f213f4b5 HZ |
1377 | } |
1378 | ||
5783994b KM |
1379 | ret = devm_snd_soc_register_component(&pdev->dev, |
1380 | &soc_component_dev_pm860x, | |
f213f4b5 HZ |
1381 | pm860x_dai, ARRAY_SIZE(pm860x_dai)); |
1382 | if (ret) { | |
5783994b | 1383 | dev_err(&pdev->dev, "Failed to register component\n"); |
b1d9e66c | 1384 | return -EINVAL; |
f213f4b5 HZ |
1385 | } |
1386 | return ret; | |
f213f4b5 HZ |
1387 | } |
1388 | ||
f213f4b5 HZ |
1389 | static struct platform_driver pm860x_codec_driver = { |
1390 | .driver = { | |
1391 | .name = "88pm860x-codec", | |
f213f4b5 HZ |
1392 | }, |
1393 | .probe = pm860x_codec_probe, | |
f213f4b5 HZ |
1394 | }; |
1395 | ||
5bbcc3c0 | 1396 | module_platform_driver(pm860x_codec_driver); |
f213f4b5 HZ |
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 |