Root/
1 | /* |
2 | * PMac Burgundy lowlevel functions |
3 | * |
4 | * Copyright (c) by Takashi Iwai <tiwai@suse.de> |
5 | * code based on dmasound.c. |
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 as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | * |
12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ |
21 | |
22 | #include <asm/io.h> |
23 | #include <linux/init.h> |
24 | #include <linux/delay.h> |
25 | #include <sound/core.h> |
26 | #include "pmac.h" |
27 | #include "burgundy.h" |
28 | |
29 | |
30 | /* Waits for busy flag to clear */ |
31 | static inline void |
32 | snd_pmac_burgundy_busy_wait(struct snd_pmac *chip) |
33 | { |
34 | int timeout = 50; |
35 | while ((in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) && timeout--) |
36 | udelay(1); |
37 | if (timeout < 0) |
38 | printk(KERN_DEBUG "burgundy_busy_wait: timeout\n"); |
39 | } |
40 | |
41 | static inline void |
42 | snd_pmac_burgundy_extend_wait(struct snd_pmac *chip) |
43 | { |
44 | int timeout; |
45 | timeout = 50; |
46 | while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--) |
47 | udelay(1); |
48 | if (timeout < 0) |
49 | printk(KERN_DEBUG "burgundy_extend_wait: timeout #1\n"); |
50 | timeout = 50; |
51 | while ((in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--) |
52 | udelay(1); |
53 | if (timeout < 0) |
54 | printk(KERN_DEBUG "burgundy_extend_wait: timeout #2\n"); |
55 | } |
56 | |
57 | static void |
58 | snd_pmac_burgundy_wcw(struct snd_pmac *chip, unsigned addr, unsigned val) |
59 | { |
60 | out_le32(&chip->awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); |
61 | snd_pmac_burgundy_busy_wait(chip); |
62 | out_le32(&chip->awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); |
63 | snd_pmac_burgundy_busy_wait(chip); |
64 | out_le32(&chip->awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); |
65 | snd_pmac_burgundy_busy_wait(chip); |
66 | out_le32(&chip->awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); |
67 | snd_pmac_burgundy_busy_wait(chip); |
68 | } |
69 | |
70 | static unsigned |
71 | snd_pmac_burgundy_rcw(struct snd_pmac *chip, unsigned addr) |
72 | { |
73 | unsigned val = 0; |
74 | unsigned long flags; |
75 | |
76 | spin_lock_irqsave(&chip->reg_lock, flags); |
77 | |
78 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); |
79 | snd_pmac_burgundy_busy_wait(chip); |
80 | snd_pmac_burgundy_extend_wait(chip); |
81 | val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; |
82 | |
83 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100100); |
84 | snd_pmac_burgundy_busy_wait(chip); |
85 | snd_pmac_burgundy_extend_wait(chip); |
86 | val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<8; |
87 | |
88 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100200); |
89 | snd_pmac_burgundy_busy_wait(chip); |
90 | snd_pmac_burgundy_extend_wait(chip); |
91 | val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<16; |
92 | |
93 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100300); |
94 | snd_pmac_burgundy_busy_wait(chip); |
95 | snd_pmac_burgundy_extend_wait(chip); |
96 | val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<24; |
97 | |
98 | spin_unlock_irqrestore(&chip->reg_lock, flags); |
99 | |
100 | return val; |
101 | } |
102 | |
103 | static void |
104 | snd_pmac_burgundy_wcb(struct snd_pmac *chip, unsigned int addr, |
105 | unsigned int val) |
106 | { |
107 | out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); |
108 | snd_pmac_burgundy_busy_wait(chip); |
109 | } |
110 | |
111 | static unsigned |
112 | snd_pmac_burgundy_rcb(struct snd_pmac *chip, unsigned int addr) |
113 | { |
114 | unsigned val = 0; |
115 | unsigned long flags; |
116 | |
117 | spin_lock_irqsave(&chip->reg_lock, flags); |
118 | |
119 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); |
120 | snd_pmac_burgundy_busy_wait(chip); |
121 | snd_pmac_burgundy_extend_wait(chip); |
122 | val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; |
123 | |
124 | spin_unlock_irqrestore(&chip->reg_lock, flags); |
125 | |
126 | return val; |
127 | } |
128 | |
129 | #define BASE2ADDR(base) ((base) << 12) |
130 | #define ADDR2BASE(addr) ((addr) >> 12) |
131 | |
132 | /* |
133 | * Burgundy volume: 0 - 100, stereo, word reg |
134 | */ |
135 | static void |
136 | snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address, |
137 | long *volume, int shift) |
138 | { |
139 | int hardvolume, lvolume, rvolume; |
140 | |
141 | if (volume[0] < 0 || volume[0] > 100 || |
142 | volume[1] < 0 || volume[1] > 100) |
143 | return; /* -EINVAL */ |
144 | lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; |
145 | rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; |
146 | |
147 | hardvolume = lvolume + (rvolume << shift); |
148 | if (shift == 8) |
149 | hardvolume |= hardvolume << 16; |
150 | |
151 | snd_pmac_burgundy_wcw(chip, address, hardvolume); |
152 | } |
153 | |
154 | static void |
155 | snd_pmac_burgundy_read_volume(struct snd_pmac *chip, unsigned int address, |
156 | long *volume, int shift) |
157 | { |
158 | int wvolume; |
159 | |
160 | wvolume = snd_pmac_burgundy_rcw(chip, address); |
161 | |
162 | volume[0] = wvolume & 0xff; |
163 | if (volume[0] >= BURGUNDY_VOLUME_OFFSET) |
164 | volume[0] -= BURGUNDY_VOLUME_OFFSET; |
165 | else |
166 | volume[0] = 0; |
167 | volume[1] = (wvolume >> shift) & 0xff; |
168 | if (volume[1] >= BURGUNDY_VOLUME_OFFSET) |
169 | volume[1] -= BURGUNDY_VOLUME_OFFSET; |
170 | else |
171 | volume[1] = 0; |
172 | } |
173 | |
174 | static int snd_pmac_burgundy_info_volume(struct snd_kcontrol *kcontrol, |
175 | struct snd_ctl_elem_info *uinfo) |
176 | { |
177 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
178 | uinfo->count = 2; |
179 | uinfo->value.integer.min = 0; |
180 | uinfo->value.integer.max = 100; |
181 | return 0; |
182 | } |
183 | |
184 | static int snd_pmac_burgundy_get_volume(struct snd_kcontrol *kcontrol, |
185 | struct snd_ctl_elem_value *ucontrol) |
186 | { |
187 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
188 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); |
189 | int shift = (kcontrol->private_value >> 8) & 0xff; |
190 | snd_pmac_burgundy_read_volume(chip, addr, |
191 | ucontrol->value.integer.value, shift); |
192 | return 0; |
193 | } |
194 | |
195 | static int snd_pmac_burgundy_put_volume(struct snd_kcontrol *kcontrol, |
196 | struct snd_ctl_elem_value *ucontrol) |
197 | { |
198 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
199 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); |
200 | int shift = (kcontrol->private_value >> 8) & 0xff; |
201 | long nvoices[2]; |
202 | |
203 | snd_pmac_burgundy_write_volume(chip, addr, |
204 | ucontrol->value.integer.value, shift); |
205 | snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift); |
206 | return (nvoices[0] != ucontrol->value.integer.value[0] || |
207 | nvoices[1] != ucontrol->value.integer.value[1]); |
208 | } |
209 | |
210 | #define BURGUNDY_VOLUME_W(xname, xindex, addr, shift) \ |
211 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ |
212 | .info = snd_pmac_burgundy_info_volume,\ |
213 | .get = snd_pmac_burgundy_get_volume,\ |
214 | .put = snd_pmac_burgundy_put_volume,\ |
215 | .private_value = ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) } |
216 | |
217 | /* |
218 | * Burgundy volume: 0 - 100, stereo, 2-byte reg |
219 | */ |
220 | static void |
221 | snd_pmac_burgundy_write_volume_2b(struct snd_pmac *chip, unsigned int address, |
222 | long *volume, int off) |
223 | { |
224 | int lvolume, rvolume; |
225 | |
226 | off |= off << 2; |
227 | lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; |
228 | rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; |
229 | |
230 | snd_pmac_burgundy_wcb(chip, address + off, lvolume); |
231 | snd_pmac_burgundy_wcb(chip, address + off + 0x500, rvolume); |
232 | } |
233 | |
234 | static void |
235 | snd_pmac_burgundy_read_volume_2b(struct snd_pmac *chip, unsigned int address, |
236 | long *volume, int off) |
237 | { |
238 | volume[0] = snd_pmac_burgundy_rcb(chip, address + off); |
239 | if (volume[0] >= BURGUNDY_VOLUME_OFFSET) |
240 | volume[0] -= BURGUNDY_VOLUME_OFFSET; |
241 | else |
242 | volume[0] = 0; |
243 | volume[1] = snd_pmac_burgundy_rcb(chip, address + off + 0x100); |
244 | if (volume[1] >= BURGUNDY_VOLUME_OFFSET) |
245 | volume[1] -= BURGUNDY_VOLUME_OFFSET; |
246 | else |
247 | volume[1] = 0; |
248 | } |
249 | |
250 | static int snd_pmac_burgundy_info_volume_2b(struct snd_kcontrol *kcontrol, |
251 | struct snd_ctl_elem_info *uinfo) |
252 | { |
253 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
254 | uinfo->count = 2; |
255 | uinfo->value.integer.min = 0; |
256 | uinfo->value.integer.max = 100; |
257 | return 0; |
258 | } |
259 | |
260 | static int snd_pmac_burgundy_get_volume_2b(struct snd_kcontrol *kcontrol, |
261 | struct snd_ctl_elem_value *ucontrol) |
262 | { |
263 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
264 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); |
265 | int off = kcontrol->private_value & 0x300; |
266 | snd_pmac_burgundy_read_volume_2b(chip, addr, |
267 | ucontrol->value.integer.value, off); |
268 | return 0; |
269 | } |
270 | |
271 | static int snd_pmac_burgundy_put_volume_2b(struct snd_kcontrol *kcontrol, |
272 | struct snd_ctl_elem_value *ucontrol) |
273 | { |
274 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
275 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); |
276 | int off = kcontrol->private_value & 0x300; |
277 | long nvoices[2]; |
278 | |
279 | snd_pmac_burgundy_write_volume_2b(chip, addr, |
280 | ucontrol->value.integer.value, off); |
281 | snd_pmac_burgundy_read_volume_2b(chip, addr, nvoices, off); |
282 | return (nvoices[0] != ucontrol->value.integer.value[0] || |
283 | nvoices[1] != ucontrol->value.integer.value[1]); |
284 | } |
285 | |
286 | #define BURGUNDY_VOLUME_2B(xname, xindex, addr, off) \ |
287 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ |
288 | .info = snd_pmac_burgundy_info_volume_2b,\ |
289 | .get = snd_pmac_burgundy_get_volume_2b,\ |
290 | .put = snd_pmac_burgundy_put_volume_2b,\ |
291 | .private_value = ((ADDR2BASE(addr) & 0xff) | ((off) << 8)) } |
292 | |
293 | /* |
294 | * Burgundy gain/attenuation: 0 - 15, mono/stereo, byte reg |
295 | */ |
296 | static int snd_pmac_burgundy_info_gain(struct snd_kcontrol *kcontrol, |
297 | struct snd_ctl_elem_info *uinfo) |
298 | { |
299 | int stereo = (kcontrol->private_value >> 24) & 1; |
300 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
301 | uinfo->count = stereo + 1; |
302 | uinfo->value.integer.min = 0; |
303 | uinfo->value.integer.max = 15; |
304 | return 0; |
305 | } |
306 | |
307 | static int snd_pmac_burgundy_get_gain(struct snd_kcontrol *kcontrol, |
308 | struct snd_ctl_elem_value *ucontrol) |
309 | { |
310 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
311 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); |
312 | int stereo = (kcontrol->private_value >> 24) & 1; |
313 | int atten = (kcontrol->private_value >> 25) & 1; |
314 | int oval; |
315 | |
316 | oval = snd_pmac_burgundy_rcb(chip, addr); |
317 | if (atten) |
318 | oval = ~oval & 0xff; |
319 | ucontrol->value.integer.value[0] = oval & 0xf; |
320 | if (stereo) |
321 | ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; |
322 | return 0; |
323 | } |
324 | |
325 | static int snd_pmac_burgundy_put_gain(struct snd_kcontrol *kcontrol, |
326 | struct snd_ctl_elem_value *ucontrol) |
327 | { |
328 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
329 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); |
330 | int stereo = (kcontrol->private_value >> 24) & 1; |
331 | int atten = (kcontrol->private_value >> 25) & 1; |
332 | int oval, val; |
333 | |
334 | oval = snd_pmac_burgundy_rcb(chip, addr); |
335 | if (atten) |
336 | oval = ~oval & 0xff; |
337 | val = ucontrol->value.integer.value[0]; |
338 | if (stereo) |
339 | val |= ucontrol->value.integer.value[1] << 4; |
340 | else |
341 | val |= ucontrol->value.integer.value[0] << 4; |
342 | if (atten) |
343 | val = ~val & 0xff; |
344 | snd_pmac_burgundy_wcb(chip, addr, val); |
345 | return val != oval; |
346 | } |
347 | |
348 | #define BURGUNDY_VOLUME_B(xname, xindex, addr, stereo, atten) \ |
349 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ |
350 | .info = snd_pmac_burgundy_info_gain,\ |
351 | .get = snd_pmac_burgundy_get_gain,\ |
352 | .put = snd_pmac_burgundy_put_gain,\ |
353 | .private_value = (ADDR2BASE(addr) | ((stereo) << 24) | ((atten) << 25)) } |
354 | |
355 | /* |
356 | * Burgundy switch: 0/1, mono/stereo, word reg |
357 | */ |
358 | static int snd_pmac_burgundy_info_switch_w(struct snd_kcontrol *kcontrol, |
359 | struct snd_ctl_elem_info *uinfo) |
360 | { |
361 | int stereo = (kcontrol->private_value >> 24) & 1; |
362 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
363 | uinfo->count = stereo + 1; |
364 | uinfo->value.integer.min = 0; |
365 | uinfo->value.integer.max = 1; |
366 | return 0; |
367 | } |
368 | |
369 | static int snd_pmac_burgundy_get_switch_w(struct snd_kcontrol *kcontrol, |
370 | struct snd_ctl_elem_value *ucontrol) |
371 | { |
372 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
373 | unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); |
374 | int lmask = 1 << (kcontrol->private_value & 0xff); |
375 | int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff); |
376 | int stereo = (kcontrol->private_value >> 24) & 1; |
377 | int val = snd_pmac_burgundy_rcw(chip, addr); |
378 | ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; |
379 | if (stereo) |
380 | ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; |
381 | return 0; |
382 | } |
383 | |
384 | static int snd_pmac_burgundy_put_switch_w(struct snd_kcontrol *kcontrol, |
385 | struct snd_ctl_elem_value *ucontrol) |
386 | { |
387 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
388 | unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); |
389 | int lmask = 1 << (kcontrol->private_value & 0xff); |
390 | int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff); |
391 | int stereo = (kcontrol->private_value >> 24) & 1; |
392 | int val, oval; |
393 | oval = snd_pmac_burgundy_rcw(chip, addr); |
394 | val = oval & ~(lmask | (stereo ? rmask : 0)); |
395 | if (ucontrol->value.integer.value[0]) |
396 | val |= lmask; |
397 | if (stereo && ucontrol->value.integer.value[1]) |
398 | val |= rmask; |
399 | snd_pmac_burgundy_wcw(chip, addr, val); |
400 | return val != oval; |
401 | } |
402 | |
403 | #define BURGUNDY_SWITCH_W(xname, xindex, addr, lbit, rbit, stereo) \ |
404 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ |
405 | .info = snd_pmac_burgundy_info_switch_w,\ |
406 | .get = snd_pmac_burgundy_get_switch_w,\ |
407 | .put = snd_pmac_burgundy_put_switch_w,\ |
408 | .private_value = ((lbit) | ((rbit) << 8)\ |
409 | | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) } |
410 | |
411 | /* |
412 | * Burgundy switch: 0/1, mono/stereo, byte reg, bit mask |
413 | */ |
414 | static int snd_pmac_burgundy_info_switch_b(struct snd_kcontrol *kcontrol, |
415 | struct snd_ctl_elem_info *uinfo) |
416 | { |
417 | int stereo = (kcontrol->private_value >> 24) & 1; |
418 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
419 | uinfo->count = stereo + 1; |
420 | uinfo->value.integer.min = 0; |
421 | uinfo->value.integer.max = 1; |
422 | return 0; |
423 | } |
424 | |
425 | static int snd_pmac_burgundy_get_switch_b(struct snd_kcontrol *kcontrol, |
426 | struct snd_ctl_elem_value *ucontrol) |
427 | { |
428 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
429 | unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); |
430 | int lmask = kcontrol->private_value & 0xff; |
431 | int rmask = (kcontrol->private_value >> 8) & 0xff; |
432 | int stereo = (kcontrol->private_value >> 24) & 1; |
433 | int val = snd_pmac_burgundy_rcb(chip, addr); |
434 | ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; |
435 | if (stereo) |
436 | ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; |
437 | return 0; |
438 | } |
439 | |
440 | static int snd_pmac_burgundy_put_switch_b(struct snd_kcontrol *kcontrol, |
441 | struct snd_ctl_elem_value *ucontrol) |
442 | { |
443 | struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); |
444 | unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); |
445 | int lmask = kcontrol->private_value & 0xff; |
446 | int rmask = (kcontrol->private_value >> 8) & 0xff; |
447 | int stereo = (kcontrol->private_value >> 24) & 1; |
448 | int val, oval; |
449 | oval = snd_pmac_burgundy_rcb(chip, addr); |
450 | val = oval & ~(lmask | rmask); |
451 | if (ucontrol->value.integer.value[0]) |
452 | val |= lmask; |
453 | if (stereo && ucontrol->value.integer.value[1]) |
454 | val |= rmask; |
455 | snd_pmac_burgundy_wcb(chip, addr, val); |
456 | return val != oval; |
457 | } |
458 | |
459 | #define BURGUNDY_SWITCH_B(xname, xindex, addr, lmask, rmask, stereo) \ |
460 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ |
461 | .info = snd_pmac_burgundy_info_switch_b,\ |
462 | .get = snd_pmac_burgundy_get_switch_b,\ |
463 | .put = snd_pmac_burgundy_put_switch_b,\ |
464 | .private_value = ((lmask) | ((rmask) << 8)\ |
465 | | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) } |
466 | |
467 | /* |
468 | * Burgundy mixers |
469 | */ |
470 | static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __devinitdata = { |
471 | BURGUNDY_VOLUME_W("Master Playback Volume", 0, |
472 | MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), |
473 | BURGUNDY_VOLUME_W("CD Capture Volume", 0, |
474 | MASK_ADDR_BURGUNDY_VOLCD, 16), |
475 | BURGUNDY_VOLUME_2B("Input Capture Volume", 0, |
476 | MASK_ADDR_BURGUNDY_VOLMIX01, 2), |
477 | BURGUNDY_VOLUME_2B("Mixer Playback Volume", 0, |
478 | MASK_ADDR_BURGUNDY_VOLMIX23, 0), |
479 | BURGUNDY_VOLUME_B("CD Gain Capture Volume", 0, |
480 | MASK_ADDR_BURGUNDY_GAINCD, 1, 0), |
481 | BURGUNDY_SWITCH_W("Master Capture Switch", 0, |
482 | MASK_ADDR_BURGUNDY_OUTPUTENABLES, 24, 0, 0), |
483 | BURGUNDY_SWITCH_W("CD Capture Switch", 0, |
484 | MASK_ADDR_BURGUNDY_CAPTURESELECTS, 0, 16, 1), |
485 | BURGUNDY_SWITCH_W("CD Playback Switch", 0, |
486 | MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 0, 16, 1), |
487 | /* BURGUNDY_SWITCH_W("Loop Capture Switch", 0, |
488 | * MASK_ADDR_BURGUNDY_CAPTURESELECTS, 8, 24, 1), |
489 | * BURGUNDY_SWITCH_B("Mixer out Capture Switch", 0, |
490 | * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x02, 0, 0), |
491 | * BURGUNDY_SWITCH_B("Mixer Capture Switch", 0, |
492 | * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x01, 0, 0), |
493 | * BURGUNDY_SWITCH_B("PCM out Capture Switch", 0, |
494 | * MASK_ADDR_BURGUNDY_HOSTIFEH, 0x02, 0, 0), |
495 | */ BURGUNDY_SWITCH_B("PCM Capture Switch", 0, |
496 | MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0) |
497 | }; |
498 | static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __devinitdata = { |
499 | BURGUNDY_VOLUME_W("Line in Capture Volume", 0, |
500 | MASK_ADDR_BURGUNDY_VOLLINE, 16), |
501 | BURGUNDY_VOLUME_W("Mic Capture Volume", 0, |
502 | MASK_ADDR_BURGUNDY_VOLMIC, 16), |
503 | BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0, |
504 | MASK_ADDR_BURGUNDY_GAINLINE, 1, 0), |
505 | BURGUNDY_VOLUME_B("Mic Gain Capture Volume", 0, |
506 | MASK_ADDR_BURGUNDY_GAINMIC, 1, 0), |
507 | BURGUNDY_VOLUME_B("Speaker Playback Volume", 0, |
508 | MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1), |
509 | BURGUNDY_VOLUME_B("Line out Playback Volume", 0, |
510 | MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1, 1), |
511 | BURGUNDY_VOLUME_B("Headphone Playback Volume", 0, |
512 | MASK_ADDR_BURGUNDY_ATTENHP, 1, 1), |
513 | BURGUNDY_SWITCH_W("Line in Capture Switch", 0, |
514 | MASK_ADDR_BURGUNDY_CAPTURESELECTS, 1, 17, 1), |
515 | BURGUNDY_SWITCH_W("Mic Capture Switch", 0, |
516 | MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1), |
517 | BURGUNDY_SWITCH_W("Line in Playback Switch", 0, |
518 | MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 1, 17, 1), |
519 | BURGUNDY_SWITCH_W("Mic Playback Switch", 0, |
520 | MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1), |
521 | BURGUNDY_SWITCH_B("Mic Boost Capture Switch", 0, |
522 | MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) |
523 | }; |
524 | static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __devinitdata = { |
525 | BURGUNDY_VOLUME_W("Line in Capture Volume", 0, |
526 | MASK_ADDR_BURGUNDY_VOLMIC, 16), |
527 | BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0, |
528 | MASK_ADDR_BURGUNDY_GAINMIC, 1, 0), |
529 | BURGUNDY_VOLUME_B("Speaker Playback Volume", 0, |
530 | MASK_ADDR_BURGUNDY_ATTENMONO, 0, 1), |
531 | BURGUNDY_VOLUME_B("Line out Playback Volume", 0, |
532 | MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1), |
533 | BURGUNDY_SWITCH_W("Line in Capture Switch", 0, |
534 | MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1), |
535 | BURGUNDY_SWITCH_W("Line in Playback Switch", 0, |
536 | MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1), |
537 | /* BURGUNDY_SWITCH_B("Line in Boost Capture Switch", 0, |
538 | * MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) */ |
539 | }; |
540 | static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac __devinitdata = |
541 | BURGUNDY_SWITCH_B("Master Playback Switch", 0, |
542 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, |
543 | BURGUNDY_OUTPUT_LEFT | BURGUNDY_LINEOUT_LEFT | BURGUNDY_HP_LEFT, |
544 | BURGUNDY_OUTPUT_RIGHT | BURGUNDY_LINEOUT_RIGHT | BURGUNDY_HP_RIGHT, 1); |
545 | static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac __devinitdata = |
546 | BURGUNDY_SWITCH_B("Master Playback Switch", 0, |
547 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, |
548 | BURGUNDY_OUTPUT_INTERN |
549 | | BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); |
550 | static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __devinitdata = |
551 | BURGUNDY_SWITCH_B("Speaker Playback Switch", 0, |
552 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, |
553 | BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); |
554 | static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __devinitdata = |
555 | BURGUNDY_SWITCH_B("Speaker Playback Switch", 0, |
556 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, |
557 | BURGUNDY_OUTPUT_INTERN, 0, 0); |
558 | static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __devinitdata = |
559 | BURGUNDY_SWITCH_B("Line out Playback Switch", 0, |
560 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, |
561 | BURGUNDY_LINEOUT_LEFT, BURGUNDY_LINEOUT_RIGHT, 1); |
562 | static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac __devinitdata = |
563 | BURGUNDY_SWITCH_B("Line out Playback Switch", 0, |
564 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, |
565 | BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); |
566 | static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac __devinitdata = |
567 | BURGUNDY_SWITCH_B("Headphone Playback Switch", 0, |
568 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, |
569 | BURGUNDY_HP_LEFT, BURGUNDY_HP_RIGHT, 1); |
570 | |
571 | |
572 | #ifdef PMAC_SUPPORT_AUTOMUTE |
573 | /* |
574 | * auto-mute stuffs |
575 | */ |
576 | static int snd_pmac_burgundy_detect_headphone(struct snd_pmac *chip) |
577 | { |
578 | return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0; |
579 | } |
580 | |
581 | static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_notify) |
582 | { |
583 | if (chip->auto_mute) { |
584 | int imac = of_machine_is_compatible("iMac"); |
585 | int reg, oreg; |
586 | reg = oreg = snd_pmac_burgundy_rcb(chip, |
587 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); |
588 | reg &= imac ? ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT |
589 | | BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT) |
590 | : ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT |
591 | | BURGUNDY_OUTPUT_INTERN); |
592 | if (snd_pmac_burgundy_detect_headphone(chip)) |
593 | reg |= imac ? (BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT) |
594 | : (BURGUNDY_OUTPUT_LEFT |
595 | | BURGUNDY_OUTPUT_RIGHT); |
596 | else |
597 | reg |= imac ? (BURGUNDY_OUTPUT_LEFT |
598 | | BURGUNDY_OUTPUT_RIGHT) |
599 | : (BURGUNDY_OUTPUT_INTERN); |
600 | if (do_notify && reg == oreg) |
601 | return; |
602 | snd_pmac_burgundy_wcb(chip, |
603 | MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg); |
604 | if (do_notify) { |
605 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, |
606 | &chip->master_sw_ctl->id); |
607 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, |
608 | &chip->speaker_sw_ctl->id); |
609 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, |
610 | &chip->hp_detect_ctl->id); |
611 | } |
612 | } |
613 | } |
614 | #endif /* PMAC_SUPPORT_AUTOMUTE */ |
615 | |
616 | |
617 | /* |
618 | * initialize burgundy |
619 | */ |
620 | int __devinit snd_pmac_burgundy_init(struct snd_pmac *chip) |
621 | { |
622 | int imac = of_machine_is_compatible("iMac"); |
623 | int i, err; |
624 | |
625 | /* Checks to see the chip is alive and kicking */ |
626 | if ((in_le32(&chip->awacs->codec_ctrl) & MASK_ERRCODE) == 0xf0000) { |
627 | printk(KERN_WARNING "pmac burgundy: disabled by MacOS :-(\n"); |
628 | return 1; |
629 | } |
630 | |
631 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, |
632 | DEF_BURGUNDY_OUTPUTENABLES); |
633 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, |
634 | DEF_BURGUNDY_MORE_OUTPUTENABLES); |
635 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTSELECTS, |
636 | DEF_BURGUNDY_OUTPUTSELECTS); |
637 | |
638 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21, |
639 | DEF_BURGUNDY_INPSEL21); |
640 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3, |
641 | imac ? DEF_BURGUNDY_INPSEL3_IMAC |
642 | : DEF_BURGUNDY_INPSEL3_PMAC); |
643 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD, |
644 | DEF_BURGUNDY_GAINCD); |
645 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE, |
646 | DEF_BURGUNDY_GAINLINE); |
647 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMIC, |
648 | DEF_BURGUNDY_GAINMIC); |
649 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMODEM, |
650 | DEF_BURGUNDY_GAINMODEM); |
651 | |
652 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENSPEAKER, |
653 | DEF_BURGUNDY_ATTENSPEAKER); |
654 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENLINEOUT, |
655 | DEF_BURGUNDY_ATTENLINEOUT); |
656 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENHP, |
657 | DEF_BURGUNDY_ATTENHP); |
658 | |
659 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_MASTER_VOLUME, |
660 | DEF_BURGUNDY_MASTER_VOLUME); |
661 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLCD, |
662 | DEF_BURGUNDY_VOLCD); |
663 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLLINE, |
664 | DEF_BURGUNDY_VOLLINE); |
665 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC, |
666 | DEF_BURGUNDY_VOLMIC); |
667 | |
668 | if (chip->hp_stat_mask == 0) { |
669 | /* set headphone-jack detection bit */ |
670 | if (imac) |
671 | chip->hp_stat_mask = BURGUNDY_HPDETECT_IMAC_UPPER |
672 | | BURGUNDY_HPDETECT_IMAC_LOWER |
673 | | BURGUNDY_HPDETECT_IMAC_SIDE; |
674 | else |
675 | chip->hp_stat_mask = BURGUNDY_HPDETECT_PMAC_BACK; |
676 | } |
677 | /* |
678 | * build burgundy mixers |
679 | */ |
680 | strcpy(chip->card->mixername, "PowerMac Burgundy"); |
681 | |
682 | for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) { |
683 | err = snd_ctl_add(chip->card, |
684 | snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip)); |
685 | if (err < 0) |
686 | return err; |
687 | } |
688 | for (i = 0; i < (imac ? ARRAY_SIZE(snd_pmac_burgundy_mixers_imac) |
689 | : ARRAY_SIZE(snd_pmac_burgundy_mixers_pmac)); i++) { |
690 | err = snd_ctl_add(chip->card, |
691 | snd_ctl_new1(imac ? &snd_pmac_burgundy_mixers_imac[i] |
692 | : &snd_pmac_burgundy_mixers_pmac[i], chip)); |
693 | if (err < 0) |
694 | return err; |
695 | } |
696 | chip->master_sw_ctl = snd_ctl_new1(imac |
697 | ? &snd_pmac_burgundy_master_sw_imac |
698 | : &snd_pmac_burgundy_master_sw_pmac, chip); |
699 | err = snd_ctl_add(chip->card, chip->master_sw_ctl); |
700 | if (err < 0) |
701 | return err; |
702 | chip->master_sw_ctl = snd_ctl_new1(imac |
703 | ? &snd_pmac_burgundy_line_sw_imac |
704 | : &snd_pmac_burgundy_line_sw_pmac, chip); |
705 | err = snd_ctl_add(chip->card, chip->master_sw_ctl); |
706 | if (err < 0) |
707 | return err; |
708 | if (imac) { |
709 | chip->master_sw_ctl = snd_ctl_new1( |
710 | &snd_pmac_burgundy_hp_sw_imac, chip); |
711 | err = snd_ctl_add(chip->card, chip->master_sw_ctl); |
712 | if (err < 0) |
713 | return err; |
714 | } |
715 | chip->speaker_sw_ctl = snd_ctl_new1(imac |
716 | ? &snd_pmac_burgundy_speaker_sw_imac |
717 | : &snd_pmac_burgundy_speaker_sw_pmac, chip); |
718 | err = snd_ctl_add(chip->card, chip->speaker_sw_ctl); |
719 | if (err < 0) |
720 | return err; |
721 | #ifdef PMAC_SUPPORT_AUTOMUTE |
722 | err = snd_pmac_add_automute(chip); |
723 | if (err < 0) |
724 | return err; |
725 | |
726 | chip->detect_headphone = snd_pmac_burgundy_detect_headphone; |
727 | chip->update_automute = snd_pmac_burgundy_update_automute; |
728 | snd_pmac_burgundy_update_automute(chip, 0); /* update the status only */ |
729 | #endif |
730 | |
731 | return 0; |
732 | } |
733 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9