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