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