1/* $OpenBSD: harmony.c,v 1.41 2024/05/22 14:25:47 jsg Exp $ */ 2 3/* 4 * Copyright (c) 2003 Jason L. Wright (jason@thought.net) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29/* 30 * Harmony (CS4215/AD1849 LASI) audio interface. 31 */ 32 33#include <sys/param.h> 34#include <sys/kernel.h> 35#include <sys/systm.h> 36#include <sys/errno.h> 37#include <sys/ioctl.h> 38#include <sys/device.h> 39#include <sys/proc.h> 40#include <sys/malloc.h> 41 42#include <sys/audioio.h> 43#include <dev/audio_if.h> 44 45#include <machine/cpu.h> 46#include <machine/intr.h> 47#include <machine/iomod.h> 48#include <machine/autoconf.h> 49#include <machine/bus.h> 50 51#include <hppa/dev/cpudevs.h> 52#include <hppa/gsc/gscbusvar.h> 53#include <hppa/gsc/harmonyreg.h> 54#include <hppa/gsc/harmonyvar.h> 55 56int harmony_open(void *, int); 57void harmony_close(void *); 58int harmony_set_params(void *, int, int, struct audio_params *, 59 struct audio_params *); 60int harmony_round_blocksize(void *, int); 61int harmony_commit_settings(void *); 62int harmony_halt_output(void *); 63int harmony_halt_input(void *); 64int harmony_set_port(void *, mixer_ctrl_t *); 65int harmony_get_port(void *, mixer_ctrl_t *); 66int harmony_query_devinfo(void *addr, mixer_devinfo_t *); 67void * harmony_allocm(void *, int, size_t, int, int); 68void harmony_freem(void *, void *, int); 69size_t harmony_round_buffersize(void *, int, size_t); 70int harmony_trigger_output(void *, void *, void *, int, 71 void (*intr)(void *), void *, struct audio_params *); 72int harmony_trigger_input(void *, void *, void *, int, 73 void (*intr)(void *), void *, struct audio_params *); 74 75const struct audio_hw_if harmony_sa_hw_if = { 76 .open = harmony_open, 77 .close = harmony_close, 78 .set_params = harmony_set_params, 79 .round_blocksize = harmony_round_blocksize, 80 .commit_settings = harmony_commit_settings, 81 .halt_output = harmony_halt_output, 82 .halt_input = harmony_halt_input, 83 .set_port = harmony_set_port, 84 .get_port = harmony_get_port, 85 .query_devinfo = harmony_query_devinfo, 86 .allocm = harmony_allocm, 87 .freem = harmony_freem, 88 .round_buffersize = harmony_round_buffersize, 89 .trigger_output = harmony_trigger_output, 90 .trigger_input = harmony_trigger_input, 91}; 92 93int harmony_match(struct device *, void *, void *); 94void harmony_attach(struct device *, struct device *, void *); 95int harmony_intr(void *); 96void harmony_intr_enable(struct harmony_softc *); 97void harmony_intr_disable(struct harmony_softc *); 98u_int32_t harmony_speed_bits(struct harmony_softc *, u_long *); 99int harmony_set_gainctl(struct harmony_softc *); 100void harmony_reset_codec(struct harmony_softc *); 101void harmony_start_cp(struct harmony_softc *); 102void harmony_try_more(struct harmony_softc *); 103 104void harmony_acc_tmo(void *); 105#define ADD_CLKALLICA(sc) do { \ 106 (sc)->sc_acc <<= 1; \ 107 (sc)->sc_acc |= READ_REG((sc), HARMONY_DIAG) & DIAG_CO; \ 108 if ((sc)->sc_acc_cnt++ && !((sc)->sc_acc_cnt % 32)) \ 109 enqueue_randomness((sc)->sc_acc_num ^= (sc)->sc_acc); \ 110} while(0) 111 112int 113harmony_match(parent, match, aux) 114 struct device *parent; 115 void *match, *aux; 116{ 117 struct gsc_attach_args *ga = aux; 118 bus_space_handle_t bh; 119 u_int32_t cntl; 120 121 if (ga->ga_type.iodc_type == HPPA_TYPE_FIO) { 122 if (ga->ga_type.iodc_sv_model == HPPA_FIO_A1 || 123 ga->ga_type.iodc_sv_model == HPPA_FIO_A2NB || 124 ga->ga_type.iodc_sv_model == HPPA_FIO_A1NB || 125 ga->ga_type.iodc_sv_model == HPPA_FIO_A2) { 126 if (bus_space_map(ga->ga_iot, ga->ga_hpa, 127 HARMONY_NREGS, 0, &bh) != 0) 128 return (0); 129 cntl = bus_space_read_4(ga->ga_iot, bh, HARMONY_ID) & 130 ID_REV_MASK; 131 bus_space_unmap(ga->ga_iot, bh, HARMONY_NREGS); 132 if (cntl == ID_REV_TS || cntl == ID_REV_NOTS) 133 return (1); 134 } 135 } 136 return (0); 137} 138 139void 140harmony_attach(parent, self, aux) 141 struct device *parent, *self; 142 void *aux; 143{ 144 struct harmony_softc *sc = (struct harmony_softc *)self; 145 struct gsc_attach_args *ga = aux; 146 u_int8_t rev; 147 u_int32_t cntl; 148 int i; 149 150 sc->sc_bt = ga->ga_iot; 151 sc->sc_dmat = ga->ga_dmatag; 152 153 if (bus_space_map(sc->sc_bt, ga->ga_hpa, HARMONY_NREGS, 0, 154 &sc->sc_bh) != 0) { 155 printf(": couldn't map registers\n"); 156 return; 157 } 158 159 cntl = READ_REG(sc, HARMONY_ID); 160 sc->sc_teleshare = (cntl & ID_REV_MASK) == ID_REV_TS; 161 162 if (bus_dmamem_alloc(sc->sc_dmat, sizeof(struct harmony_empty), 163 PAGE_SIZE, 0, &sc->sc_empty_seg, 1, &sc->sc_empty_rseg, 164 BUS_DMA_NOWAIT) != 0) { 165 printf(": couldn't alloc DMA memory\n"); 166 bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS); 167 return; 168 } 169 if (bus_dmamem_map(sc->sc_dmat, &sc->sc_empty_seg, 1, 170 sizeof(struct harmony_empty), (caddr_t *)&sc->sc_empty_kva, 171 BUS_DMA_NOWAIT) != 0) { 172 printf(": couldn't map DMA memory\n"); 173 bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg, 174 sc->sc_empty_rseg); 175 bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS); 176 return; 177 } 178 if (bus_dmamap_create(sc->sc_dmat, sizeof(struct harmony_empty), 1, 179 sizeof(struct harmony_empty), 0, BUS_DMA_NOWAIT, 180 &sc->sc_empty_map) != 0) { 181 printf(": can't create DMA map\n"); 182 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva, 183 sizeof(struct harmony_empty)); 184 bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg, 185 sc->sc_empty_rseg); 186 bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS); 187 return; 188 } 189 if (bus_dmamap_load(sc->sc_dmat, sc->sc_empty_map, sc->sc_empty_kva, 190 sizeof(struct harmony_empty), NULL, BUS_DMA_NOWAIT) != 0) { 191 printf(": can't load DMA map\n"); 192 bus_dmamap_destroy(sc->sc_dmat, sc->sc_empty_map); 193 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva, 194 sizeof(struct harmony_empty)); 195 bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg, 196 sc->sc_empty_rseg); 197 bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS); 198 return; 199 } 200 201 sc->sc_playback_empty = 0; 202 for (i = 0; i < PLAYBACK_EMPTYS; i++) 203 sc->sc_playback_paddrs[i] = 204 sc->sc_empty_map->dm_segs[0].ds_addr + 205 offsetof(struct harmony_empty, playback[i][0]); 206 207 sc->sc_capture_empty = 0; 208 for (i = 0; i < CAPTURE_EMPTYS; i++) 209 sc->sc_capture_paddrs[i] = 210 sc->sc_empty_map->dm_segs[0].ds_addr + 211 offsetof(struct harmony_empty, playback[i][0]); 212 213 bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map, 214 offsetof(struct harmony_empty, playback[0][0]), 215 PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE); 216 217 (void)gsc_intr_establish((struct gsc_softc *)parent, ga->ga_irq, 218 IPL_AUDIO, harmony_intr, sc, sc->sc_dv.dv_xname); 219 220 /* set defaults */ 221 sc->sc_in_port = HARMONY_IN_LINE; 222 sc->sc_out_port = HARMONY_OUT_SPEAKER; 223 sc->sc_input_lvl.left = sc->sc_input_lvl.right = 240; 224 sc->sc_output_lvl.left = sc->sc_output_lvl.right = 244; 225 sc->sc_monitor_lvl.left = sc->sc_monitor_lvl.right = 208; 226 sc->sc_outputgain = 0; 227 228 /* reset chip, and push default gain controls */ 229 harmony_reset_codec(sc); 230 231 cntl = READ_REG(sc, HARMONY_CNTL); 232 rev = (cntl & CNTL_CODEC_REV_MASK) >> CNTL_CODEC_REV_SHIFT; 233 printf(": rev %u", rev); 234 235 if (sc->sc_teleshare) 236 printf(", teleshare"); 237 printf("\n"); 238 239 if ((rev & CS4215_REV_VER) >= CS4215_REV_VER_E) 240 sc->sc_hasulinear8 = 1; 241 242 audio_attach_mi(&harmony_sa_hw_if, sc, NULL, &sc->sc_dv); 243 244 timeout_set(&sc->sc_acc_tmo, harmony_acc_tmo, sc); 245 sc->sc_acc_num = 0xa5a5a5a5; 246} 247 248void 249harmony_reset_codec(struct harmony_softc *sc) 250{ 251 /* silence */ 252 WRITE_REG(sc, HARMONY_GAINCTL, GAINCTL_OUTPUT_LEFT_M | 253 GAINCTL_OUTPUT_RIGHT_M | GAINCTL_MONITOR_M); 254 255 /* start reset */ 256 WRITE_REG(sc, HARMONY_RESET, RESET_RST); 257 258 DELAY(100000); /* wait at least 0.05 sec */ 259 260 harmony_set_gainctl(sc); 261 WRITE_REG(sc, HARMONY_RESET, 0); 262} 263 264void 265harmony_acc_tmo(void *v) 266{ 267 struct harmony_softc *sc = v; 268 269 ADD_CLKALLICA(sc); 270 timeout_add(&sc->sc_acc_tmo, 1); 271} 272 273/* 274 * interrupt handler 275 */ 276int 277harmony_intr(vsc) 278 void *vsc; 279{ 280 struct harmony_softc *sc = vsc; 281 struct harmony_channel *c; 282 u_int32_t dstatus; 283 int r = 0; 284 285 mtx_enter(&audio_lock); 286 ADD_CLKALLICA(sc); 287 288 harmony_intr_disable(sc); 289 290 dstatus = READ_REG(sc, HARMONY_DSTATUS); 291 292 if (dstatus & DSTATUS_PN) { 293 struct harmony_dma *d; 294 bus_addr_t nextaddr; 295 bus_size_t togo; 296 297 r = 1; 298 c = &sc->sc_playback; 299 d = c->c_current; 300 togo = c->c_segsz - c->c_cnt; 301 if (togo == 0) { 302 nextaddr = d->d_map->dm_segs[0].ds_addr; 303 c->c_cnt = togo = c->c_blksz; 304 } else { 305 nextaddr = c->c_lastaddr; 306 if (togo > c->c_blksz) 307 togo = c->c_blksz; 308 c->c_cnt += togo; 309 } 310 311 bus_dmamap_sync(sc->sc_dmat, d->d_map, 312 nextaddr - d->d_map->dm_segs[0].ds_addr, 313 c->c_blksz, BUS_DMASYNC_PREWRITE); 314 315 WRITE_REG(sc, HARMONY_PNXTADD, nextaddr); 316 SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE); 317 c->c_lastaddr = nextaddr + togo; 318 harmony_try_more(sc); 319 } 320 321 dstatus = READ_REG(sc, HARMONY_DSTATUS); 322 323 if (dstatus & DSTATUS_RN) { 324 c = &sc->sc_capture; 325 r = 1; 326 harmony_start_cp(sc); 327 if (sc->sc_capturing && c->c_intr != NULL) 328 (*c->c_intr)(c->c_intrarg); 329 } 330 331 if (READ_REG(sc, HARMONY_OV) & OV_OV) { 332 sc->sc_ov = 1; 333 WRITE_REG(sc, HARMONY_OV, 0); 334 } else 335 sc->sc_ov = 0; 336 337 harmony_intr_enable(sc); 338 mtx_leave(&audio_lock); 339 return (r); 340} 341 342void 343harmony_intr_enable(struct harmony_softc *sc) 344{ 345 WRITE_REG(sc, HARMONY_DSTATUS, DSTATUS_IE); 346 SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE); 347} 348 349void 350harmony_intr_disable(struct harmony_softc *sc) 351{ 352 WRITE_REG(sc, HARMONY_DSTATUS, 0); 353 SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE); 354} 355 356int 357harmony_open(void *vsc, int flags) 358{ 359 struct harmony_softc *sc = vsc; 360 361 if (sc->sc_open) 362 return (EBUSY); 363 sc->sc_open = 1; 364 return (0); 365} 366 367void 368harmony_close(void *vsc) 369{ 370 struct harmony_softc *sc = vsc; 371 372 /* XXX: not useful, halt_*() already called */ 373 harmony_halt_input(sc); 374 harmony_halt_output(sc); 375 harmony_intr_disable(sc); 376 sc->sc_open = 0; 377} 378 379int 380harmony_set_params(void *vsc, int setmode, int usemode, 381 struct audio_params *p, struct audio_params *r) 382{ 383 struct harmony_softc *sc = vsc; 384 u_int32_t bits; 385 386 switch (p->encoding) { 387 case AUDIO_ENCODING_ULAW: 388 bits = CNTL_FORMAT_ULAW; 389 p->precision = 8; 390 break; 391 case AUDIO_ENCODING_ALAW: 392 bits = CNTL_FORMAT_ALAW; 393 p->precision = 8; 394 break; 395 case AUDIO_ENCODING_SLINEAR_BE: 396 if (p->precision == 16) { 397 bits = CNTL_FORMAT_SLINEAR16BE; 398 break; 399 } 400 return (EINVAL); 401 case AUDIO_ENCODING_ULINEAR_LE: 402 case AUDIO_ENCODING_ULINEAR_BE: 403 if (p->precision == 8) { 404 bits = CNTL_FORMAT_ULINEAR8; 405 break; 406 } 407 return (EINVAL); 408 default: 409 return (EINVAL); 410 } 411 412 if (sc->sc_outputgain) 413 bits |= CNTL_OLB; 414 415 if (p->channels == 1) 416 bits |= CNTL_CHANS_MONO; 417 else if (p->channels == 2) 418 bits |= CNTL_CHANS_STEREO; 419 else 420 return (EINVAL); 421 422 r->sample_rate = p->sample_rate; 423 r->encoding = p->encoding; 424 r->precision = p->precision; 425 p->bps = AUDIO_BPS(p->precision); 426 r->bps = AUDIO_BPS(r->precision); 427 p->msb = r->msb = 1; 428 429 bits |= harmony_speed_bits(sc, &p->sample_rate); 430 sc->sc_cntlbits = bits; 431 sc->sc_need_commit = 1; 432 433 return (0); 434} 435 436int 437harmony_round_blocksize(void *vsc, int blk) 438{ 439 return (HARMONY_BUFSIZE); 440} 441 442int 443harmony_commit_settings(void *vsc) 444{ 445 struct harmony_softc *sc = vsc; 446 u_int32_t reg; 447 u_int8_t quietchar; 448 int i; 449 450 if (sc->sc_need_commit == 0) 451 return (0); 452 453 harmony_intr_disable(sc); 454 455 for (;;) { 456 reg = READ_REG(sc, HARMONY_DSTATUS); 457 if ((reg & (DSTATUS_PC | DSTATUS_RC)) == 0) 458 break; 459 } 460 461 /* Setting some bits in gainctl requires a reset */ 462 harmony_reset_codec(sc); 463 464 /* set the silence character based on the encoding type */ 465 bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map, 466 offsetof(struct harmony_empty, playback[0][0]), 467 PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_POSTWRITE); 468 switch (sc->sc_cntlbits & CNTL_FORMAT_MASK) { 469 case CNTL_FORMAT_ULAW: 470 quietchar = 0x7f; 471 break; 472 case CNTL_FORMAT_ALAW: 473 quietchar = 0x55; 474 break; 475 case CNTL_FORMAT_SLINEAR16BE: 476 case CNTL_FORMAT_ULINEAR8: 477 default: 478 quietchar = 0; 479 break; 480 } 481 for (i = 0; i < PLAYBACK_EMPTYS; i++) 482 memset(&sc->sc_empty_kva->playback[i][0], 483 quietchar, HARMONY_BUFSIZE); 484 bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map, 485 offsetof(struct harmony_empty, playback[0][0]), 486 PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE); 487 488 for (;;) { 489 /* Wait for it to come out of control mode */ 490 reg = READ_REG(sc, HARMONY_CNTL); 491 if ((reg & CNTL_C) == 0) 492 break; 493 } 494 495 bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CNTL, 496 sc->sc_cntlbits | CNTL_C); 497 498 for (;;) { 499 /* Wait for it to come out of control mode */ 500 reg = READ_REG(sc, HARMONY_CNTL); 501 if ((reg & CNTL_C) == 0) 502 break; 503 } 504 505 sc->sc_need_commit = 0; 506 507 if (sc->sc_playing || sc->sc_capturing) 508 harmony_intr_enable(sc); 509 510 return (0); 511} 512 513int 514harmony_halt_output(void *vsc) 515{ 516 struct harmony_softc *sc = vsc; 517 518 /* XXX: disable interrupts */ 519 sc->sc_playing = 0; 520 return (0); 521} 522 523int 524harmony_halt_input(void *vsc) 525{ 526 struct harmony_softc *sc = vsc; 527 528 /* XXX: disable interrupts */ 529 sc->sc_capturing = 0; 530 return (0); 531} 532 533int 534harmony_set_port(void *vsc, mixer_ctrl_t *cp) 535{ 536 struct harmony_softc *sc = vsc; 537 int err = EINVAL; 538 539 switch (cp->dev) { 540 case HARMONY_PORT_INPUT_LVL: 541 if (cp->type != AUDIO_MIXER_VALUE) 542 break; 543 if (cp->un.value.num_channels == 1) 544 sc->sc_input_lvl.left = sc->sc_input_lvl.right = 545 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]; 546 else if (cp->un.value.num_channels == 2) { 547 sc->sc_input_lvl.left = 548 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 549 sc->sc_input_lvl.right = 550 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 551 } else 552 break; 553 sc->sc_need_commit = 1; 554 err = 0; 555 break; 556 case HARMONY_PORT_OUTPUT_LVL: 557 if (cp->type != AUDIO_MIXER_VALUE) 558 break; 559 if (cp->un.value.num_channels == 1) 560 sc->sc_output_lvl.left = sc->sc_output_lvl.right = 561 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]; 562 else if (cp->un.value.num_channels == 2) { 563 sc->sc_output_lvl.left = 564 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 565 sc->sc_output_lvl.right = 566 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 567 } else 568 break; 569 sc->sc_need_commit = 1; 570 err = 0; 571 break; 572 case HARMONY_PORT_OUTPUT_GAIN: 573 if (cp->type != AUDIO_MIXER_ENUM) 574 break; 575 sc->sc_outputgain = cp->un.ord ? 1 : 0; 576 err = 0; 577 break; 578 case HARMONY_PORT_MONITOR_LVL: 579 if (cp->type != AUDIO_MIXER_VALUE) 580 break; 581 if (cp->un.value.num_channels != 1) 582 break; 583 sc->sc_monitor_lvl.left = sc->sc_input_lvl.right = 584 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]; 585 sc->sc_need_commit = 1; 586 err = 0; 587 break; 588 case HARMONY_PORT_RECORD_SOURCE: 589 if (cp->type != AUDIO_MIXER_ENUM) 590 break; 591 if (cp->un.ord != HARMONY_IN_LINE && 592 cp->un.ord != HARMONY_IN_MIC) 593 break; 594 sc->sc_in_port = cp->un.ord; 595 err = 0; 596 sc->sc_need_commit = 1; 597 break; 598 case HARMONY_PORT_OUTPUT_SOURCE: 599 if (cp->type != AUDIO_MIXER_ENUM) 600 break; 601 if (cp->un.ord != HARMONY_OUT_LINE && 602 cp->un.ord != HARMONY_OUT_SPEAKER && 603 cp->un.ord != HARMONY_OUT_HEADPHONE) 604 break; 605 sc->sc_out_port = cp->un.ord; 606 err = 0; 607 sc->sc_need_commit = 1; 608 break; 609 } 610 611 return (err); 612} 613 614int 615harmony_get_port(void *vsc, mixer_ctrl_t *cp) 616{ 617 struct harmony_softc *sc = vsc; 618 int err = EINVAL; 619 620 switch (cp->dev) { 621 case HARMONY_PORT_INPUT_LVL: 622 if (cp->type != AUDIO_MIXER_VALUE) 623 break; 624 if (cp->un.value.num_channels == 1) { 625 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = 626 sc->sc_input_lvl.left; 627 } else if (cp->un.value.num_channels == 2) { 628 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 629 sc->sc_input_lvl.left; 630 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 631 sc->sc_input_lvl.right; 632 } else 633 break; 634 err = 0; 635 break; 636 case HARMONY_PORT_INPUT_OV: 637 if (cp->type != AUDIO_MIXER_ENUM) 638 break; 639 cp->un.ord = sc->sc_ov ? 1 : 0; 640 err = 0; 641 break; 642 case HARMONY_PORT_OUTPUT_LVL: 643 if (cp->type != AUDIO_MIXER_VALUE) 644 break; 645 if (cp->un.value.num_channels == 1) { 646 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = 647 sc->sc_output_lvl.left; 648 } else if (cp->un.value.num_channels == 2) { 649 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 650 sc->sc_output_lvl.left; 651 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 652 sc->sc_output_lvl.right; 653 } else 654 break; 655 err = 0; 656 break; 657 case HARMONY_PORT_OUTPUT_GAIN: 658 if (cp->type != AUDIO_MIXER_ENUM) 659 break; 660 cp->un.ord = sc->sc_outputgain ? 1 : 0; 661 err = 0; 662 break; 663 case HARMONY_PORT_MONITOR_LVL: 664 if (cp->type != AUDIO_MIXER_VALUE) 665 break; 666 if (cp->un.value.num_channels != 1) 667 break; 668 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = 669 sc->sc_monitor_lvl.left; 670 err = 0; 671 break; 672 case HARMONY_PORT_RECORD_SOURCE: 673 if (cp->type != AUDIO_MIXER_ENUM) 674 break; 675 cp->un.ord = sc->sc_in_port; 676 err = 0; 677 break; 678 case HARMONY_PORT_OUTPUT_SOURCE: 679 if (cp->type != AUDIO_MIXER_ENUM) 680 break; 681 cp->un.ord = sc->sc_out_port; 682 err = 0; 683 break; 684 } 685 return (0); 686} 687 688int 689harmony_query_devinfo(void *vsc, mixer_devinfo_t *dip) 690{ 691 int err = 0; 692 693 switch (dip->index) { 694 case HARMONY_PORT_INPUT_LVL: 695 dip->type = AUDIO_MIXER_VALUE; 696 dip->mixer_class = HARMONY_PORT_INPUT_CLASS; 697 dip->prev = dip->next = AUDIO_MIXER_LAST; 698 strlcpy(dip->label.name, AudioNinput, sizeof dip->label.name); 699 dip->un.v.num_channels = 2; 700 strlcpy(dip->un.v.units.name, AudioNvolume, 701 sizeof dip->un.v.units.name); 702 break; 703 case HARMONY_PORT_INPUT_OV: 704 dip->type = AUDIO_MIXER_ENUM; 705 dip->mixer_class = HARMONY_PORT_INPUT_CLASS; 706 dip->prev = dip->next = AUDIO_MIXER_LAST; 707 strlcpy(dip->label.name, "overrange", sizeof dip->label.name); 708 dip->un.e.num_mem = 2; 709 strlcpy(dip->un.e.member[0].label.name, AudioNoff, 710 sizeof dip->un.e.member[0].label.name); 711 dip->un.e.member[0].ord = 0; 712 strlcpy(dip->un.e.member[1].label.name, AudioNon, 713 sizeof dip->un.e.member[1].label.name); 714 dip->un.e.member[1].ord = 1; 715 break; 716 case HARMONY_PORT_OUTPUT_LVL: 717 dip->type = AUDIO_MIXER_VALUE; 718 dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS; 719 dip->prev = dip->next = AUDIO_MIXER_LAST; 720 strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name); 721 dip->un.v.num_channels = 2; 722 strlcpy(dip->un.v.units.name, AudioNvolume, 723 sizeof dip->un.v.units.name); 724 break; 725 case HARMONY_PORT_OUTPUT_GAIN: 726 dip->type = AUDIO_MIXER_ENUM; 727 dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS; 728 dip->prev = dip->next = AUDIO_MIXER_LAST; 729 strlcpy(dip->label.name, "gain", sizeof dip->label.name); 730 dip->un.e.num_mem = 2; 731 strlcpy(dip->un.e.member[0].label.name, AudioNoff, 732 sizeof dip->un.e.member[0].label.name); 733 dip->un.e.member[0].ord = 0; 734 strlcpy(dip->un.e.member[1].label.name, AudioNon, 735 sizeof dip->un.e.member[1].label.name); 736 dip->un.e.member[1].ord = 1; 737 break; 738 case HARMONY_PORT_MONITOR_LVL: 739 dip->type = AUDIO_MIXER_VALUE; 740 dip->mixer_class = HARMONY_PORT_MONITOR_CLASS; 741 dip->prev = dip->next = AUDIO_MIXER_LAST; 742 strlcpy(dip->label.name, AudioNmonitor, sizeof dip->label.name); 743 dip->un.v.num_channels = 1; 744 strlcpy(dip->un.v.units.name, AudioNvolume, 745 sizeof dip->un.v.units.name); 746 break; 747 case HARMONY_PORT_RECORD_SOURCE: 748 dip->type = AUDIO_MIXER_ENUM; 749 dip->mixer_class = HARMONY_PORT_RECORD_CLASS; 750 dip->prev = dip->next = AUDIO_MIXER_LAST; 751 strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name); 752 dip->un.e.num_mem = 2; 753 strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone, 754 sizeof dip->un.e.member[0].label.name); 755 dip->un.e.member[0].ord = HARMONY_IN_MIC; 756 strlcpy(dip->un.e.member[1].label.name, AudioNline, 757 sizeof dip->un.e.member[1].label.name); 758 dip->un.e.member[1].ord = HARMONY_IN_LINE; 759 break; 760 case HARMONY_PORT_OUTPUT_SOURCE: 761 dip->type = AUDIO_MIXER_ENUM; 762 dip->mixer_class = HARMONY_PORT_MONITOR_CLASS; 763 dip->prev = dip->next = AUDIO_MIXER_LAST; 764 strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name); 765 dip->un.e.num_mem = 3; 766 strlcpy(dip->un.e.member[0].label.name, AudioNline, 767 sizeof dip->un.e.member[0].label.name); 768 dip->un.e.member[0].ord = HARMONY_OUT_LINE; 769 strlcpy(dip->un.e.member[1].label.name, AudioNspeaker, 770 sizeof dip->un.e.member[1].label.name); 771 dip->un.e.member[1].ord = HARMONY_OUT_SPEAKER; 772 strlcpy(dip->un.e.member[2].label.name, AudioNheadphone, 773 sizeof dip->un.e.member[2].label.name); 774 dip->un.e.member[2].ord = HARMONY_OUT_HEADPHONE; 775 break; 776 case HARMONY_PORT_INPUT_CLASS: 777 dip->type = AUDIO_MIXER_CLASS; 778 dip->mixer_class = HARMONY_PORT_INPUT_CLASS; 779 dip->prev = dip->next = AUDIO_MIXER_LAST; 780 strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name); 781 break; 782 case HARMONY_PORT_OUTPUT_CLASS: 783 dip->type = AUDIO_MIXER_CLASS; 784 dip->mixer_class = HARMONY_PORT_INPUT_CLASS; 785 dip->prev = dip->next = AUDIO_MIXER_LAST; 786 strlcpy(dip->label.name, AudioCoutputs, sizeof dip->label.name); 787 break; 788 case HARMONY_PORT_MONITOR_CLASS: 789 dip->type = AUDIO_MIXER_CLASS; 790 dip->mixer_class = HARMONY_PORT_INPUT_CLASS; 791 dip->prev = dip->next = AUDIO_MIXER_LAST; 792 strlcpy(dip->label.name, AudioCmonitor, sizeof dip->label.name); 793 break; 794 case HARMONY_PORT_RECORD_CLASS: 795 dip->type = AUDIO_MIXER_CLASS; 796 dip->mixer_class = HARMONY_PORT_RECORD_CLASS; 797 dip->prev = dip->next = AUDIO_MIXER_LAST; 798 strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name); 799 break; 800 default: 801 err = ENXIO; 802 break; 803 } 804 805 return (err); 806} 807 808void * 809harmony_allocm(void *vsc, int dir, size_t size, int pool, int flags) 810{ 811 struct harmony_softc *sc = vsc; 812 struct harmony_dma *d; 813 int rseg; 814 815 d = (struct harmony_dma *)malloc(sizeof(struct harmony_dma), pool, flags); 816 if (d == NULL) 817 goto fail; 818 819 if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT, 820 &d->d_map) != 0) 821 goto fail1; 822 823 if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &d->d_seg, 1, 824 &rseg, BUS_DMA_NOWAIT) != 0) 825 goto fail2; 826 827 if (bus_dmamem_map(sc->sc_dmat, &d->d_seg, 1, size, &d->d_kva, 828 BUS_DMA_NOWAIT) != 0) 829 goto fail3; 830 831 if (bus_dmamap_load(sc->sc_dmat, d->d_map, d->d_kva, size, NULL, 832 BUS_DMA_NOWAIT) != 0) 833 goto fail4; 834 835 d->d_next = sc->sc_dmas; 836 sc->sc_dmas = d; 837 d->d_size = size; 838 return (d->d_kva); 839 840fail4: 841 bus_dmamem_unmap(sc->sc_dmat, d->d_kva, size); 842fail3: 843 bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1); 844fail2: 845 bus_dmamap_destroy(sc->sc_dmat, d->d_map); 846fail1: 847 free(d, pool, sizeof *d); 848fail: 849 return (NULL); 850} 851 852void 853harmony_freem(void *vsc, void *ptr, int pool) 854{ 855 struct harmony_softc *sc = vsc; 856 struct harmony_dma *d, **dd; 857 858 for (dd = &sc->sc_dmas; (d = *dd) != NULL; dd = &(*dd)->d_next) { 859 if (d->d_kva != ptr) 860 continue; 861 bus_dmamap_unload(sc->sc_dmat, d->d_map); 862 bus_dmamem_unmap(sc->sc_dmat, d->d_kva, d->d_size); 863 bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1); 864 bus_dmamap_destroy(sc->sc_dmat, d->d_map); 865 free(d, pool, sizeof *d); 866 return; 867 } 868 printf("%s: free rogue pointer\n", sc->sc_dv.dv_xname); 869} 870 871size_t 872harmony_round_buffersize(void *vsc, int direction, size_t size) 873{ 874 return ((size + HARMONY_BUFSIZE - 1) & (size_t)(-HARMONY_BUFSIZE)); 875} 876 877int 878harmony_trigger_output(void *vsc, void *start, void *end, int blksize, 879 void (*intr)(void *), void *intrarg, struct audio_params *param) 880{ 881 struct harmony_softc *sc = vsc; 882 struct harmony_channel *c = &sc->sc_playback; 883 struct harmony_dma *d; 884 bus_addr_t nextaddr; 885 bus_size_t togo; 886 887 for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next) 888 /*EMPTY*/; 889 if (d == NULL) { 890 printf("%s: trigger_output: bad addr: %p\n", 891 sc->sc_dv.dv_xname, start); 892 return (EINVAL); 893 } 894 895 c->c_intr = intr; 896 c->c_intrarg = intrarg; 897 c->c_blksz = blksize; 898 c->c_current = d; 899 c->c_segsz = (caddr_t)end - (caddr_t)start; 900 c->c_cnt = 0; 901 c->c_lastaddr = d->d_map->dm_segs[0].ds_addr; 902 903 sc->sc_playing = 1; 904 905 togo = c->c_segsz - c->c_cnt; 906 if (togo == 0) { 907 nextaddr = d->d_map->dm_segs[0].ds_addr; 908 c->c_cnt = togo = c->c_blksz; 909 } else { 910 nextaddr = c->c_lastaddr; 911 if (togo > c->c_blksz) 912 togo = c->c_blksz; 913 c->c_cnt += togo; 914 } 915 916 bus_dmamap_sync(sc->sc_dmat, d->d_map, 917 nextaddr - d->d_map->dm_segs[0].ds_addr, 918 c->c_blksz, BUS_DMASYNC_PREWRITE); 919 920 mtx_enter(&audio_lock); 921 WRITE_REG(sc, HARMONY_PNXTADD, nextaddr); 922 c->c_theaddr = nextaddr; 923 SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE); 924 c->c_lastaddr = nextaddr + togo; 925 926 harmony_start_cp(sc); 927 harmony_intr_enable(sc); 928 mtx_leave(&audio_lock); 929 return (0); 930} 931 932void 933harmony_start_cp(struct harmony_softc *sc) 934{ 935 struct harmony_channel *c = &sc->sc_capture; 936 struct harmony_dma *d; 937 bus_addr_t nextaddr; 938 bus_size_t togo; 939 940 if (sc->sc_capturing == 0) { 941 WRITE_REG(sc, HARMONY_RNXTADD, 942 sc->sc_capture_paddrs[sc->sc_capture_empty]); 943 if (++sc->sc_capture_empty == CAPTURE_EMPTYS) 944 sc->sc_capture_empty = 0; 945 } else { 946 d = c->c_current; 947 togo = c->c_segsz - c->c_cnt; 948 if (togo == 0) { 949 nextaddr = d->d_map->dm_segs[0].ds_addr; 950 c->c_cnt = togo = c->c_blksz; 951 } else { 952 nextaddr = c->c_lastaddr; 953 if (togo > c->c_blksz) 954 togo = c->c_blksz; 955 c->c_cnt += togo; 956 } 957 958 bus_dmamap_sync(sc->sc_dmat, d->d_map, 959 nextaddr - d->d_map->dm_segs[0].ds_addr, 960 c->c_blksz, BUS_DMASYNC_PREWRITE); 961 962 WRITE_REG(sc, HARMONY_RNXTADD, nextaddr); 963 SYNC_REG(sc, HARMONY_RNXTADD, BUS_SPACE_BARRIER_WRITE); 964 c->c_lastaddr = nextaddr + togo; 965 } 966 967 timeout_add(&sc->sc_acc_tmo, 1); 968} 969 970int 971harmony_trigger_input(void *vsc, void *start, void *end, int blksize, 972 void (*intr)(void *), void *intrarg, struct audio_params *param) 973{ 974 struct harmony_softc *sc = vsc; 975 struct harmony_channel *c = &sc->sc_capture; 976 struct harmony_dma *d; 977 978 for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next) 979 /*EMPTY*/; 980 if (d == NULL) { 981 printf("%s: trigger_input: bad addr: %p\n", 982 sc->sc_dv.dv_xname, start); 983 return (EINVAL); 984 } 985 986 c->c_intr = intr; 987 c->c_intrarg = intrarg; 988 c->c_blksz = blksize; 989 c->c_current = d; 990 c->c_segsz = (caddr_t)end - (caddr_t)start; 991 c->c_cnt = 0; 992 c->c_lastaddr = d->d_map->dm_segs[0].ds_addr; 993 mtx_enter(&audio_lock); 994 sc->sc_capturing = 1; 995 harmony_start_cp(sc); 996 harmony_intr_enable(sc); 997 mtx_leave(&audio_lock); 998 return (0); 999} 1000 1001static const struct speed_struct { 1002 u_int32_t speed; 1003 u_int32_t bits; 1004} harmony_speeds[] = { 1005 { 5125, CNTL_RATE_5125 }, 1006 { 6615, CNTL_RATE_6615 }, 1007 { 8000, CNTL_RATE_8000 }, 1008 { 9600, CNTL_RATE_9600 }, 1009 { 11025, CNTL_RATE_11025 }, 1010 { 16000, CNTL_RATE_16000 }, 1011 { 18900, CNTL_RATE_18900 }, 1012 { 22050, CNTL_RATE_22050 }, 1013 { 27428, CNTL_RATE_27428 }, 1014 { 32000, CNTL_RATE_32000 }, 1015 { 33075, CNTL_RATE_33075 }, 1016 { 37800, CNTL_RATE_37800 }, 1017 { 44100, CNTL_RATE_44100 }, 1018 { 48000, CNTL_RATE_48000 }, 1019}; 1020 1021u_int32_t 1022harmony_speed_bits(struct harmony_softc *sc, u_long *speedp) 1023{ 1024 int i, n, selected = -1; 1025 1026 n = sizeof(harmony_speeds) / sizeof(harmony_speeds[0]); 1027 1028 if ((*speedp) <= harmony_speeds[0].speed) 1029 selected = 0; 1030 else if ((*speedp) >= harmony_speeds[n - 1].speed) 1031 selected = n - 1; 1032 else { 1033 for (i = 1; selected == -1 && i < n; i++) { 1034 if ((*speedp) == harmony_speeds[i].speed) 1035 selected = i; 1036 else if ((*speedp) < harmony_speeds[i].speed) { 1037 int diff1, diff2; 1038 1039 diff1 = (*speedp) - harmony_speeds[i - 1].speed; 1040 diff2 = harmony_speeds[i].speed - (*speedp); 1041 if (diff1 < diff2) 1042 selected = i - 1; 1043 else 1044 selected = i; 1045 } 1046 } 1047 } 1048 1049 if (selected == -1) 1050 selected = 2; 1051 1052 *speedp = harmony_speeds[selected].speed; 1053 return (harmony_speeds[selected].bits); 1054} 1055 1056int 1057harmony_set_gainctl(struct harmony_softc *sc) 1058{ 1059 u_int32_t bits, mask, val, old; 1060 1061 /* XXX leave these bits alone or the chip will not come out of CNTL */ 1062 bits = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK; 1063 1064 /* input level */ 1065 bits |= ((sc->sc_input_lvl.left >> (8 - GAINCTL_INPUT_BITS)) << 1066 GAINCTL_INPUT_LEFT_S) & GAINCTL_INPUT_LEFT_M; 1067 bits |= ((sc->sc_input_lvl.right >> (8 - GAINCTL_INPUT_BITS)) << 1068 GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M; 1069 1070 /* output level (inverted) */ 1071 mask = (1 << GAINCTL_OUTPUT_BITS) - 1; 1072 val = mask - (sc->sc_output_lvl.left >> (8 - GAINCTL_OUTPUT_BITS)); 1073 bits |= (val << GAINCTL_OUTPUT_LEFT_S) & GAINCTL_OUTPUT_LEFT_M; 1074 val = mask - (sc->sc_output_lvl.right >> (8 - GAINCTL_OUTPUT_BITS)); 1075 bits |= (val << GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M; 1076 1077 /* monitor level (inverted) */ 1078 mask = (1 << GAINCTL_MONITOR_BITS) - 1; 1079 val = mask - (sc->sc_monitor_lvl.left >> (8 - GAINCTL_MONITOR_BITS)); 1080 bits |= (val << GAINCTL_MONITOR_S) & GAINCTL_MONITOR_M; 1081 1082 /* XXX messing with these causes CNTL_C to get stuck... grr. */ 1083 bits &= ~GAINCTL_IS_MASK; 1084 if (sc->sc_in_port == HARMONY_IN_MIC) 1085 bits |= GAINCTL_IS_LINE; 1086 else 1087 bits |= GAINCTL_IS_MICROPHONE; 1088 1089 /* XXX messing with these causes CNTL_C to get stuck... grr. */ 1090 bits &= ~(GAINCTL_LE | GAINCTL_HE | GAINCTL_SE); 1091 if (sc->sc_out_port == HARMONY_OUT_LINE) 1092 bits |= GAINCTL_LE; 1093 else if (sc->sc_out_port == HARMONY_OUT_SPEAKER) 1094 bits |= GAINCTL_SE; 1095 else 1096 bits |= GAINCTL_HE; 1097 1098 mask = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK; 1099 old = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL); 1100 bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL, bits); 1101 if ((old & mask) != (bits & mask)) 1102 return (1); 1103 return (0); 1104} 1105 1106void 1107harmony_try_more(struct harmony_softc *sc) 1108{ 1109 struct harmony_channel *c = &sc->sc_playback; 1110 struct harmony_dma *d = c->c_current; 1111 u_int32_t cur; 1112 int i, nsegs; 1113 1114 cur = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_PCURADD); 1115 cur &= PCURADD_BUFMASK; 1116 nsegs = 0; 1117 1118#ifdef DIAGNOSTIC 1119 if (cur < d->d_map->dm_segs[0].ds_addr || 1120 cur >= (d->d_map->dm_segs[0].ds_addr + c->c_segsz)) 1121 panic("%s: bad current %x < %lx || %x > %lx", 1122 sc->sc_dv.dv_xname, cur, d->d_map->dm_segs[0].ds_addr, cur, 1123 d->d_map->dm_segs[0].ds_addr + c->c_segsz); 1124#endif /* DIAGNOSTIC */ 1125 1126 if (cur > c->c_theaddr) { 1127 nsegs = (cur - c->c_theaddr) / HARMONY_BUFSIZE; 1128 } else if (cur < c->c_theaddr) { 1129 nsegs = (d->d_map->dm_segs[0].ds_addr + c->c_segsz - 1130 c->c_theaddr) / HARMONY_BUFSIZE; 1131 nsegs += (cur - d->d_map->dm_segs[0].ds_addr) / 1132 HARMONY_BUFSIZE; 1133 } 1134 1135 if (nsegs != 0 && c->c_intr != NULL) { 1136 for (i = 0; i < nsegs; i++) 1137 (*c->c_intr)(c->c_intrarg); 1138 c->c_theaddr = cur; 1139 } 1140} 1141 1142struct cfdriver harmony_cd = { 1143 NULL, "harmony", DV_DULL 1144}; 1145 1146const struct cfattach harmony_ca = { 1147 sizeof(struct harmony_softc), harmony_match, harmony_attach 1148}; 1149