1 /*-
2 * Copyright 2008 by Marco Trillo. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD$
26 */
27
28 /*
29 * Apple DAVbus audio controller.
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40 #include <sys/rman.h>
41
42 #include <dev/ofw/ofw_bus.h>
43
44 #ifdef HAVE_KERNEL_OPTION_HEADERS
45 #include "opt_snd.h"
46 #endif
47
48 #include <dev/sound/pcm/sound.h>
49
50 #include <dev/sound/macio/aoa.h>
51 #include <dev/sound/macio/davbusreg.h>
52
53 #include <machine/intr_machdep.h>
54 #include <machine/resource.h>
55 #include <machine/bus.h>
56
57 #include "mixer_if.h"
58
59 struct davbus_softc {
60 struct aoa_softc aoa;
61 phandle_t node;
62 phandle_t soundnode;
63 struct resource *reg;
64 struct mtx mutex;
65 int device_id;
66 u_int output_mask;
67 u_int (*read_status)(struct davbus_softc *, u_int);
68 void (*set_outputs)(struct davbus_softc *, u_int);
69 };
70
71 static int davbus_probe(device_t);
72 static int davbus_attach(device_t);
73 static void davbus_cint(void *);
74
75 static device_method_t pcm_davbus_methods[] = {
76 /* Device interface. */
77 DEVMETHOD(device_probe, davbus_probe),
78 DEVMETHOD(device_attach, davbus_attach),
79
80 { 0, 0 }
81 };
82
83 static driver_t pcm_davbus_driver = {
84 "pcm",
85 pcm_davbus_methods,
86 PCM_SOFTC_SIZE
87 };
88
89 DRIVER_MODULE(pcm_davbus, macio, pcm_davbus_driver, pcm_devclass, 0, 0);
90 MODULE_DEPEND(pcm_davbus, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
91
92 /*****************************************************************************
93 Probe and attachment routines.
94 *****************************************************************************/
95 static int
96 davbus_probe(device_t self)
97 {
98 const char *name;
99
100 name = ofw_bus_get_name(self);
101 if (!name)
102 return (ENXIO);
103
104 if (strcmp(name, "davbus") != 0)
105 return (ENXIO);
106
107 device_set_desc(self, "Apple DAVBus Audio Controller");
108
109 return (0);
110 }
111
112 /*
113 * Burgundy codec control
114 */
115
116 static int burgundy_init(struct snd_mixer *m);
117 static int burgundy_uninit(struct snd_mixer *m);
118 static int burgundy_reinit(struct snd_mixer *m);
119 static void burgundy_write_locked(struct davbus_softc *, u_int, u_int);
120 static void burgundy_set_outputs(struct davbus_softc *d, u_int mask);
121 static u_int burgundy_read_status(struct davbus_softc *d, u_int status);
122 static int burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left,
123 unsigned right);
124 static u_int32_t burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src);
125
126 static kobj_method_t burgundy_mixer_methods[] = {
127 KOBJMETHOD(mixer_init, burgundy_init),
128 KOBJMETHOD(mixer_uninit, burgundy_uninit),
129 KOBJMETHOD(mixer_reinit, burgundy_reinit),
130 KOBJMETHOD(mixer_set, burgundy_set),
131 KOBJMETHOD(mixer_setrecsrc, burgundy_setrecsrc),
132 KOBJMETHOD_END
133 };
134
135 MIXER_DECLARE(burgundy_mixer);
136
137 static int
138 burgundy_init(struct snd_mixer *m)
139 {
140 struct davbus_softc *d;
141
142 d = mix_getdevinfo(m);
143
144 d->read_status = burgundy_read_status;
145 d->set_outputs = burgundy_set_outputs;
146
147 /*
148 * We configure the Burgundy codec as follows:
149 *
150 * o Input subframe 0 is connected to input digital
151 * stream A (ISA).
152 * o Stream A (ISA) is mixed in mixer 2 (MIX2).
153 * o Output of mixer 2 (MIX2) is routed to output sources
154 * OS0 and OS1 which can be converted to analog.
155 *
156 */
157 mtx_lock(&d->mutex);
158
159 burgundy_write_locked(d, 0x16700, 0x40);
160
161 burgundy_write_locked(d, BURGUNDY_MIX0_REG, 0);
162 burgundy_write_locked(d, BURGUNDY_MIX1_REG, 0);
163 burgundy_write_locked(d, BURGUNDY_MIX2_REG, BURGUNDY_MIX_ISA);
164 burgundy_write_locked(d, BURGUNDY_MIX3_REG, 0);
165
166 burgundy_write_locked(d, BURGUNDY_OS_REG, BURGUNDY_OS0_MIX2 |
167 BURGUNDY_OS1_MIX2);
168
169 burgundy_write_locked(d, BURGUNDY_SDIN_REG, BURGUNDY_ISA_SF0);
170
171 /* Set several digital scalers to unity gain. */
172 burgundy_write_locked(d, BURGUNDY_MXS2L_REG, BURGUNDY_MXS_UNITY);
173 burgundy_write_locked(d, BURGUNDY_MXS2R_REG, BURGUNDY_MXS_UNITY);
174 burgundy_write_locked(d, BURGUNDY_OSS0L_REG, BURGUNDY_OSS_UNITY);
175 burgundy_write_locked(d, BURGUNDY_OSS0R_REG, BURGUNDY_OSS_UNITY);
176 burgundy_write_locked(d, BURGUNDY_OSS1L_REG, BURGUNDY_OSS_UNITY);
177 burgundy_write_locked(d, BURGUNDY_OSS1R_REG, BURGUNDY_OSS_UNITY);
178 burgundy_write_locked(d, BURGUNDY_ISSAL_REG, BURGUNDY_ISS_UNITY);
179 burgundy_write_locked(d, BURGUNDY_ISSAR_REG, BURGUNDY_ISS_UNITY);
180
181 burgundy_set_outputs(d, burgundy_read_status(d,
182 bus_read_4(d->reg, DAVBUS_CODEC_STATUS)));
183
184 mtx_unlock(&d->mutex);
185
186 mix_setdevs(m, SOUND_MASK_VOLUME);
187
188 return (0);
189 }
190
191 static int
192 burgundy_uninit(struct snd_mixer *m)
193 {
194 return (0);
195 }
196
197 static int
198 burgundy_reinit(struct snd_mixer *m)
199 {
200 return (0);
201 }
202
203 static void
204 burgundy_write_locked(struct davbus_softc *d, u_int reg, u_int val)
205 {
206 u_int size, addr, offset, data, i;
207
208 size = (reg & 0x00FF0000) >> 16;
209 addr = (reg & 0x0000FF00) >> 8;
210 offset = reg & 0xFF;
211
212 for (i = offset; i < offset + size; ++i) {
213 data = BURGUNDY_CTRL_WRITE | (addr << 12) |
214 ((size + offset - 1) << 10) | (i << 8) | (val & 0xFF);
215 if (i == offset)
216 data |= BURGUNDY_CTRL_RESET;
217
218 bus_write_4(d->reg, DAVBUS_CODEC_CTRL, data);
219
220 while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) &
221 DAVBUS_CODEC_BUSY)
222 DELAY(1);
223
224 val >>= 8; /* next byte. */
225 }
226 }
227
228 /* Must be called with d->mutex held. */
229 static void
230 burgundy_set_outputs(struct davbus_softc *d, u_int mask)
231 {
232 u_int x = 0;
233
234 if (mask == d->output_mask)
235 return;
236
237 /*
238 * Bordeaux card wirings:
239 * Port 15: RCA out
240 * Port 16: Minijack out
241 * Port 17: Internal speaker
242 *
243 * B&W G3 wirings:
244 * Port 14: Minijack out
245 * Port 17: Internal speaker
246 */
247
248 DPRINTF(("Enabled outputs:"));
249 if (mask & (1 << 0)) {
250 DPRINTF((" SPEAKER"));
251 x |= BURGUNDY_P17M_EN;
252 }
253 if (mask & (1 << 1)) {
254 DPRINTF((" HEADPHONES"));
255 x |= BURGUNDY_P14L_EN | BURGUNDY_P14R_EN;
256 }
257 DPRINTF(("\n"));
258
259 burgundy_write_locked(d, BURGUNDY_MUTE_REG, x);
260 d->output_mask = mask;
261 }
262
263 static u_int
264 burgundy_read_status(struct davbus_softc *d, u_int status)
265 {
266 if (status & 0x4)
267 return (1 << 1);
268 else
269 return (1 << 0);
270 }
271
272 static int
273 burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
274 {
275 struct davbus_softc *d;
276 int lval, rval;
277
278 lval = ((100 - left) * 15 / 100) & 0xf;
279 rval = ((100 - right) * 15 / 100) & 0xf;
280 DPRINTF(("volume %d %d\n", lval, rval));
281
282 d = mix_getdevinfo(m);
283
284 switch (dev) {
285 case SOUND_MIXER_VOLUME:
286 mtx_lock(&d->mutex);
287
288 burgundy_write_locked(d, BURGUNDY_OL13_REG, lval);
289 burgundy_write_locked(d, BURGUNDY_OL14_REG, (rval << 4) | lval);
290 burgundy_write_locked(d, BURGUNDY_OL15_REG, (rval << 4) | lval);
291 burgundy_write_locked(d, BURGUNDY_OL16_REG, (rval << 4) | lval);
292 burgundy_write_locked(d, BURGUNDY_OL17_REG, lval);
293
294 mtx_unlock(&d->mutex);
295
296 return (left | (right << 8));
297 }
298
299 return (0);
300 }
301
302 static u_int32_t
303 burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src)
304 {
305 return (0);
306 }
307
308 /*
309 * Screamer Codec Control
310 */
311
312 static int screamer_init(struct snd_mixer *m);
313 static int screamer_uninit(struct snd_mixer *m);
314 static int screamer_reinit(struct snd_mixer *m);
315 static void screamer_write_locked(struct davbus_softc *, u_int, u_int);
316 static void screamer_set_outputs(struct davbus_softc *d, u_int mask);
317 static u_int screamer_read_status(struct davbus_softc *d, u_int status);
318 static int screamer_set(struct snd_mixer *m, unsigned dev, unsigned left,
319 unsigned right);
320 static u_int32_t screamer_setrecsrc(struct snd_mixer *m, u_int32_t src);
321
322 static kobj_method_t screamer_mixer_methods[] = {
323 KOBJMETHOD(mixer_init, screamer_init),
324 KOBJMETHOD(mixer_uninit, screamer_uninit),
325 KOBJMETHOD(mixer_reinit, screamer_reinit),
326 KOBJMETHOD(mixer_set, screamer_set),
327 KOBJMETHOD(mixer_setrecsrc, screamer_setrecsrc),
328 KOBJMETHOD_END
329 };
330
331 MIXER_DECLARE(screamer_mixer);
332
333 static int
334 screamer_init(struct snd_mixer *m)
335 {
336 struct davbus_softc *d;
337
338 d = mix_getdevinfo(m);
339
340 d->read_status = screamer_read_status;
341 d->set_outputs = screamer_set_outputs;
342
343 mtx_lock(&d->mutex);
344
345 screamer_write_locked(d, SCREAMER_CODEC_ADDR0, SCREAMER_INPUT_CD |
346 SCREAMER_DEFAULT_CD_GAIN);
347
348 screamer_set_outputs(d, screamer_read_status(d,
349 bus_read_4(d->reg, DAVBUS_CODEC_STATUS)));
350
351 screamer_write_locked(d, SCREAMER_CODEC_ADDR2, 0);
352 screamer_write_locked(d, SCREAMER_CODEC_ADDR4, 0);
353 screamer_write_locked(d, SCREAMER_CODEC_ADDR5, 0);
354 screamer_write_locked(d, SCREAMER_CODEC_ADDR6, 0);
355
356 mtx_unlock(&d->mutex);
357
358 mix_setdevs(m, SOUND_MASK_VOLUME);
359
360 return (0);
361 }
362
363 static int
364 screamer_uninit(struct snd_mixer *m)
365 {
366 return (0);
367 }
368
369 static int
370 screamer_reinit(struct snd_mixer *m)
371 {
372 return (0);
373 }
374
375
376 static void
377 screamer_write_locked(struct davbus_softc *d, u_int reg, u_int val)
378 {
379 u_int x;
380
381 KASSERT(val == (val & 0xfff), ("bad val"));
382
383 while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & DAVBUS_CODEC_BUSY)
384 DELAY(100);
385
386 x = reg;
387 x |= SCREAMER_CODEC_EMSEL0;
388 x |= val;
389 bus_write_4(d->reg, DAVBUS_CODEC_CTRL, x);
390
391 while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & DAVBUS_CODEC_BUSY)
392 DELAY(100);
393 }
394
395 /* Must be called with d->mutex held. */
396 static void
397 screamer_set_outputs(struct davbus_softc *d, u_int mask)
398 {
399 u_int x;
400
401 if (mask == d->output_mask) {
402 return;
403 }
404
405 x = SCREAMER_MUTE_SPEAKER | SCREAMER_MUTE_HEADPHONES;
406
407 DPRINTF(("Enabled outputs: "));
408
409 if (mask & (1 << 0)) {
410 DPRINTF(("SPEAKER "));
411 x &= ~SCREAMER_MUTE_SPEAKER;
412 }
413 if (mask & (1 << 1)) {
414 DPRINTF(("HEADPHONES "));
415 x &= ~SCREAMER_MUTE_HEADPHONES;
416 }
417
418 DPRINTF(("\n"));
419
420 if (d->device_id == 5 || d->device_id == 11) {
421 DPRINTF(("Enabling programmable output.\n"));
422 x |= SCREAMER_PROG_OUTPUT0;
423 }
424 if (d->device_id == 8 || d->device_id == 11) {
425 x &= ~SCREAMER_MUTE_SPEAKER;
426
427 if (mask & (1 << 0))
428 x |= SCREAMER_PROG_OUTPUT1; /* enable speaker. */
429 }
430
431 screamer_write_locked(d, SCREAMER_CODEC_ADDR1, x);
432 d->output_mask = mask;
433 }
434
435 static u_int
436 screamer_read_status(struct davbus_softc *d, u_int status)
437 {
438 int headphones;
439
440 switch (d->device_id) {
441 case 5: /* Sawtooth */
442 headphones = (status & 0x4);
443 break;
444
445 case 8:
446 case 11: /* iMac DV */
447 /* The iMac DV has 2 headphone outputs. */
448 headphones = (status & 0x7);
449 break;
450
451 default:
452 headphones = (status & 0x8);
453 }
454
455 if (headphones)
456 return (1 << 1);
457 else
458 return (1 << 0);
459 }
460
461 static int
462 screamer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
463 {
464 struct davbus_softc *d;
465 int lval, rval;
466
467 lval = ((100 - left) * 15 / 100) & 0xf;
468 rval = ((100 - right) * 15 / 100) & 0xf;
469 DPRINTF(("volume %d %d\n", lval, rval));
470
471 d = mix_getdevinfo(m);
472
473 switch (dev) {
474 case SOUND_MIXER_VOLUME:
475 mtx_lock(&d->mutex);
476 screamer_write_locked(d, SCREAMER_CODEC_ADDR2, (lval << 6) |
477 rval);
478 screamer_write_locked(d, SCREAMER_CODEC_ADDR4, (lval << 6) |
479 rval);
480 mtx_unlock(&d->mutex);
481
482 return (left | (right << 8));
483 }
484
485 return (0);
486 }
487
488 static u_int32_t
489 screamer_setrecsrc(struct snd_mixer *m, u_int32_t src)
490 {
491 return (0);
492 }
493
494 static int
495 davbus_attach(device_t self)
496 {
497 struct davbus_softc *sc;
498 struct resource *dbdma_irq, *cintr;
499 void *cookie;
500 char compat[64];
501 int rid, oirq, err;
502
503 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
504
505 sc->aoa.sc_dev = self;
506 sc->node = ofw_bus_get_node(self);
507 sc->soundnode = OF_child(sc->node);
508
509 /* Map the controller register space. */
510 rid = 0;
511 sc->reg = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
512 if (sc->reg == NULL)
513 return (ENXIO);
514
515 /* Map the DBDMA channel register space. */
516 rid = 1;
517 sc->aoa.sc_odma = bus_alloc_resource_any(self, SYS_RES_MEMORY,
518 &rid, RF_ACTIVE);
519 if (sc->aoa.sc_odma == NULL)
520 return (ENXIO);
521
522 /* Establish the DBDMA channel edge-triggered interrupt. */
523 rid = 1;
524 dbdma_irq = bus_alloc_resource_any(self, SYS_RES_IRQ,
525 &rid, RF_SHAREABLE | RF_ACTIVE);
526 if (dbdma_irq == NULL)
527 return (ENXIO);
528
529 oirq = rman_get_start(dbdma_irq);
530
531 DPRINTF(("interrupting at irq %d\n", oirq));
532
533 err = powerpc_config_intr(oirq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
534 if (err != 0)
535 return (err);
536
537 snd_setup_intr(self, dbdma_irq, INTR_MPSAFE, aoa_interrupt,
538 sc, &cookie);
539
540 /* Now initialize the controller. */
541
542 bzero(compat, sizeof(compat));
543 OF_getprop(sc->soundnode, "compatible", compat, sizeof(compat));
544 OF_getprop(sc->soundnode, "device-id", &sc->device_id, sizeof(u_int));
545
546 mtx_init(&sc->mutex, "DAVbus", NULL, MTX_DEF);
547
548 device_printf(self, "codec: <%s>\n", compat);
549
550 /* Setup the control interrupt. */
551 rid = 0;
552 cintr = bus_alloc_resource_any(self, SYS_RES_IRQ,
553 &rid, RF_SHAREABLE | RF_ACTIVE);
554 if (cintr != NULL)
555 bus_setup_intr(self, cintr, INTR_TYPE_MISC | INTR_MPSAFE,
556 NULL, davbus_cint, sc, &cookie);
557
558 /* Initialize controller registers. */
559 bus_write_4(sc->reg, DAVBUS_SOUND_CTRL, DAVBUS_INPUT_SUBFRAME0 |
560 DAVBUS_OUTPUT_SUBFRAME0 | DAVBUS_RATE_44100 | DAVBUS_INTR_PORTCHG);
561
562 /* Attach DBDMA engine and PCM layer */
563 err = aoa_attach(sc);
564 if (err)
565 return (err);
566
567 /* Install codec module */
568 if (strcmp(compat, "screamer") == 0)
569 mixer_init(self, &screamer_mixer_class, sc);
570 else if (strcmp(compat, "burgundy") == 0)
571 mixer_init(self, &burgundy_mixer_class, sc);
572
573 return (0);
574 }
575
576 static void
577 davbus_cint(void *ptr)
578 {
579 struct davbus_softc *d = ptr;
580 u_int reg, status, mask;
581
582 mtx_lock(&d->mutex);
583
584 reg = bus_read_4(d->reg, DAVBUS_SOUND_CTRL);
585 if (reg & DAVBUS_PORTCHG) {
586
587 status = bus_read_4(d->reg, DAVBUS_CODEC_STATUS);
588
589 if (d->read_status && d->set_outputs) {
590
591 mask = (*d->read_status)(d, status);
592 (*d->set_outputs)(d, mask);
593 }
594
595 /* Clear the interrupt. */
596 bus_write_4(d->reg, DAVBUS_SOUND_CTRL, reg);
597 }
598
599 mtx_unlock(&d->mutex);
600 }
601
Cache object: 9d98f00317d000515b504da53b9aed40
|