162587Sitojun/*- 262587Sitojun * Copyright (c) 2012 Ruslan Bukin <br@bsdpad.com> 362587Sitojun * All rights reserved. 456723Sshin * 556723Sshin * Redistribution and use in source and binary forms, with or without 656723Sshin * modification, are permitted provided that the following conditions 756723Sshin * are met: 856723Sshin * 1. Redistributions of source code must retain the above copyright 956723Sshin * notice, this list of conditions and the following disclaimer. 1056723Sshin * 2. Redistributions in binary form must reproduce the above copyright 1156723Sshin * notice, this list of conditions and the following disclaimer in the 1256723Sshin * documentation and/or other materials provided with the distribution. 1356723Sshin * 1456723Sshin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1556723Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1656723Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1756723Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1856723Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1956723Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2056723Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2156723Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2256723Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2356723Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2456723Sshin * SUCH DAMAGE. 2556723Sshin */ 2656723Sshin 2756723Sshin/* 2856723Sshin * RME HDSPe driver for FreeBSD (pcm-part). 2956723Sshin * Supported cards: AIO, RayDAT. 3056723Sshin */ 3156723Sshin 3256723Sshin#include <dev/sound/pcm/sound.h> 3356723Sshin#include <dev/sound/pci/hdspe.h> 3456723Sshin#include <dev/sound/chip.h> 3556723Sshin 3656723Sshin#include <dev/pci/pcireg.h> 3756723Sshin#include <dev/pci/pcivar.h> 3856723Sshin 3956723Sshin#include <mixer_if.h> 4056723Sshin 4156723SshinSND_DECLARE_FILE("$FreeBSD$"); 4256723Sshin 4356723Sshinstruct hdspe_latency { 4456723Sshin uint32_t n; 4556723Sshin uint32_t period; 4656723Sshin float ms; 4756723Sshin}; 4862587Sitojun 4956723Sshinstatic struct hdspe_latency latency_map[] = { 5056723Sshin { 7, 32, 0.7 }, 5156723Sshin { 0, 64, 1.5 }, 5256723Sshin { 1, 128, 3 }, 5362587Sitojun { 2, 256, 6 }, 5462587Sitojun { 3, 512, 12 }, 5556723Sshin { 4, 1024, 23 }, 5656723Sshin { 5, 2048, 46 }, 5756723Sshin { 6, 4096, 93 }, 5856723Sshin 5956723Sshin { 0, 0, 0 }, 6056723Sshin}; 6156723Sshin 6262587Sitojunstruct hdspe_rate { 6356723Sshin uint32_t speed; 6462587Sitojun uint32_t reg; 6556723Sshin}; 6656723Sshin 6756723Sshinstatic struct hdspe_rate rate_map[] = { 6856723Sshin { 32000, (HDSPE_FREQ_32000) }, 6956723Sshin { 44100, (HDSPE_FREQ_44100) }, 7056723Sshin { 48000, (HDSPE_FREQ_48000) }, 7156723Sshin { 64000, (HDSPE_FREQ_32000 | HDSPE_FREQ_DOUBLE) }, 7256723Sshin { 88200, (HDSPE_FREQ_44100 | HDSPE_FREQ_DOUBLE) }, 7356723Sshin { 96000, (HDSPE_FREQ_48000 | HDSPE_FREQ_DOUBLE) }, 7456723Sshin { 128000, (HDSPE_FREQ_32000 | HDSPE_FREQ_QUAD) }, 7556723Sshin { 176400, (HDSPE_FREQ_44100 | HDSPE_FREQ_QUAD) }, 7656723Sshin { 192000, (HDSPE_FREQ_48000 | HDSPE_FREQ_QUAD) }, 7756723Sshin 7856723Sshin { 0, 0 }, 7956723Sshin}; 8056723Sshin 8156723Sshin 8256723Sshinstatic int 8356723Sshinhdspe_hw_mixer(struct sc_chinfo *ch, unsigned int dst, 8456723Sshin unsigned int src, unsigned short data) 8556723Sshin{ 8656723Sshin struct sc_pcminfo *scp = ch->parent; 8756723Sshin struct sc_info *sc = scp->sc; 8856723Sshin int offs = 0; 8956723Sshin 9056723Sshin if (ch->dir == PCMDIR_PLAY) 9156723Sshin offs = 64; 9256723Sshin 9356723Sshin hdspe_write_4(sc, HDSPE_MIXER_BASE + 9456723Sshin ((offs + src + 128 * dst) * sizeof(uint32_t)), 9556723Sshin data & 0xFFFF); 9656723Sshin 9756723Sshin return 0; 9856723Sshin}; 9956723Sshin 10056723Sshinstatic int 10156723Sshinhdspechan_setgain(struct sc_chinfo *ch) 10256723Sshin{ 10356723Sshin 10456723Sshin hdspe_hw_mixer(ch, ch->lslot, ch->lslot, 10556723Sshin ch->lvol * HDSPE_MAX_GAIN / 100); 10656723Sshin hdspe_hw_mixer(ch, ch->rslot, ch->rslot, 10756723Sshin ch->rvol * HDSPE_MAX_GAIN / 100); 10856723Sshin 10956723Sshin return 0; 11056723Sshin} 11156723Sshin 11256723Sshinstatic int 11356723Sshinhdspemixer_init(struct snd_mixer *m) 11456723Sshin{ 11556723Sshin struct sc_pcminfo *scp = mix_getdevinfo(m); 11656723Sshin struct sc_info *sc = scp->sc; 11756723Sshin int mask; 11856723Sshin 11956723Sshin if (sc == NULL) 12056723Sshin return -1; 12156723Sshin 12256723Sshin mask = SOUND_MASK_PCM; 12356723Sshin 12456723Sshin if (scp->hc->play) 12556723Sshin mask |= SOUND_MASK_VOLUME; 12656723Sshin 12756723Sshin if (scp->hc->rec) 12856723Sshin mask |= SOUND_MASK_RECLEV; 12956723Sshin 13056723Sshin snd_mtxlock(sc->lock); 13156723Sshin pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL); 13256723Sshin mix_setdevs(m, mask); 13356723Sshin snd_mtxunlock(sc->lock); 13456723Sshin 13556723Sshin return 0; 13656723Sshin} 13756723Sshin 13856723Sshinstatic int 13956723Sshinhdspemixer_set(struct snd_mixer *m, unsigned dev, 14056723Sshin unsigned left, unsigned right) 14156723Sshin{ 14262587Sitojun struct sc_pcminfo *scp = mix_getdevinfo(m); 14356723Sshin struct sc_chinfo *ch; 14456723Sshin int i; 14556723Sshin 14662587Sitojun#if 0 14756723Sshin device_printf(scp->dev, "hdspemixer_set() %d %d\n", 14862587Sitojun left,right); 14956723Sshin#endif 15056723Sshin 15162587Sitojun for (i = 0; i < scp->chnum; i++) { 15262587Sitojun ch = &scp->chan[i]; 15356723Sshin if ((dev == SOUND_MIXER_VOLUME && ch->dir == PCMDIR_PLAY) || 15456723Sshin (dev == SOUND_MIXER_RECLEV && ch->dir == PCMDIR_REC)) { 15562587Sitojun ch->lvol = left; 15656723Sshin ch->rvol = right; 15756723Sshin if (ch->run) 15856723Sshin hdspechan_setgain(ch); 15956723Sshin } 16056723Sshin } 16162587Sitojun 16256723Sshin return 0; 16356723Sshin} 16462587Sitojun 16562587Sitojunstatic kobj_method_t hdspemixer_methods[] = { 16662587Sitojun KOBJMETHOD(mixer_init, hdspemixer_init), 16762587Sitojun KOBJMETHOD(mixer_set, hdspemixer_set), 16862587Sitojun KOBJMETHOD_END 16962587Sitojun}; 17062587SitojunMIXER_DECLARE(hdspemixer); 17162587Sitojun 17262587Sitojunstatic void 17362587Sitojunhdspechan_enable(struct sc_chinfo *ch, int value) 17462587Sitojun{ 17562587Sitojun struct sc_pcminfo *scp = ch->parent; 17662587Sitojun struct sc_info *sc = scp->sc; 17762587Sitojun int reg; 17862587Sitojun 17962587Sitojun if (ch->dir == PCMDIR_PLAY) 18062587Sitojun reg = HDSPE_OUT_ENABLE_BASE; 18162587Sitojun else 18262587Sitojun reg = HDSPE_IN_ENABLE_BASE; 18356723Sshin 18456723Sshin ch->run = value; 18556723Sshin 18656723Sshin hdspe_write_1(sc, reg + (4 * ch->lslot), value); 18756723Sshin hdspe_write_1(sc, reg + (4 * ch->rslot), value); 18856723Sshin} 18956723Sshin 19056723Sshinstatic int 19156723Sshinhdspe_running(struct sc_info *sc) 19256723Sshin{ 19356723Sshin struct sc_pcminfo *scp; 19456723Sshin struct sc_chinfo *ch; 19556723Sshin int i, j, devcount, err; 19656723Sshin device_t *devlist; 19756723Sshin 19856723Sshin if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) 19956723Sshin goto bad; 20056723Sshin 20156723Sshin for (i = 0; i < devcount; i++) { 20256723Sshin scp = device_get_ivars(devlist[i]); 20356723Sshin for (j = 0; j < scp->chnum; j++) { 20456723Sshin ch = &scp->chan[j]; 20556723Sshin if (ch->run) 20656723Sshin goto bad; 20756723Sshin } 20856723Sshin } 20956723Sshin 21056723Sshin free(devlist, M_TEMP); 21156723Sshin return 0; 21256723Sshinbad: 21356723Sshin 21462587Sitojun#if 0 21556723Sshin device_printf(sc->dev,"hdspe is running\n"); 21656723Sshin#endif 21756723Sshin 21856723Sshin free(devlist, M_TEMP); 21956723Sshin return 1; 22056723Sshin} 22156723Sshin 22256723Sshinstatic void 22356723Sshinhdspe_start_audio(struct sc_info *sc) 22456723Sshin{ 22562587Sitojun 22656723Sshin sc->ctrl_register |= (HDSPE_AUDIO_INT_ENABLE | HDSPE_ENABLE); 22756723Sshin hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register); 22856723Sshin} 22962587Sitojun 23056723Sshinstatic void 23156723Sshinhdspe_stop_audio(struct sc_info *sc) 23256723Sshin{ 23356723Sshin 23456723Sshin if (hdspe_running(sc) == 1) 23556723Sshin return; 23656723Sshin 23756723Sshin sc->ctrl_register &= ~(HDSPE_AUDIO_INT_ENABLE | HDSPE_ENABLE); 23856723Sshin hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register); 23956723Sshin} 24056723Sshin 24156723Sshin/* Multiplex / demultiplex: 2.0 <-> 2 x 1.0. */ 24256723Sshinstatic void 24356723Sshinbuffer_copy(struct sc_chinfo *ch) 24456723Sshin{ 24556723Sshin struct sc_pcminfo *scp = ch->parent; 24656723Sshin struct sc_info *sc = scp->sc; 24756723Sshin int length,src,dst; 24856723Sshin int ssize, dsize; 24956723Sshin int i; 25056723Sshin 25156723Sshin length = sndbuf_getready(ch->buffer) / 25256723Sshin (4 /* Bytes per sample. */ * 2 /* channels */); 25356723Sshin 25456723Sshin if (ch->dir == PCMDIR_PLAY) { 25556723Sshin src = sndbuf_getreadyptr(ch->buffer); 25656723Sshin } else { 25756723Sshin src = sndbuf_getfreeptr(ch->buffer); 25856723Sshin } 25956723Sshin 26056723Sshin src /= 4; /* Bytes per sample. */ 26156723Sshin dst = src / 2; /* Destination buffer twice smaller. */ 26256723Sshin 26356723Sshin ssize = ch->size / 4; 26456723Sshin dsize = ch->size / 8; 26556723Sshin 26656723Sshin /* 26756723Sshin * Use two fragment buffer to avoid sound clipping. 26856723Sshin */ 26956723Sshin 27056723Sshin for (i = 0; i < sc->period * 2 /* fragments */; i++) { 27156723Sshin if (ch->dir == PCMDIR_PLAY) { 27256723Sshin sc->pbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot] = 27356723Sshin ch->data[src]; 27456723Sshin sc->pbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot] = 27556723Sshin ch->data[src + 1]; 27656723Sshin 27756723Sshin } else { 278 ch->data[src] = 279 sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot]; 280 ch->data[src+1] = 281 sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot]; 282 } 283 284 dst+=1; 285 dst %= dsize; 286 src+=2; 287 src %= ssize; 288 } 289} 290 291static int 292clean(struct sc_chinfo *ch){ 293 struct sc_pcminfo *scp = ch->parent; 294 struct sc_info *sc = scp->sc; 295 uint32_t *buf = sc->rbuf; 296 297 if (ch->dir == PCMDIR_PLAY) { 298 buf = sc->pbuf; 299 } 300 301 bzero(buf + HDSPE_CHANBUF_SAMPLES * ch->lslot, HDSPE_CHANBUF_SIZE); 302 bzero(buf + HDSPE_CHANBUF_SAMPLES * ch->rslot, HDSPE_CHANBUF_SIZE); 303 304 return 0; 305} 306 307 308/* Channel interface. */ 309static void * 310hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 311 struct pcm_channel *c, int dir) 312{ 313 struct sc_pcminfo *scp = devinfo; 314 struct sc_info *sc = scp->sc; 315 struct sc_chinfo *ch; 316 int num; 317 318 snd_mtxlock(sc->lock); 319 num = scp->chnum; 320 321 ch = &scp->chan[num]; 322 ch->lslot = scp->hc->left; 323 ch->rslot = scp->hc->right; 324 ch->run = 0; 325 ch->lvol = 0; 326 ch->rvol = 0; 327 328 ch->size = HDSPE_CHANBUF_SIZE * 2 /* slots */; 329 ch->data = malloc(ch->size, M_HDSPE, M_NOWAIT); 330 331 ch->buffer = b; 332 ch->channel = c; 333 ch->parent = scp; 334 335 ch->dir = dir; 336 337 snd_mtxunlock(sc->lock); 338 339 if (sndbuf_setup(ch->buffer, ch->data, ch->size) != 0) { 340 device_printf(scp->dev, "Can't setup sndbuf.\n"); 341 return NULL; 342 } 343 344 return ch; 345} 346 347static int 348hdspechan_trigger(kobj_t obj, void *data, int go) 349{ 350 struct sc_chinfo *ch = data; 351 struct sc_pcminfo *scp = ch->parent; 352 struct sc_info *sc = scp->sc; 353 354 snd_mtxlock(sc->lock); 355 switch (go) { 356 case PCMTRIG_START: 357#if 0 358 device_printf(scp->dev, "hdspechan_trigger(): start\n"); 359#endif 360 hdspechan_enable(ch, 1); 361 hdspechan_setgain(ch); 362 hdspe_start_audio(sc); 363 break; 364 365 case PCMTRIG_STOP: 366 case PCMTRIG_ABORT: 367#if 0 368 device_printf(scp->dev, "hdspechan_trigger(): stop or abort\n"); 369#endif 370 clean(ch); 371 hdspechan_enable(ch, 0); 372 hdspe_stop_audio(sc); 373 break; 374 375 case PCMTRIG_EMLDMAWR: 376 case PCMTRIG_EMLDMARD: 377 if(ch->run) 378 buffer_copy(ch); 379 break; 380 } 381 382 snd_mtxunlock(sc->lock); 383 384 return 0; 385} 386 387static uint32_t 388hdspechan_getptr(kobj_t obj, void *data) 389{ 390 struct sc_chinfo *ch = data; 391 struct sc_pcminfo *scp = ch->parent; 392 struct sc_info *sc = scp->sc; 393 uint32_t ret, pos; 394 395 snd_mtxlock(sc->lock); 396 ret = hdspe_read_2(sc, HDSPE_STATUS_REG); 397 snd_mtxunlock(sc->lock); 398 399 pos = ret & HDSPE_BUF_POSITION_MASK; 400 pos *= 2; /* Hardbuf twice bigger. */ 401 402 return pos; 403} 404 405static int 406hdspechan_free(kobj_t obj, void *data) 407{ 408 struct sc_chinfo *ch = data; 409 struct sc_pcminfo *scp = ch->parent; 410 struct sc_info *sc = scp->sc; 411 412#if 0 413 device_printf(scp->dev, "hdspechan_free()\n"); 414#endif 415 snd_mtxlock(sc->lock); 416 if (ch->data != NULL) { 417 free(ch->data, M_HDSPE); 418 ch->data = NULL; 419 } 420 snd_mtxunlock(sc->lock); 421 422 return 0; 423} 424 425static int 426hdspechan_setformat(kobj_t obj, void *data, uint32_t format) 427{ 428 struct sc_chinfo *ch = data; 429 430#if 0 431 struct sc_pcminfo *scp = ch->parent; 432 device_printf(scp->dev, "hdspechan_setformat(%d)\n", format); 433#endif 434 435 ch->format = format; 436 437 return 0; 438} 439 440static uint32_t 441hdspechan_setspeed(kobj_t obj, void *data, uint32_t speed) 442{ 443 struct sc_chinfo *ch = data; 444 struct sc_pcminfo *scp = ch->parent; 445 struct sc_info *sc = scp->sc; 446 struct hdspe_rate *hr = NULL; 447 long long period; 448 int threshold; 449 int i; 450 451#if 0 452 device_printf(scp->dev, "hdspechan_setspeed(%d)\n", speed); 453#endif 454 455 if (hdspe_running(sc) == 1) 456 goto end; 457 458 /* First look for equal frequency. */ 459 for (i = 0; rate_map[i].speed != 0; i++) { 460 if (rate_map[i].speed == speed) 461 hr = &rate_map[i]; 462 } 463 464 /* If no match, just find nearest. */ 465 if (hr == NULL) { 466 for (i = 0; rate_map[i].speed != 0; i++) { 467 hr = &rate_map[i]; 468 threshold = hr->speed + ((rate_map[i + 1].speed != 0) ? 469 ((rate_map[i + 1].speed - hr->speed) >> 1) : 0); 470 if (speed < threshold) 471 break; 472 } 473 } 474 475 switch (sc->type) { 476 case RAYDAT: 477 case AIO: 478 period = HDSPE_FREQ_AIO; 479 break; 480 default: 481 /* Unsupported card. */ 482 goto end; 483 } 484 485 /* Write frequency on the device. */ 486 sc->ctrl_register &= ~HDSPE_FREQ_MASK; 487 sc->ctrl_register |= hr->reg; 488 hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register); 489 490 speed = hr->speed; 491 if (speed > 96000) 492 speed /= 4; 493 else if (speed > 48000) 494 speed /= 2; 495 496 /* Set DDS value. */ 497 period /= speed; 498 hdspe_write_4(sc, HDSPE_FREQ_REG, period); 499 500 sc->speed = hr->speed; 501end: 502 return sc->speed; 503} 504 505static uint32_t 506hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) 507{ 508 struct sc_chinfo *ch = data; 509 struct sc_pcminfo *scp = ch->parent; 510 struct sc_info *sc = scp->sc; 511 struct hdspe_latency *hl = NULL; 512 int threshold; 513 int i; 514 515#if 0 516 device_printf(scp->dev, "hdspechan_setblocksize(%d)\n", blocksize); 517#endif 518 519 if (hdspe_running(sc) == 1) 520 goto end; 521 522 if (blocksize > HDSPE_LAT_BYTES_MAX) 523 blocksize = HDSPE_LAT_BYTES_MAX; 524 else if (blocksize < HDSPE_LAT_BYTES_MIN) 525 blocksize = HDSPE_LAT_BYTES_MIN; 526 527 blocksize /= 4 /* samples */; 528 529 /* First look for equal latency. */ 530 for (i = 0; latency_map[i].period != 0; i++) { 531 if (latency_map[i].period == blocksize) { 532 hl = &latency_map[i]; 533 } 534 } 535 536 /* If no match, just find nearest. */ 537 if (hl == NULL) { 538 for (i = 0; latency_map[i].period != 0; i++) { 539 hl = &latency_map[i]; 540 threshold = hl->period + ((latency_map[i + 1].period != 0) ? 541 ((latency_map[i + 1].period - hl->period) >> 1) : 0); 542 if (blocksize < threshold) 543 break; 544 } 545 } 546 547 snd_mtxlock(sc->lock); 548 sc->ctrl_register &= ~HDSPE_LAT_MASK; 549 sc->ctrl_register |= hdspe_encode_latency(hl->n); 550 hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register); 551 sc->period = hl->period; 552 snd_mtxunlock(sc->lock); 553 554#if 0 555 device_printf(scp->dev, "New period=%d\n", sc->period); 556#endif 557 558 sndbuf_resize(ch->buffer, (HDSPE_CHANBUF_SIZE * 2) / (sc->period * 4), 559 (sc->period * 4)); 560end: 561 return sndbuf_getblksz(ch->buffer); 562} 563 564static uint32_t hdspe_rfmt[] = { 565 SND_FORMAT(AFMT_S32_LE, 2, 0), 566 0 567}; 568 569static struct pcmchan_caps hdspe_rcaps = {32000, 192000, hdspe_rfmt, 0}; 570 571static uint32_t hdspe_pfmt[] = { 572 SND_FORMAT(AFMT_S32_LE, 2, 0), 573 0 574}; 575 576static struct pcmchan_caps hdspe_pcaps = {32000, 192000, hdspe_pfmt, 0}; 577 578static struct pcmchan_caps * 579hdspechan_getcaps(kobj_t obj, void *data) 580{ 581 struct sc_chinfo *ch = data; 582 583#if 0 584 struct sc_pcminfo *scl = ch->parent; 585 device_printf(scp->dev, "hdspechan_getcaps()\n"); 586#endif 587 588 return (ch->dir == PCMDIR_PLAY) ? 589 &hdspe_pcaps : &hdspe_rcaps; 590} 591 592static kobj_method_t hdspechan_methods[] = { 593 KOBJMETHOD(channel_init, hdspechan_init), 594 KOBJMETHOD(channel_free, hdspechan_free), 595 KOBJMETHOD(channel_setformat, hdspechan_setformat), 596 KOBJMETHOD(channel_setspeed, hdspechan_setspeed), 597 KOBJMETHOD(channel_setblocksize, hdspechan_setblocksize), 598 KOBJMETHOD(channel_trigger, hdspechan_trigger), 599 KOBJMETHOD(channel_getptr, hdspechan_getptr), 600 KOBJMETHOD(channel_getcaps, hdspechan_getcaps), 601 KOBJMETHOD_END 602}; 603CHANNEL_DECLARE(hdspechan); 604 605 606static int 607hdspe_pcm_probe(device_t dev) 608{ 609 610#if 0 611 device_printf(dev,"hdspe_pcm_probe()\n"); 612#endif 613 614 return 0; 615} 616 617static uint32_t 618hdspe_pcm_intr(struct sc_pcminfo *scp) { 619 struct sc_chinfo *ch; 620 struct sc_info *sc = scp->sc; 621 int i; 622 623 for (i = 0; i < scp->chnum; i++) { 624 ch = &scp->chan[i]; 625 snd_mtxunlock(sc->lock); 626 chn_intr(ch->channel); 627 snd_mtxlock(sc->lock); 628 } 629 630 return 0; 631} 632 633static int 634hdspe_pcm_attach(device_t dev) 635{ 636 struct sc_pcminfo *scp; 637 char status[SND_STATUSLEN]; 638 char desc[64]; 639 int i, err; 640 641 scp = device_get_ivars(dev); 642 scp->ih = &hdspe_pcm_intr; 643 644 bzero(desc, sizeof(desc)); 645 snprintf(desc, sizeof(desc), "HDSPe AIO [%s]", scp->hc->descr); 646 device_set_desc_copy(dev, desc); 647 648 /* 649 * We don't register interrupt handler with snd_setup_intr 650 * in pcm device. Mark pcm device as MPSAFE manually. 651 */ 652 pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); 653 654 err = pcm_register(dev, scp, scp->hc->play, scp->hc->rec); 655 if (err) { 656 device_printf(dev, "Can't register pcm.\n"); 657 return ENXIO; 658 } 659 660 scp->chnum = 0; 661 for (i = 0; i < scp->hc->play; i++) { 662 pcm_addchan(dev, PCMDIR_PLAY, &hdspechan_class, scp); 663 scp->chnum++; 664 } 665 666 for (i = 0; i < scp->hc->rec; i++) { 667 pcm_addchan(dev, PCMDIR_REC, &hdspechan_class, scp); 668 scp->chnum++; 669 } 670 671 snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", 672 rman_get_start(scp->sc->cs), 673 rman_get_start(scp->sc->irq), 674 PCM_KLDSTRING(snd_hdspe)); 675 pcm_setstatus(dev, status); 676 677 mixer_init(dev, &hdspemixer_class, scp); 678 679 return 0; 680} 681 682static int 683hdspe_pcm_detach(device_t dev) 684{ 685 int err; 686 687 err = pcm_unregister(dev); 688 if (err) { 689 device_printf(dev, "Can't unregister device.\n"); 690 return err; 691 } 692 693 return 0; 694} 695 696static device_method_t hdspe_pcm_methods[] = { 697 DEVMETHOD(device_probe, hdspe_pcm_probe), 698 DEVMETHOD(device_attach, hdspe_pcm_attach), 699 DEVMETHOD(device_detach, hdspe_pcm_detach), 700 { 0, 0 } 701}; 702 703static driver_t hdspe_pcm_driver = { 704 "pcm", 705 hdspe_pcm_methods, 706 PCM_SOFTC_SIZE, 707}; 708 709DRIVER_MODULE(snd_hdspe_pcm, hdspe, hdspe_pcm_driver, pcm_devclass, 0, 0); 710MODULE_DEPEND(snd_hdspe, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 711MODULE_VERSION(snd_hdspe, 1); 712