diff options
author | Ondrej Zary <linux@rainbow-software.org> | 2012-10-14 15:09:20 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-10-17 03:01:28 -0400 |
commit | 45d44e5a945296693b392ea807768329c1a80af6 (patch) | |
tree | 8db17abe0e1265323c0228dbce8592797dcde462 | |
parent | 267bccaf0409e2a4e17591bb31e8d1ceb866988c (diff) |
ALSA: ice1712: Add Wolfson Microelectronics WM8766 codec support
Needed by Philips PSC724 subdriver. The code does not contain any
card-specific bits so other ice17xx cards using this codec could be
converted to use this generic code.
Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/pci/ice1712/wm8766.c | 361 | ||||
-rw-r--r-- | sound/pci/ice1712/wm8766.h | 163 |
2 files changed, 524 insertions, 0 deletions
diff --git a/sound/pci/ice1712/wm8766.c b/sound/pci/ice1712/wm8766.c new file mode 100644 index 000000000000..8072adeecf68 --- /dev/null +++ b/sound/pci/ice1712/wm8766.c | |||
@@ -0,0 +1,361 @@ | |||
1 | /* | ||
2 | * ALSA driver for ICEnsemble VT17xx | ||
3 | * | ||
4 | * Lowlevel functions for WM8766 codec | ||
5 | * | ||
6 | * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/delay.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/control.h> | ||
27 | #include <sound/tlv.h> | ||
28 | #include "wm8766.h" | ||
29 | |||
30 | /* low-level access */ | ||
31 | |||
32 | static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data) | ||
33 | { | ||
34 | if (addr < WM8766_REG_RESET) | ||
35 | wm->regs[addr] = data; | ||
36 | wm->ops.write(wm, addr, data); | ||
37 | } | ||
38 | |||
39 | /* mixer controls */ | ||
40 | |||
41 | static const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1); | ||
42 | |||
43 | static struct snd_wm8766_ctl snd_wm8766_default_ctl[WM8766_CTL_COUNT] = { | ||
44 | [WM8766_CTL_CH1_VOL] = { | ||
45 | .name = "Channel 1 Playback Volume", | ||
46 | .type = SNDRV_CTL_ELEM_TYPE_INTEGER, | ||
47 | .tlv = wm8766_tlv, | ||
48 | .reg1 = WM8766_REG_DACL1, | ||
49 | .reg2 = WM8766_REG_DACR1, | ||
50 | .mask1 = WM8766_VOL_MASK, | ||
51 | .mask2 = WM8766_VOL_MASK, | ||
52 | .max = 0xff, | ||
53 | .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE, | ||
54 | }, | ||
55 | [WM8766_CTL_CH2_VOL] = { | ||
56 | .name = "Channel 2 Playback Volume", | ||
57 | .type = SNDRV_CTL_ELEM_TYPE_INTEGER, | ||
58 | .tlv = wm8766_tlv, | ||
59 | .reg1 = WM8766_REG_DACL2, | ||
60 | .reg2 = WM8766_REG_DACR2, | ||
61 | .mask1 = WM8766_VOL_MASK, | ||
62 | .mask2 = WM8766_VOL_MASK, | ||
63 | .max = 0xff, | ||
64 | .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE, | ||
65 | }, | ||
66 | [WM8766_CTL_CH3_VOL] = { | ||
67 | .name = "Channel 3 Playback Volume", | ||
68 | .type = SNDRV_CTL_ELEM_TYPE_INTEGER, | ||
69 | .tlv = wm8766_tlv, | ||
70 | .reg1 = WM8766_REG_DACL3, | ||
71 | .reg2 = WM8766_REG_DACR3, | ||
72 | .mask1 = WM8766_VOL_MASK, | ||
73 | .mask2 = WM8766_VOL_MASK, | ||
74 | .max = 0xff, | ||
75 | .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE, | ||
76 | }, | ||
77 | [WM8766_CTL_CH1_SW] = { | ||
78 | .name = "Channel 1 Playback Switch", | ||
79 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
80 | .reg1 = WM8766_REG_DACCTRL2, | ||
81 | .mask1 = WM8766_DAC2_MUTE1, | ||
82 | .flags = WM8766_FLAG_INVERT, | ||
83 | }, | ||
84 | [WM8766_CTL_CH2_SW] = { | ||
85 | .name = "Channel 2 Playback Switch", | ||
86 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
87 | .reg1 = WM8766_REG_DACCTRL2, | ||
88 | .mask1 = WM8766_DAC2_MUTE2, | ||
89 | .flags = WM8766_FLAG_INVERT, | ||
90 | }, | ||
91 | [WM8766_CTL_CH3_SW] = { | ||
92 | .name = "Channel 3 Playback Switch", | ||
93 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
94 | .reg1 = WM8766_REG_DACCTRL2, | ||
95 | .mask1 = WM8766_DAC2_MUTE3, | ||
96 | .flags = WM8766_FLAG_INVERT, | ||
97 | }, | ||
98 | [WM8766_CTL_PHASE1_SW] = { | ||
99 | .name = "Channel 1 Phase Invert Playback Switch", | ||
100 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
101 | .reg1 = WM8766_REG_IFCTRL, | ||
102 | .mask1 = WM8766_PHASE_INVERT1, | ||
103 | }, | ||
104 | [WM8766_CTL_PHASE2_SW] = { | ||
105 | .name = "Channel 2 Phase Invert Playback Switch", | ||
106 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
107 | .reg1 = WM8766_REG_IFCTRL, | ||
108 | .mask1 = WM8766_PHASE_INVERT2, | ||
109 | }, | ||
110 | [WM8766_CTL_PHASE3_SW] = { | ||
111 | .name = "Channel 3 Phase Invert Playback Switch", | ||
112 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
113 | .reg1 = WM8766_REG_IFCTRL, | ||
114 | .mask1 = WM8766_PHASE_INVERT3, | ||
115 | }, | ||
116 | [WM8766_CTL_DEEMPH1_SW] = { | ||
117 | .name = "Channel 1 Deemphasis Playback Switch", | ||
118 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
119 | .reg1 = WM8766_REG_DACCTRL2, | ||
120 | .mask1 = WM8766_DAC2_DEEMP1, | ||
121 | }, | ||
122 | [WM8766_CTL_DEEMPH2_SW] = { | ||
123 | .name = "Channel 2 Deemphasis Playback Switch", | ||
124 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
125 | .reg1 = WM8766_REG_DACCTRL2, | ||
126 | .mask1 = WM8766_DAC2_DEEMP2, | ||
127 | }, | ||
128 | [WM8766_CTL_DEEMPH3_SW] = { | ||
129 | .name = "Channel 3 Deemphasis Playback Switch", | ||
130 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
131 | .reg1 = WM8766_REG_DACCTRL2, | ||
132 | .mask1 = WM8766_DAC2_DEEMP3, | ||
133 | }, | ||
134 | [WM8766_CTL_IZD_SW] = { | ||
135 | .name = "Infinite Zero Detect Playback Switch", | ||
136 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
137 | .reg1 = WM8766_REG_DACCTRL1, | ||
138 | .mask1 = WM8766_DAC_IZD, | ||
139 | }, | ||
140 | [WM8766_CTL_ZC_SW] = { | ||
141 | .name = "Zero Cross Detect Playback Switch", | ||
142 | .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, | ||
143 | .reg1 = WM8766_REG_DACCTRL2, | ||
144 | .mask1 = WM8766_DAC2_ZCD, | ||
145 | .flags = WM8766_FLAG_INVERT, | ||
146 | }, | ||
147 | }; | ||
148 | |||
149 | /* exported functions */ | ||
150 | |||
151 | void snd_wm8766_init(struct snd_wm8766 *wm) | ||
152 | { | ||
153 | int i; | ||
154 | static const u16 default_values[] = { | ||
155 | 0x000, 0x100, | ||
156 | 0x120, 0x000, | ||
157 | 0x000, 0x100, 0x000, 0x100, 0x000, | ||
158 | 0x000, 0x080, | ||
159 | }; | ||
160 | |||
161 | memcpy(wm->ctl, snd_wm8766_default_ctl, sizeof(wm->ctl)); | ||
162 | |||
163 | snd_wm8766_write(wm, WM8766_REG_RESET, 0x00); /* reset */ | ||
164 | udelay(10); | ||
165 | /* load defaults */ | ||
166 | for (i = 0; i < ARRAY_SIZE(default_values); i++) | ||
167 | snd_wm8766_write(wm, i, default_values[i]); | ||
168 | } | ||
169 | |||
170 | void snd_wm8766_resume(struct snd_wm8766 *wm) | ||
171 | { | ||
172 | int i; | ||
173 | |||
174 | for (i = 0; i < WM8766_REG_COUNT; i++) | ||
175 | snd_wm8766_write(wm, i, wm->regs[i]); | ||
176 | } | ||
177 | |||
178 | void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac) | ||
179 | { | ||
180 | u16 val = wm->regs[WM8766_REG_IFCTRL] & ~WM8766_IF_MASK; | ||
181 | |||
182 | dac &= WM8766_IF_MASK; | ||
183 | snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac); | ||
184 | } | ||
185 | |||
186 | void snd_wm8766_set_master_mode(struct snd_wm8766 *wm, u16 mode) | ||
187 | { | ||
188 | u16 val = wm->regs[WM8766_REG_DACCTRL3] & ~WM8766_DAC3_MSTR_MASK; | ||
189 | |||
190 | mode &= WM8766_DAC3_MSTR_MASK; | ||
191 | snd_wm8766_write(wm, WM8766_REG_DACCTRL3, val | mode); | ||
192 | } | ||
193 | |||
194 | void snd_wm8766_set_power(struct snd_wm8766 *wm, u16 power) | ||
195 | { | ||
196 | u16 val = wm->regs[WM8766_REG_DACCTRL3] & ~WM8766_DAC3_POWER_MASK; | ||
197 | |||
198 | power &= WM8766_DAC3_POWER_MASK; | ||
199 | snd_wm8766_write(wm, WM8766_REG_DACCTRL3, val | power); | ||
200 | } | ||
201 | |||
202 | void snd_wm8766_volume_restore(struct snd_wm8766 *wm) | ||
203 | { | ||
204 | u16 val = wm->regs[WM8766_REG_DACR1]; | ||
205 | /* restore volume after MCLK stopped */ | ||
206 | snd_wm8766_write(wm, WM8766_REG_DACR1, val | WM8766_VOL_UPDATE); | ||
207 | } | ||
208 | |||
209 | /* mixer callbacks */ | ||
210 | |||
211 | static int snd_wm8766_volume_info(struct snd_kcontrol *kcontrol, | ||
212 | struct snd_ctl_elem_info *uinfo) | ||
213 | { | ||
214 | struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol); | ||
215 | int n = kcontrol->private_value; | ||
216 | |||
217 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
218 | uinfo->count = (wm->ctl[n].flags & WM8766_FLAG_STEREO) ? 2 : 1; | ||
219 | uinfo->value.integer.min = wm->ctl[n].min; | ||
220 | uinfo->value.integer.max = wm->ctl[n].max; | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int snd_wm8766_enum_info(struct snd_kcontrol *kcontrol, | ||
226 | struct snd_ctl_elem_info *uinfo) | ||
227 | { | ||
228 | struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol); | ||
229 | int n = kcontrol->private_value; | ||
230 | |||
231 | return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max, | ||
232 | wm->ctl[n].enum_names); | ||
233 | } | ||
234 | |||
235 | static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol, | ||
236 | struct snd_ctl_elem_value *ucontrol) | ||
237 | { | ||
238 | struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol); | ||
239 | int n = kcontrol->private_value; | ||
240 | u16 val1, val2; | ||
241 | |||
242 | if (wm->ctl[n].get) | ||
243 | wm->ctl[n].get(wm, &val1, &val2); | ||
244 | else { | ||
245 | val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1; | ||
246 | val1 >>= __ffs(wm->ctl[n].mask1); | ||
247 | if (wm->ctl[n].flags & WM8766_FLAG_STEREO) { | ||
248 | val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2; | ||
249 | val2 >>= __ffs(wm->ctl[n].mask2); | ||
250 | if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE) | ||
251 | val2 &= ~WM8766_VOL_UPDATE; | ||
252 | } | ||
253 | } | ||
254 | if (wm->ctl[n].flags & WM8766_FLAG_INVERT) { | ||
255 | val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min); | ||
256 | val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); | ||
257 | } | ||
258 | ucontrol->value.integer.value[0] = val1; | ||
259 | if (wm->ctl[n].flags & WM8766_FLAG_STEREO) | ||
260 | ucontrol->value.integer.value[1] = val2; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static int snd_wm8766_ctl_put(struct snd_kcontrol *kcontrol, | ||
266 | struct snd_ctl_elem_value *ucontrol) | ||
267 | { | ||
268 | struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol); | ||
269 | int n = kcontrol->private_value; | ||
270 | u16 val, regval1, regval2; | ||
271 | |||
272 | /* this also works for enum because value is an union */ | ||
273 | regval1 = ucontrol->value.integer.value[0]; | ||
274 | regval2 = ucontrol->value.integer.value[1]; | ||
275 | if (wm->ctl[n].flags & WM8766_FLAG_INVERT) { | ||
276 | regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min); | ||
277 | regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min); | ||
278 | } | ||
279 | if (wm->ctl[n].set) | ||
280 | wm->ctl[n].set(wm, regval1, regval2); | ||
281 | else { | ||
282 | val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1; | ||
283 | val |= regval1 << __ffs(wm->ctl[n].mask1); | ||
284 | /* both stereo controls in one register */ | ||
285 | if (wm->ctl[n].flags & WM8766_FLAG_STEREO && | ||
286 | wm->ctl[n].reg1 == wm->ctl[n].reg2) { | ||
287 | val &= ~wm->ctl[n].mask2; | ||
288 | val |= regval2 << __ffs(wm->ctl[n].mask2); | ||
289 | } | ||
290 | snd_wm8766_write(wm, wm->ctl[n].reg1, val); | ||
291 | /* stereo controls in different registers */ | ||
292 | if (wm->ctl[n].flags & WM8766_FLAG_STEREO && | ||
293 | wm->ctl[n].reg1 != wm->ctl[n].reg2) { | ||
294 | val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2; | ||
295 | val |= regval2 << __ffs(wm->ctl[n].mask2); | ||
296 | if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE) | ||
297 | val |= WM8766_VOL_UPDATE; | ||
298 | snd_wm8766_write(wm, wm->ctl[n].reg2, val); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static int snd_wm8766_add_control(struct snd_wm8766 *wm, int num) | ||
306 | { | ||
307 | struct snd_kcontrol_new cont; | ||
308 | struct snd_kcontrol *ctl; | ||
309 | |||
310 | memset(&cont, 0, sizeof(cont)); | ||
311 | cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
312 | cont.private_value = num; | ||
313 | cont.name = wm->ctl[num].name; | ||
314 | cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
315 | if (wm->ctl[num].flags & WM8766_FLAG_LIM || | ||
316 | wm->ctl[num].flags & WM8766_FLAG_ALC) | ||
317 | cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | ||
318 | cont.tlv.p = NULL; | ||
319 | cont.get = snd_wm8766_ctl_get; | ||
320 | cont.put = snd_wm8766_ctl_put; | ||
321 | |||
322 | switch (wm->ctl[num].type) { | ||
323 | case SNDRV_CTL_ELEM_TYPE_INTEGER: | ||
324 | cont.info = snd_wm8766_volume_info; | ||
325 | cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; | ||
326 | cont.tlv.p = wm->ctl[num].tlv; | ||
327 | break; | ||
328 | case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | ||
329 | wm->ctl[num].max = 1; | ||
330 | if (wm->ctl[num].flags & WM8766_FLAG_STEREO) | ||
331 | cont.info = snd_ctl_boolean_stereo_info; | ||
332 | else | ||
333 | cont.info = snd_ctl_boolean_mono_info; | ||
334 | break; | ||
335 | case SNDRV_CTL_ELEM_TYPE_ENUMERATED: | ||
336 | cont.info = snd_wm8766_enum_info; | ||
337 | break; | ||
338 | default: | ||
339 | return -EINVAL; | ||
340 | } | ||
341 | ctl = snd_ctl_new1(&cont, wm); | ||
342 | if (!ctl) | ||
343 | return -ENOMEM; | ||
344 | wm->ctl[num].kctl = ctl; | ||
345 | |||
346 | return snd_ctl_add(wm->card, ctl); | ||
347 | } | ||
348 | |||
349 | int snd_wm8766_build_controls(struct snd_wm8766 *wm) | ||
350 | { | ||
351 | int err, i; | ||
352 | |||
353 | for (i = 0; i < WM8766_CTL_COUNT; i++) | ||
354 | if (wm->ctl[i].name) { | ||
355 | err = snd_wm8766_add_control(wm, i); | ||
356 | if (err < 0) | ||
357 | return err; | ||
358 | } | ||
359 | |||
360 | return 0; | ||
361 | } | ||
diff --git a/sound/pci/ice1712/wm8766.h b/sound/pci/ice1712/wm8766.h new file mode 100644 index 000000000000..dfa3bbfbc9a5 --- /dev/null +++ b/sound/pci/ice1712/wm8766.h | |||
@@ -0,0 +1,163 @@ | |||
1 | #ifndef __SOUND_WM8766_H | ||
2 | #define __SOUND_WM8766_H | ||
3 | |||
4 | /* | ||
5 | * ALSA driver for ICEnsemble VT17xx | ||
6 | * | ||
7 | * Lowlevel functions for WM8766 codec | ||
8 | * | ||
9 | * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #define WM8766_REG_DACL1 0x00 | ||
28 | #define WM8766_REG_DACR1 0x01 | ||
29 | #define WM8766_VOL_MASK 0x1ff /* incl. update bit */ | ||
30 | #define WM8766_VOL_UPDATE (1 << 8) /* update volume */ | ||
31 | #define WM8766_REG_DACCTRL1 0x02 | ||
32 | #define WM8766_DAC_MUTEALL (1 << 0) | ||
33 | #define WM8766_DAC_DEEMPALL (1 << 1) | ||
34 | #define WM8766_DAC_PDWN (1 << 2) | ||
35 | #define WM8766_DAC_ATC (1 << 3) | ||
36 | #define WM8766_DAC_IZD (1 << 4) | ||
37 | #define WM8766_DAC_PL_MASK 0x1e0 | ||
38 | #define WM8766_DAC_PL_LL (1 << 5) /* L chan: L signal */ | ||
39 | #define WM8766_DAC_PL_LR (2 << 5) /* L chan: R signal */ | ||
40 | #define WM8766_DAC_PL_LB (3 << 5) /* L chan: both */ | ||
41 | #define WM8766_DAC_PL_RL (1 << 7) /* R chan: L signal */ | ||
42 | #define WM8766_DAC_PL_RR (2 << 7) /* R chan: R signal */ | ||
43 | #define WM8766_DAC_PL_RB (3 << 7) /* R chan: both */ | ||
44 | #define WM8766_REG_IFCTRL 0x03 | ||
45 | #define WM8766_IF_FMT_RIGHTJ (0 << 0) | ||
46 | #define WM8766_IF_FMT_LEFTJ (1 << 0) | ||
47 | #define WM8766_IF_FMT_I2S (2 << 0) | ||
48 | #define WM8766_IF_FMT_DSP (3 << 0) | ||
49 | #define WM8766_IF_DSP_LATE (1 << 2) /* in DSP mode */ | ||
50 | #define WM8766_IF_LRC_INVERTED (1 << 2) /* in other modes */ | ||
51 | #define WM8766_IF_BCLK_INVERTED (1 << 3) | ||
52 | #define WM8766_IF_IWL_16BIT (0 << 4) | ||
53 | #define WM8766_IF_IWL_20BIT (1 << 4) | ||
54 | #define WM8766_IF_IWL_24BIT (2 << 4) | ||
55 | #define WM8766_IF_IWL_32BIT (3 << 4) | ||
56 | #define WM8766_IF_MASK 0x3f | ||
57 | #define WM8766_PHASE_INVERT1 (1 << 6) | ||
58 | #define WM8766_PHASE_INVERT2 (1 << 7) | ||
59 | #define WM8766_PHASE_INVERT3 (1 << 8) | ||
60 | #define WM8766_REG_DACL2 0x04 | ||
61 | #define WM8766_REG_DACR2 0x05 | ||
62 | #define WM8766_REG_DACL3 0x06 | ||
63 | #define WM8766_REG_DACR3 0x07 | ||
64 | #define WM8766_REG_MASTDA 0x08 | ||
65 | #define WM8766_REG_DACCTRL2 0x09 | ||
66 | #define WM8766_DAC2_ZCD (1 << 0) | ||
67 | #define WM8766_DAC2_ZFLAG_ALL (0 << 1) | ||
68 | #define WM8766_DAC2_ZFLAG_1 (1 << 1) | ||
69 | #define WM8766_DAC2_ZFLAG_2 (2 << 1) | ||
70 | #define WM8766_DAC2_ZFLAG_3 (3 << 1) | ||
71 | #define WM8766_DAC2_MUTE1 (1 << 3) | ||
72 | #define WM8766_DAC2_MUTE2 (1 << 4) | ||
73 | #define WM8766_DAC2_MUTE3 (1 << 5) | ||
74 | #define WM8766_DAC2_DEEMP1 (1 << 6) | ||
75 | #define WM8766_DAC2_DEEMP2 (1 << 7) | ||
76 | #define WM8766_DAC2_DEEMP3 (1 << 8) | ||
77 | #define WM8766_REG_DACCTRL3 0x0a | ||
78 | #define WM8766_DAC3_DACPD1 (1 << 1) | ||
79 | #define WM8766_DAC3_DACPD2 (1 << 2) | ||
80 | #define WM8766_DAC3_DACPD3 (1 << 3) | ||
81 | #define WM8766_DAC3_PWRDNALL (1 << 4) | ||
82 | #define WM8766_DAC3_POWER_MASK 0x1e | ||
83 | #define WM8766_DAC3_MASTER (1 << 5) | ||
84 | #define WM8766_DAC3_DAC128FS (0 << 6) | ||
85 | #define WM8766_DAC3_DAC192FS (1 << 6) | ||
86 | #define WM8766_DAC3_DAC256FS (2 << 6) | ||
87 | #define WM8766_DAC3_DAC384FS (3 << 6) | ||
88 | #define WM8766_DAC3_DAC512FS (4 << 6) | ||
89 | #define WM8766_DAC3_DAC768FS (5 << 6) | ||
90 | #define WM8766_DAC3_MSTR_MASK 0x1e0 | ||
91 | #define WM8766_REG_MUTE1 0x0c | ||
92 | #define WM8766_MUTE1_MPD (1 << 6) | ||
93 | #define WM8766_REG_MUTE2 0x0f | ||
94 | #define WM8766_MUTE2_MPD (1 << 5) | ||
95 | #define WM8766_REG_RESET 0x1f | ||
96 | |||
97 | #define WM8766_REG_COUNT 0x10 /* don't cache the RESET register */ | ||
98 | |||
99 | struct snd_wm8766; | ||
100 | |||
101 | struct snd_wm8766_ops { | ||
102 | void (*write)(struct snd_wm8766 *wm, u16 addr, u16 data); | ||
103 | }; | ||
104 | |||
105 | enum snd_wm8766_ctl_id { | ||
106 | WM8766_CTL_CH1_VOL, | ||
107 | WM8766_CTL_CH2_VOL, | ||
108 | WM8766_CTL_CH3_VOL, | ||
109 | WM8766_CTL_CH1_SW, | ||
110 | WM8766_CTL_CH2_SW, | ||
111 | WM8766_CTL_CH3_SW, | ||
112 | WM8766_CTL_PHASE1_SW, | ||
113 | WM8766_CTL_PHASE2_SW, | ||
114 | WM8766_CTL_PHASE3_SW, | ||
115 | WM8766_CTL_DEEMPH1_SW, | ||
116 | WM8766_CTL_DEEMPH2_SW, | ||
117 | WM8766_CTL_DEEMPH3_SW, | ||
118 | WM8766_CTL_IZD_SW, | ||
119 | WM8766_CTL_ZC_SW, | ||
120 | |||
121 | WM8766_CTL_COUNT, | ||
122 | }; | ||
123 | |||
124 | #define WM8766_ENUM_MAX 16 | ||
125 | |||
126 | #define WM8766_FLAG_STEREO (1 << 0) | ||
127 | #define WM8766_FLAG_VOL_UPDATE (1 << 1) | ||
128 | #define WM8766_FLAG_INVERT (1 << 2) | ||
129 | #define WM8766_FLAG_LIM (1 << 3) | ||
130 | #define WM8766_FLAG_ALC (1 << 4) | ||
131 | |||
132 | struct snd_wm8766_ctl { | ||
133 | struct snd_kcontrol *kctl; | ||
134 | char *name; | ||
135 | snd_ctl_elem_type_t type; | ||
136 | const char *const enum_names[WM8766_ENUM_MAX]; | ||
137 | const unsigned int *tlv; | ||
138 | u16 reg1, reg2, mask1, mask2, min, max, flags; | ||
139 | void (*set)(struct snd_wm8766 *wm, u16 ch1, u16 ch2); | ||
140 | void (*get)(struct snd_wm8766 *wm, u16 *ch1, u16 *ch2); | ||
141 | }; | ||
142 | |||
143 | enum snd_wm8766_agc_mode { WM8766_AGC_OFF, WM8766_AGC_LIM, WM8766_AGC_ALC }; | ||
144 | |||
145 | struct snd_wm8766 { | ||
146 | struct snd_card *card; | ||
147 | struct snd_wm8766_ctl ctl[WM8766_CTL_COUNT]; | ||
148 | enum snd_wm8766_agc_mode agc_mode; | ||
149 | struct snd_wm8766_ops ops; | ||
150 | u16 regs[WM8766_REG_COUNT]; /* 9-bit registers */ | ||
151 | }; | ||
152 | |||
153 | |||
154 | |||
155 | void snd_wm8766_init(struct snd_wm8766 *wm); | ||
156 | void snd_wm8766_resume(struct snd_wm8766 *wm); | ||
157 | void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac); | ||
158 | void snd_wm8766_set_master_mode(struct snd_wm8766 *wm, u16 mode); | ||
159 | void snd_wm8766_set_power(struct snd_wm8766 *wm, u16 power); | ||
160 | void snd_wm8766_volume_restore(struct snd_wm8766 *wm); | ||
161 | int snd_wm8766_build_controls(struct snd_wm8766 *wm); | ||
162 | |||
163 | #endif /* __SOUND_WM8766_H */ | ||