1 /* $NetBSD: repulse.c,v 1.16 2008/04/28 20:23:12 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Ignatios Souvatzis. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: repulse.c,v 1.16 2008/04/28 20:23:12 martin Exp $"); 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/device.h> 40 #include <sys/fcntl.h> /* FREAD */ 41 42 #include <machine/bus.h> 43 44 #include <sys/audioio.h> 45 #include <dev/audio_if.h> 46 #include <dev/mulaw.h> 47 48 #include <dev/ic/ac97reg.h> 49 #include <dev/ic/ac97var.h> 50 51 #include <amiga/dev/zbusvar.h> 52 #include <amiga/amiga/isr.h> 53 54 #include <amiga/dev/repulse_firmware.h> 55 56 #ifndef vu_int8_t 57 #define vu_int8_t volatile uint8_t 58 #endif 59 #ifndef vu_int16_t 60 #define vu_int16_t volatile uint16_t 61 #endif 62 #ifndef vu_int32_t 63 #define vu_int32_t volatile uint32_t 64 #endif 65 66 /* ac97 attachment functions */ 67 68 int repac_attach(void *, struct ac97_codec_if *); 69 int repac_read(void *, uint8_t, uint16_t *); 70 int repac_write(void *, uint8_t, uint16_t); 71 int repac_reset(void *); 72 enum ac97_host_flag repac_flags(void *); 73 74 /* audio attachment functions */ 75 76 void rep_close(void *); 77 int rep_getdev(void *, struct audio_device *); 78 int rep_get_props(void *); 79 int rep_halt_output(void *); 80 int rep_halt_input(void *); 81 int rep_query_encoding(void *, struct audio_encoding *); 82 int rep_set_params(void *, int, int, audio_params_t *, 83 audio_params_t *, stream_filter_list_t *, stream_filter_list_t *); 84 int rep_round_blocksize(void *, int, int, const audio_params_t *); 85 int rep_set_port(void *, mixer_ctrl_t *); 86 int rep_get_port(void *, mixer_ctrl_t *); 87 int rep_query_devinfo(void *, mixer_devinfo_t *); 88 size_t rep_round_buffersize(void *, int, size_t); 89 90 int rep_start_input(void *, void *, int, void (*)(void *), void *); 91 int rep_start_output(void *, void *, int, void (*)(void *), void *); 92 93 int rep_intr(void *); 94 95 96 /* audio attachment */ 97 98 const struct audio_hw_if rep_hw_if = { 99 /* open */ 0, 100 rep_close, 101 /* drain */ 0, 102 rep_query_encoding, 103 rep_set_params, 104 rep_round_blocksize, 105 /* commit_setting */ 0, 106 /* init_output */ 0, 107 /* init_input */ 0, 108 rep_start_output, 109 rep_start_input, 110 rep_halt_output, 111 rep_halt_input, 112 /* speaker_ctl */ 0, 113 rep_getdev, 114 /* getfd */ 0, 115 rep_set_port, 116 rep_get_port, 117 rep_query_devinfo, 118 /* allocm */ 0, 119 /* freem */ 0, 120 rep_round_buffersize, 121 /* mappage */ 0, 122 rep_get_props, 123 /* trigger_output */ 0, 124 /* trigger_input */ 0, 125 /* dev_ioctl */ 0, 126 }; 127 128 /* hardware registers */ 129 130 struct repulse_hw { 131 vu_int16_t rhw_status; 132 vu_int16_t rhw_fifostatus; /* 0xrrrrpppp0000flag */ 133 vu_int16_t rhw_reg_address; 134 vu_int16_t rhw_reg_data; 135 /* 0x08 */ 136 vu_int16_t rhw_fifo_lh; 137 vu_int16_t rhw_fifo_ll; 138 vu_int16_t rhw_fifo_rh; 139 vu_int16_t rhw_fifo_rl; 140 /* 0x10 */ 141 vu_int16_t rhw_fifo_pack; 142 vu_int16_t rhw_play_fifosz; 143 vu_int32_t rhw_spdifin_stat; 144 #define rhw_spdifout_stat rhw_spdifin_stat; 145 146 /* 0x18 */ 147 vu_int16_t rhw_capt_fifosz; 148 vu_int16_t rhw_version; 149 vu_int16_t rhw_dummy1; 150 vu_int8_t rhw_firmwareload; 151 /* 0x1F */ 152 vu_int8_t rhw_dummy2[66 - 31]; 153 /* 0x42 */ 154 vu_int16_t rhw_reset; 155 } /* __attribute__((packed)) */; 156 157 #define REPSTATUS_PLAY 0x0001 158 #define REPSTATUS_RECORD 0x0002 159 #define REPSTATUS_PLAYFIFORST 0x0004 160 #define REPSTATUS_RECFIFORST 0x0008 161 162 #define REPSTATUS_REGSENDBUSY 0x0010 163 #define REPSTATUS_LOOPBACK 0x0020 164 #define REPSTATUS_ENSPDIFIN 0x0040 165 #define REPSTATUS_ENSPDIFOUT 0x0080 166 167 #define REPSTATUS_CODECRESET 0x0200 168 #define REPSTATUS_SPDIFOUT24 0x0400 169 #define REPSTATUS_SPDIFIN24 0x0800 170 171 #define REPSTATUS_RECIRQENABLE 0x1000 172 #define REPSTATUS_RECIRQACK 0x2000 173 #define REPSTATUS_PLAYIRQENABLE 0x4000 174 #define REPSTATUS_PLAYIRQACK 0x8000 175 176 #define REPFIFO_PLAYFIFOFULL 0x0001 177 #define REPFIFO_PLAYFIFOEMPTY 0x0002 178 #define REPFIFO_RECFIFOFULL 0x0004 179 #define REPFIFO_RECFIFOEMPTY 0x0008 180 #define REPFIFO_PLAYFIFOGAUGE(x) ((x << 4) & 0xf000) 181 #define REPFIFO_RECFIFOGAUGE(x) (x & 0xf000) 182 183 /* ac97 data stream transfer functions */ 184 void rep_read_16_stereo(struct repulse_hw *, uint8_t *, int, unsigned); 185 void rep_read_16_mono(struct repulse_hw *, uint8_t *, int, unsigned); 186 void rep_write_16_stereo(struct repulse_hw *, uint8_t *, int, unsigned); 187 void rep_write_16_mono(struct repulse_hw *, uint8_t *, int, unsigned); 188 void rep_read_8_stereo(struct repulse_hw *, uint8_t *, int, unsigned); 189 void rep_read_8_mono(struct repulse_hw *, uint8_t *, int, unsigned); 190 void rep_write_8_stereo(struct repulse_hw *, uint8_t *, int, unsigned); 191 void rep_write_8_mono(struct repulse_hw *, uint8_t *, int, unsigned); 192 193 /* AmigaDOS Delay() ticks */ 194 195 #define USECPERTICK (1000000/50) 196 197 /* NetBSD device attachment */ 198 199 struct repulse_softc { 200 struct device sc_dev; 201 struct isr sc_isr; 202 struct ac97_host_if sc_achost; 203 struct ac97_codec_if *sc_codec_if; 204 205 struct repulse_hw *sc_boardp; 206 207 void (*sc_captmore)(void *); 208 void *sc_captarg; 209 210 void (*sc_captfun)(struct repulse_hw *, uint8_t *, int, unsigned); 211 void *sc_captbuf; 212 int sc_captscale; 213 int sc_captbufsz; 214 unsigned sc_captflags; 215 216 217 void (*sc_playmore)(void *); 218 void *sc_playarg; 219 void (*sc_playfun)(struct repulse_hw *, uint8_t *, int, unsigned); 220 int sc_playscale; 221 unsigned sc_playflags; 222 223 }; 224 225 int repulse_match (struct device *, struct cfdata *, void *); 226 void repulse_attach (struct device *, struct device *, void *); 227 228 CFATTACH_DECL(repulse, sizeof(struct repulse_softc), 229 repulse_match, repulse_attach, NULL, NULL); 230 231 int 232 repulse_match(struct device *parent, struct cfdata *cfp, void *aux) 233 { 234 struct zbus_args *zap; 235 236 zap = aux; 237 238 if (zap->manid != 0x4144) 239 return (0); 240 241 if (zap->prodid != 0) 242 return (0); 243 244 return (1); 245 } 246 247 void 248 repulse_attach(struct device *parent, struct device *self, void *aux) 249 { 250 struct repulse_softc *sc; 251 struct zbus_args *zap; 252 struct repulse_hw *bp; 253 const uint8_t *fwp; 254 int needs_firmware; 255 uint16_t a; 256 257 sc = (struct repulse_softc *)self; 258 zap = aux; 259 bp = (struct repulse_hw *)zap->va; 260 sc->sc_boardp = bp; 261 262 needs_firmware = 0; 263 if (bp->rhw_fifostatus & 0x00f0) 264 needs_firmware = 1; 265 else { 266 bp->rhw_status = 0x000c; 267 if (bp->rhw_status != 0 || bp->rhw_fifostatus != 0x0f0a) 268 needs_firmware = 1; 269 } 270 271 printf(": "); 272 if (needs_firmware) { 273 printf("loading "); 274 bp->rhw_reset = 0; 275 276 delay(1 * USECPERTICK); 277 278 for (fwp = (const uint8_t *)repulse_firmware; 279 fwp < (repulse_firmware_size + 280 (const uint8_t *)repulse_firmware); fwp++) 281 bp->rhw_firmwareload = *fwp; 282 283 delay(1 * USECPERTICK); 284 285 if (bp->rhw_fifostatus & 0x00f0) 286 goto Initerr; 287 288 a = /* bp->rhw_status; 289 a |= */ REPSTATUS_CODECRESET; 290 bp->rhw_status = a; 291 292 a = bp->rhw_status; 293 if ((a & REPSTATUS_CODECRESET) == 0) 294 goto Initerr; 295 296 (void)bp->rhw_status; 297 (void)bp->rhw_status; 298 (void)bp->rhw_status; 299 a = bp->rhw_status; 300 a &= ~REPSTATUS_CODECRESET; 301 bp->rhw_status = a; 302 } 303 304 printf("firmware version 0x%x\n", bp->rhw_version); 305 306 sc->sc_achost.arg = sc; 307 308 sc->sc_achost.reset = repac_reset; 309 sc->sc_achost.read = repac_read; 310 sc->sc_achost.write = repac_write; 311 sc->sc_achost.attach = repac_attach; 312 sc->sc_achost.flags = 0; 313 314 if (ac97_attach(&sc->sc_achost, self)) { 315 printf("%s: error attaching codec\n", self->dv_xname); 316 return; 317 } 318 319 #ifdef DIAGNOSTIC 320 /* 321 * Print a warning if the codec doesn't support hardware variable 322 * rate audio. As the initial incarnations of the Repulse board 323 * are AC'97 2.1, it is epxected that we'll always have VRA. 324 */ 325 /* 326 * XXX this should be a panic(). OTOH, audio codec speed is not 327 * important enough to do this. 328 */ 329 a = sc->sc_codec_if->vtbl->get_extcaps(sc->sc_codec_if); 330 if (!(a & AC97_EXT_AUDIO_VRA)) { 331 printf("%s: warning: codec doesn't support " 332 "hardware AC'97 2.0 Variable Rate Audio\n", 333 sc->sc_dev.dv_xname); 334 } 335 #endif 336 337 sc->sc_isr.isr_ipl = 2; 338 sc->sc_isr.isr_arg = sc; 339 sc->sc_isr.isr_intr = rep_intr; 340 add_isr(&sc->sc_isr); 341 342 audio_attach_mi(&rep_hw_if, sc, &sc->sc_dev); 343 344 return; 345 346 Initerr: 347 printf("\n%s: firmware not successfully loaded\n", self->dv_xname); 348 return; 349 350 } 351 352 int 353 repac_reset(void *arg) 354 { 355 struct repulse_softc *sc; 356 struct repulse_hw *bp; 357 uint16_t a; 358 359 sc = arg; 360 bp = sc->sc_boardp; 361 a = bp->rhw_status; 362 a |= REPSTATUS_CODECRESET; 363 bp->rhw_status = a; 364 365 a = bp->rhw_status; 366 #ifdef DIAGNOSTIC 367 if ((a & REPSTATUS_CODECRESET) == 0) 368 panic("%s: cannot set reset bit", sc->sc_dev.dv_xname); 369 #endif 370 371 a = bp->rhw_status; 372 a = bp->rhw_status; 373 a = bp->rhw_status; 374 a = bp->rhw_status; 375 a &= ~REPSTATUS_CODECRESET; 376 bp->rhw_status = a; 377 return 0; 378 } 379 380 int 381 repac_read(void *arg, u_int8_t reg, u_int16_t *valuep) 382 { 383 struct repulse_softc *sc; 384 struct repulse_hw *bp; 385 386 sc = arg; 387 bp = sc->sc_boardp; 388 while (bp->rhw_status & REPSTATUS_REGSENDBUSY) 389 continue; 390 bp->rhw_reg_address = (reg & 0x7F) | 0x80; 391 392 while (bp->rhw_status & REPSTATUS_REGSENDBUSY) 393 continue; 394 395 *valuep = bp->rhw_reg_data; 396 397 return 0; 398 } 399 400 int 401 repac_write(void *arg, uint8_t reg, uint16_t value) 402 { 403 struct repulse_softc *sc; 404 struct repulse_hw *bp; 405 406 sc = arg; 407 bp = sc->sc_boardp; 408 bp->rhw_reg_data = value; 409 bp->rhw_reg_address = reg & 0x7F; 410 411 while (bp->rhw_status & REPSTATUS_REGSENDBUSY) 412 continue; 413 414 return 0; 415 } 416 417 int 418 repac_attach(void *arg, struct ac97_codec_if *acip) 419 { 420 struct repulse_softc *sc; 421 422 sc = arg; 423 sc->sc_codec_if = acip; 424 425 return 0; 426 } 427 428 /* audio(9) support stuff which is not ac97-constant */ 429 430 void 431 rep_close(void *arg) 432 { 433 struct repulse_softc *sc; 434 435 sc = arg; 436 rep_halt_output(sc); 437 rep_halt_input(sc); 438 } 439 440 int 441 rep_getdev(void *arg, struct audio_device *retp) 442 { 443 struct repulse_softc *sc; 444 struct repulse_hw *bp; 445 446 if (retp != NULL) { 447 sc = arg; 448 bp = sc->sc_boardp; 449 strncpy(retp->name, "Repulse", sizeof(retp->name)); 450 snprintf(retp->version, sizeof(retp->version), "0x%x", 451 bp->rhw_version); 452 strncpy(retp->config, "", sizeof(retp->config)); 453 } 454 455 return 0; 456 } 457 458 int 459 rep_get_props(void *v) 460 { 461 return AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX; 462 } 463 464 int 465 rep_halt_output(void *arg) 466 { 467 struct repulse_softc *sc; 468 struct repulse_hw *bp; 469 470 sc = arg; 471 bp = sc->sc_boardp; 472 bp->rhw_status &= ~(REPSTATUS_PLAYIRQENABLE|REPSTATUS_PLAY); 473 474 475 return 0; 476 } 477 478 int 479 rep_halt_input(void *arg) 480 { 481 struct repulse_softc *sc; 482 struct repulse_hw *bp; 483 484 sc = arg; 485 bp = sc->sc_boardp; 486 bp->rhw_status &= ~(REPSTATUS_RECIRQENABLE|REPSTATUS_RECORD); 487 488 return 0; 489 } 490 491 /* 492 * Encoding support. 493 * 494 * TODO: add 24bit and 32bit modes here and in setparams. 495 */ 496 497 const struct repulse_encoding_query { 498 const char *name; 499 int encoding, precision, flags; 500 } rep_encoding_queries[] = { 501 { AudioEulinear, AUDIO_ENCODING_ULINEAR, 8, 0}, 502 { AudioEmulaw, AUDIO_ENCODING_ULAW, 8, AUDIO_ENCODINGFLAG_EMULATED}, 503 { AudioEalaw, AUDIO_ENCODING_ALAW, 8, AUDIO_ENCODINGFLAG_EMULATED}, 504 { AudioEslinear, AUDIO_ENCODING_SLINEAR, 8, 0}, 505 { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE, 16, 0}, 506 { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE, 16, 0}, 507 { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE, 16, 0}, 508 { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE, 16, 0}, 509 }; 510 511 int 512 rep_query_encoding(void *arg, struct audio_encoding *fp) 513 { 514 int i; 515 const struct repulse_encoding_query *p; 516 517 i = fp->index; 518 519 if (i >= sizeof(rep_encoding_queries) / 520 sizeof(struct repulse_encoding_query)) 521 return EINVAL; 522 523 p = &rep_encoding_queries[i]; 524 525 strncpy (fp->name, p->name, sizeof(fp->name)); 526 fp->encoding = p->encoding; 527 fp->precision = p->precision; 528 fp->flags = p->flags; 529 530 return 0; 531 } 532 533 /* 534 * XXX the following three functions need to be enhanced for the FPGA s/pdif 535 * mode. Generic ac97 versions for now. 536 */ 537 538 int 539 rep_get_port(void *arg, mixer_ctrl_t *cp) 540 { 541 struct repulse_softc *sc; 542 543 sc = arg; 544 return sc->sc_codec_if->vtbl->mixer_get_port(sc->sc_codec_if, cp); 545 } 546 547 int 548 rep_set_port(void *arg, mixer_ctrl_t *cp) 549 { 550 struct repulse_softc *sc; 551 552 sc = arg; 553 return sc->sc_codec_if->vtbl->mixer_set_port(sc->sc_codec_if, cp); 554 } 555 556 int 557 rep_query_devinfo(void *arg, mixer_devinfo_t *dip) 558 { 559 struct repulse_softc *sc; 560 561 sc = arg; 562 return sc->sc_codec_if->vtbl->query_devinfo(sc->sc_codec_if, dip); 563 } 564 565 int 566 rep_round_blocksize(void *arg, int blk, int mode, const audio_params_t *param) 567 { 568 int b1; 569 570 b1 = (blk & -32); 571 572 if (b1 > 65536 / 2 / 2 /* channels */ / 4 /* bytes per channel */) 573 b1 = 65536 / 2 / 2 / 4; 574 return b1; 575 } 576 577 size_t 578 rep_round_buffersize(void *arg, int direction, size_t size) 579 { 580 return size; 581 } 582 583 584 int 585 rep_set_params(void *addr, int setmode, int usemode, 586 audio_params_t *play, audio_params_t *rec, 587 stream_filter_list_t *pfil, stream_filter_list_t *rfil) 588 { 589 audio_params_t hw; 590 struct repulse_softc *sc; 591 audio_params_t *p; 592 int mode, reg; 593 unsigned flags; 594 u_int16_t a; 595 596 sc = addr; 597 /* for mode in (RECORD, PLAY) */ 598 for (mode = AUMODE_RECORD; mode != -1; 599 mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { 600 601 if ((setmode & mode) == 0) 602 continue; 603 604 p = mode == AUMODE_PLAY ? play : rec; 605 606 /* TODO XXX we can do upto 32bit, 96000 */ 607 if (p->sample_rate < 4000 || p->sample_rate > 48000 || 608 (p->precision != 8 && p->precision != 16) || 609 (p->channels != 1 && p->channels != 2)) 610 return EINVAL; 611 612 reg = mode == AUMODE_PLAY ? 613 AC97_REG_PCM_FRONT_DAC_RATE : AC97_REG_PCM_LR_ADC_RATE; 614 615 repac_write(sc, reg, (uint16_t) p->sample_rate); 616 repac_read(sc, reg, &a); 617 p->sample_rate = a; 618 619 if (mode == AUMODE_PLAY) 620 sc->sc_playscale = p->channels * p->precision / 8; 621 else 622 sc->sc_captscale = p->channels * p->precision / 8; 623 624 hw = *p; 625 626 /* everything else is software, alas... */ 627 /* XXX TBD signed/unsigned, *law, etc */ 628 629 flags = 0; 630 if (p->encoding == AUDIO_ENCODING_ULINEAR_LE || 631 p->encoding == AUDIO_ENCODING_ULINEAR_BE || 632 p->encoding == AUDIO_ENCODING_ULINEAR) 633 flags |= 1; 634 635 if (p->encoding == AUDIO_ENCODING_SLINEAR_LE || 636 p->encoding == AUDIO_ENCODING_ULINEAR_LE) 637 flags |= 2; 638 639 if (mode == AUMODE_PLAY) { 640 sc->sc_playflags = flags; 641 if (p->encoding == AUDIO_ENCODING_ULAW) { 642 sc->sc_playfun = p->channels == 1 ? 643 rep_write_16_mono : 644 rep_write_16_stereo; 645 sc->sc_playflags = 0; 646 sc->sc_playscale = p->channels * 2; 647 hw.encoding = AUDIO_ENCODING_SLINEAR_BE; 648 hw.precision = hw.validbits = 16; 649 pfil->append(pfil, mulaw_to_linear16, &hw); 650 } else 651 if (p->encoding == AUDIO_ENCODING_ALAW) { 652 sc->sc_playfun = p->channels == 1 ? 653 rep_write_16_mono : 654 rep_write_16_stereo; 655 sc->sc_playflags = 0; 656 sc->sc_playscale = p->channels * 2; 657 hw.encoding = AUDIO_ENCODING_SLINEAR_BE; 658 hw.precision = hw.validbits = 16; 659 pfil->append(pfil, alaw_to_linear16, &hw); 660 } else 661 if (p->precision == 8 && p->channels == 1) 662 sc->sc_playfun = rep_write_8_mono; 663 else if (p->precision == 8 && p->channels == 2) 664 sc->sc_playfun = rep_write_8_stereo; 665 else if (p->precision == 16 && p->channels == 1) 666 sc->sc_playfun = rep_write_16_mono; 667 else if (p->precision == 16 && p->channels == 2) 668 sc->sc_playfun = rep_write_16_stereo; 669 } else { 670 sc->sc_captflags = flags; 671 if (p->encoding == AUDIO_ENCODING_ULAW) { 672 sc->sc_captfun = p->channels == 1 ? 673 rep_read_8_mono : 674 rep_read_8_stereo; 675 sc->sc_captflags = 0; 676 hw.encoding = AUDIO_ENCODING_SLINEAR_LE; 677 rfil->append(rfil, linear8_to_mulaw, &hw); 678 } else 679 if (p->encoding == AUDIO_ENCODING_ALAW) { 680 sc->sc_captfun = p->channels == 1 ? 681 rep_read_8_mono : 682 rep_read_8_stereo; 683 sc->sc_captflags = 0; 684 rfil->append(rfil, linear8_to_alaw, &hw); 685 } else 686 if (p->precision == 8 && p->channels == 1) 687 sc->sc_captfun = rep_read_8_mono; 688 else if (p->precision == 8 && p->channels == 2) 689 sc->sc_captfun = rep_read_8_stereo; 690 else if (p->precision == 16 && p->channels == 1) 691 sc->sc_captfun = rep_read_16_mono; 692 else if (p->precision == 16 && p->channels == 2) 693 sc->sc_captfun = rep_read_16_stereo; 694 } 695 /* TBD: mu-law, A-law */ 696 } 697 return 0; 698 } 699 700 void 701 rep_write_8_mono(struct repulse_hw *bp, uint8_t *p, int length, unsigned flags) 702 { 703 uint16_t sample; 704 uint16_t xor; 705 706 xor = flags & 1 ? 0x8000 : 0; 707 708 bp->rhw_fifo_pack = 0; 709 710 while (length-- > 0) { 711 sample = ((*p++) << 8) ^ xor; 712 bp->rhw_fifo_lh = sample; 713 bp->rhw_fifo_rh = sample; 714 } 715 } 716 717 void 718 rep_write_8_stereo(struct repulse_hw *bp, uint8_t *p, int length, 719 unsigned flags) 720 { 721 uint16_t xor; 722 723 xor = flags & 1 ? 0x8000 : 0; 724 725 bp->rhw_fifo_pack = 0; 726 727 while (length-- > 0) { 728 bp->rhw_fifo_lh = ((*p++) << 8) ^ xor; 729 bp->rhw_fifo_rh = ((*p++) << 8) ^ xor; 730 } 731 } 732 733 void 734 rep_write_16_mono(struct repulse_hw *bp, uint8_t *p, int length, 735 unsigned flags) 736 { 737 uint16_t *q; 738 uint16_t sample; 739 uint16_t xor; 740 741 q = (uint16_t *)p; 742 xor = flags & 1 ? 0x8000 : 0; 743 744 bp->rhw_fifo_pack = 0; 745 746 if (flags & 2) { 747 while (length > 0) { 748 sample = bswap16(*q++) ^ xor; 749 bp->rhw_fifo_lh = sample; 750 bp->rhw_fifo_rh = sample; 751 length -= 2; 752 } 753 return; 754 } 755 756 while (length > 0) { 757 sample = (*q++) ^ xor; 758 bp->rhw_fifo_lh = sample; 759 bp->rhw_fifo_rh = sample; 760 length -= 2; 761 } 762 } 763 764 void 765 rep_write_16_stereo(struct repulse_hw *bp, uint8_t *p, int length, 766 unsigned flags) 767 { 768 uint16_t *q; 769 uint16_t xor; 770 771 q = (uint16_t *)p; 772 xor = flags & 1 ? 0x8000 : 0; 773 774 bp->rhw_fifo_pack = 0; 775 776 if (flags & 2) { 777 while (length > 0) { 778 bp->rhw_fifo_lh = bswap16(*q++) ^ xor; 779 bp->rhw_fifo_rh = bswap16(*q++) ^ xor; 780 length -= 4; 781 } 782 return; 783 } 784 while (length > 0) { 785 bp->rhw_fifo_lh = (*q++) ^ xor; 786 bp->rhw_fifo_rh = (*q++) ^ xor; 787 length -= 4; 788 } 789 } 790 791 void 792 rep_read_8_mono(struct repulse_hw *bp, uint8_t *p, int length, unsigned flags) 793 { 794 uint16_t v; 795 uint16_t xor; 796 797 xor = flags & 1 ? 0x8000 : 0; 798 799 while (length > 0) { 800 *p++ = (bp->rhw_fifo_lh ^ xor) >> 8; 801 v = bp->rhw_fifo_rh; 802 length--; 803 } 804 } 805 806 void 807 rep_read_16_mono(struct repulse_hw *bp, uint8_t *p, int length, unsigned flags) 808 { 809 uint16_t *q; 810 uint16_t v; 811 uint16_t xor; 812 813 q = (uint16_t *)p; 814 xor = flags & 1 ? 0x8000 : 0; 815 816 if (flags & 2) { 817 while (length > 0) { 818 *q++ = bswap16(bp->rhw_fifo_lh ^ xor); 819 v = bp->rhw_fifo_rh; 820 length -= 2; 821 } 822 return; 823 } 824 825 while (length > 0) { 826 *q++ = bp->rhw_fifo_lh ^ xor; 827 v = bp->rhw_fifo_rh; 828 length -= 2; 829 } 830 } 831 832 void 833 rep_read_8_stereo(struct repulse_hw *bp, uint8_t *p, int length, 834 unsigned flags) 835 { 836 uint16_t xor; 837 838 xor = flags & 1 ? 0x8000 : 0; 839 while (length > 0) { 840 *p++ = (bp->rhw_fifo_lh ^ xor) >> 8; 841 *p++ = (bp->rhw_fifo_rh ^ xor) >> 8; 842 length -= 2; 843 } 844 } 845 846 void 847 rep_read_16_stereo(struct repulse_hw *bp, uint8_t *p, int length, 848 unsigned flags) 849 { 850 uint16_t *q; 851 uint16_t xor; 852 853 q = (uint16_t *)p; 854 xor = flags & 1 ? 0x8000 : 0; 855 856 if (flags & 2) { 857 while (length > 0) { 858 *q++ = bswap16(bp->rhw_fifo_lh ^ xor); 859 *q++ = bswap16(bp->rhw_fifo_rh ^ xor); 860 length -= 4; 861 } 862 return; 863 } 864 while (length > 0) { 865 *q++ = bp->rhw_fifo_lh ^ xor; 866 *q++ = bp->rhw_fifo_rh ^ xor; 867 length -= 4; 868 } 869 } 870 871 /* 872 * At this point the transfer function is set. 873 */ 874 875 int 876 rep_start_output(void *addr, void *block, int blksize, 877 void (*intr)(void*), void *intrarg) 878 { 879 struct repulse_softc *sc; 880 uint8_t *buf; 881 struct repulse_hw *bp; 882 uint16_t status; 883 884 885 sc = addr; 886 bp = sc->sc_boardp; 887 buf = block; 888 889 /* TODO: prepare hw if necessary */ 890 status = bp->rhw_status; 891 if (!(status & REPSTATUS_PLAY)) 892 bp->rhw_status = status | 893 REPSTATUS_PLAY | REPSTATUS_PLAYFIFORST; 894 895 /* copy data */ 896 (*sc->sc_playfun)(bp, buf, blksize, sc->sc_playflags); 897 898 /* TODO: set hw if necessary */ 899 if (intr) { 900 bp->rhw_status |= REPSTATUS_PLAYIRQENABLE; 901 bp->rhw_play_fifosz = blksize / sc->sc_playscale / 2; 902 /* /2: give us time to return on the first call */ 903 } 904 905 /* save callback function */ 906 sc->sc_playarg = intrarg; 907 sc->sc_playmore = intr; 908 909 return 0; 910 } 911 912 int 913 rep_start_input(void *addr, void *block, int blksize, 914 void (*intr)(void*), void *intrarg) 915 { 916 struct repulse_softc *sc; 917 struct repulse_hw *bp; 918 uint16_t status; 919 920 sc = addr; 921 bp = sc->sc_boardp; 922 923 sc->sc_captbuf = block; 924 sc->sc_captbufsz = blksize; 925 sc->sc_captarg = intrarg; 926 sc->sc_captmore = intr; 927 928 status = bp->rhw_status; 929 if (!(status & REPSTATUS_RECORD)) 930 bp->rhw_status = status | REPSTATUS_RECORD 931 | REPSTATUS_RECFIFORST; 932 933 bp->rhw_status |= REPSTATUS_RECIRQENABLE; 934 bp->rhw_capt_fifosz = blksize / sc->sc_captscale; 935 936 return 0; 937 } 938 939 /* irq handler */ 940 941 int 942 rep_intr(void *tag) 943 { 944 struct repulse_softc *sc; 945 struct repulse_hw *bp; 946 int foundone; 947 uint16_t status; 948 949 foundone = 0; 950 951 sc = tag; 952 bp = sc->sc_boardp; 953 status = bp->rhw_status; 954 955 if (status & REPSTATUS_PLAYIRQACK) { 956 foundone = 1; 957 status &= ~REPSTATUS_PLAYIRQENABLE; 958 bp->rhw_status = status; 959 (*sc->sc_playmore)(sc->sc_playarg); 960 } 961 962 if (status & REPSTATUS_RECIRQACK) { 963 foundone = 1; 964 status &= ~REPSTATUS_RECIRQENABLE; 965 bp->rhw_status = status; 966 (*sc->sc_captfun)(bp, sc->sc_captbuf, sc->sc_captbufsz, 967 sc->sc_captflags); 968 (*sc->sc_captmore)(sc->sc_captarg); 969 } 970 971 return foundone; 972 } 973