sbc.c revision 108064
182180Scg/* 253553Stanimura * Copyright (c) 1999 Seigo Tanimura 353553Stanimura * All rights reserved. 453553Stanimura * 553553Stanimura * Redistribution and use in source and binary forms, with or without 653553Stanimura * modification, are permitted provided that the following conditions 753553Stanimura * are met: 853553Stanimura * 1. Redistributions of source code must retain the above copyright 953553Stanimura * notice, this list of conditions and the following disclaimer. 1053553Stanimura * 2. Redistributions in binary form must reproduce the above copyright 1153553Stanimura * notice, this list of conditions and the following disclaimer in the 1253553Stanimura * documentation and/or other materials provided with the distribution. 1353553Stanimura * 1453553Stanimura * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1553553Stanimura * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1653553Stanimura * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1753553Stanimura * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1853553Stanimura * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1953553Stanimura * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2053553Stanimura * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2153553Stanimura * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2253553Stanimura * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2353553Stanimura * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2453553Stanimura * SUCH DAMAGE. 2553553Stanimura */ 2653553Stanimura 2753553Stanimura#include <dev/sound/chip.h> 2854462Scg#include <dev/sound/pcm/sound.h> 2962483Scg#include <dev/sound/isa/sb.h> 3053553Stanimura 3182180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/isa/sbc.c 108064 2002-12-18 22:53:24Z semenu $"); 3282180Scg 3354824Scg#define IO_MAX 3 3454824Scg#define IRQ_MAX 1 3554824Scg#define DRQ_MAX 2 3654824Scg#define INTR_MAX 2 3754824Scg 3874763Scgstruct sbc_softc; 3974763Scg 4054824Scgstruct sbc_ihl { 4154824Scg driver_intr_t *intr[INTR_MAX]; 4254824Scg void *intr_arg[INTR_MAX]; 4374763Scg struct sbc_softc *parent; 4454824Scg}; 4554824Scg 4653553Stanimura/* Here is the parameter structure per a device. */ 4753553Stanimurastruct sbc_softc { 4853553Stanimura device_t dev; /* device */ 4974395Scg device_t child_pcm, child_midi1, child_midi2; 5054462Scg 5154824Scg int io_rid[IO_MAX]; /* io port rids */ 5254824Scg struct resource *io[IO_MAX]; /* io port resources */ 5354824Scg int io_alloced[IO_MAX]; /* io port alloc flag */ 5454462Scg 5554824Scg int irq_rid[IRQ_MAX]; /* irq rids */ 5654824Scg struct resource *irq[IRQ_MAX]; /* irq resources */ 5754824Scg int irq_alloced[IRQ_MAX]; /* irq alloc flag */ 5854462Scg 5954824Scg int drq_rid[DRQ_MAX]; /* drq rids */ 6054824Scg struct resource *drq[DRQ_MAX]; /* drq resources */ 6154824Scg int drq_alloced[DRQ_MAX]; /* drq alloc flag */ 6254462Scg 6354824Scg struct sbc_ihl ihl[IRQ_MAX]; 6454824Scg 6565644Scg void *ih[IRQ_MAX]; 6654824Scg 67107285Scg struct mtx *lock; 6874763Scg 6954462Scg u_int32_t bd_ver; 7053553Stanimura}; 7153553Stanimura 7253553Stanimurastatic int sbc_probe(device_t dev); 7353553Stanimurastatic int sbc_attach(device_t dev); 7454824Scgstatic void sbc_intr(void *p); 7554824Scg 7653553Stanimurastatic struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 7754462Scg u_long start, u_long end, u_long count, u_int flags); 7853553Stanimurastatic int sbc_release_resource(device_t bus, device_t child, int type, int rid, 7954462Scg struct resource *r); 8054824Scgstatic int sbc_setup_intr(device_t dev, device_t child, struct resource *irq, 8154824Scg int flags, driver_intr_t *intr, void *arg, 8254824Scg void **cookiep); 8354824Scgstatic int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq, 8454824Scg void *cookie); 8553553Stanimura 8654462Scgstatic int alloc_resource(struct sbc_softc *scp); 8754462Scgstatic int release_resource(struct sbc_softc *scp); 8853553Stanimura 8953553Stanimurastatic devclass_t sbc_devclass; 9053553Stanimura 9154462Scgstatic int io_range[3] = {0x10, 0x2, 0x4}; 9254462Scg 9360711Snyan#ifdef PC98 /* I/O address table for PC98 */ 9460711Snyanstatic bus_addr_t pcm_iat[] = { 9560711Snyan 0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 9660711Snyan 0x800, 0x900, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00 9760711Snyan}; 9860711Snyanstatic bus_addr_t midi_iat[] = { 9960711Snyan 0x000, 0x100 10060711Snyan}; 10160711Snyanstatic bus_addr_t opl_iat[] = { 10260711Snyan 0x000, 0x100, 0x200, 0x300 10360711Snyan}; 10460711Snyanstatic bus_addr_t *sb_iat[] = { pcm_iat, midi_iat, opl_iat }; 10560711Snyan#endif 10660711Snyan 10754462Scgstatic int sb_rd(struct resource *io, int reg); 10854462Scgstatic void sb_wr(struct resource *io, int reg, u_int8_t val); 10954462Scgstatic int sb_dspready(struct resource *io); 11054462Scgstatic int sb_cmd(struct resource *io, u_char val); 11154462Scgstatic u_int sb_get_byte(struct resource *io); 11254462Scgstatic void sb_setmixer(struct resource *io, u_int port, u_int value); 11354462Scg 11474763Scgstatic void 11574763Scgsbc_lockinit(struct sbc_softc *scp) 11674763Scg{ 11793816Sjhb scp->lock = snd_mtxcreate(device_get_nameunit(scp->dev), "sound softc"); 11874763Scg} 11974763Scg 12074763Scgstatic void 12174763Scgsbc_lockdestroy(struct sbc_softc *scp) 12274763Scg{ 12374763Scg snd_mtxfree(scp->lock); 12474763Scg} 12574763Scg 12674763Scgvoid 12774763Scgsbc_lock(struct sbc_softc *scp) 12874763Scg{ 12974763Scg snd_mtxlock(scp->lock); 13074763Scg} 13174763Scg 13274763Scgvoid 13374763Scgsbc_unlock(struct sbc_softc *scp) 13474763Scg{ 13574763Scg snd_mtxunlock(scp->lock); 13674763Scg} 13774763Scg 13854462Scgstatic int 13954462Scgsb_rd(struct resource *io, int reg) 14054462Scg{ 14154462Scg return bus_space_read_1(rman_get_bustag(io), 14254462Scg rman_get_bushandle(io), 14354462Scg reg); 14454462Scg} 14554462Scg 14654462Scgstatic void 14754462Scgsb_wr(struct resource *io, int reg, u_int8_t val) 14854462Scg{ 149108064Ssemenu bus_space_write_1(rman_get_bustag(io), 150108064Ssemenu rman_get_bushandle(io), 151108064Ssemenu reg, val); 15254462Scg} 15354462Scg 15454462Scgstatic int 15554462Scgsb_dspready(struct resource *io) 15654462Scg{ 15754462Scg return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0); 15854462Scg} 15954462Scg 16054462Scgstatic int 16154462Scgsb_dspwr(struct resource *io, u_char val) 16254462Scg{ 16354462Scg int i; 16454462Scg 16554462Scg for (i = 0; i < 1000; i++) { 16654462Scg if (sb_dspready(io)) { 16754462Scg sb_wr(io, SBDSP_CMD, val); 16854462Scg return 1; 16954462Scg } 17054462Scg if (i > 10) DELAY((i > 100)? 1000 : 10); 17154462Scg } 17254462Scg printf("sb_dspwr(0x%02x) timed out.\n", val); 17354462Scg return 0; 17454462Scg} 17554462Scg 17654462Scgstatic int 17754462Scgsb_cmd(struct resource *io, u_char val) 17854462Scg{ 17954462Scg return sb_dspwr(io, val); 18054462Scg} 18154462Scg 18254462Scgstatic void 18354462Scgsb_setmixer(struct resource *io, u_int port, u_int value) 18454462Scg{ 18554462Scg u_long flags; 18654462Scg 18754462Scg flags = spltty(); 18854462Scg sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 18954462Scg DELAY(10); 19054462Scg sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff)); 19154462Scg DELAY(10); 19254462Scg splx(flags); 19354462Scg} 19454462Scg 19554462Scgstatic u_int 19654462Scgsb_get_byte(struct resource *io) 19754462Scg{ 19854462Scg int i; 19954462Scg 20054462Scg for (i = 1000; i > 0; i--) { 20154462Scg if (sb_rd(io, DSP_DATA_AVAIL) & 0x80) 20254462Scg return sb_rd(io, DSP_READ); 20354462Scg else 20454462Scg DELAY(20); 20554462Scg } 20654462Scg return 0xffff; 20754462Scg} 20854462Scg 20954462Scgstatic int 21054462Scgsb_reset_dsp(struct resource *io) 21154462Scg{ 21254462Scg sb_wr(io, SBDSP_RST, 3); 21354462Scg DELAY(100); 21454462Scg sb_wr(io, SBDSP_RST, 0); 21554462Scg return (sb_get_byte(io) == 0xAA)? 0 : ENXIO; 21654462Scg} 21754462Scg 21854462Scgstatic int 21954462Scgsb_identify_board(struct resource *io) 22054462Scg{ 22154462Scg int ver, essver, rev; 22254462Scg 22354462Scg sb_cmd(io, DSP_CMD_GETVER); /* Get version */ 22454462Scg ver = (sb_get_byte(io) << 8) | sb_get_byte(io); 22554462Scg if (ver < 0x100 || ver > 0x4ff) return 0; 22654462Scg if (ver == 0x0301) { 22754462Scg /* Try to detect ESS chips. */ 22854462Scg sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */ 22954462Scg essver = (sb_get_byte(io) << 8) | sb_get_byte(io); 23054462Scg rev = essver & 0x000f; 23154462Scg essver &= 0xfff0; 23254462Scg if (essver == 0x4880) ver |= 0x1000; 23354462Scg else if (essver == 0x6880) ver = 0x0500 | rev; 23454462Scg } 23554462Scg return ver; 23654462Scg} 23754462Scg 23854118Stanimurastatic struct isa_pnp_id sbc_ids[] = { 23954961Speter {0x01008c0e, "Creative ViBRA16C"}, /* CTL0001 */ 24054961Speter {0x31008c0e, "Creative SB16/SB32"}, /* CTL0031 */ 24154961Speter {0x41008c0e, "Creative SB16/SB32"}, /* CTL0041 */ 24254961Speter {0x42008c0e, "Creative SB AWE64"}, /* CTL0042 */ 24354961Speter {0x43008c0e, "Creative ViBRA16X"}, /* CTL0043 */ 24454961Speter {0x44008c0e, "Creative SB AWE64 Gold"}, /* CTL0044 */ 24554961Speter {0x45008c0e, "Creative SB AWE64"}, /* CTL0045 */ 24684134Sgreid {0x46008c0e, "Creative SB AWE64"}, /* CTL0046 */ 24754462Scg 24858385Scg {0x01000000, "Avance Logic ALS100+"}, /* @@@0001 - ViBRA16X clone */ 24954961Speter {0x01100000, "Avance Asound 110"}, /* @@@1001 */ 25058385Scg {0x01200000, "Avance Logic ALS120"}, /* @@@2001 - ViBRA16X clone */ 25154462Scg 25270668Simp {0x81167316, "ESS ES1681"}, /* ESS1681 */ 25356721Sgallatin {0x02017316, "ESS ES1688"}, /* ESS1688 */ 25454961Speter {0x68187316, "ESS ES1868"}, /* ESS1868 */ 25561061Skuriyama {0x03007316, "ESS ES1869"}, /* ESS1869 */ 25654961Speter {0x69187316, "ESS ES1869"}, /* ESS1869 */ 25761569Sbrian {0xabb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ab */ 25854961Speter {0xacb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ac */ 25955848Sdeischen {0x78187316, "ESS ES1878"}, /* ESS1878 */ 26054961Speter {0x79187316, "ESS ES1879"}, /* ESS1879 */ 26154961Speter {0x88187316, "ESS ES1888"}, /* ESS1888 */ 26254961Speter {0x07017316, "ESS ES1888 (DEC OEM)"}, /* ESS0107 */ 26364845Scg {0x06017316, "ESS ES1888 (Dell OEM)"}, /* ESS0106 */ 26454118Stanimura {0} 26554118Stanimura}; 26654118Stanimura 26753553Stanimurastatic int 26853553Stanimurasbc_probe(device_t dev) 26953553Stanimura{ 27054462Scg char *s = NULL; 27156774Scg u_int32_t lid, vid; 27256774Scg 27356774Scg lid = isa_get_logicalid(dev); 27456774Scg vid = isa_get_vendorid(dev); 27556774Scg if (lid) { 27656774Scg if (lid == 0x01000000 && vid != 0x01009305) /* ALS0001 */ 27756774Scg return ENXIO; 27854462Scg /* Check pnp ids */ 27954462Scg return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids); 28054462Scg } else { 28154462Scg int rid = 0, ver; 28254462Scg struct resource *io; 28353553Stanimura 28460711Snyan#ifdef PC98 28560711Snyan io = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, 28660711Snyan pcm_iat, 16, RF_ACTIVE); 28760711Snyan#else 28854462Scg io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 28954462Scg 0, ~0, 16, RF_ACTIVE); 29060711Snyan#endif 29154462Scg if (!io) goto bad; 29260711Snyan#ifdef PC98 29360711Snyan isa_load_resourcev(io, pcm_iat, 16); 29460711Snyan#endif 29554462Scg if (sb_reset_dsp(io)) goto bad2; 29654462Scg ver = sb_identify_board(io); 29754462Scg if (ver == 0) goto bad2; 29854462Scg switch ((ver & 0x00000f00) >> 8) { 29954462Scg case 1: 30067651Scg device_set_desc(dev, "SoundBlaster 1.0 (not supported)"); 30167651Scg s = NULL; 30267651Scg break; 30367651Scg 30454462Scg case 2: 30567651Scg s = "SoundBlaster 2.0"; 30654462Scg break; 30754462Scg 30854462Scg case 3: 30967651Scg s = (ver & 0x0000f000)? "ESS 488" : "SoundBlaster Pro"; 31054462Scg break; 31154462Scg 31254462Scg case 4: 31367651Scg s = "SoundBlaster 16"; 31454462Scg break; 31554462Scg 31654462Scg case 5: 31754462Scg s = (ver & 0x00000008)? "ESS 688" : "ESS 1688"; 31854462Scg break; 31954462Scg } 32054462Scg if (s) device_set_desc(dev, s); 32154462Scgbad2: bus_release_resource(dev, SYS_RES_IOPORT, rid, io); 32254462Scgbad: return s? 0 : ENXIO; 32354462Scg } 32453553Stanimura} 32553553Stanimura 32653553Stanimurastatic int 32753553Stanimurasbc_attach(device_t dev) 32853553Stanimura{ 32954791Scg char *err = NULL; 33054462Scg struct sbc_softc *scp; 33154462Scg struct sndcard_func *func; 33254462Scg u_int32_t logical_id = isa_get_logicalid(dev); 33354462Scg int flags = device_get_flags(dev); 33454824Scg int f, dh, dl, x, irq, i; 33553553Stanimura 33654462Scg if (!logical_id && (flags & DV_F_DUAL_DMA)) { 33754462Scg bus_set_resource(dev, SYS_RES_DRQ, 1, 33854462Scg flags & DV_F_DRQ_MASK, 1); 33954462Scg } 34054462Scg 34153553Stanimura scp = device_get_softc(dev); 34253553Stanimura bzero(scp, sizeof(*scp)); 34354462Scg scp->dev = dev; 34474763Scg sbc_lockinit(scp); 34554791Scg err = "alloc_resource"; 34654462Scg if (alloc_resource(scp)) goto bad; 34753553Stanimura 34854791Scg err = "sb_reset_dsp"; 34954462Scg if (sb_reset_dsp(scp->io[0])) goto bad; 35054791Scg err = "sb_identify_board"; 35154462Scg scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff; 35254462Scg if (scp->bd_ver == 0) goto bad; 35354791Scg f = 0; 35458385Scg if (logical_id == 0x01200000 && scp->bd_ver < 0x0400) scp->bd_ver = 0x0499; 35554462Scg switch ((scp->bd_ver & 0x0f00) >> 8) { 35654462Scg case 1: /* old sound blaster has nothing... */ 35754462Scg break; 35854462Scg 35954462Scg case 2: 36054462Scg f |= BD_F_DUP_MIDI; 36154462Scg if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335; 36254462Scg break; 36354462Scg 36454462Scg case 5: 36554462Scg f |= BD_F_ESS; 36654462Scg scp->bd_ver = 0x0301; 36754462Scg case 3: 36854462Scg f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; 36954462Scg break; 37054462Scg 37154462Scg case 4: 37254462Scg f |= BD_F_SB16 | BD_F_MIX_CT1745; 37354791Scg if (scp->drq[0]) dl = rman_get_start(scp->drq[0]); else dl = -1; 37454462Scg if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl; 37554846Scg if (!logical_id && (dh < dl)) { 37654846Scg struct resource *r; 37754846Scg r = scp->drq[0]; 37854846Scg scp->drq[0] = scp->drq[1]; 37954846Scg scp->drq[1] = r; 38054846Scg dl = rman_get_start(scp->drq[0]); 38154846Scg dh = rman_get_start(scp->drq[1]); 38254462Scg } 38354791Scg /* soft irq/dma configuration */ 38454791Scg x = -1; 38554824Scg irq = rman_get_start(scp->irq[0]); 38660711Snyan#ifdef PC98 38760711Snyan /* SB16 in PC98 use different IRQ table */ 38860711Snyan if (irq == 3) x = 1; 38960711Snyan else if (irq == 5) x = 8; 39060711Snyan else if (irq == 10) x = 2; 39160711Snyan else if (irq == 12) x = 4; 39260711Snyan if (x == -1) { 39360711Snyan err = "bad irq (3/5/10/12 valid)"; 39460711Snyan goto bad; 39560711Snyan } 39660711Snyan else sb_setmixer(scp->io[0], IRQ_NR, x); 39760711Snyan /* SB16 in PC98 use different dma setting */ 39860711Snyan sb_setmixer(scp->io[0], DMA_NR, dh == 0 ? 1 : 2); 39960711Snyan#else 40054791Scg if (irq == 5) x = 2; 40154791Scg else if (irq == 7) x = 4; 40254791Scg else if (irq == 9) x = 1; 40354791Scg else if (irq == 10) x = 8; 40454791Scg if (x == -1) { 40554791Scg err = "bad irq (5/7/9/10 valid)"; 40654791Scg goto bad; 40754791Scg } 40854791Scg else sb_setmixer(scp->io[0], IRQ_NR, x); 40954791Scg sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl)); 41060711Snyan#endif 41165644Scg if (bootverbose) { 41265644Scg device_printf(dev, "setting card to irq %d, drq %d", irq, dl); 41365644Scg if (dl != dh) printf(", %d", dh); 41465644Scg printf("\n"); 41565644Scg } 41654462Scg break; 41754462Scg } 41854462Scg 41954462Scg switch (logical_id) { 42054961Speter case 0x43008c0e: /* CTL0043 */ 42158385Scg case 0x01200000: 42258385Scg case 0x01000000: 42354462Scg f |= BD_F_SB16X; 42454462Scg break; 42553553Stanimura } 42654462Scg scp->bd_ver |= f << 16; 42753553Stanimura 42854824Scg err = "setup_intr"; 42954824Scg for (i = 0; i < IRQ_MAX; i++) { 43074763Scg scp->ihl[i].parent = scp; 43174763Scg if (snd_setup_intr(dev, scp->irq[i], INTR_MPSAFE, sbc_intr, &scp->ihl[i], &scp->ih[i])) 43254824Scg goto bad; 43354824Scg } 43454824Scg 43554118Stanimura /* PCM Audio */ 43678564Sgreid func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); 43754462Scg if (func == NULL) goto bad; 43854118Stanimura func->func = SCF_PCM; 43974395Scg scp->child_pcm = device_add_child(dev, "pcm", -1); 44074395Scg device_set_ivars(scp->child_pcm, func); 44154118Stanimura 44254118Stanimura /* Midi Interface */ 44378564Sgreid func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); 44454462Scg if (func == NULL) goto bad; 44554118Stanimura func->func = SCF_MIDI; 44674395Scg scp->child_midi1 = device_add_child(dev, "midi", -1); 44774395Scg device_set_ivars(scp->child_midi1, func); 44854118Stanimura 44954118Stanimura /* OPL FM Synthesizer */ 45078564Sgreid func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); 45154462Scg if (func == NULL) goto bad; 45254118Stanimura func->func = SCF_SYNTH; 45374395Scg scp->child_midi2 = device_add_child(dev, "midi", -1); 45474395Scg device_set_ivars(scp->child_midi2, func); 45554118Stanimura 45654462Scg /* probe/attach kids */ 45753553Stanimura bus_generic_attach(dev); 45853553Stanimura 45953553Stanimura return (0); 46054462Scg 46154791Scgbad: if (err) device_printf(dev, "%s\n", err); 46254791Scg release_resource(scp); 46354462Scg return (ENXIO); 46453553Stanimura} 46553553Stanimura 46665644Scgstatic int 46765644Scgsbc_detach(device_t dev) 46865644Scg{ 46965644Scg struct sbc_softc *scp = device_get_softc(dev); 47065644Scg 47174763Scg sbc_lock(scp); 47274395Scg device_delete_child(dev, scp->child_midi2); 47374395Scg device_delete_child(dev, scp->child_midi1); 47474395Scg device_delete_child(dev, scp->child_pcm); 47565644Scg release_resource(scp); 47674763Scg sbc_lockdestroy(scp); 47765644Scg return bus_generic_detach(dev); 47865644Scg} 47965644Scg 48054824Scgstatic void 48154824Scgsbc_intr(void *p) 48254824Scg{ 48354824Scg struct sbc_ihl *ihl = p; 48454824Scg int i; 48554824Scg 48674763Scg /* sbc_lock(ihl->parent); */ 48754824Scg i = 0; 48854824Scg while (i < INTR_MAX) { 48954824Scg if (ihl->intr[i] != NULL) ihl->intr[i](ihl->intr_arg[i]); 49054824Scg i++; 49154824Scg } 49274763Scg /* sbc_unlock(ihl->parent); */ 49354824Scg} 49454824Scg 49554824Scgstatic int 49654824Scgsbc_setup_intr(device_t dev, device_t child, struct resource *irq, 49754824Scg int flags, driver_intr_t *intr, void *arg, 49854824Scg void **cookiep) 49954824Scg{ 50054824Scg struct sbc_softc *scp = device_get_softc(dev); 50154824Scg struct sbc_ihl *ihl = NULL; 50274763Scg int i, ret; 50354824Scg 50474763Scg sbc_lock(scp); 50554824Scg i = 0; 50654824Scg while (i < IRQ_MAX) { 50754824Scg if (irq == scp->irq[i]) ihl = &scp->ihl[i]; 50854824Scg i++; 50954824Scg } 51074763Scg ret = 0; 51174763Scg if (ihl == NULL) ret = EINVAL; 51254824Scg i = 0; 51374763Scg while ((ret == 0) && (i < INTR_MAX)) { 51454824Scg if (ihl->intr[i] == NULL) { 51554824Scg ihl->intr[i] = intr; 51654824Scg ihl->intr_arg[i] = arg; 51754824Scg *cookiep = &ihl->intr[i]; 51874763Scg ret = -1; 51954824Scg } else i++; 52054824Scg } 52174763Scg sbc_unlock(scp); 52274763Scg return (ret > 0)? EINVAL : 0; 52354824Scg} 52454824Scg 52554824Scgstatic int 52654824Scgsbc_teardown_intr(device_t dev, device_t child, struct resource *irq, 52754824Scg void *cookie) 52854824Scg{ 52954824Scg struct sbc_softc *scp = device_get_softc(dev); 53054824Scg struct sbc_ihl *ihl = NULL; 53174763Scg int i, ret; 53254824Scg 53374763Scg sbc_lock(scp); 53454824Scg i = 0; 53554824Scg while (i < IRQ_MAX) { 53654824Scg if (irq == scp->irq[i]) ihl = &scp->ihl[i]; 53754824Scg i++; 53854824Scg } 53974763Scg ret = 0; 54074763Scg if (ihl == NULL) ret = EINVAL; 54154824Scg i = 0; 54274763Scg while ((ret == 0) && (i < INTR_MAX)) { 54354824Scg if (cookie == &ihl->intr[i]) { 54454824Scg ihl->intr[i] = NULL; 54554824Scg ihl->intr_arg[i] = NULL; 54654824Scg return 0; 54754824Scg } else i++; 54854824Scg } 54974763Scg sbc_unlock(scp); 55074763Scg return (ret > 0)? EINVAL : 0; 55154824Scg} 55254824Scg 55353553Stanimurastatic struct resource * 55453553Stanimurasbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 55553553Stanimura u_long start, u_long end, u_long count, u_int flags) 55653553Stanimura{ 55754462Scg struct sbc_softc *scp; 55853553Stanimura int *alloced, rid_max, alloced_max; 55953553Stanimura struct resource **res; 56060711Snyan#ifdef PC98 56160711Snyan int i; 56260711Snyan#endif 56353553Stanimura 56453553Stanimura scp = device_get_softc(bus); 56553553Stanimura switch (type) { 56653553Stanimura case SYS_RES_IOPORT: 56753553Stanimura alloced = scp->io_alloced; 56853553Stanimura res = scp->io; 56960711Snyan#ifdef PC98 57060711Snyan rid_max = 0; 57160711Snyan for (i = 0; i < IO_MAX; i++) 57260711Snyan rid_max += io_range[i]; 57360711Snyan#else 57454824Scg rid_max = IO_MAX - 1; 57560711Snyan#endif 57653553Stanimura alloced_max = 1; 57753553Stanimura break; 57854462Scg case SYS_RES_DRQ: 57954462Scg alloced = scp->drq_alloced; 58054462Scg res = scp->drq; 58154824Scg rid_max = DRQ_MAX - 1; 58254462Scg alloced_max = 1; 58354462Scg break; 58453553Stanimura case SYS_RES_IRQ: 58554824Scg alloced = scp->irq_alloced; 58654824Scg res = scp->irq; 58754824Scg rid_max = IRQ_MAX - 1; 58854824Scg alloced_max = INTR_MAX; /* pcm and mpu may share the irq. */ 58953553Stanimura break; 59053553Stanimura default: 59153553Stanimura return (NULL); 59253553Stanimura } 59353553Stanimura 59453553Stanimura if (*rid > rid_max || alloced[*rid] == alloced_max) 59553553Stanimura return (NULL); 59653553Stanimura 59753553Stanimura alloced[*rid]++; 59854961Speter return (res[*rid]); 59953553Stanimura} 60053553Stanimura 60153553Stanimurastatic int 60253553Stanimurasbc_release_resource(device_t bus, device_t child, int type, int rid, 60353553Stanimura struct resource *r) 60453553Stanimura{ 60554462Scg struct sbc_softc *scp; 60653553Stanimura int *alloced, rid_max; 60753553Stanimura 60853553Stanimura scp = device_get_softc(bus); 60953553Stanimura switch (type) { 61053553Stanimura case SYS_RES_IOPORT: 61153553Stanimura alloced = scp->io_alloced; 61254824Scg rid_max = IO_MAX - 1; 61353553Stanimura break; 61454462Scg case SYS_RES_DRQ: 61554462Scg alloced = scp->drq_alloced; 61654824Scg rid_max = DRQ_MAX - 1; 61754462Scg break; 61853553Stanimura case SYS_RES_IRQ: 61954824Scg alloced = scp->irq_alloced; 62054824Scg rid_max = IRQ_MAX - 1; 62153553Stanimura break; 62253553Stanimura default: 62353553Stanimura return (1); 62453553Stanimura } 62553553Stanimura 62653553Stanimura if (rid > rid_max || alloced[rid] == 0) 62753553Stanimura return (1); 62853553Stanimura 62953553Stanimura alloced[rid]--; 63053553Stanimura return (0); 63153553Stanimura} 63253553Stanimura 63353553Stanimurastatic int 63454462Scgsbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 63553553Stanimura{ 63654462Scg struct sbc_softc *scp = device_get_softc(bus); 63754462Scg struct sndcard_func *func = device_get_ivars(dev); 63854462Scg 63954462Scg switch (index) { 64054462Scg case 0: 64154462Scg *result = func->func; 64254462Scg break; 64354462Scg 64454462Scg case 1: 64554462Scg *result = scp->bd_ver; 64654462Scg break; 64754462Scg 64854462Scg default: 64954462Scg return ENOENT; 65054462Scg } 65154462Scg 65254462Scg return 0; 65354462Scg} 65454462Scg 65554462Scgstatic int 65654462Scgsbc_write_ivar(device_t bus, device_t dev, 65754462Scg int index, uintptr_t value) 65854462Scg{ 65954462Scg switch (index) { 66054462Scg case 0: 66154462Scg case 1: 66254462Scg return EINVAL; 66354462Scg 66454462Scg default: 66554462Scg return (ENOENT); 66654462Scg } 66754462Scg} 66854462Scg 66954462Scgstatic int 67054462Scgalloc_resource(struct sbc_softc *scp) 67154462Scg{ 67253553Stanimura int i; 67353553Stanimura 67454824Scg for (i = 0 ; i < IO_MAX ; i++) { 67553553Stanimura if (scp->io[i] == NULL) { 67660711Snyan#ifdef PC98 67760711Snyan scp->io_rid[i] = i > 0 ? 67860711Snyan scp->io_rid[i - 1] + io_range[i - 1] : 0; 67960711Snyan scp->io[i] = isa_alloc_resourcev(scp->dev, 68060711Snyan SYS_RES_IOPORT, 68160711Snyan &scp->io_rid[i], 68260711Snyan sb_iat[i], 68360711Snyan io_range[i], 68460711Snyan RF_ACTIVE); 68560711Snyan if (scp->io[i] != NULL) 68660711Snyan isa_load_resourcev(scp->io[i], sb_iat[i], 68760711Snyan io_range[i]); 68860711Snyan#else 68953553Stanimura scp->io_rid[i] = i; 69053553Stanimura scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], 69153553Stanimura 0, ~0, io_range[i], RF_ACTIVE); 69260711Snyan#endif 69354462Scg if (i == 0 && scp->io[i] == NULL) 69453553Stanimura return (1); 69553553Stanimura scp->io_alloced[i] = 0; 69653553Stanimura } 69753553Stanimura } 69854824Scg for (i = 0 ; i < DRQ_MAX ; i++) { 69953553Stanimura if (scp->drq[i] == NULL) { 70053553Stanimura scp->drq_rid[i] = i; 70153553Stanimura scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], 70253553Stanimura 0, ~0, 1, RF_ACTIVE); 70354462Scg if (i == 0 && scp->drq[i] == NULL) 70453553Stanimura return (1); 70553553Stanimura scp->drq_alloced[i] = 0; 70653553Stanimura } 70753553Stanimura } 70854824Scg for (i = 0 ; i < IRQ_MAX ; i++) { 70965644Scg if (scp->irq[i] == NULL) { 71054824Scg scp->irq_rid[i] = i; 71154824Scg scp->irq[i] = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid[i], 71254824Scg 0, ~0, 1, RF_ACTIVE); 71354824Scg if (i == 0 && scp->irq[i] == NULL) 71454824Scg return (1); 71554824Scg scp->irq_alloced[i] = 0; 71654824Scg } 71754462Scg } 71853553Stanimura return (0); 71953553Stanimura} 72053553Stanimura 72153553Stanimurastatic int 72254462Scgrelease_resource(struct sbc_softc *scp) 72353553Stanimura{ 72453553Stanimura int i; 72553553Stanimura 72654824Scg for (i = 0 ; i < IO_MAX ; i++) { 72753553Stanimura if (scp->io[i] != NULL) { 72853553Stanimura bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); 72953553Stanimura scp->io[i] = NULL; 73053553Stanimura } 73153553Stanimura } 73254824Scg for (i = 0 ; i < DRQ_MAX ; i++) { 73353553Stanimura if (scp->drq[i] != NULL) { 73453553Stanimura bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); 73553553Stanimura scp->drq[i] = NULL; 73653553Stanimura } 73753553Stanimura } 73854824Scg for (i = 0 ; i < IRQ_MAX ; i++) { 73954824Scg if (scp->irq[i] != NULL) { 74065644Scg if (scp->ih[i] != NULL) 74165644Scg bus_teardown_intr(scp->dev, scp->irq[i], scp->ih[i]); 74265644Scg scp->ih[i] = NULL; 74354824Scg bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid[i], scp->irq[i]); 74454824Scg scp->irq[i] = NULL; 74554824Scg } 74654824Scg } 74753553Stanimura return (0); 74853553Stanimura} 74953553Stanimura 75053553Stanimurastatic device_method_t sbc_methods[] = { 75153553Stanimura /* Device interface */ 75253553Stanimura DEVMETHOD(device_probe, sbc_probe), 75353553Stanimura DEVMETHOD(device_attach, sbc_attach), 75465644Scg DEVMETHOD(device_detach, sbc_detach), 75553553Stanimura DEVMETHOD(device_shutdown, bus_generic_shutdown), 75653553Stanimura DEVMETHOD(device_suspend, bus_generic_suspend), 75753553Stanimura DEVMETHOD(device_resume, bus_generic_resume), 75853553Stanimura 75953553Stanimura /* Bus interface */ 76054462Scg DEVMETHOD(bus_read_ivar, sbc_read_ivar), 76154462Scg DEVMETHOD(bus_write_ivar, sbc_write_ivar), 76253553Stanimura DEVMETHOD(bus_print_child, bus_generic_print_child), 76353553Stanimura DEVMETHOD(bus_alloc_resource, sbc_alloc_resource), 76453553Stanimura DEVMETHOD(bus_release_resource, sbc_release_resource), 76553553Stanimura DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 76653553Stanimura DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 76754824Scg DEVMETHOD(bus_setup_intr, sbc_setup_intr), 76854824Scg DEVMETHOD(bus_teardown_intr, sbc_teardown_intr), 76953553Stanimura 77053553Stanimura { 0, 0 } 77153553Stanimura}; 77253553Stanimura 77353553Stanimurastatic driver_t sbc_driver = { 77453553Stanimura "sbc", 77553553Stanimura sbc_methods, 77653553Stanimura sizeof(struct sbc_softc), 77753553Stanimura}; 77853553Stanimura 77954462Scg/* sbc can be attached to an isa bus. */ 78062483ScgDRIVER_MODULE(snd_sbc, isa, sbc_driver, sbc_devclass, 0, 0); 78162483ScgMODULE_DEPEND(snd_sbc, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 78262483ScgMODULE_VERSION(snd_sbc, 1); 783