1 /* $NetBSD: repulse.c,v 1.24 2022/02/12 23:30:30 andvar 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.24 2022/02/12 23:30:30 andvar 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 #include <sys/bus.h> 42 43 #include <sys/audioio.h> 44 #include <dev/audio/audio_if.h> 45 46 #include <dev/ic/ac97reg.h> 47 #include <dev/ic/ac97var.h> 48 49 #include <amiga/dev/zbusvar.h> 50 #include <amiga/amiga/isr.h> 51 52 #include <amiga/dev/repulse_firmware.h> 53 54 #ifndef vu_int8_t 55 #define vu_int8_t volatile uint8_t 56 #endif 57 #ifndef vu_int16_t 58 #define vu_int16_t volatile uint16_t 59 #endif 60 #ifndef vu_int32_t 61 #define vu_int32_t volatile uint32_t 62 #endif 63 64 /* ac97 attachment functions */ 65 66 int repac_attach(void *, struct ac97_codec_if *); 67 int repac_read(void *, uint8_t, uint16_t *); 68 int repac_write(void *, uint8_t, uint16_t); 69 int repac_reset(void *); 70 enum ac97_host_flag repac_flags(void *); 71 72 /* audio attachment functions */ 73 74 int rep_getdev(void *, struct audio_device *); 75 int rep_get_props(void *); 76 int rep_halt_output(void *); 77 int rep_halt_input(void *); 78 int rep_query_format(void *, audio_format_query_t *); 79 int rep_set_format(void *, int, const audio_params_t *, const audio_params_t *, 80 audio_filter_reg_t *, audio_filter_reg_t *); 81 int rep_round_blocksize(void *, int, int, const audio_params_t *); 82 int rep_set_port(void *, mixer_ctrl_t *); 83 int rep_get_port(void *, mixer_ctrl_t *); 84 int rep_query_devinfo(void *, mixer_devinfo_t *); 85 void rep_get_locks(void *, kmutex_t **, kmutex_t **); 86 87 int rep_start_input(void *, void *, int, void (*)(void *), void *); 88 int rep_start_output(void *, void *, int, void (*)(void *), void *); 89 90 int rep_intr(void *); 91 92 93 /* audio attachment */ 94 95 const struct audio_hw_if rep_hw_if = { 96 .query_format = rep_query_format, 97 .set_format = rep_set_format, 98 .round_blocksize = rep_round_blocksize, 99 .start_output = rep_start_output, 100 .start_input = rep_start_input, 101 .halt_output = rep_halt_output, 102 .halt_input = rep_halt_input, 103 .getdev = rep_getdev, 104 .set_port = rep_set_port, 105 .get_port = rep_get_port, 106 .query_devinfo = rep_query_devinfo, 107 .get_props = rep_get_props, 108 .get_locks = rep_get_locks, 109 }; 110 111 /* hardware registers */ 112 113 struct repulse_hw { 114 vu_int16_t rhw_status; 115 vu_int16_t rhw_fifostatus; /* 0xrrrrpppp0000flag */ 116 vu_int16_t rhw_reg_address; 117 vu_int16_t rhw_reg_data; 118 /* 0x08 */ 119 vu_int16_t rhw_fifo_lh; 120 vu_int16_t rhw_fifo_ll; 121 vu_int16_t rhw_fifo_rh; 122 vu_int16_t rhw_fifo_rl; 123 /* 0x10 */ 124 vu_int16_t rhw_fifo_pack; 125 vu_int16_t rhw_play_fifosz; 126 vu_int32_t rhw_spdifin_stat; 127 #define rhw_spdifout_stat rhw_spdifin_stat; 128 129 /* 0x18 */ 130 vu_int16_t rhw_capt_fifosz; 131 vu_int16_t rhw_version; 132 vu_int16_t rhw_dummy1; 133 vu_int8_t rhw_firmwareload; 134 /* 0x1F */ 135 vu_int8_t rhw_dummy2[66 - 31]; 136 /* 0x42 */ 137 vu_int16_t rhw_reset; 138 } /* __attribute__((packed)) */; 139 140 #define REPSTATUS_PLAY 0x0001 141 #define REPSTATUS_RECORD 0x0002 142 #define REPSTATUS_PLAYFIFORST 0x0004 143 #define REPSTATUS_RECFIFORST 0x0008 144 145 #define REPSTATUS_REGSENDBUSY 0x0010 146 #define REPSTATUS_LOOPBACK 0x0020 147 #define REPSTATUS_ENSPDIFIN 0x0040 148 #define REPSTATUS_ENSPDIFOUT 0x0080 149 150 #define REPSTATUS_CODECRESET 0x0200 151 #define REPSTATUS_SPDIFOUT24 0x0400 152 #define REPSTATUS_SPDIFIN24 0x0800 153 154 #define REPSTATUS_RECIRQENABLE 0x1000 155 #define REPSTATUS_RECIRQACK 0x2000 156 #define REPSTATUS_PLAYIRQENABLE 0x4000 157 #define REPSTATUS_PLAYIRQACK 0x8000 158 159 #define REPFIFO_PLAYFIFOFULL 0x0001 160 #define REPFIFO_PLAYFIFOEMPTY 0x0002 161 #define REPFIFO_RECFIFOFULL 0x0004 162 #define REPFIFO_RECFIFOEMPTY 0x0008 163 #define REPFIFO_PLAYFIFOGAUGE(x) ((x << 4) & 0xf000) 164 #define REPFIFO_RECFIFOGAUGE(x) (x & 0xf000) 165 166 /* AmigaDOS Delay() ticks */ 167 168 #define USECPERTICK (1000000/50) 169 170 /* NetBSD device attachment */ 171 172 struct repulse_softc { 173 device_t sc_dev; 174 struct isr sc_isr; 175 struct ac97_host_if sc_achost; 176 struct ac97_codec_if *sc_codec_if; 177 178 struct repulse_hw *sc_boardp; 179 180 void (*sc_captmore)(void *); 181 void *sc_captarg; 182 183 void *sc_captbuf; 184 int sc_captscale; 185 int sc_captbufsz; 186 unsigned sc_captflags; 187 188 189 void (*sc_playmore)(void *); 190 void *sc_playarg; 191 int sc_playscale; 192 unsigned sc_playflags; 193 194 kmutex_t sc_lock; 195 kmutex_t sc_intr_lock; 196 }; 197 198 const struct audio_format repulse_format = { 199 .mode = AUMODE_PLAY | AUMODE_RECORD, 200 .encoding = AUDIO_ENCODING_SLINEAR_BE, 201 .validbits = 16, 202 .precision = 16, 203 .channels = 2, 204 .channel_mask = AUFMT_STEREO, 205 .frequency_type = 6, 206 .frequency = { 8000, 16000, 22050, 32000, 44100, 48000 }, 207 }; 208 209 int repulse_match (device_t, cfdata_t, void *); 210 void repulse_attach (device_t, device_t, void *); 211 212 CFATTACH_DECL_NEW(repulse, sizeof(struct repulse_softc), 213 repulse_match, repulse_attach, NULL, NULL); 214 215 int 216 repulse_match(device_t parent, cfdata_t cf, void *aux) 217 { 218 struct zbus_args *zap; 219 220 zap = aux; 221 222 if (zap->manid != 0x4144) 223 return (0); 224 225 if (zap->prodid != 0) 226 return (0); 227 228 return (1); 229 } 230 231 void 232 repulse_attach(device_t parent, device_t self, void *aux) 233 { 234 struct repulse_softc *sc; 235 struct zbus_args *zap; 236 struct repulse_hw *bp; 237 const uint8_t *fwp; 238 int needs_firmware; 239 uint16_t a; 240 241 sc = device_private(self); 242 sc->sc_dev = self; 243 zap = aux; 244 bp = (struct repulse_hw *)zap->va; 245 sc->sc_boardp = bp; 246 sc->sc_playscale = 4; 247 sc->sc_captscale = 4; 248 249 needs_firmware = 0; 250 if (bp->rhw_fifostatus & 0x00f0) 251 needs_firmware = 1; 252 else { 253 bp->rhw_status = 0x000c; 254 if (bp->rhw_status != 0 || bp->rhw_fifostatus != 0x0f0a) 255 needs_firmware = 1; 256 } 257 258 printf(": "); 259 if (needs_firmware) { 260 printf("loading "); 261 bp->rhw_reset = 0; 262 263 delay(1 * USECPERTICK); 264 265 for (fwp = (const uint8_t *)repulse_firmware; 266 fwp < (repulse_firmware_size + 267 (const uint8_t *)repulse_firmware); fwp++) 268 bp->rhw_firmwareload = *fwp; 269 270 delay(1 * USECPERTICK); 271 272 if (bp->rhw_fifostatus & 0x00f0) 273 goto Initerr; 274 275 a = /* bp->rhw_status; 276 a |= */ REPSTATUS_CODECRESET; 277 bp->rhw_status = a; 278 279 a = bp->rhw_status; 280 if ((a & REPSTATUS_CODECRESET) == 0) 281 goto Initerr; 282 283 (void)bp->rhw_status; 284 (void)bp->rhw_status; 285 (void)bp->rhw_status; 286 a = bp->rhw_status; 287 a &= ~REPSTATUS_CODECRESET; 288 bp->rhw_status = a; 289 } 290 291 printf("firmware version 0x%x\n", bp->rhw_version); 292 293 sc->sc_achost.arg = sc; 294 295 sc->sc_achost.reset = repac_reset; 296 sc->sc_achost.read = repac_read; 297 sc->sc_achost.write = repac_write; 298 sc->sc_achost.attach = repac_attach; 299 sc->sc_achost.flags = 0; 300 301 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 302 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); 303 304 if (ac97_attach(&sc->sc_achost, self, &sc->sc_lock)) { 305 printf("%s: error attaching codec\n", device_xname(self)); 306 return; 307 } 308 309 #ifdef DIAGNOSTIC 310 /* 311 * Print a warning if the codec doesn't support hardware variable 312 * rate audio. As the initial incarnations of the Repulse board 313 * are AC'97 2.1, it is expected that we'll always have VRA. 314 */ 315 /* 316 * XXX this should be a panic(). OTOH, audio codec speed is not 317 * important enough to do this. 318 */ 319 a = sc->sc_codec_if->vtbl->get_extcaps(sc->sc_codec_if); 320 if (!(a & AC97_EXT_AUDIO_VRA)) { 321 printf("%s: warning: codec doesn't support " 322 "hardware AC'97 2.0 Variable Rate Audio\n", 323 device_xname(self)); 324 } 325 #endif 326 327 sc->sc_isr.isr_ipl = 2; 328 sc->sc_isr.isr_arg = sc; 329 sc->sc_isr.isr_intr = rep_intr; 330 add_isr(&sc->sc_isr); 331 332 audio_attach_mi(&rep_hw_if, sc, self); 333 334 return; 335 336 Initerr: 337 printf("\n%s: firmware not successfully loaded\n", device_xname(self)); 338 return; 339 340 } 341 342 int 343 repac_reset(void *arg) 344 { 345 struct repulse_softc *sc; 346 struct repulse_hw *bp; 347 uint16_t a; 348 349 sc = arg; 350 bp = sc->sc_boardp; 351 a = bp->rhw_status; 352 a |= REPSTATUS_CODECRESET; 353 bp->rhw_status = a; 354 355 a = bp->rhw_status; 356 #ifdef DIAGNOSTIC 357 if ((a & REPSTATUS_CODECRESET) == 0) 358 panic("%s: cannot set reset bit", device_xname(sc->sc_dev)); 359 #endif 360 361 a = bp->rhw_status; 362 a = bp->rhw_status; 363 a = bp->rhw_status; 364 a = bp->rhw_status; 365 a &= ~REPSTATUS_CODECRESET; 366 bp->rhw_status = a; 367 return 0; 368 } 369 370 int 371 repac_read(void *arg, u_int8_t reg, u_int16_t *valuep) 372 { 373 struct repulse_softc *sc; 374 struct repulse_hw *bp; 375 376 sc = arg; 377 bp = sc->sc_boardp; 378 while (bp->rhw_status & REPSTATUS_REGSENDBUSY) 379 continue; 380 bp->rhw_reg_address = (reg & 0x7F) | 0x80; 381 382 while (bp->rhw_status & REPSTATUS_REGSENDBUSY) 383 continue; 384 385 *valuep = bp->rhw_reg_data; 386 387 return 0; 388 } 389 390 int 391 repac_write(void *arg, uint8_t reg, uint16_t value) 392 { 393 struct repulse_softc *sc; 394 struct repulse_hw *bp; 395 396 sc = arg; 397 bp = sc->sc_boardp; 398 bp->rhw_reg_data = value; 399 bp->rhw_reg_address = reg & 0x7F; 400 401 while (bp->rhw_status & REPSTATUS_REGSENDBUSY) 402 continue; 403 404 return 0; 405 } 406 407 int 408 repac_attach(void *arg, struct ac97_codec_if *acip) 409 { 410 struct repulse_softc *sc; 411 412 sc = arg; 413 sc->sc_codec_if = acip; 414 415 return 0; 416 } 417 418 /* audio(9) support stuff which is not ac97-constant */ 419 420 int 421 rep_getdev(void *arg, struct audio_device *retp) 422 { 423 struct repulse_softc *sc; 424 struct repulse_hw *bp; 425 426 if (retp != NULL) { 427 sc = arg; 428 bp = sc->sc_boardp; 429 strncpy(retp->name, "Repulse", sizeof(retp->name)); 430 snprintf(retp->version, sizeof(retp->version), "0x%x", 431 bp->rhw_version); 432 strncpy(retp->config, "", sizeof(retp->config)); 433 } 434 435 return 0; 436 } 437 438 int 439 rep_get_props(void *v) 440 { 441 442 return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE | 443 AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX; 444 } 445 446 int 447 rep_halt_output(void *arg) 448 { 449 struct repulse_softc *sc; 450 struct repulse_hw *bp; 451 452 sc = arg; 453 bp = sc->sc_boardp; 454 bp->rhw_status &= ~(REPSTATUS_PLAYIRQENABLE|REPSTATUS_PLAY); 455 456 457 return 0; 458 } 459 460 int 461 rep_halt_input(void *arg) 462 { 463 struct repulse_softc *sc; 464 struct repulse_hw *bp; 465 466 sc = arg; 467 bp = sc->sc_boardp; 468 bp->rhw_status &= ~(REPSTATUS_RECIRQENABLE|REPSTATUS_RECORD); 469 470 return 0; 471 } 472 473 int 474 rep_query_format(void *arg, audio_format_query_t *afp) 475 { 476 477 return audio_query_format(&repulse_format, 1, afp); 478 } 479 480 /* 481 * XXX the following three functions need to be enhanced for the FPGA s/pdif 482 * mode. Generic ac97 versions for now. 483 */ 484 485 int 486 rep_get_port(void *arg, mixer_ctrl_t *cp) 487 { 488 struct repulse_softc *sc; 489 490 sc = arg; 491 return sc->sc_codec_if->vtbl->mixer_get_port(sc->sc_codec_if, cp); 492 } 493 494 int 495 rep_set_port(void *arg, mixer_ctrl_t *cp) 496 { 497 struct repulse_softc *sc; 498 499 sc = arg; 500 return sc->sc_codec_if->vtbl->mixer_set_port(sc->sc_codec_if, cp); 501 } 502 503 int 504 rep_query_devinfo(void *arg, mixer_devinfo_t *dip) 505 { 506 struct repulse_softc *sc; 507 508 sc = arg; 509 return sc->sc_codec_if->vtbl->query_devinfo(sc->sc_codec_if, dip); 510 } 511 512 int 513 rep_round_blocksize(void *arg, int blk, int mode, const audio_params_t *param) 514 { 515 int b1; 516 517 b1 = (blk & -32); 518 519 if (b1 > 65536 / 2 / 2 /* channels */ / 4 /* bytes per channel */) 520 b1 = 65536 / 2 / 2 / 4; 521 return b1; 522 } 523 524 void 525 rep_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread) 526 { 527 struct repulse_softc *sc = opaque; 528 529 *intr = &sc->sc_intr_lock; 530 *thread = &sc->sc_lock; 531 } 532 533 534 int 535 rep_set_format(void *addr, int setmode, 536 const audio_params_t *play, const audio_params_t *rec, 537 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 538 { 539 struct repulse_softc *sc; 540 541 sc = addr; 542 /* XXX 96kHz */ 543 if ((setmode & AUMODE_PLAY)) { 544 repac_write(sc, AC97_REG_PCM_FRONT_DAC_RATE, play->sample_rate); 545 } 546 if ((setmode & AUMODE_RECORD)) { 547 repac_write(sc, AC97_REG_PCM_LR_ADC_RATE, rec->sample_rate); 548 } 549 550 return 0; 551 } 552 553 554 int 555 rep_start_output(void *addr, void *block, int blksize, 556 void (*intr)(void*), void *intrarg) 557 { 558 struct repulse_softc *sc; 559 struct repulse_hw *bp; 560 uint16_t status; 561 562 563 sc = addr; 564 bp = sc->sc_boardp; 565 566 /* TODO: prepare hw if necessary */ 567 status = bp->rhw_status; 568 if (!(status & REPSTATUS_PLAY)) 569 bp->rhw_status = status | 570 REPSTATUS_PLAY | REPSTATUS_PLAYFIFORST; 571 572 /* copy data */ 573 uint16_t *q = block; 574 int length = blksize; 575 while (length > 0) { 576 bp->rhw_fifo_lh = *q++; 577 bp->rhw_fifo_rh = *q++; 578 length -= 4; 579 } 580 581 /* TODO: set hw if necessary */ 582 if (intr) { 583 bp->rhw_status |= REPSTATUS_PLAYIRQENABLE; 584 bp->rhw_play_fifosz = blksize / sc->sc_playscale / 2; 585 /* /2: give us time to return on the first call */ 586 } 587 588 /* save callback function */ 589 sc->sc_playarg = intrarg; 590 sc->sc_playmore = intr; 591 592 return 0; 593 } 594 595 int 596 rep_start_input(void *addr, void *block, int blksize, 597 void (*intr)(void*), void *intrarg) 598 { 599 struct repulse_softc *sc; 600 struct repulse_hw *bp; 601 uint16_t status; 602 603 sc = addr; 604 bp = sc->sc_boardp; 605 606 sc->sc_captbuf = block; 607 sc->sc_captbufsz = blksize; 608 sc->sc_captarg = intrarg; 609 sc->sc_captmore = intr; 610 611 status = bp->rhw_status; 612 if (!(status & REPSTATUS_RECORD)) 613 bp->rhw_status = status | REPSTATUS_RECORD 614 | REPSTATUS_RECFIFORST; 615 616 bp->rhw_status |= REPSTATUS_RECIRQENABLE; 617 bp->rhw_capt_fifosz = blksize / sc->sc_captscale; 618 619 return 0; 620 } 621 622 /* irq handler */ 623 624 int 625 rep_intr(void *tag) 626 { 627 struct repulse_softc *sc; 628 struct repulse_hw *bp; 629 int foundone; 630 uint16_t status; 631 632 foundone = 0; 633 634 sc = tag; 635 636 mutex_spin_enter(&sc->sc_intr_lock); 637 638 bp = sc->sc_boardp; 639 status = bp->rhw_status; 640 641 if (status & REPSTATUS_PLAYIRQACK) { 642 foundone = 1; 643 status &= ~REPSTATUS_PLAYIRQENABLE; 644 bp->rhw_status = status; 645 (*sc->sc_playmore)(sc->sc_playarg); 646 } 647 648 if (status & REPSTATUS_RECIRQACK) { 649 foundone = 1; 650 status &= ~REPSTATUS_RECIRQENABLE; 651 bp->rhw_status = status; 652 uint16_t *q = sc->sc_captbuf; 653 int length = sc->sc_captbufsz; 654 while (length > 0) { 655 *q++ = bp->rhw_fifo_lh; 656 *q++ = bp->rhw_fifo_rh; 657 length -= 4; 658 } 659 (*sc->sc_captmore)(sc->sc_captarg); 660 } 661 662 mutex_spin_exit(&sc->sc_intr_lock); 663 664 return foundone; 665 } 666