sbc.c revision 64845
190075Sobrien/*- 2169689Skan * Copyright (c) 1999 Seigo Tanimura 3169689Skan * All rights reserved. 490075Sobrien * 590075Sobrien * Redistribution and use in source and binary forms, with or without 690075Sobrien * modification, are permitted provided that the following conditions 7132718Skan * are met: 890075Sobrien * 1. Redistributions of source code must retain the above copyright 9132718Skan * notice, this list of conditions and the following disclaimer. 1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1190075Sobrien * notice, this list of conditions and the following disclaimer in the 1290075Sobrien * documentation and/or other materials provided with the distribution. 1390075Sobrien * 14132718Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1790075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1890075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20132718Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2390075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2490075Sobrien * SUCH DAMAGE. 2590075Sobrien * 2690075Sobrien * $FreeBSD: head/sys/dev/sound/isa/sbc.c 64845 2000-08-19 18:17:15Z cg $ 2790075Sobrien */ 2890075Sobrien 2990075Sobrien#include <dev/sound/chip.h> 3090075Sobrien#include <dev/sound/pcm/sound.h> 3190075Sobrien#include <dev/sound/isa/sb.h> 3290075Sobrien 33117395Skan#define IO_MAX 3 34117395Skan#define IRQ_MAX 1 35117395Skan#define DRQ_MAX 2 36117395Skan#define INTR_MAX 2 37117395Skan 38117395Skanstruct sbc_ihl { 39117395Skan driver_intr_t *intr[INTR_MAX]; 40117395Skan void *intr_arg[INTR_MAX]; 41117395Skan}; 42117395Skan 43117395Skan/* Here is the parameter structure per a device. */ 44117395Skanstruct sbc_softc { 45132718Skan device_t dev; /* device */ 46132718Skan 47132718Skan int io_rid[IO_MAX]; /* io port rids */ 48132718Skan struct resource *io[IO_MAX]; /* io port resources */ 4996263Sobrien int io_alloced[IO_MAX]; /* io port alloc flag */ 50132718Skan 51132718Skan int irq_rid[IRQ_MAX]; /* irq rids */ 5290075Sobrien struct resource *irq[IRQ_MAX]; /* irq resources */ 5390075Sobrien int irq_alloced[IRQ_MAX]; /* irq alloc flag */ 5490075Sobrien 5596263Sobrien int drq_rid[DRQ_MAX]; /* drq rids */ 5696263Sobrien struct resource *drq[DRQ_MAX]; /* drq resources */ 57169689Skan int drq_alloced[DRQ_MAX]; /* drq alloc flag */ 58169689Skan 59169689Skan struct sbc_ihl ihl[IRQ_MAX]; 6096263Sobrien 61132718Skan void *ih; 62132718Skan 63132718Skan u_int32_t bd_ver; 64132718Skan}; 65132718Skan 66132718Skanstatic int sbc_probe(device_t dev); 67132718Skanstatic int sbc_attach(device_t dev); 68132718Skanstatic void sbc_intr(void *p); 69132718Skan 70117395Skanstatic struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 71117395Skan u_long start, u_long end, u_long count, u_int flags); 72117395Skanstatic int sbc_release_resource(device_t bus, device_t child, int type, int rid, 73117395Skan struct resource *r); 74132718Skanstatic int sbc_setup_intr(device_t dev, device_t child, struct resource *irq, 75117395Skan int flags, driver_intr_t *intr, void *arg, 76117395Skan void **cookiep); 77169689Skanstatic int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq, 78169689Skan void *cookie); 79169689Skan 80169689Skanstatic int alloc_resource(struct sbc_softc *scp); 81117395Skanstatic int release_resource(struct sbc_softc *scp); 82117395Skan 83117395Skanstatic devclass_t sbc_devclass; 84117395Skan 85169689Skanstatic int io_range[3] = {0x10, 0x2, 0x4}; 86169689Skan 8790075Sobrien#ifdef PC98 /* I/O address table for PC98 */ 88169689Skanstatic bus_addr_t pcm_iat[] = { 89169689Skan 0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 90169689Skan 0x800, 0x900, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00 91169689Skan}; 92169689Skanstatic bus_addr_t midi_iat[] = { 93169689Skan 0x000, 0x100 9490075Sobrien}; 9590075Sobrienstatic bus_addr_t opl_iat[] = { 9690075Sobrien 0x000, 0x100, 0x200, 0x300 9790075Sobrien}; 98169689Skanstatic bus_addr_t *sb_iat[] = { pcm_iat, midi_iat, opl_iat }; 9990075Sobrien#endif 10090075Sobrien 10190075Sobrienstatic int sb_rd(struct resource *io, int reg); 10290075Sobrienstatic void sb_wr(struct resource *io, int reg, u_int8_t val); 10390075Sobrienstatic int sb_dspready(struct resource *io); 10490075Sobrienstatic int sb_cmd(struct resource *io, u_char val); 105132718Skanstatic u_int sb_get_byte(struct resource *io); 106169689Skanstatic void sb_setmixer(struct resource *io, u_int port, u_int value); 107132718Skan 108132718Skanstatic int 109132718Skansb_rd(struct resource *io, int reg) 110132718Skan{ 111169689Skan return bus_space_read_1(rman_get_bustag(io), 112132718Skan rman_get_bushandle(io), 113132718Skan reg); 114132718Skan} 115132718Skan 116132718Skanstatic void 117132718Skansb_wr(struct resource *io, int reg, u_int8_t val) 11890075Sobrien{ 11990075Sobrien return bus_space_write_1(rman_get_bustag(io), 12090075Sobrien rman_get_bushandle(io), 12190075Sobrien reg, val); 12290075Sobrien} 12390075Sobrien 12490075Sobrienstatic int 12590075Sobriensb_dspready(struct resource *io) 12690075Sobrien{ 12790075Sobrien return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0); 12890075Sobrien} 12990075Sobrien 13090075Sobrienstatic int 13190075Sobriensb_dspwr(struct resource *io, u_char val) 13290075Sobrien{ 13390075Sobrien int i; 13490075Sobrien 135132718Skan for (i = 0; i < 1000; i++) { 136132718Skan if (sb_dspready(io)) { 137132718Skan sb_wr(io, SBDSP_CMD, val); 13890075Sobrien return 1; 13990075Sobrien } 14090075Sobrien if (i > 10) DELAY((i > 100)? 1000 : 10); 14190075Sobrien } 142132718Skan printf("sb_dspwr(0x%02x) timed out.\n", val); 143132718Skan return 0; 144132718Skan} 14590075Sobrien 14690075Sobrienstatic int 14790075Sobriensb_cmd(struct resource *io, u_char val) 14890075Sobrien{ 14990075Sobrien return sb_dspwr(io, val); 15090075Sobrien} 15190075Sobrien 15290075Sobrienstatic void 15390075Sobriensb_setmixer(struct resource *io, u_int port, u_int value) 15490075Sobrien{ 15590075Sobrien u_long flags; 15690075Sobrien 15790075Sobrien flags = spltty(); 15890075Sobrien sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 15990075Sobrien DELAY(10); 16090075Sobrien sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff)); 16190075Sobrien DELAY(10); 16290075Sobrien splx(flags); 16390075Sobrien} 16490075Sobrien 16590075Sobrienstatic u_int 16690075Sobriensb_get_byte(struct resource *io) 16790075Sobrien{ 16890075Sobrien int i; 16990075Sobrien 17090075Sobrien for (i = 1000; i > 0; i--) { 17190075Sobrien if (sb_rd(io, DSP_DATA_AVAIL) & 0x80) 17290075Sobrien return sb_rd(io, DSP_READ); 17390075Sobrien else 174132718Skan DELAY(20); 17590075Sobrien } 17690075Sobrien return 0xffff; 17790075Sobrien} 17890075Sobrien 17990075Sobrienstatic int 18090075Sobriensb_reset_dsp(struct resource *io) 18190075Sobrien{ 18290075Sobrien sb_wr(io, SBDSP_RST, 3); 18390075Sobrien DELAY(100); 18490075Sobrien sb_wr(io, SBDSP_RST, 0); 18590075Sobrien return (sb_get_byte(io) == 0xAA)? 0 : ENXIO; 18690075Sobrien} 18790075Sobrien 18890075Sobrienstatic int 18990075Sobriensb_identify_board(struct resource *io) 19090075Sobrien{ 19190075Sobrien int ver, essver, rev; 19290075Sobrien 19390075Sobrien sb_cmd(io, DSP_CMD_GETVER); /* Get version */ 19490075Sobrien ver = (sb_get_byte(io) << 8) | sb_get_byte(io); 19590075Sobrien if (ver < 0x100 || ver > 0x4ff) return 0; 19690075Sobrien if (ver == 0x0301) { 19790075Sobrien /* Try to detect ESS chips. */ 19890075Sobrien sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */ 19990075Sobrien essver = (sb_get_byte(io) << 8) | sb_get_byte(io); 20090075Sobrien rev = essver & 0x000f; 20190075Sobrien essver &= 0xfff0; 20290075Sobrien if (essver == 0x4880) ver |= 0x1000; 20390075Sobrien else if (essver == 0x6880) ver = 0x0500 | rev; 20490075Sobrien } 20590075Sobrien return ver; 20690075Sobrien} 20790075Sobrien 20890075Sobrienstatic struct isa_pnp_id sbc_ids[] = { 20990075Sobrien {0x01008c0e, "Creative ViBRA16C"}, /* CTL0001 */ 21090075Sobrien {0x31008c0e, "Creative SB16/SB32"}, /* CTL0031 */ 21190075Sobrien {0x41008c0e, "Creative SB16/SB32"}, /* CTL0041 */ 21290075Sobrien {0x42008c0e, "Creative SB AWE64"}, /* CTL0042 */ 21390075Sobrien {0x43008c0e, "Creative ViBRA16X"}, /* CTL0043 */ 21490075Sobrien {0x44008c0e, "Creative SB AWE64 Gold"}, /* CTL0044 */ 21590075Sobrien {0x45008c0e, "Creative SB AWE64"}, /* CTL0045 */ 21690075Sobrien 21790075Sobrien {0x01000000, "Avance Logic ALS100+"}, /* @@@0001 - ViBRA16X clone */ 21890075Sobrien {0x01100000, "Avance Asound 110"}, /* @@@1001 */ 21990075Sobrien {0x01200000, "Avance Logic ALS120"}, /* @@@2001 - ViBRA16X clone */ 22090075Sobrien 22190075Sobrien {0x02017316, "ESS ES1688"}, /* ESS1688 */ 22290075Sobrien {0x68187316, "ESS ES1868"}, /* ESS1868 */ 22390075Sobrien {0x03007316, "ESS ES1869"}, /* ESS1869 */ 22490075Sobrien {0x69187316, "ESS ES1869"}, /* ESS1869 */ 22590075Sobrien {0xabb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ab */ 22690075Sobrien {0xacb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ac */ 22790075Sobrien {0x78187316, "ESS ES1878"}, /* ESS1878 */ 22890075Sobrien {0x79187316, "ESS ES1879"}, /* ESS1879 */ 22990075Sobrien {0x88187316, "ESS ES1888"}, /* ESS1888 */ 23090075Sobrien {0x07017316, "ESS ES1888 (DEC OEM)"}, /* ESS0107 */ 23190075Sobrien {0x06017316, "ESS ES1888 (Dell OEM)"}, /* ESS0106 */ 23290075Sobrien {0} 23390075Sobrien}; 23490075Sobrien 235117395Skanstatic int 23690075Sobriensbc_probe(device_t dev) 23790075Sobrien{ 238117395Skan char *s = NULL; 23990075Sobrien u_int32_t lid, vid; 24090075Sobrien 24190075Sobrien lid = isa_get_logicalid(dev); 24290075Sobrien vid = isa_get_vendorid(dev); 24390075Sobrien if (lid) { 24490075Sobrien if (lid == 0x01000000 && vid != 0x01009305) /* ALS0001 */ 24590075Sobrien return ENXIO; 24690075Sobrien /* Check pnp ids */ 24790075Sobrien return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids); 24890075Sobrien } else { 249117395Skan int rid = 0, ver; 25090075Sobrien struct resource *io; 25196263Sobrien 25290075Sobrien#ifdef PC98 253117395Skan io = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, 254117395Skan pcm_iat, 16, RF_ACTIVE); 255117395Skan#else 256117395Skan io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 257117395Skan 0, ~0, 16, RF_ACTIVE); 258117395Skan#endif 259117395Skan if (!io) goto bad; 260117395Skan#ifdef PC98 261117395Skan isa_load_resourcev(io, pcm_iat, 16); 262117395Skan#endif 263117395Skan if (sb_reset_dsp(io)) goto bad2; 264117395Skan ver = sb_identify_board(io); 265117395Skan if (ver == 0) goto bad2; 266117395Skan switch ((ver & 0x00000f00) >> 8) { 267117395Skan case 1: 26890075Sobrien case 2: 26990075Sobrien s = "Soundblaster"; 27090075Sobrien break; 27190075Sobrien 27290075Sobrien case 3: 27390075Sobrien s = (ver & 0x0000f000)? "ESS 488" : "Soundblaster Pro"; 27490075Sobrien break; 27590075Sobrien 27690075Sobrien case 4: 27790075Sobrien s = "Soundblaster 16"; 27890075Sobrien break; 27990075Sobrien 28090075Sobrien case 5: 28190075Sobrien s = (ver & 0x00000008)? "ESS 688" : "ESS 1688"; 28290075Sobrien break; 283132718Skan } 284169689Skan if (s) device_set_desc(dev, s); 28590075Sobrienbad2: bus_release_resource(dev, SYS_RES_IOPORT, rid, io); 286132718Skanbad: return s? 0 : ENXIO; 287169689Skan } 28890075Sobrien} 28990075Sobrien 29090075Sobrienstatic int 29190075Sobriensbc_attach(device_t dev) 29290075Sobrien{ 29390075Sobrien char *err = NULL; 29490075Sobrien struct sbc_softc *scp; 29590075Sobrien struct sndcard_func *func; 29690075Sobrien device_t child; 29790075Sobrien u_int32_t logical_id = isa_get_logicalid(dev); 29890075Sobrien int flags = device_get_flags(dev); 29990075Sobrien int f, dh, dl, x, irq, i; 30090075Sobrien 30190075Sobrien if (!logical_id && (flags & DV_F_DUAL_DMA)) { 30290075Sobrien bus_set_resource(dev, SYS_RES_DRQ, 1, 30390075Sobrien flags & DV_F_DRQ_MASK, 1); 30490075Sobrien } 30590075Sobrien 30690075Sobrien scp = device_get_softc(dev); 30790075Sobrien bzero(scp, sizeof(*scp)); 30890075Sobrien scp->dev = dev; 30990075Sobrien err = "alloc_resource"; 31090075Sobrien if (alloc_resource(scp)) goto bad; 31190075Sobrien 31290075Sobrien err = "sb_reset_dsp"; 31390075Sobrien if (sb_reset_dsp(scp->io[0])) goto bad; 31490075Sobrien err = "sb_identify_board"; 31590075Sobrien scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff; 31696263Sobrien if (scp->bd_ver == 0) goto bad; 31790075Sobrien f = 0; 31890075Sobrien if (logical_id == 0x01200000 && scp->bd_ver < 0x0400) scp->bd_ver = 0x0499; 31990075Sobrien switch ((scp->bd_ver & 0x0f00) >> 8) { 32090075Sobrien case 1: /* old sound blaster has nothing... */ 321122180Skan break; 32290075Sobrien 32390075Sobrien case 2: 32490075Sobrien f |= BD_F_DUP_MIDI; 32590075Sobrien if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335; 32690075Sobrien break; 327169689Skan 32890075Sobrien case 5: 32990075Sobrien f |= BD_F_ESS; 33090075Sobrien scp->bd_ver = 0x0301; 331122180Skan case 3: 33290075Sobrien f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; 33390075Sobrien break; 33490075Sobrien 33590075Sobrien case 4: 33690075Sobrien f |= BD_F_SB16 | BD_F_MIX_CT1745; 33790075Sobrien if (scp->drq[0]) dl = rman_get_start(scp->drq[0]); else dl = -1; 33890075Sobrien if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl; 33990075Sobrien if (!logical_id && (dh < dl)) { 34090075Sobrien struct resource *r; 341122180Skan r = scp->drq[0]; 342122180Skan scp->drq[0] = scp->drq[1]; 343122180Skan scp->drq[1] = r; 344122180Skan dl = rman_get_start(scp->drq[0]); 345122180Skan dh = rman_get_start(scp->drq[1]); 34690075Sobrien } 34790075Sobrien /* soft irq/dma configuration */ 34890075Sobrien x = -1; 34990075Sobrien irq = rman_get_start(scp->irq[0]); 35090075Sobrien#ifdef PC98 35190075Sobrien /* SB16 in PC98 use different IRQ table */ 35290075Sobrien if (irq == 3) x = 1; 35390075Sobrien else if (irq == 5) x = 8; 35490075Sobrien else if (irq == 10) x = 2; 35590075Sobrien else if (irq == 12) x = 4; 35690075Sobrien if (x == -1) { 35790075Sobrien err = "bad irq (3/5/10/12 valid)"; 35890075Sobrien goto bad; 35990075Sobrien } 36090075Sobrien else sb_setmixer(scp->io[0], IRQ_NR, x); 36190075Sobrien /* SB16 in PC98 use different dma setting */ 36290075Sobrien sb_setmixer(scp->io[0], DMA_NR, dh == 0 ? 1 : 2); 36390075Sobrien#else 36490075Sobrien if (irq == 5) x = 2; 36590075Sobrien else if (irq == 7) x = 4; 36690075Sobrien else if (irq == 9) x = 1; 36790075Sobrien else if (irq == 10) x = 8; 36890075Sobrien if (x == -1) { 36990075Sobrien err = "bad irq (5/7/9/10 valid)"; 37090075Sobrien goto bad; 37190075Sobrien } 37290075Sobrien else sb_setmixer(scp->io[0], IRQ_NR, x); 37390075Sobrien sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl)); 37490075Sobrien#endif 37590075Sobrien device_printf(dev, "setting card to irq %d, drq %d", irq, dl); 37690075Sobrien if (dl != dh) printf(", %d", dh); 37796263Sobrien printf("\n"); 37890075Sobrien break; 37990075Sobrien } 38090075Sobrien 38190075Sobrien switch (logical_id) { 38290075Sobrien case 0x43008c0e: /* CTL0043 */ 38390075Sobrien case 0x01200000: 38490075Sobrien case 0x01000000: 38590075Sobrien f |= BD_F_SB16X; 38690075Sobrien break; 38790075Sobrien } 38890075Sobrien scp->bd_ver |= f << 16; 38990075Sobrien 39090075Sobrien err = "setup_intr"; 39190075Sobrien for (i = 0; i < IRQ_MAX; i++) { 39290075Sobrien if (bus_setup_intr(dev, scp->irq[i], INTR_TYPE_TTY, sbc_intr, &scp->ihl[i], &scp->ih)) 39390075Sobrien goto bad; 39490075Sobrien } 39590075Sobrien 39690075Sobrien /* PCM Audio */ 39790075Sobrien func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); 39890075Sobrien if (func == NULL) goto bad; 39990075Sobrien bzero(func, sizeof(*func)); 40090075Sobrien func->func = SCF_PCM; 40190075Sobrien child = device_add_child(dev, "pcm", -1); 40290075Sobrien device_set_ivars(child, func); 40390075Sobrien 40490075Sobrien /* Midi Interface */ 40590075Sobrien func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); 40690075Sobrien if (func == NULL) goto bad; 40790075Sobrien bzero(func, sizeof(*func)); 408122180Skan func->func = SCF_MIDI; 409122180Skan child = device_add_child(dev, "midi", -1); 41090075Sobrien device_set_ivars(child, func); 41190075Sobrien 41290075Sobrien /* OPL FM Synthesizer */ 41390075Sobrien func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); 41490075Sobrien if (func == NULL) goto bad; 41590075Sobrien bzero(func, sizeof(*func)); 41690075Sobrien func->func = SCF_SYNTH; 41790075Sobrien child = device_add_child(dev, "midi", -1); 41890075Sobrien device_set_ivars(child, func); 41990075Sobrien 42090075Sobrien /* probe/attach kids */ 42190075Sobrien bus_generic_attach(dev); 42290075Sobrien 42390075Sobrien return (0); 42490075Sobrien 42590075Sobrienbad: if (err) device_printf(dev, "%s\n", err); 42690075Sobrien release_resource(scp); 42790075Sobrien return (ENXIO); 42890075Sobrien} 42990075Sobrien 43090075Sobrienstatic void 43190075Sobriensbc_intr(void *p) 43290075Sobrien{ 43390075Sobrien struct sbc_ihl *ihl = p; 43490075Sobrien int i; 43590075Sobrien 43690075Sobrien i = 0; 43790075Sobrien while (i < INTR_MAX) { 43890075Sobrien if (ihl->intr[i] != NULL) ihl->intr[i](ihl->intr_arg[i]); 43990075Sobrien i++; 44090075Sobrien } 44190075Sobrien} 44290075Sobrien 443122180Skanstatic int 444122180Skansbc_setup_intr(device_t dev, device_t child, struct resource *irq, 44590075Sobrien int flags, driver_intr_t *intr, void *arg, 44690075Sobrien void **cookiep) 44796263Sobrien{ 44890075Sobrien struct sbc_softc *scp = device_get_softc(dev); 44996263Sobrien struct sbc_ihl *ihl = NULL; 45090075Sobrien int i; 45196263Sobrien 452132718Skan i = 0; 45390075Sobrien while (i < IRQ_MAX) { 45490075Sobrien if (irq == scp->irq[i]) ihl = &scp->ihl[i]; 45590075Sobrien i++; 45690075Sobrien } 457169689Skan if (ihl == NULL) return (EINVAL); 45890075Sobrien i = 0; 45990075Sobrien while (i < INTR_MAX) { 46090075Sobrien if (ihl->intr[i] == NULL) { 46190075Sobrien ihl->intr[i] = intr; 46290075Sobrien ihl->intr_arg[i] = arg; 46390075Sobrien *cookiep = &ihl->intr[i]; 46490075Sobrien return 0; 46590075Sobrien } else i++; 466169689Skan } 46790075Sobrien return (EINVAL); 46890075Sobrien} 46990075Sobrien 47090075Sobrienstatic int 47190075Sobriensbc_teardown_intr(device_t dev, device_t child, struct resource *irq, 47290075Sobrien void *cookie) 47390075Sobrien{ 47490075Sobrien struct sbc_softc *scp = device_get_softc(dev); 475169689Skan struct sbc_ihl *ihl = NULL; 47690075Sobrien int i; 47790075Sobrien 47890075Sobrien i = 0; 47990075Sobrien while (i < IRQ_MAX) { 48090075Sobrien if (irq == scp->irq[i]) ihl = &scp->ihl[i]; 481122180Skan i++; 482122180Skan } 48390075Sobrien if (ihl == NULL) return (EINVAL); 48490075Sobrien i = 0; 48590075Sobrien while (i < INTR_MAX) { 48690075Sobrien if (cookie == &ihl->intr[i]) { 48790075Sobrien ihl->intr[i] = NULL; 48890075Sobrien ihl->intr_arg[i] = NULL; 48990075Sobrien return 0; 49090075Sobrien } else i++; 49190075Sobrien } 49290075Sobrien return (EINVAL); 49390075Sobrien} 49490075Sobrien 49590075Sobrienstatic struct resource * 49690075Sobriensbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 49790075Sobrien u_long start, u_long end, u_long count, u_int flags) 49890075Sobrien{ 49990075Sobrien struct sbc_softc *scp; 50090075Sobrien int *alloced, rid_max, alloced_max; 50190075Sobrien struct resource **res; 50290075Sobrien#ifdef PC98 50390075Sobrien int i; 50490075Sobrien#endif 50590075Sobrien 50690075Sobrien scp = device_get_softc(bus); 50790075Sobrien switch (type) { 50890075Sobrien case SYS_RES_IOPORT: 509169689Skan alloced = scp->io_alloced; 51090075Sobrien res = scp->io; 51190075Sobrien#ifdef PC98 51290075Sobrien rid_max = 0; 51390075Sobrien for (i = 0; i < IO_MAX; i++) 51490075Sobrien rid_max += io_range[i]; 51590075Sobrien#else 516132718Skan rid_max = IO_MAX - 1; 51790075Sobrien#endif 51890075Sobrien alloced_max = 1; 51990075Sobrien break; 52090075Sobrien case SYS_RES_DRQ: 52190075Sobrien alloced = scp->drq_alloced; 52290075Sobrien res = scp->drq; 52390075Sobrien rid_max = DRQ_MAX - 1; 52490075Sobrien alloced_max = 1; 52590075Sobrien break; 52690075Sobrien case SYS_RES_IRQ: 52790075Sobrien alloced = scp->irq_alloced; 52890075Sobrien res = scp->irq; 52990075Sobrien rid_max = IRQ_MAX - 1; 53090075Sobrien alloced_max = INTR_MAX; /* pcm and mpu may share the irq. */ 53190075Sobrien break; 53290075Sobrien default: 53390075Sobrien return (NULL); 53490075Sobrien } 53590075Sobrien 53690075Sobrien if (*rid > rid_max || alloced[*rid] == alloced_max) 53790075Sobrien return (NULL); 53890075Sobrien 53990075Sobrien alloced[*rid]++; 54090075Sobrien return (res[*rid]); 54190075Sobrien} 54290075Sobrien 54390075Sobrienstatic int 54490075Sobriensbc_release_resource(device_t bus, device_t child, int type, int rid, 54590075Sobrien struct resource *r) 54690075Sobrien{ 54790075Sobrien struct sbc_softc *scp; 54890075Sobrien int *alloced, rid_max; 54990075Sobrien 55090075Sobrien scp = device_get_softc(bus); 55190075Sobrien switch (type) { 55290075Sobrien case SYS_RES_IOPORT: 55390075Sobrien alloced = scp->io_alloced; 55490075Sobrien rid_max = IO_MAX - 1; 55590075Sobrien break; 55690075Sobrien case SYS_RES_DRQ: 55790075Sobrien alloced = scp->drq_alloced; 55890075Sobrien rid_max = DRQ_MAX - 1; 55990075Sobrien break; 56090075Sobrien case SYS_RES_IRQ: 56190075Sobrien alloced = scp->irq_alloced; 56290075Sobrien rid_max = IRQ_MAX - 1; 56390075Sobrien break; 56490075Sobrien default: 56590075Sobrien return (1); 56690075Sobrien } 56790075Sobrien 56890075Sobrien if (rid > rid_max || alloced[rid] == 0) 56990075Sobrien return (1); 57090075Sobrien 57190075Sobrien alloced[rid]--; 57290075Sobrien return (0); 57390075Sobrien} 57490075Sobrien 57590075Sobrienstatic int 57690075Sobriensbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 57790075Sobrien{ 57890075Sobrien struct sbc_softc *scp = device_get_softc(bus); 57990075Sobrien struct sndcard_func *func = device_get_ivars(dev); 58090075Sobrien 58190075Sobrien switch (index) { 58290075Sobrien case 0: 58390075Sobrien *result = func->func; 58490075Sobrien break; 58590075Sobrien 58690075Sobrien case 1: 58790075Sobrien *result = scp->bd_ver; 58890075Sobrien break; 58990075Sobrien 59090075Sobrien default: 59190075Sobrien return ENOENT; 59290075Sobrien } 59390075Sobrien 59490075Sobrien return 0; 59590075Sobrien} 59690075Sobrien 59790075Sobrienstatic int 59890075Sobriensbc_write_ivar(device_t bus, device_t dev, 59990075Sobrien int index, uintptr_t value) 60090075Sobrien{ 60190075Sobrien switch (index) { 60290075Sobrien case 0: 60390075Sobrien case 1: 60490075Sobrien return EINVAL; 60590075Sobrien 60690075Sobrien default: 60790075Sobrien return (ENOENT); 60890075Sobrien } 60990075Sobrien} 61090075Sobrien 61190075Sobrienstatic int 61290075Sobrienalloc_resource(struct sbc_softc *scp) 61390075Sobrien{ 61490075Sobrien int i; 61590075Sobrien 61690075Sobrien for (i = 0 ; i < IO_MAX ; i++) { 61790075Sobrien if (scp->io[i] == NULL) { 61890075Sobrien#ifdef PC98 61990075Sobrien scp->io_rid[i] = i > 0 ? 62090075Sobrien scp->io_rid[i - 1] + io_range[i - 1] : 0; 62190075Sobrien scp->io[i] = isa_alloc_resourcev(scp->dev, 62290075Sobrien SYS_RES_IOPORT, 62390075Sobrien &scp->io_rid[i], 62490075Sobrien sb_iat[i], 62590075Sobrien io_range[i], 626122180Skan RF_ACTIVE); 62790075Sobrien if (scp->io[i] != NULL) 62890075Sobrien isa_load_resourcev(scp->io[i], sb_iat[i], 62990075Sobrien io_range[i]); 63090075Sobrien#else 63190075Sobrien scp->io_rid[i] = i; 63290075Sobrien scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], 63390075Sobrien 0, ~0, io_range[i], RF_ACTIVE); 63490075Sobrien#endif 63590075Sobrien if (i == 0 && scp->io[i] == NULL) 63690075Sobrien return (1); 63790075Sobrien scp->io_alloced[i] = 0; 63890075Sobrien } 63990075Sobrien } 64090075Sobrien for (i = 0 ; i < DRQ_MAX ; i++) { 64190075Sobrien if (scp->drq[i] == NULL) { 64290075Sobrien scp->drq_rid[i] = i; 64390075Sobrien scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], 644132718Skan 0, ~0, 1, RF_ACTIVE); 645169689Skan if (i == 0 && scp->drq[i] == NULL) 64690075Sobrien return (1); 64790075Sobrien scp->drq_alloced[i] = 0; 64890075Sobrien } 64990075Sobrien } 65090075Sobrien for (i = 0 ; i < IRQ_MAX ; i++) { 65190075Sobrien if (scp->irq[i] == NULL) { 65290075Sobrien scp->irq_rid[i] = i; 65390075Sobrien scp->irq[i] = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid[i], 65490075Sobrien 0, ~0, 1, RF_ACTIVE); 65590075Sobrien if (i == 0 && scp->irq[i] == NULL) 656132718Skan return (1); 65790075Sobrien scp->irq_alloced[i] = 0; 65890075Sobrien } 659169689Skan } 660169689Skan return (0); 66190075Sobrien} 66290075Sobrien 66390075Sobrienstatic int 66490075Sobrienrelease_resource(struct sbc_softc *scp) 66590075Sobrien{ 66690075Sobrien int i; 66790075Sobrien 66890075Sobrien for (i = 0 ; i < IO_MAX ; i++) { 66990075Sobrien if (scp->io[i] != NULL) { 67090075Sobrien bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); 67190075Sobrien scp->io[i] = NULL; 67290075Sobrien } 673132718Skan } 67490075Sobrien for (i = 0 ; i < DRQ_MAX ; i++) { 67590075Sobrien if (scp->drq[i] != NULL) { 67690075Sobrien bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); 677169689Skan scp->drq[i] = NULL; 678169689Skan } 67990075Sobrien } 680132718Skan for (i = 0 ; i < IRQ_MAX ; i++) { 681132718Skan if (scp->irq[i] != NULL) { 682132718Skan bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid[i], scp->irq[i]); 683132718Skan scp->irq[i] = NULL; 684132718Skan } 685132718Skan } 686132718Skan return (0); 68790075Sobrien} 68890075Sobrien 68990075Sobrienstatic device_method_t sbc_methods[] = { 69090075Sobrien /* Device interface */ 69190075Sobrien DEVMETHOD(device_probe, sbc_probe), 69290075Sobrien DEVMETHOD(device_attach, sbc_attach), 69390075Sobrien DEVMETHOD(device_detach, bus_generic_detach), 69490075Sobrien DEVMETHOD(device_shutdown, bus_generic_shutdown), 69590075Sobrien DEVMETHOD(device_suspend, bus_generic_suspend), 69690075Sobrien DEVMETHOD(device_resume, bus_generic_resume), 69790075Sobrien 69890075Sobrien /* Bus interface */ 69990075Sobrien DEVMETHOD(bus_read_ivar, sbc_read_ivar), 70090075Sobrien DEVMETHOD(bus_write_ivar, sbc_write_ivar), 70190075Sobrien DEVMETHOD(bus_print_child, bus_generic_print_child), 70290075Sobrien DEVMETHOD(bus_alloc_resource, sbc_alloc_resource), 70390075Sobrien DEVMETHOD(bus_release_resource, sbc_release_resource), 70490075Sobrien DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 70590075Sobrien DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 70690075Sobrien DEVMETHOD(bus_setup_intr, sbc_setup_intr), 70790075Sobrien DEVMETHOD(bus_teardown_intr, sbc_teardown_intr), 70890075Sobrien 70990075Sobrien { 0, 0 } 71090075Sobrien}; 71190075Sobrien 71290075Sobrienstatic driver_t sbc_driver = { 71390075Sobrien "sbc", 71490075Sobrien sbc_methods, 71590075Sobrien sizeof(struct sbc_softc), 71690075Sobrien}; 71790075Sobrien 71890075Sobrien/* sbc can be attached to an isa bus. */ 71990075SobrienDRIVER_MODULE(snd_sbc, isa, sbc_driver, sbc_devclass, 0, 0); 72090075SobrienMODULE_DEPEND(snd_sbc, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 72190075SobrienMODULE_VERSION(snd_sbc, 1); 72290075Sobrien 72390075Sobrien 724169689Skan