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