ad1816.c revision 78564
1/* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * Copyright Luigi Rizzo, 1997,1998 4 * Copyright by Hannu Savolainen 1994, 1995 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 AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: head/sys/dev/sound/isa/ad1816.c 78564 2001-06-21 19:45:59Z greid $ 29 */ 30 31#include <dev/sound/pcm/sound.h> 32#include <dev/sound/isa/ad1816.h> 33 34#include "mixer_if.h" 35 36struct ad1816_info; 37 38struct ad1816_chinfo { 39 struct ad1816_info *parent; 40 struct pcm_channel *channel; 41 struct snd_dbuf *buffer; 42 int dir, blksz; 43}; 44 45struct ad1816_info { 46 struct resource *io_base; /* primary I/O address for the board */ 47 int io_rid; 48 struct resource *irq; 49 int irq_rid; 50 struct resource *drq1; /* play */ 51 int drq1_rid; 52 struct resource *drq2; /* rec */ 53 int drq2_rid; 54 void *ih; 55 bus_dma_tag_t parent_dmat; 56 void *lock; 57 58 struct ad1816_chinfo pch, rch; 59}; 60 61static driver_intr_t ad1816_intr; 62static int ad1816_probe(device_t dev); 63static int ad1816_attach(device_t dev); 64 65/* IO primitives */ 66static int ad1816_wait_init(struct ad1816_info *ad1816, int x); 67static u_short ad1816_read(struct ad1816_info *ad1816, u_int reg); 68static void ad1816_write(struct ad1816_info *ad1816, u_int reg, u_short data); 69 70static u_int32_t ad1816_fmt[] = { 71 AFMT_U8, 72 AFMT_STEREO | AFMT_U8, 73 AFMT_S16_LE, 74 AFMT_STEREO | AFMT_S16_LE, 75 AFMT_MU_LAW, 76 AFMT_STEREO | AFMT_MU_LAW, 77 AFMT_A_LAW, 78 AFMT_STEREO | AFMT_A_LAW, 79 0 80}; 81 82static struct pcmchan_caps ad1816_caps = {4000, 55200, ad1816_fmt, 0}; 83 84#define AD1816_MUTE 31 /* value for mute */ 85 86static void 87ad1816_lock(struct ad1816_info *ad1816) 88{ 89 snd_mtxlock(ad1816->lock); 90} 91 92static void 93ad1816_unlock(struct ad1816_info *ad1816) 94{ 95 snd_mtxunlock(ad1816->lock); 96} 97 98static int 99port_rd(struct resource *port, int off) 100{ 101 if (port) 102 return bus_space_read_1(rman_get_bustag(port), 103 rman_get_bushandle(port), 104 off); 105 else 106 return -1; 107} 108 109static void 110port_wr(struct resource *port, int off, u_int8_t data) 111{ 112 if (port) 113 return bus_space_write_1(rman_get_bustag(port), 114 rman_get_bushandle(port), 115 off, data); 116} 117 118static int 119io_rd(struct ad1816_info *ad1816, int reg) 120{ 121 return port_rd(ad1816->io_base, reg); 122} 123 124static void 125io_wr(struct ad1816_info *ad1816, int reg, u_int8_t data) 126{ 127 return port_wr(ad1816->io_base, reg, data); 128} 129 130static void 131ad1816_intr(void *arg) 132{ 133 struct ad1816_info *ad1816 = (struct ad1816_info *)arg; 134 unsigned char c, served = 0; 135 136 ad1816_lock(ad1816); 137 /* get interupt status */ 138 c = io_rd(ad1816, AD1816_INT); 139 140 /* check for stray interupts */ 141 if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) { 142 printf("pcm: stray int (%x)\n", c); 143 c &= AD1816_INTRCI | AD1816_INTRPI; 144 } 145 /* check for capture interupt */ 146 if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) { 147 chn_intr(ad1816->rch.channel); 148 served |= AD1816_INTRCI; /* cp served */ 149 } 150 /* check for playback interupt */ 151 if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) { 152 chn_intr(ad1816->pch.channel); 153 served |= AD1816_INTRPI; /* pb served */ 154 } 155 if (served == 0) { 156 /* this probably means this is not a (working) ad1816 chip, */ 157 /* or an error in dma handling */ 158 printf("pcm: int without reason (%x)\n", c); 159 c = 0; 160 } else c &= ~served; 161 io_wr(ad1816, AD1816_INT, c); 162 c = io_rd(ad1816, AD1816_INT); 163 if (c != 0) printf("pcm: int clear failed (%x)\n", c); 164 ad1816_unlock(ad1816); 165} 166 167static int 168ad1816_wait_init(struct ad1816_info *ad1816, int x) 169{ 170 int n = 0; /* to shut up the compiler... */ 171 172 for (; x--;) 173 if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10); 174 else return n; 175 printf("ad1816_wait_init failed 0x%02x.\n", n); 176 return -1; 177} 178 179static unsigned short 180ad1816_read(struct ad1816_info *ad1816, unsigned int reg) 181{ 182 u_short x = 0; 183 184 if (ad1816_wait_init(ad1816, 100) == -1) return 0; 185 io_wr(ad1816, AD1816_ALE, 0); 186 io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK)); 187 if (ad1816_wait_init(ad1816, 100) == -1) return 0; 188 x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW); 189 return x; 190} 191 192static void 193ad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data) 194{ 195 if (ad1816_wait_init(ad1816, 100) == -1) return; 196 io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK)); 197 io_wr(ad1816, AD1816_LOW, (data & 0x000000ff)); 198 io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8); 199} 200 201/* -------------------------------------------------------------------- */ 202 203static int 204ad1816mix_init(struct snd_mixer *m) 205{ 206 mix_setdevs(m, AD1816_MIXER_DEVICES); 207 mix_setrecdevs(m, AD1816_REC_DEVICES); 208 return 0; 209} 210 211static int 212ad1816mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 213{ 214 struct ad1816_info *ad1816 = mix_getdevinfo(m); 215 u_short reg = 0; 216 217 /* Scale volumes */ 218 left = AD1816_MUTE - (AD1816_MUTE * left) / 100; 219 right = AD1816_MUTE - (AD1816_MUTE * right) / 100; 220 221 reg = (left << 8) | right; 222 223 /* do channel selective muting if volume is zero */ 224 if (left == AD1816_MUTE) reg |= 0x8000; 225 if (right == AD1816_MUTE) reg |= 0x0080; 226 227 ad1816_lock(ad1816); 228 switch (dev) { 229 case SOUND_MIXER_VOLUME: /* Register 14 master volume */ 230 ad1816_write(ad1816, 14, reg); 231 break; 232 233 case SOUND_MIXER_CD: /* Register 15 cd */ 234 case SOUND_MIXER_LINE1: 235 ad1816_write(ad1816, 15, reg); 236 break; 237 238 case SOUND_MIXER_SYNTH: /* Register 16 synth */ 239 ad1816_write(ad1816, 16, reg); 240 break; 241 242 case SOUND_MIXER_PCM: /* Register 4 pcm */ 243 ad1816_write(ad1816, 4, reg); 244 break; 245 246 case SOUND_MIXER_LINE: 247 case SOUND_MIXER_LINE3: /* Register 18 line in */ 248 ad1816_write(ad1816, 18, reg); 249 break; 250 251 case SOUND_MIXER_MIC: /* Register 19 mic volume */ 252 ad1816_write(ad1816, 19, reg & ~0xff); /* mic is mono */ 253 break; 254 255 case SOUND_MIXER_IGAIN: 256 /* and now to something completely different ... */ 257 ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f) 258 | (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */ 259 | ((AD1816_MUTE - right) / 2))); 260 break; 261 262 default: 263 printf("ad1816_mixer_set(): unknown device.\n"); 264 break; 265 } 266 ad1816_unlock(ad1816); 267 268 left = ((AD1816_MUTE - left) * 100) / AD1816_MUTE; 269 right = ((AD1816_MUTE - right) * 100) / AD1816_MUTE; 270 271 return left | (right << 8); 272} 273 274static int 275ad1816mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 276{ 277 struct ad1816_info *ad1816 = mix_getdevinfo(m); 278 int dev; 279 280 switch (src) { 281 case SOUND_MASK_LINE: 282 case SOUND_MASK_LINE3: 283 dev = 0x00; 284 break; 285 286 case SOUND_MASK_CD: 287 case SOUND_MASK_LINE1: 288 dev = 0x20; 289 break; 290 291 case SOUND_MASK_MIC: 292 default: 293 dev = 0x50; 294 src = SOUND_MASK_MIC; 295 } 296 297 dev |= dev << 8; 298 ad1816_lock(ad1816); 299 ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev); 300 ad1816_unlock(ad1816); 301 return src; 302} 303 304static kobj_method_t ad1816mixer_methods[] = { 305 KOBJMETHOD(mixer_init, ad1816mix_init), 306 KOBJMETHOD(mixer_set, ad1816mix_set), 307 KOBJMETHOD(mixer_setrecsrc, ad1816mix_setrecsrc), 308 { 0, 0 } 309}; 310MIXER_DECLARE(ad1816mixer); 311 312/* -------------------------------------------------------------------- */ 313/* channel interface */ 314static void * 315ad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 316{ 317 struct ad1816_info *ad1816 = devinfo; 318 struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch; 319 320 ch->parent = ad1816; 321 ch->channel = c; 322 ch->buffer = b; 323 if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, DSP_BUFFSIZE) == -1) return NULL; 324 return ch; 325} 326 327static int 328ad1816chan_setdir(kobj_t obj, void *data, int dir) 329{ 330 struct ad1816_chinfo *ch = data; 331 struct ad1816_info *ad1816 = ch->parent; 332 333 sndbuf_isadmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2); 334 ch->dir = dir; 335 return 0; 336} 337 338static int 339ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format) 340{ 341 struct ad1816_chinfo *ch = data; 342 struct ad1816_info *ad1816 = ch->parent; 343 int fmt = AD1816_U8, reg; 344 345 ad1816_lock(ad1816); 346 if (ch->dir == PCMDIR_PLAY) { 347 reg = AD1816_PLAY; 348 ad1816_write(ad1816, 8, 0x0000); /* reset base and current counter */ 349 ad1816_write(ad1816, 9, 0x0000); /* for playback and capture */ 350 } else { 351 reg = AD1816_CAPT; 352 ad1816_write(ad1816, 10, 0x0000); 353 ad1816_write(ad1816, 11, 0x0000); 354 } 355 switch (format & ~AFMT_STEREO) { 356 case AFMT_A_LAW: 357 fmt = AD1816_ALAW; 358 break; 359 360 case AFMT_MU_LAW: 361 fmt = AD1816_MULAW; 362 break; 363 364 case AFMT_S16_LE: 365 fmt = AD1816_S16LE; 366 break; 367 368 case AFMT_S16_BE: 369 fmt = AD1816_S16BE; 370 break; 371 372 case AFMT_U8: 373 fmt = AD1816_U8; 374 break; 375 } 376 if (format & AFMT_STEREO) fmt |= AD1816_STEREO; 377 io_wr(ad1816, reg, fmt); 378 ad1816_unlock(ad1816); 379 return format; 380} 381 382static int 383ad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed) 384{ 385 struct ad1816_chinfo *ch = data; 386 struct ad1816_info *ad1816 = ch->parent; 387 388 RANGE(speed, 4000, 55200); 389 ad1816_lock(ad1816); 390 ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed); 391 ad1816_unlock(ad1816); 392 return speed; 393} 394 395static int 396ad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 397{ 398 struct ad1816_chinfo *ch = data; 399 400 ch->blksz = blocksize; 401 return ch->blksz; 402} 403 404static int 405ad1816chan_trigger(kobj_t obj, void *data, int go) 406{ 407 struct ad1816_chinfo *ch = data; 408 struct ad1816_info *ad1816 = ch->parent; 409 int wr, reg; 410 411 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 412 return 0; 413 414 sndbuf_isadma(ch->buffer, go); 415 wr = (ch->dir == PCMDIR_PLAY); 416 reg = wr? AD1816_PLAY : AD1816_CAPT; 417 ad1816_lock(ad1816); 418 switch (go) { 419 case PCMTRIG_START: 420 /* start only if not already running */ 421 if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) { 422 int cnt = ((ch->blksz) >> 2) - 1; 423 ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */ 424 ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */ 425 ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) | 426 (wr? 0x8000 : 0x4000)); /* enable int */ 427 /* enable playback */ 428 io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE); 429 if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) 430 printf("ad1816: failed to start %s DMA!\n", 431 wr? "play" : "rec"); 432 } 433 break; 434 435 case PCMTRIG_STOP: 436 case PCMTRIG_ABORT: /* XXX check this... */ 437 /* we don't test here if it is running... */ 438 if (wr) { 439 ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) & 440 ~(wr? 0x8000 : 0x4000)); 441 /* disable int */ 442 io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE); 443 /* disable playback */ 444 if (io_rd(ad1816, reg) & AD1816_ENABLE) 445 printf("ad1816: failed to stop %s DMA!\n", 446 wr? "play" : "rec"); 447 ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */ 448 ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */ 449 } 450 break; 451 } 452 ad1816_unlock(ad1816); 453 return 0; 454} 455 456static int 457ad1816chan_getptr(kobj_t obj, void *data) 458{ 459 struct ad1816_chinfo *ch = data; 460 return sndbuf_isadmaptr(ch->buffer); 461} 462 463static struct pcmchan_caps * 464ad1816chan_getcaps(kobj_t obj, void *data) 465{ 466 return &ad1816_caps; 467} 468 469static kobj_method_t ad1816chan_methods[] = { 470 KOBJMETHOD(channel_init, ad1816chan_init), 471 KOBJMETHOD(channel_setdir, ad1816chan_setdir), 472 KOBJMETHOD(channel_setformat, ad1816chan_setformat), 473 KOBJMETHOD(channel_setspeed, ad1816chan_setspeed), 474 KOBJMETHOD(channel_setblocksize, ad1816chan_setblocksize), 475 KOBJMETHOD(channel_trigger, ad1816chan_trigger), 476 KOBJMETHOD(channel_getptr, ad1816chan_getptr), 477 KOBJMETHOD(channel_getcaps, ad1816chan_getcaps), 478 { 0, 0 } 479}; 480CHANNEL_DECLARE(ad1816chan); 481 482/* -------------------------------------------------------------------- */ 483 484static void 485ad1816_release_resources(struct ad1816_info *ad1816, device_t dev) 486{ 487 if (ad1816->irq) { 488 if (ad1816->ih) 489 bus_teardown_intr(dev, ad1816->irq, ad1816->ih); 490 bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid, 491 ad1816->irq); 492 ad1816->irq = 0; 493 } 494 if (ad1816->drq1) { 495 bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid, 496 ad1816->drq1); 497 ad1816->drq1 = 0; 498 } 499 if (ad1816->drq2) { 500 bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid, 501 ad1816->drq2); 502 ad1816->drq2 = 0; 503 } 504 if (ad1816->io_base) { 505 bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid, 506 ad1816->io_base); 507 ad1816->io_base = 0; 508 } 509 if (ad1816->parent_dmat) { 510 bus_dma_tag_destroy(ad1816->parent_dmat); 511 ad1816->parent_dmat = 0; 512 } 513 if (ad1816->lock) snd_mtxfree(ad1816->lock); 514 free(ad1816, M_DEVBUF); 515} 516 517static int 518ad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev) 519{ 520 int ok = 1, pdma, rdma; 521 if (!ad1816->io_base) 522 ad1816->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &ad1816->io_rid, 523 0, ~0, 1, RF_ACTIVE); 524 if (!ad1816->irq) 525 ad1816->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ad1816->irq_rid, 526 0, ~0, 1, RF_ACTIVE); 527 if (!ad1816->drq1) 528 ad1816->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq1_rid, 529 0, ~0, 1, RF_ACTIVE); 530 if (!ad1816->drq2) 531 ad1816->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq2_rid, 532 0, ~0, 1, RF_ACTIVE); 533 534 if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0; 535 536 if (ok) { 537 pdma = rman_get_start(ad1816->drq1); 538 isa_dma_acquire(pdma); 539 isa_dmainit(pdma, DSP_BUFFSIZE); 540 if (ad1816->drq2) { 541 rdma = rman_get_start(ad1816->drq2); 542 isa_dma_acquire(rdma); 543 isa_dmainit(rdma, DSP_BUFFSIZE); 544 } else rdma = pdma; 545 if (pdma == rdma) 546 pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 547 } 548 return ok; 549} 550 551static int 552ad1816_init(struct ad1816_info *ad1816, device_t dev) 553{ 554 ad1816_write(ad1816, 1, 0x2); /* disable interrupts */ 555 ad1816_write(ad1816, 32, 0x90F0); /* SoundSys Mode, split fmt */ 556 557 ad1816_write(ad1816, 5, 0x8080); /* FM volume mute */ 558 ad1816_write(ad1816, 6, 0x8080); /* I2S1 volume mute */ 559 ad1816_write(ad1816, 7, 0x8080); /* I2S0 volume mute */ 560 ad1816_write(ad1816, 17, 0x8888); /* VID Volume mute */ 561 ad1816_write(ad1816, 20, 0x5050); /* recsrc mic, agc off */ 562 /* adc gain is set to 0 */ 563 564 return 0; 565} 566 567static int 568ad1816_probe(device_t dev) 569{ 570 char *s = NULL; 571 u_int32_t logical_id = isa_get_logicalid(dev); 572 573 switch (logical_id) { 574 case 0x80719304: /* ADS7180 */ 575 s = "AD1816"; 576 break; 577 } 578 579 if (s) { 580 device_set_desc(dev, s); 581 return 0; 582 } 583 return ENXIO; 584} 585 586static int 587ad1816_attach(device_t dev) 588{ 589 struct ad1816_info *ad1816; 590 char status[SND_STATUSLEN]; 591 592 ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT | M_ZERO); 593 if (!ad1816) return ENXIO; 594 595 ad1816->lock = snd_mtxcreate(device_get_nameunit(dev)); 596 ad1816->io_rid = 2; 597 ad1816->irq_rid = 0; 598 ad1816->drq1_rid = 0; 599 ad1816->drq2_rid = 1; 600 601 if (!ad1816_alloc_resources(ad1816, dev)) goto no; 602 ad1816_init(ad1816, dev); 603 if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no; 604 605 snd_setup_intr(dev, ad1816->irq, INTR_MPSAFE, ad1816_intr, ad1816, &ad1816->ih); 606 if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 607 /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 608 /*highaddr*/BUS_SPACE_MAXADDR, 609 /*filter*/NULL, /*filterarg*/NULL, 610 /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, 611 /*maxsegz*/0x3ffff, 612 /*flags*/0, &ad1816->parent_dmat) != 0) { 613 device_printf(dev, "unable to create dma tag\n"); 614 goto no; 615 } 616 snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld", 617 rman_get_start(ad1816->io_base), 618 rman_get_start(ad1816->irq), 619 rman_get_start(ad1816->drq1)); 620 if (ad1816->drq2) snprintf(status + strlen(status), 621 SND_STATUSLEN - strlen(status), ":%ld", 622 rman_get_start(ad1816->drq2)); 623 624 if (pcm_register(dev, ad1816, 1, 1)) goto no; 625 pcm_addchan(dev, PCMDIR_REC, &ad1816chan_class, ad1816); 626 pcm_addchan(dev, PCMDIR_PLAY, &ad1816chan_class, ad1816); 627 pcm_setstatus(dev, status); 628 629 return 0; 630no: 631 ad1816_release_resources(ad1816, dev); 632 633 return ENXIO; 634 635} 636 637static int 638ad1816_detach(device_t dev) 639{ 640 int r; 641 struct ad1816_info *ad1816; 642 643 r = pcm_unregister(dev); 644 if (r) 645 return r; 646 647 ad1816 = pcm_getdevinfo(dev); 648 ad1816_release_resources(ad1816, dev); 649 return 0; 650} 651 652static device_method_t ad1816_methods[] = { 653 /* Device interface */ 654 DEVMETHOD(device_probe, ad1816_probe), 655 DEVMETHOD(device_attach, ad1816_attach), 656 DEVMETHOD(device_detach, ad1816_detach), 657 658 { 0, 0 } 659}; 660 661static driver_t ad1816_driver = { 662 "pcm", 663 ad1816_methods, 664 sizeof(struct snddev_info), 665}; 666 667DRIVER_MODULE(snd_ad1816, isa, ad1816_driver, pcm_devclass, 0, 0); 668MODULE_DEPEND(snd_ad1816, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 669MODULE_VERSION(snd_ad1816, 1); 670 671 672