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