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