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