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