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