sbc.c revision 54791
1/*- 2 * Copyright (c) 1999 Seigo Tanimura 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/dev/sound/isa/sbc.c 54791 1999-12-18 22:21:47Z cg $ 27 */ 28 29#include "isa.h" 30 31#include <dev/sound/chip.h> 32#include <dev/sound/pcm/sound.h> 33#define __SB_MIXER_C__ /* XXX warning... */ 34#define SB_NOMIXER 35#include <dev/sound/isa/sb.h> 36 37/* Here is the parameter structure per a device. */ 38struct sbc_softc { 39 device_t dev; /* device */ 40 41 int io_rid[3]; /* io port rids */ 42 struct resource *io[3]; /* io port resources */ 43 int io_alloced[3]; /* io port alloc flag */ 44 45 int irq_rid; /* irq rids */ 46 struct resource *irq; /* irq resources */ 47 int irq_alloced; /* irq alloc flag */ 48 49 int drq_rid[2]; /* drq rids */ 50 struct resource *drq[2]; /* drq resources */ 51 int drq_alloced[2]; /* drq alloc flag */ 52 53 u_int32_t bd_ver; 54}; 55 56static int sbc_probe(device_t dev); 57static int sbc_attach(device_t dev); 58static struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 59 u_long start, u_long end, u_long count, u_int flags); 60static int sbc_release_resource(device_t bus, device_t child, int type, int rid, 61 struct resource *r); 62 63static int alloc_resource(struct sbc_softc *scp); 64static int release_resource(struct sbc_softc *scp); 65 66static devclass_t sbc_devclass; 67 68static int io_range[3] = {0x10, 0x2, 0x4}; 69 70static int sb_rd(struct resource *io, int reg); 71static void sb_wr(struct resource *io, int reg, u_int8_t val); 72static int sb_dspready(struct resource *io); 73static int sb_cmd(struct resource *io, u_char val); 74static u_int sb_get_byte(struct resource *io); 75static void sb_setmixer(struct resource *io, u_int port, u_int value); 76 77static int 78sb_rd(struct resource *io, int reg) 79{ 80 return bus_space_read_1(rman_get_bustag(io), 81 rman_get_bushandle(io), 82 reg); 83} 84 85static void 86sb_wr(struct resource *io, int reg, u_int8_t val) 87{ 88 return bus_space_write_1(rman_get_bustag(io), 89 rman_get_bushandle(io), 90 reg, val); 91} 92 93static int 94sb_dspready(struct resource *io) 95{ 96 return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0); 97} 98 99static int 100sb_dspwr(struct resource *io, u_char val) 101{ 102 int i; 103 104 for (i = 0; i < 1000; i++) { 105 if (sb_dspready(io)) { 106 sb_wr(io, SBDSP_CMD, val); 107 return 1; 108 } 109 if (i > 10) DELAY((i > 100)? 1000 : 10); 110 } 111 printf("sb_dspwr(0x%02x) timed out.\n", val); 112 return 0; 113} 114 115static int 116sb_cmd(struct resource *io, u_char val) 117{ 118 return sb_dspwr(io, val); 119} 120 121static void 122sb_setmixer(struct resource *io, u_int port, u_int value) 123{ 124 u_long flags; 125 126 flags = spltty(); 127 sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 128 DELAY(10); 129 sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff)); 130 DELAY(10); 131 splx(flags); 132} 133 134static u_int 135sb_get_byte(struct resource *io) 136{ 137 int i; 138 139 for (i = 1000; i > 0; i--) { 140 if (sb_rd(io, DSP_DATA_AVAIL) & 0x80) 141 return sb_rd(io, DSP_READ); 142 else 143 DELAY(20); 144 } 145 return 0xffff; 146} 147 148static int 149sb_reset_dsp(struct resource *io) 150{ 151 sb_wr(io, SBDSP_RST, 3); 152 DELAY(100); 153 sb_wr(io, SBDSP_RST, 0); 154 return (sb_get_byte(io) == 0xAA)? 0 : ENXIO; 155} 156 157static int 158sb_identify_board(struct resource *io) 159{ 160 int ver, essver, rev; 161 162 sb_cmd(io, DSP_CMD_GETVER); /* Get version */ 163 ver = (sb_get_byte(io) << 8) | sb_get_byte(io); 164 if (ver < 0x100 || ver > 0x4ff) return 0; 165 if (ver == 0x0301) { 166 /* Try to detect ESS chips. */ 167 sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */ 168 essver = (sb_get_byte(io) << 8) | sb_get_byte(io); 169 rev = essver & 0x000f; 170 essver &= 0xfff0; 171 if (essver == 0x4880) ver |= 0x1000; 172 else if (essver == 0x6880) ver = 0x0500 | rev; 173 } 174 return ver; 175} 176 177static struct isa_pnp_id sbc_ids[] = { 178 {0x01008c0e, "Creative ViBRA16C"}, 179 {0x31008c0e, "Creative SB16/SB32"}, 180 {0x41008c0e, "Creative SB16/SB32"}, 181 {0x42008c0e, "Creative SB AWE64"}, /* CTL00c1 */ 182 {0x43008c0e, "Creative ViBRA16X"}, 183 {0x44008c0e, "Creative SB AWE64 Gold"}, 184 {0x45008c0e, "Creative SB AWE64"}, /* CTL0045 */ 185 186 {0x01000000, "Avance Asound 100"}, 187 {0x01100000, "Avance Asound 110"}, 188 {0x01200001, "Avance Logic ALS120"}, 189 190 {0x68187316, "ESS ES1868"}, /* ESS1868 */ 191 {0x69187316, "ESS ES1869"}, /* ESS1869 */ 192 {0xacb0110e, "ESS ES1869 (Compaq OEM)"}, 193 {0x79187316, "ESS ES1879"}, /* ESS1879 */ 194 {0x88187316, "ESS ES1888"}, /* ESS1888 */ 195 196 {0} 197}; 198 199static int 200sbc_probe(device_t dev) 201{ 202 char *s = NULL; 203 u_int32_t logical_id = isa_get_logicalid(dev); 204 if (logical_id) { 205 /* Check pnp ids */ 206 return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids); 207 } else { 208 int rid = 0, ver; 209 struct resource *io; 210 211 io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 212 0, ~0, 16, RF_ACTIVE); 213 if (!io) goto bad; 214 if (sb_reset_dsp(io)) goto bad2; 215 ver = sb_identify_board(io); 216 if (ver == 0) goto bad2; 217 switch ((ver & 0x00000f00) >> 8) { 218 case 1: 219 case 2: 220 s = "Soundblaster"; 221 break; 222 223 case 3: 224 s = (ver & 0x0000f000)? "ESS 488" : "Soundblaster Pro"; 225 break; 226 227 case 4: 228 s = "Soundblaster 16"; 229 break; 230 231 case 5: 232 s = (ver & 0x00000008)? "ESS 688" : "ESS 1688"; 233 break; 234 } 235 if (s) device_set_desc(dev, s); 236bad2: bus_release_resource(dev, SYS_RES_IOPORT, rid, io); 237bad: return s? 0 : ENXIO; 238 } 239} 240 241static int 242sbc_attach(device_t dev) 243{ 244 char *err = NULL; 245 struct sbc_softc *scp; 246 struct sndcard_func *func; 247 device_t child; 248 u_int32_t logical_id = isa_get_logicalid(dev); 249 int flags = device_get_flags(dev); 250 int f, dh, dl, x, irq; 251 252 if (!logical_id && (flags & DV_F_DUAL_DMA)) { 253 bus_set_resource(dev, SYS_RES_DRQ, 1, 254 flags & DV_F_DRQ_MASK, 1); 255 } 256 257 scp = device_get_softc(dev); 258 bzero(scp, sizeof(*scp)); 259 scp->dev = dev; 260 err = "alloc_resource"; 261 if (alloc_resource(scp)) goto bad; 262 263 err = "sb_reset_dsp"; 264 if (sb_reset_dsp(scp->io[0])) goto bad; 265 err = "sb_identify_board"; 266 scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff; 267 if (scp->bd_ver == 0) goto bad; 268 f = 0; 269 switch ((scp->bd_ver & 0x0f00) >> 8) { 270 case 1: /* old sound blaster has nothing... */ 271 break; 272 273 case 2: 274 f |= BD_F_DUP_MIDI; 275 if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335; 276 break; 277 278 case 5: 279 f |= BD_F_ESS; 280 scp->bd_ver = 0x0301; 281 case 3: 282 f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; 283 break; 284 285 case 4: 286 f |= BD_F_SB16 | BD_F_MIX_CT1745; 287 if (scp->drq[0]) dl = rman_get_start(scp->drq[0]); else dl = -1; 288 if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl; 289 if (!logical_id) { 290 if (dh < dl) { 291 struct resource *r; 292 r = scp->drq[0]; 293 scp->drq[0] = scp->drq[1]; 294 scp->drq[1] = r; 295 dl = rman_get_start(scp->drq[0]); 296 dh = rman_get_start(scp->drq[1]); 297 } 298 } 299 /* soft irq/dma configuration */ 300 x = -1; 301 irq = rman_get_start(scp->irq); 302 if (irq == 5) x = 2; 303 else if (irq == 7) x = 4; 304 else if (irq == 9) x = 1; 305 else if (irq == 10) x = 8; 306 if (x == -1) { 307 err = "bad irq (5/7/9/10 valid)"; 308 goto bad; 309 } 310 else sb_setmixer(scp->io[0], IRQ_NR, x); 311 sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl)); 312 device_printf(dev, "setting card to irq %d, drq %d", irq, dl); 313 if (dl != dh) printf(", %d", dh); 314 printf("\n"); 315 break; 316 } 317 318 switch (logical_id) { 319 case 0x01008c0e: /* CTL0001 */ 320 case 0x43008c0e: /* CTL0043 */ 321 f |= BD_F_SB16X; 322 break; 323 } 324 scp->bd_ver |= f << 16; 325 326 /* PCM Audio */ 327 func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); 328 if (func == NULL) goto bad; 329 bzero(func, sizeof(*func)); 330 func->func = SCF_PCM; 331 child = device_add_child(dev, "pcm", -1); 332 device_set_ivars(child, func); 333 334#if notyet 335 /* Midi Interface */ 336 func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); 337 if (func == NULL) goto bad; 338 bzero(func, sizeof(*func)); 339 func->func = SCF_MIDI; 340 child = device_add_child(dev, "midi", -1); 341 device_set_ivars(child, func); 342 343 /* OPL FM Synthesizer */ 344 func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); 345 if (func == NULL) goto bad; 346 bzero(func, sizeof(*func)); 347 func->func = SCF_SYNTH; 348 child = device_add_child(dev, "midi", -1); 349 device_set_ivars(child, func); 350#endif /* notyet */ 351 352 /* probe/attach kids */ 353 bus_generic_attach(dev); 354 355 return (0); 356 357bad: if (err) device_printf(dev, "%s\n", err); 358 release_resource(scp); 359 return (ENXIO); 360} 361 362static struct resource * 363sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 364 u_long start, u_long end, u_long count, u_int flags) 365{ 366 struct sbc_softc *scp; 367 int *alloced, rid_max, alloced_max; 368 struct resource **res; 369 370 scp = device_get_softc(bus); 371 switch (type) { 372 case SYS_RES_IOPORT: 373 alloced = scp->io_alloced; 374 res = scp->io; 375 rid_max = 2; 376 alloced_max = 1; 377 break; 378 case SYS_RES_DRQ: 379 alloced = scp->drq_alloced; 380 res = scp->drq; 381 rid_max = 1; 382 alloced_max = 1; 383 break; 384 case SYS_RES_IRQ: 385 alloced = &scp->irq_alloced; 386 res = &scp->irq; 387 rid_max = 0; 388 alloced_max = 2; /* pcm and mpu may share the irq. */ 389 break; 390 default: 391 return (NULL); 392 } 393 394 if (*rid > rid_max || alloced[*rid] == alloced_max) 395 return (NULL); 396 397 alloced[*rid]++; 398 return (res[*rid]); 399} 400 401static int 402sbc_release_resource(device_t bus, device_t child, int type, int rid, 403 struct resource *r) 404{ 405 struct sbc_softc *scp; 406 int *alloced, rid_max; 407 408 scp = device_get_softc(bus); 409 switch (type) { 410 case SYS_RES_IOPORT: 411 alloced = scp->io_alloced; 412 rid_max = 2; 413 break; 414 case SYS_RES_DRQ: 415 alloced = scp->drq_alloced; 416 rid_max = 1; 417 break; 418 case SYS_RES_IRQ: 419 alloced = &scp->irq_alloced; 420 rid_max = 0; 421 break; 422 default: 423 return (1); 424 } 425 426 if (rid > rid_max || alloced[rid] == 0) 427 return (1); 428 429 alloced[rid]--; 430 return (0); 431} 432 433static int 434sbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 435{ 436 struct sbc_softc *scp = device_get_softc(bus); 437 struct sndcard_func *func = device_get_ivars(dev); 438 439 switch (index) { 440 case 0: 441 *result = func->func; 442 break; 443 444 case 1: 445 *result = scp->bd_ver; 446 break; 447 448 default: 449 return ENOENT; 450 } 451 452 return 0; 453} 454 455static int 456sbc_write_ivar(device_t bus, device_t dev, 457 int index, uintptr_t value) 458{ 459 switch (index) { 460 case 0: 461 case 1: 462 return EINVAL; 463 464 default: 465 return (ENOENT); 466 } 467} 468 469static int 470alloc_resource(struct sbc_softc *scp) 471{ 472 int i; 473 474 for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { 475 if (scp->io[i] == NULL) { 476 scp->io_rid[i] = i; 477 scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], 478 0, ~0, io_range[i], RF_ACTIVE); 479 if (i == 0 && scp->io[i] == NULL) 480 return (1); 481 scp->io_alloced[i] = 0; 482 } 483 } 484 for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { 485 if (scp->drq[i] == NULL) { 486 scp->drq_rid[i] = i; 487 scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], 488 0, ~0, 1, RF_ACTIVE); 489 if (i == 0 && scp->drq[i] == NULL) 490 return (1); 491 scp->drq_alloced[i] = 0; 492 } 493 } 494 if (scp->irq == NULL) { 495 scp->irq_rid = 0; 496 scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid, 497 0, ~0, 1, RF_ACTIVE); 498 if (scp->irq == NULL) 499 return (1); 500 scp->irq_alloced = 0; 501 } 502 return (0); 503} 504 505static int 506release_resource(struct sbc_softc *scp) 507{ 508 int i; 509 510 for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { 511 if (scp->io[i] != NULL) { 512 bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); 513 scp->io[i] = NULL; 514 } 515 } 516 if (scp->irq != NULL) { 517 bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid, scp->irq); 518 scp->irq = NULL; 519 } 520 for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { 521 if (scp->drq[i] != NULL) { 522 bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); 523 scp->drq[i] = NULL; 524 } 525 } 526 return (0); 527} 528 529static device_method_t sbc_methods[] = { 530 /* Device interface */ 531 DEVMETHOD(device_probe, sbc_probe), 532 DEVMETHOD(device_attach, sbc_attach), 533 DEVMETHOD(device_detach, bus_generic_detach), 534 DEVMETHOD(device_shutdown, bus_generic_shutdown), 535 DEVMETHOD(device_suspend, bus_generic_suspend), 536 DEVMETHOD(device_resume, bus_generic_resume), 537 538 /* Bus interface */ 539 DEVMETHOD(bus_read_ivar, sbc_read_ivar), 540 DEVMETHOD(bus_write_ivar, sbc_write_ivar), 541 DEVMETHOD(bus_print_child, bus_generic_print_child), 542 DEVMETHOD(bus_alloc_resource, sbc_alloc_resource), 543 DEVMETHOD(bus_release_resource, sbc_release_resource), 544 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 545 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 546 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 547 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 548 549 { 0, 0 } 550}; 551 552static driver_t sbc_driver = { 553 "sbc", 554 sbc_methods, 555 sizeof(struct sbc_softc), 556}; 557 558/* sbc can be attached to an isa bus. */ 559DRIVER_MODULE(sbc, isa, sbc_driver, sbc_devclass, 0, 0); 560 561