1139749Simp/*- 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 27193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 28193640Sariff#include "opt_snd.h" 29193640Sariff#endif 30193640Sariff 3153553Stanimura#include <dev/sound/chip.h> 3254462Scg#include <dev/sound/pcm/sound.h> 3362483Scg#include <dev/sound/isa/sb.h> 3453553Stanimura 35110499Snyan#include <isa/isavar.h> 36110499Snyan 3782180ScgSND_DECLARE_FILE("$FreeBSD$"); 3882180Scg 3954824Scg#define IO_MAX 3 4054824Scg#define IRQ_MAX 1 4154824Scg#define DRQ_MAX 2 4254824Scg#define INTR_MAX 2 4354824Scg 4474763Scgstruct sbc_softc; 4574763Scg 4654824Scgstruct sbc_ihl { 4754824Scg driver_intr_t *intr[INTR_MAX]; 4854824Scg void *intr_arg[INTR_MAX]; 4974763Scg struct sbc_softc *parent; 5054824Scg}; 5154824Scg 5253553Stanimura/* Here is the parameter structure per a device. */ 5353553Stanimurastruct sbc_softc { 5453553Stanimura device_t dev; /* device */ 5574395Scg device_t child_pcm, child_midi1, child_midi2; 5654462Scg 5754824Scg int io_rid[IO_MAX]; /* io port rids */ 5854824Scg struct resource *io[IO_MAX]; /* io port resources */ 5954824Scg int io_alloced[IO_MAX]; /* io port alloc flag */ 6054462Scg 6154824Scg int irq_rid[IRQ_MAX]; /* irq rids */ 6254824Scg struct resource *irq[IRQ_MAX]; /* irq resources */ 6354824Scg int irq_alloced[IRQ_MAX]; /* irq alloc flag */ 6454462Scg 6554824Scg int drq_rid[DRQ_MAX]; /* drq rids */ 6654824Scg struct resource *drq[DRQ_MAX]; /* drq resources */ 6754824Scg int drq_alloced[DRQ_MAX]; /* drq alloc flag */ 6854462Scg 6954824Scg struct sbc_ihl ihl[IRQ_MAX]; 7054824Scg 7165644Scg void *ih[IRQ_MAX]; 7254824Scg 73107285Scg struct mtx *lock; 7474763Scg 7554462Scg u_int32_t bd_ver; 7653553Stanimura}; 7753553Stanimura 7853553Stanimurastatic int sbc_probe(device_t dev); 7953553Stanimurastatic int sbc_attach(device_t dev); 8054824Scgstatic void sbc_intr(void *p); 8154824Scg 8253553Stanimurastatic struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 8354462Scg u_long start, u_long end, u_long count, u_int flags); 8453553Stanimurastatic int sbc_release_resource(device_t bus, device_t child, int type, int rid, 8554462Scg struct resource *r); 8654824Scgstatic int sbc_setup_intr(device_t dev, device_t child, struct resource *irq, 87166918Sariff int flags, 88166918Sariff#if __FreeBSD_version >= 700031 89166918Sariff driver_filter_t *filter, 90166918Sariff#endif 91166918Sariff driver_intr_t *intr, 92166901Spiso void *arg, void **cookiep); 9354824Scgstatic int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq, 9454824Scg void *cookie); 9553553Stanimura 9654462Scgstatic int alloc_resource(struct sbc_softc *scp); 9754462Scgstatic int release_resource(struct sbc_softc *scp); 9853553Stanimura 9953553Stanimurastatic devclass_t sbc_devclass; 10053553Stanimura 10154462Scgstatic int io_range[3] = {0x10, 0x2, 0x4}; 10254462Scg 10360711Snyan#ifdef PC98 /* I/O address table for PC98 */ 10460711Snyanstatic bus_addr_t pcm_iat[] = { 10560711Snyan 0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 10660711Snyan 0x800, 0x900, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00 10760711Snyan}; 10860711Snyanstatic bus_addr_t midi_iat[] = { 10960711Snyan 0x000, 0x100 11060711Snyan}; 11160711Snyanstatic bus_addr_t opl_iat[] = { 11260711Snyan 0x000, 0x100, 0x200, 0x300 11360711Snyan}; 11460711Snyanstatic bus_addr_t *sb_iat[] = { pcm_iat, midi_iat, opl_iat }; 11560711Snyan#endif 11660711Snyan 11754462Scgstatic int sb_rd(struct resource *io, int reg); 11854462Scgstatic void sb_wr(struct resource *io, int reg, u_int8_t val); 11954462Scgstatic int sb_dspready(struct resource *io); 12054462Scgstatic int sb_cmd(struct resource *io, u_char val); 12154462Scgstatic u_int sb_get_byte(struct resource *io); 12254462Scgstatic void sb_setmixer(struct resource *io, u_int port, u_int value); 12354462Scg 12474763Scgstatic void 12574763Scgsbc_lockinit(struct sbc_softc *scp) 12674763Scg{ 127167608Sariff scp->lock = snd_mtxcreate(device_get_nameunit(scp->dev), 128167608Sariff "snd_sbc softc"); 12974763Scg} 13074763Scg 13174763Scgstatic void 13274763Scgsbc_lockdestroy(struct sbc_softc *scp) 13374763Scg{ 13474763Scg snd_mtxfree(scp->lock); 13574763Scg} 13674763Scg 13774763Scgvoid 13874763Scgsbc_lock(struct sbc_softc *scp) 13974763Scg{ 14074763Scg snd_mtxlock(scp->lock); 14174763Scg} 14274763Scg 14374763Scgvoid 144129180Struckmansbc_lockassert(struct sbc_softc *scp) 145129180Struckman{ 146129180Struckman snd_mtxassert(scp->lock); 147129180Struckman} 148129180Struckman 149129180Struckmanvoid 15074763Scgsbc_unlock(struct sbc_softc *scp) 15174763Scg{ 15274763Scg snd_mtxunlock(scp->lock); 15374763Scg} 15474763Scg 15554462Scgstatic int 15654462Scgsb_rd(struct resource *io, int reg) 15754462Scg{ 15854462Scg return bus_space_read_1(rman_get_bustag(io), 15954462Scg rman_get_bushandle(io), 16054462Scg reg); 16154462Scg} 16254462Scg 16354462Scgstatic void 16454462Scgsb_wr(struct resource *io, int reg, u_int8_t val) 16554462Scg{ 166108064Ssemenu bus_space_write_1(rman_get_bustag(io), 167108064Ssemenu rman_get_bushandle(io), 168108064Ssemenu reg, val); 16954462Scg} 17054462Scg 17154462Scgstatic int 17254462Scgsb_dspready(struct resource *io) 17354462Scg{ 17454462Scg return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0); 17554462Scg} 17654462Scg 17754462Scgstatic int 17854462Scgsb_dspwr(struct resource *io, u_char val) 17954462Scg{ 18054462Scg int i; 18154462Scg 18254462Scg for (i = 0; i < 1000; i++) { 18354462Scg if (sb_dspready(io)) { 18454462Scg sb_wr(io, SBDSP_CMD, val); 18554462Scg return 1; 18654462Scg } 18754462Scg if (i > 10) DELAY((i > 100)? 1000 : 10); 18854462Scg } 18954462Scg printf("sb_dspwr(0x%02x) timed out.\n", val); 19054462Scg return 0; 19154462Scg} 19254462Scg 19354462Scgstatic int 19454462Scgsb_cmd(struct resource *io, u_char val) 19554462Scg{ 19654462Scg return sb_dspwr(io, val); 19754462Scg} 19854462Scg 19954462Scgstatic void 20054462Scgsb_setmixer(struct resource *io, u_int port, u_int value) 20154462Scg{ 20254462Scg u_long flags; 20354462Scg 20454462Scg flags = spltty(); 20554462Scg sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 20654462Scg DELAY(10); 20754462Scg sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff)); 20854462Scg DELAY(10); 20954462Scg splx(flags); 21054462Scg} 21154462Scg 21254462Scgstatic u_int 21354462Scgsb_get_byte(struct resource *io) 21454462Scg{ 21554462Scg int i; 21654462Scg 21754462Scg for (i = 1000; i > 0; i--) { 21854462Scg if (sb_rd(io, DSP_DATA_AVAIL) & 0x80) 21954462Scg return sb_rd(io, DSP_READ); 22054462Scg else 22154462Scg DELAY(20); 22254462Scg } 22354462Scg return 0xffff; 22454462Scg} 22554462Scg 22654462Scgstatic int 22754462Scgsb_reset_dsp(struct resource *io) 22854462Scg{ 22954462Scg sb_wr(io, SBDSP_RST, 3); 23054462Scg DELAY(100); 23154462Scg sb_wr(io, SBDSP_RST, 0); 23254462Scg return (sb_get_byte(io) == 0xAA)? 0 : ENXIO; 23354462Scg} 23454462Scg 23554462Scgstatic int 23654462Scgsb_identify_board(struct resource *io) 23754462Scg{ 23854462Scg int ver, essver, rev; 23954462Scg 24054462Scg sb_cmd(io, DSP_CMD_GETVER); /* Get version */ 24154462Scg ver = (sb_get_byte(io) << 8) | sb_get_byte(io); 24254462Scg if (ver < 0x100 || ver > 0x4ff) return 0; 24354462Scg if (ver == 0x0301) { 24454462Scg /* Try to detect ESS chips. */ 24554462Scg sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */ 24654462Scg essver = (sb_get_byte(io) << 8) | sb_get_byte(io); 24754462Scg rev = essver & 0x000f; 24854462Scg essver &= 0xfff0; 24954462Scg if (essver == 0x4880) ver |= 0x1000; 25054462Scg else if (essver == 0x6880) ver = 0x0500 | rev; 25154462Scg } 25254462Scg return ver; 25354462Scg} 25454462Scg 25554118Stanimurastatic struct isa_pnp_id sbc_ids[] = { 25654961Speter {0x01008c0e, "Creative ViBRA16C"}, /* CTL0001 */ 25754961Speter {0x31008c0e, "Creative SB16/SB32"}, /* CTL0031 */ 25854961Speter {0x41008c0e, "Creative SB16/SB32"}, /* CTL0041 */ 25954961Speter {0x42008c0e, "Creative SB AWE64"}, /* CTL0042 */ 26054961Speter {0x43008c0e, "Creative ViBRA16X"}, /* CTL0043 */ 26154961Speter {0x44008c0e, "Creative SB AWE64 Gold"}, /* CTL0044 */ 26254961Speter {0x45008c0e, "Creative SB AWE64"}, /* CTL0045 */ 26384134Sgreid {0x46008c0e, "Creative SB AWE64"}, /* CTL0046 */ 26454462Scg 26558385Scg {0x01000000, "Avance Logic ALS100+"}, /* @@@0001 - ViBRA16X clone */ 26654961Speter {0x01100000, "Avance Asound 110"}, /* @@@1001 */ 26758385Scg {0x01200000, "Avance Logic ALS120"}, /* @@@2001 - ViBRA16X clone */ 26854462Scg 26970668Simp {0x81167316, "ESS ES1681"}, /* ESS1681 */ 27056721Sgallatin {0x02017316, "ESS ES1688"}, /* ESS1688 */ 271148597Snetchild {0x68097316, "ESS ES1688"}, /* ESS1688 */ 27254961Speter {0x68187316, "ESS ES1868"}, /* ESS1868 */ 27361061Skuriyama {0x03007316, "ESS ES1869"}, /* ESS1869 */ 27454961Speter {0x69187316, "ESS ES1869"}, /* ESS1869 */ 27561569Sbrian {0xabb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ab */ 27654961Speter {0xacb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ac */ 27755848Sdeischen {0x78187316, "ESS ES1878"}, /* ESS1878 */ 27854961Speter {0x79187316, "ESS ES1879"}, /* ESS1879 */ 27954961Speter {0x88187316, "ESS ES1888"}, /* ESS1888 */ 28054961Speter {0x07017316, "ESS ES1888 (DEC OEM)"}, /* ESS0107 */ 28164845Scg {0x06017316, "ESS ES1888 (Dell OEM)"}, /* ESS0106 */ 28254118Stanimura {0} 28354118Stanimura}; 28454118Stanimura 28553553Stanimurastatic int 28653553Stanimurasbc_probe(device_t dev) 28753553Stanimura{ 28854462Scg char *s = NULL; 28956774Scg u_int32_t lid, vid; 29056774Scg 29156774Scg lid = isa_get_logicalid(dev); 29256774Scg vid = isa_get_vendorid(dev); 29356774Scg if (lid) { 29456774Scg if (lid == 0x01000000 && vid != 0x01009305) /* ALS0001 */ 29556774Scg return ENXIO; 29654462Scg /* Check pnp ids */ 29754462Scg return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids); 29854462Scg } else { 29954462Scg int rid = 0, ver; 30054462Scg struct resource *io; 30153553Stanimura 30260711Snyan#ifdef PC98 30360711Snyan io = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, 30460711Snyan pcm_iat, 16, RF_ACTIVE); 30560711Snyan#else 30654462Scg io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 30754462Scg 0, ~0, 16, RF_ACTIVE); 30860711Snyan#endif 30954462Scg if (!io) goto bad; 31060711Snyan#ifdef PC98 31160711Snyan isa_load_resourcev(io, pcm_iat, 16); 31260711Snyan#endif 31354462Scg if (sb_reset_dsp(io)) goto bad2; 31454462Scg ver = sb_identify_board(io); 31554462Scg if (ver == 0) goto bad2; 31654462Scg switch ((ver & 0x00000f00) >> 8) { 31754462Scg case 1: 31867651Scg device_set_desc(dev, "SoundBlaster 1.0 (not supported)"); 31967651Scg s = NULL; 32067651Scg break; 32167651Scg 32254462Scg case 2: 32367651Scg s = "SoundBlaster 2.0"; 32454462Scg break; 32554462Scg 32654462Scg case 3: 32767651Scg s = (ver & 0x0000f000)? "ESS 488" : "SoundBlaster Pro"; 32854462Scg break; 32954462Scg 33054462Scg case 4: 33167651Scg s = "SoundBlaster 16"; 33254462Scg break; 33354462Scg 33454462Scg case 5: 33554462Scg s = (ver & 0x00000008)? "ESS 688" : "ESS 1688"; 33654462Scg break; 33754462Scg } 33854462Scg if (s) device_set_desc(dev, s); 33954462Scgbad2: bus_release_resource(dev, SYS_RES_IOPORT, rid, io); 34054462Scgbad: return s? 0 : ENXIO; 34154462Scg } 34253553Stanimura} 34353553Stanimura 34453553Stanimurastatic int 34553553Stanimurasbc_attach(device_t dev) 34653553Stanimura{ 34754791Scg char *err = NULL; 34854462Scg struct sbc_softc *scp; 34954462Scg struct sndcard_func *func; 35054462Scg u_int32_t logical_id = isa_get_logicalid(dev); 35154462Scg int flags = device_get_flags(dev); 35254824Scg int f, dh, dl, x, irq, i; 35353553Stanimura 35454462Scg if (!logical_id && (flags & DV_F_DUAL_DMA)) { 35554462Scg bus_set_resource(dev, SYS_RES_DRQ, 1, 35654462Scg flags & DV_F_DRQ_MASK, 1); 35754462Scg } 35854462Scg 35953553Stanimura scp = device_get_softc(dev); 36053553Stanimura bzero(scp, sizeof(*scp)); 36154462Scg scp->dev = dev; 36274763Scg sbc_lockinit(scp); 36354791Scg err = "alloc_resource"; 36454462Scg if (alloc_resource(scp)) goto bad; 36553553Stanimura 36654791Scg err = "sb_reset_dsp"; 36754462Scg if (sb_reset_dsp(scp->io[0])) goto bad; 36854791Scg err = "sb_identify_board"; 36954462Scg scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff; 37054462Scg if (scp->bd_ver == 0) goto bad; 37154791Scg f = 0; 37258385Scg if (logical_id == 0x01200000 && scp->bd_ver < 0x0400) scp->bd_ver = 0x0499; 37354462Scg switch ((scp->bd_ver & 0x0f00) >> 8) { 37454462Scg case 1: /* old sound blaster has nothing... */ 37554462Scg break; 37654462Scg 37754462Scg case 2: 37854462Scg f |= BD_F_DUP_MIDI; 37954462Scg if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335; 38054462Scg break; 38154462Scg 38254462Scg case 5: 38354462Scg f |= BD_F_ESS; 38454462Scg scp->bd_ver = 0x0301; 38554462Scg case 3: 38654462Scg f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; 38754462Scg break; 38854462Scg 38954462Scg case 4: 39054462Scg f |= BD_F_SB16 | BD_F_MIX_CT1745; 39154791Scg if (scp->drq[0]) dl = rman_get_start(scp->drq[0]); else dl = -1; 39254462Scg if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl; 39354846Scg if (!logical_id && (dh < dl)) { 39454846Scg struct resource *r; 39554846Scg r = scp->drq[0]; 39654846Scg scp->drq[0] = scp->drq[1]; 39754846Scg scp->drq[1] = r; 39854846Scg dl = rman_get_start(scp->drq[0]); 39954846Scg dh = rman_get_start(scp->drq[1]); 40054462Scg } 40154791Scg /* soft irq/dma configuration */ 40254791Scg x = -1; 40354824Scg irq = rman_get_start(scp->irq[0]); 40460711Snyan#ifdef PC98 40560711Snyan /* SB16 in PC98 use different IRQ table */ 40660711Snyan if (irq == 3) x = 1; 40760711Snyan else if (irq == 5) x = 8; 40860711Snyan else if (irq == 10) x = 2; 40960711Snyan else if (irq == 12) x = 4; 41060711Snyan if (x == -1) { 41160711Snyan err = "bad irq (3/5/10/12 valid)"; 41260711Snyan goto bad; 41360711Snyan } 41460711Snyan else sb_setmixer(scp->io[0], IRQ_NR, x); 41560711Snyan /* SB16 in PC98 use different dma setting */ 41660711Snyan sb_setmixer(scp->io[0], DMA_NR, dh == 0 ? 1 : 2); 41760711Snyan#else 41854791Scg if (irq == 5) x = 2; 41954791Scg else if (irq == 7) x = 4; 42054791Scg else if (irq == 9) x = 1; 42154791Scg else if (irq == 10) x = 8; 42254791Scg if (x == -1) { 42354791Scg err = "bad irq (5/7/9/10 valid)"; 42454791Scg goto bad; 42554791Scg } 42654791Scg else sb_setmixer(scp->io[0], IRQ_NR, x); 42754791Scg sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl)); 42860711Snyan#endif 42965644Scg if (bootverbose) { 43065644Scg device_printf(dev, "setting card to irq %d, drq %d", irq, dl); 43165644Scg if (dl != dh) printf(", %d", dh); 43265644Scg printf("\n"); 43365644Scg } 43454462Scg break; 43554462Scg } 43654462Scg 43754462Scg switch (logical_id) { 43854961Speter case 0x43008c0e: /* CTL0043 */ 43958385Scg case 0x01200000: 44058385Scg case 0x01000000: 44154462Scg f |= BD_F_SB16X; 44254462Scg break; 44353553Stanimura } 44454462Scg scp->bd_ver |= f << 16; 44553553Stanimura 44654824Scg err = "setup_intr"; 44754824Scg for (i = 0; i < IRQ_MAX; i++) { 44874763Scg scp->ihl[i].parent = scp; 449128232Sgreen if (snd_setup_intr(dev, scp->irq[i], 0, sbc_intr, &scp->ihl[i], &scp->ih[i])) 45054824Scg goto bad; 45154824Scg } 45254824Scg 45354118Stanimura /* PCM Audio */ 45478564Sgreid func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); 45554462Scg if (func == NULL) goto bad; 45654118Stanimura func->func = SCF_PCM; 45774395Scg scp->child_pcm = device_add_child(dev, "pcm", -1); 45874395Scg device_set_ivars(scp->child_pcm, func); 45954118Stanimura 46054118Stanimura /* Midi Interface */ 46178564Sgreid func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); 46254462Scg if (func == NULL) goto bad; 46354118Stanimura func->func = SCF_MIDI; 46474395Scg scp->child_midi1 = device_add_child(dev, "midi", -1); 46574395Scg device_set_ivars(scp->child_midi1, func); 46654118Stanimura 46754118Stanimura /* OPL FM Synthesizer */ 46878564Sgreid func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); 46954462Scg if (func == NULL) goto bad; 47054118Stanimura func->func = SCF_SYNTH; 47174395Scg scp->child_midi2 = device_add_child(dev, "midi", -1); 47274395Scg device_set_ivars(scp->child_midi2, func); 47354118Stanimura 47454462Scg /* probe/attach kids */ 47553553Stanimura bus_generic_attach(dev); 47653553Stanimura 47753553Stanimura return (0); 47854462Scg 47954791Scgbad: if (err) device_printf(dev, "%s\n", err); 48054791Scg release_resource(scp); 48154462Scg return (ENXIO); 48253553Stanimura} 48353553Stanimura 48465644Scgstatic int 48565644Scgsbc_detach(device_t dev) 48665644Scg{ 48765644Scg struct sbc_softc *scp = device_get_softc(dev); 48865644Scg 48974763Scg sbc_lock(scp); 49074395Scg device_delete_child(dev, scp->child_midi2); 49174395Scg device_delete_child(dev, scp->child_midi1); 49274395Scg device_delete_child(dev, scp->child_pcm); 49365644Scg release_resource(scp); 49474763Scg sbc_lockdestroy(scp); 49565644Scg return bus_generic_detach(dev); 49665644Scg} 49765644Scg 49854824Scgstatic void 49954824Scgsbc_intr(void *p) 50054824Scg{ 50154824Scg struct sbc_ihl *ihl = p; 50254824Scg int i; 50354824Scg 50474763Scg /* sbc_lock(ihl->parent); */ 50554824Scg i = 0; 50654824Scg while (i < INTR_MAX) { 50754824Scg if (ihl->intr[i] != NULL) ihl->intr[i](ihl->intr_arg[i]); 50854824Scg i++; 50954824Scg } 51074763Scg /* sbc_unlock(ihl->parent); */ 51154824Scg} 51254824Scg 51354824Scgstatic int 514166918Sariffsbc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, 515166918Sariff#if __FreeBSD_version >= 700031 516166918Sariff driver_filter_t *filter, 517166918Sariff#endif 518166918Sariff driver_intr_t *intr, 519166901Spiso void *arg, void **cookiep) 52054824Scg{ 52154824Scg struct sbc_softc *scp = device_get_softc(dev); 52254824Scg struct sbc_ihl *ihl = NULL; 52374763Scg int i, ret; 52454824Scg 525166918Sariff#if __FreeBSD_version >= 700031 526166901Spiso if (filter != NULL) { 527166901Spiso printf("sbc.c: we cannot use a filter here\n"); 528166901Spiso return (EINVAL); 529166901Spiso } 530166918Sariff#endif 53174763Scg sbc_lock(scp); 53254824Scg i = 0; 53354824Scg while (i < IRQ_MAX) { 53454824Scg if (irq == scp->irq[i]) ihl = &scp->ihl[i]; 53554824Scg i++; 53654824Scg } 53774763Scg ret = 0; 53874763Scg if (ihl == NULL) ret = EINVAL; 53954824Scg i = 0; 54074763Scg while ((ret == 0) && (i < INTR_MAX)) { 54154824Scg if (ihl->intr[i] == NULL) { 54254824Scg ihl->intr[i] = intr; 54354824Scg ihl->intr_arg[i] = arg; 54454824Scg *cookiep = &ihl->intr[i]; 54574763Scg ret = -1; 54654824Scg } else i++; 54754824Scg } 54874763Scg sbc_unlock(scp); 54974763Scg return (ret > 0)? EINVAL : 0; 55054824Scg} 55154824Scg 55254824Scgstatic int 55354824Scgsbc_teardown_intr(device_t dev, device_t child, struct resource *irq, 55454824Scg void *cookie) 55554824Scg{ 55654824Scg struct sbc_softc *scp = device_get_softc(dev); 55754824Scg struct sbc_ihl *ihl = NULL; 55874763Scg int i, ret; 55954824Scg 56074763Scg sbc_lock(scp); 56154824Scg i = 0; 56254824Scg while (i < IRQ_MAX) { 56354824Scg if (irq == scp->irq[i]) ihl = &scp->ihl[i]; 56454824Scg i++; 56554824Scg } 56674763Scg ret = 0; 56774763Scg if (ihl == NULL) ret = EINVAL; 56854824Scg i = 0; 56974763Scg while ((ret == 0) && (i < INTR_MAX)) { 57054824Scg if (cookie == &ihl->intr[i]) { 57154824Scg ihl->intr[i] = NULL; 57254824Scg ihl->intr_arg[i] = NULL; 57354824Scg return 0; 57454824Scg } else i++; 57554824Scg } 57674763Scg sbc_unlock(scp); 57774763Scg return (ret > 0)? EINVAL : 0; 57854824Scg} 57954824Scg 58053553Stanimurastatic struct resource * 58153553Stanimurasbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 58253553Stanimura u_long start, u_long end, u_long count, u_int flags) 58353553Stanimura{ 58454462Scg struct sbc_softc *scp; 58553553Stanimura int *alloced, rid_max, alloced_max; 58653553Stanimura struct resource **res; 58760711Snyan#ifdef PC98 58860711Snyan int i; 58960711Snyan#endif 59053553Stanimura 59153553Stanimura scp = device_get_softc(bus); 59253553Stanimura switch (type) { 59353553Stanimura case SYS_RES_IOPORT: 59453553Stanimura alloced = scp->io_alloced; 59553553Stanimura res = scp->io; 59660711Snyan#ifdef PC98 59760711Snyan rid_max = 0; 59860711Snyan for (i = 0; i < IO_MAX; i++) 59960711Snyan rid_max += io_range[i]; 60060711Snyan#else 60154824Scg rid_max = IO_MAX - 1; 60260711Snyan#endif 60353553Stanimura alloced_max = 1; 60453553Stanimura break; 60554462Scg case SYS_RES_DRQ: 60654462Scg alloced = scp->drq_alloced; 60754462Scg res = scp->drq; 60854824Scg rid_max = DRQ_MAX - 1; 60954462Scg alloced_max = 1; 61054462Scg break; 61153553Stanimura case SYS_RES_IRQ: 61254824Scg alloced = scp->irq_alloced; 61354824Scg res = scp->irq; 61454824Scg rid_max = IRQ_MAX - 1; 61554824Scg alloced_max = INTR_MAX; /* pcm and mpu may share the irq. */ 61653553Stanimura break; 61753553Stanimura default: 61853553Stanimura return (NULL); 61953553Stanimura } 62053553Stanimura 62153553Stanimura if (*rid > rid_max || alloced[*rid] == alloced_max) 62253553Stanimura return (NULL); 62353553Stanimura 62453553Stanimura alloced[*rid]++; 62554961Speter return (res[*rid]); 62653553Stanimura} 62753553Stanimura 62853553Stanimurastatic int 62953553Stanimurasbc_release_resource(device_t bus, device_t child, int type, int rid, 63053553Stanimura struct resource *r) 63153553Stanimura{ 63254462Scg struct sbc_softc *scp; 63353553Stanimura int *alloced, rid_max; 63453553Stanimura 63553553Stanimura scp = device_get_softc(bus); 63653553Stanimura switch (type) { 63753553Stanimura case SYS_RES_IOPORT: 63853553Stanimura alloced = scp->io_alloced; 63954824Scg rid_max = IO_MAX - 1; 64053553Stanimura break; 64154462Scg case SYS_RES_DRQ: 64254462Scg alloced = scp->drq_alloced; 64354824Scg rid_max = DRQ_MAX - 1; 64454462Scg break; 64553553Stanimura case SYS_RES_IRQ: 64654824Scg alloced = scp->irq_alloced; 64754824Scg rid_max = IRQ_MAX - 1; 64853553Stanimura break; 64953553Stanimura default: 65053553Stanimura return (1); 65153553Stanimura } 65253553Stanimura 65353553Stanimura if (rid > rid_max || alloced[rid] == 0) 65453553Stanimura return (1); 65553553Stanimura 65653553Stanimura alloced[rid]--; 65753553Stanimura return (0); 65853553Stanimura} 65953553Stanimura 66053553Stanimurastatic int 66154462Scgsbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 66253553Stanimura{ 66354462Scg struct sbc_softc *scp = device_get_softc(bus); 66454462Scg struct sndcard_func *func = device_get_ivars(dev); 66554462Scg 66654462Scg switch (index) { 66754462Scg case 0: 66854462Scg *result = func->func; 66954462Scg break; 67054462Scg 67154462Scg case 1: 67254462Scg *result = scp->bd_ver; 67354462Scg break; 67454462Scg 67554462Scg default: 67654462Scg return ENOENT; 67754462Scg } 67854462Scg 67954462Scg return 0; 68054462Scg} 68154462Scg 68254462Scgstatic int 68354462Scgsbc_write_ivar(device_t bus, device_t dev, 68454462Scg int index, uintptr_t value) 68554462Scg{ 68654462Scg switch (index) { 68754462Scg case 0: 68854462Scg case 1: 68954462Scg return EINVAL; 69054462Scg 69154462Scg default: 69254462Scg return (ENOENT); 69354462Scg } 69454462Scg} 69554462Scg 69654462Scgstatic int 69754462Scgalloc_resource(struct sbc_softc *scp) 69854462Scg{ 69953553Stanimura int i; 70053553Stanimura 70154824Scg for (i = 0 ; i < IO_MAX ; i++) { 70253553Stanimura if (scp->io[i] == NULL) { 70360711Snyan#ifdef PC98 70460711Snyan scp->io_rid[i] = i > 0 ? 70560711Snyan scp->io_rid[i - 1] + io_range[i - 1] : 0; 70660711Snyan scp->io[i] = isa_alloc_resourcev(scp->dev, 70760711Snyan SYS_RES_IOPORT, 70860711Snyan &scp->io_rid[i], 70960711Snyan sb_iat[i], 71060711Snyan io_range[i], 71160711Snyan RF_ACTIVE); 71260711Snyan if (scp->io[i] != NULL) 71360711Snyan isa_load_resourcev(scp->io[i], sb_iat[i], 71460711Snyan io_range[i]); 71560711Snyan#else 71653553Stanimura scp->io_rid[i] = i; 71753553Stanimura scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], 71853553Stanimura 0, ~0, io_range[i], RF_ACTIVE); 71960711Snyan#endif 72054462Scg if (i == 0 && scp->io[i] == NULL) 72153553Stanimura return (1); 72253553Stanimura scp->io_alloced[i] = 0; 72353553Stanimura } 72453553Stanimura } 72554824Scg for (i = 0 ; i < DRQ_MAX ; i++) { 72653553Stanimura if (scp->drq[i] == NULL) { 72753553Stanimura scp->drq_rid[i] = i; 728127135Snjl scp->drq[i] = bus_alloc_resource_any(scp->dev, 729127135Snjl SYS_RES_DRQ, 730127135Snjl &scp->drq_rid[i], 731127135Snjl RF_ACTIVE); 73254462Scg if (i == 0 && scp->drq[i] == NULL) 73353553Stanimura return (1); 73453553Stanimura scp->drq_alloced[i] = 0; 73553553Stanimura } 73653553Stanimura } 73754824Scg for (i = 0 ; i < IRQ_MAX ; i++) { 73865644Scg if (scp->irq[i] == NULL) { 73954824Scg scp->irq_rid[i] = i; 740127135Snjl scp->irq[i] = bus_alloc_resource_any(scp->dev, 741127135Snjl SYS_RES_IRQ, 742127135Snjl &scp->irq_rid[i], 743127135Snjl RF_ACTIVE); 74454824Scg if (i == 0 && scp->irq[i] == NULL) 74554824Scg return (1); 74654824Scg scp->irq_alloced[i] = 0; 74754824Scg } 74854462Scg } 74953553Stanimura return (0); 75053553Stanimura} 75153553Stanimura 75253553Stanimurastatic int 75354462Scgrelease_resource(struct sbc_softc *scp) 75453553Stanimura{ 75553553Stanimura int i; 75653553Stanimura 75754824Scg for (i = 0 ; i < IO_MAX ; i++) { 75853553Stanimura if (scp->io[i] != NULL) { 75953553Stanimura bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); 76053553Stanimura scp->io[i] = NULL; 76153553Stanimura } 76253553Stanimura } 76354824Scg for (i = 0 ; i < DRQ_MAX ; i++) { 76453553Stanimura if (scp->drq[i] != NULL) { 76553553Stanimura bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); 76653553Stanimura scp->drq[i] = NULL; 76753553Stanimura } 76853553Stanimura } 76954824Scg for (i = 0 ; i < IRQ_MAX ; i++) { 77054824Scg if (scp->irq[i] != NULL) { 77165644Scg if (scp->ih[i] != NULL) 77265644Scg bus_teardown_intr(scp->dev, scp->irq[i], scp->ih[i]); 77365644Scg scp->ih[i] = NULL; 77454824Scg bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid[i], scp->irq[i]); 77554824Scg scp->irq[i] = NULL; 77654824Scg } 77754824Scg } 77853553Stanimura return (0); 77953553Stanimura} 78053553Stanimura 78153553Stanimurastatic device_method_t sbc_methods[] = { 78253553Stanimura /* Device interface */ 78353553Stanimura DEVMETHOD(device_probe, sbc_probe), 78453553Stanimura DEVMETHOD(device_attach, sbc_attach), 78565644Scg DEVMETHOD(device_detach, sbc_detach), 78653553Stanimura DEVMETHOD(device_shutdown, bus_generic_shutdown), 78753553Stanimura DEVMETHOD(device_suspend, bus_generic_suspend), 78853553Stanimura DEVMETHOD(device_resume, bus_generic_resume), 78953553Stanimura 79053553Stanimura /* Bus interface */ 79154462Scg DEVMETHOD(bus_read_ivar, sbc_read_ivar), 79254462Scg DEVMETHOD(bus_write_ivar, sbc_write_ivar), 79353553Stanimura DEVMETHOD(bus_alloc_resource, sbc_alloc_resource), 79453553Stanimura DEVMETHOD(bus_release_resource, sbc_release_resource), 79553553Stanimura DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 79653553Stanimura DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 79754824Scg DEVMETHOD(bus_setup_intr, sbc_setup_intr), 79854824Scg DEVMETHOD(bus_teardown_intr, sbc_teardown_intr), 79953553Stanimura 800227843Smarius DEVMETHOD_END 80153553Stanimura}; 80253553Stanimura 80353553Stanimurastatic driver_t sbc_driver = { 80453553Stanimura "sbc", 80553553Stanimura sbc_methods, 80653553Stanimura sizeof(struct sbc_softc), 80753553Stanimura}; 80853553Stanimura 80954462Scg/* sbc can be attached to an isa bus. */ 81062483ScgDRIVER_MODULE(snd_sbc, isa, sbc_driver, sbc_devclass, 0, 0); 811136392SnjlDRIVER_MODULE(snd_sbc, acpi, sbc_driver, sbc_devclass, 0, 0); 812132236StanimuraMODULE_DEPEND(snd_sbc, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 81362483ScgMODULE_VERSION(snd_sbc, 1); 814