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