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