sb16.c revision 65340
129415Sjmg/*
250723Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
339899Sluigi * Copyright 1997,1998 Luigi Rizzo.
429415Sjmg *
529415Sjmg * Derived from files in the Voxware 3.5 distribution,
629415Sjmg * Copyright by Hannu Savolainen 1994, under the same copyright
729415Sjmg * conditions.
850723Scg * All rights reserved.
950723Scg *
1029415Sjmg * Redistribution and use in source and binary forms, with or without
1129415Sjmg * modification, are permitted provided that the following conditions
1230869Sjmg * are met:
1330869Sjmg * 1. Redistributions of source code must retain the above copyright
1430869Sjmg *    notice, this list of conditions and the following disclaimer.
1530869Sjmg * 2. Redistributions in binary form must reproduce the above copyright
1650723Scg *    notice, this list of conditions and the following disclaimer in the
1750723Scg *    documentation and/or other materials provided with the distribution.
1830869Sjmg *
1950723Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2050723Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2150723Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2250723Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2350723Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2450723Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2550723Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2650723Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2750723Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2850723Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2950723Scg * SUCH DAMAGE.
3050723Scg *
3150959Speter * $FreeBSD: head/sys/dev/sound/isa/sb16.c 65340 2000-09-01 20:09:24Z cg $
3229415Sjmg */
3329415Sjmg
3453465Scg#include <dev/sound/pcm/sound.h>
3529415Sjmg
3629415Sjmg#define __SB_MIXER_C__	/* XXX warning... */
3753465Scg#include  <dev/sound/isa/sb.h>
3853553Stanimura#include  <dev/sound/chip.h>
3929415Sjmg
4055706Scg#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16)
4155254Scg
4250723Scg/* channel interface */
4350723Scgstatic void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
4450723Scgstatic int sbchan_setdir(void *data, int dir);
4550723Scgstatic int sbchan_setformat(void *data, u_int32_t format);
4650723Scgstatic int sbchan_setspeed(void *data, u_int32_t speed);
4750723Scgstatic int sbchan_setblocksize(void *data, u_int32_t blocksize);
4850723Scgstatic int sbchan_trigger(void *data, int go);
4950723Scgstatic int sbchan_getptr(void *data);
5050723Scgstatic pcmchan_caps *sbchan_getcaps(void *data);
5129415Sjmg
5264881Scgstatic u_int32_t sb_playfmt[] = {
5350723Scg	AFMT_U8,
5464881Scg	0
5550723Scg};
5664881Scgstatic pcmchan_caps sb_playcaps = {4000, 22050, sb_playfmt, 0};
5729415Sjmg
5864881Scgstatic u_int32_t sb_recfmt[] = {
5950723Scg	AFMT_U8,
6064881Scg	0
6150723Scg};
6264881Scgstatic pcmchan_caps sb_reccaps = {4000, 13000, sb_recfmt, 0};
6329415Sjmg
6464881Scgstatic u_int32_t sbpro_playfmt[] = {
6564881Scg	AFMT_U8,
6650723Scg	AFMT_STEREO | AFMT_U8,
6764881Scg	0
6850723Scg};
6964881Scgstatic pcmchan_caps sbpro_playcaps = {4000, 45000, sbpro_playfmt, 0};
7029415Sjmg
7164881Scgstatic u_int32_t sbpro_recfmt[] = {
7264881Scg	AFMT_U8,
7350723Scg	AFMT_STEREO | AFMT_U8,
7464881Scg	0
7550723Scg};
7664881Scgstatic pcmchan_caps sbpro_reccaps = {4000, 15000, sbpro_recfmt, 0};
7729415Sjmg
7864881Scgstatic u_int32_t sb16_hfmt[] = {
7964881Scg	AFMT_S16_LE,
8050723Scg	AFMT_STEREO | AFMT_S16_LE,
8164881Scg	0
8250723Scg};
8364881Scgstatic pcmchan_caps sb16_hcaps = {5000, 45000, sb16_hfmt, 0};
8429415Sjmg
8564881Scgstatic u_int32_t sb16_lfmt[] = {
8664881Scg	AFMT_U8,
8750723Scg	AFMT_STEREO | AFMT_U8,
8864881Scg	0
8950723Scg};
9064881Scgstatic pcmchan_caps sb16_lcaps = {5000, 45000, sb16_lfmt, 0};
9129415Sjmg
9264881Scgstatic u_int32_t sb16x_fmt[] = {
9364881Scg	AFMT_U8,
9464881Scg	AFMT_STEREO | AFMT_U8,
9564881Scg	AFMT_S16_LE,
9664881Scg	AFMT_STEREO | AFMT_S16_LE,
9764881Scg	0
9854462Scg};
9964881Scgstatic pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0};
10054462Scg
10150723Scgstatic pcm_channel sb_chantemplate = {
10250723Scg	sbchan_init,
10350723Scg	sbchan_setdir,
10450723Scg	sbchan_setformat,
10550723Scg	sbchan_setspeed,
10650723Scg	sbchan_setblocksize,
10750723Scg	sbchan_trigger,
10850723Scg	sbchan_getptr,
10950723Scg	sbchan_getcaps,
11065340Scg	NULL, 			/* free */
11165340Scg	NULL, 			/* nop1 */
11265340Scg	NULL, 			/* nop2 */
11365340Scg	NULL, 			/* nop3 */
11465340Scg	NULL, 			/* nop4 */
11565340Scg	NULL, 			/* nop5 */
11665340Scg	NULL, 			/* nop6 */
11765340Scg	NULL, 			/* nop7 */
11850723Scg};
11929415Sjmg
12050723Scgstruct sb_info;
12129415Sjmg
12250723Scgstruct sb_chinfo {
12350723Scg	struct sb_info *parent;
12450723Scg	pcm_channel *channel;
12550723Scg	snd_dbuf *buffer;
12650723Scg	int dir;
12755706Scg	u_int32_t fmt, spd;
12850723Scg};
12929415Sjmg
13050723Scgstruct sb_info {
13150723Scg    	struct resource *io_base;	/* I/O address for the board */
13250723Scg    	struct resource *irq;
13354462Scg   	struct resource *drq1;
13454462Scg    	struct resource *drq2;
13555706Scg    	bus_dma_tag_t parent_dmat;
13629415Sjmg
13750723Scg    	int bd_id;
13850723Scg    	u_long bd_flags;       /* board-specific flags */
13950723Scg    	struct sb_chinfo pch, rch;
14050723Scg};
14150723Scg
14250723Scgstatic int sb_rd(struct sb_info *sb, int reg);
14350723Scgstatic void sb_wr(struct sb_info *sb, int reg, u_int8_t val);
14450723Scgstatic int sb_dspready(struct sb_info *sb);
14550723Scgstatic int sb_cmd(struct sb_info *sb, u_char val);
14650723Scgstatic int sb_cmd1(struct sb_info *sb, u_char cmd, int val);
14750723Scgstatic int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
14850723Scgstatic u_int sb_get_byte(struct sb_info *sb);
14950723Scgstatic void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
15050723Scgstatic int sb_getmixer(struct sb_info *sb, u_int port);
15154462Scgstatic int sb_reset_dsp(struct sb_info *sb);
15229415Sjmg
15350723Scgstatic void sb_intr(void *arg);
15455706Scgstatic int sb_speed(struct sb_chinfo *ch);
15550723Scgstatic int sb_start(struct sb_chinfo *ch);
15650723Scgstatic int sb_stop(struct sb_chinfo *ch);
15750723Scg
15850723Scgstatic int sbmix_init(snd_mixer *m);
15950723Scgstatic int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
16050723Scgstatic int sbmix_setrecsrc(snd_mixer *m, u_int32_t src);
16150723Scg
16250723Scgstatic snd_mixer sb_mixer = {
16365340Scg    	"SoundBlaster mixer",
16465340Scg    	sbmix_init,
16565340Scg	NULL,
16665340Scg    	sbmix_set,
16765340Scg    	sbmix_setrecsrc,
16850723Scg};
16950723Scg
17050723Scgstatic devclass_t pcm_devclass;
17150723Scg
17229415Sjmg/*
17350723Scg * Common code for the midi and pcm functions
17429415Sjmg *
17550723Scg * sb_cmd write a single byte to the CMD port.
17650723Scg * sb_cmd1 write a CMD + 1 byte arg
17750723Scg * sb_cmd2 write a CMD + 2 byte arg
17850723Scg * sb_get_byte returns a single byte from the DSP data port
17950723Scg *
18050723Scg * ess_write is actually sb_cmd1
18150723Scg * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte
18229415Sjmg */
18329415Sjmg
18429415Sjmgstatic int
18550723Scgport_rd(struct resource *port, int off)
18629415Sjmg{
18750723Scg	return bus_space_read_1(rman_get_bustag(port),
18850723Scg				rman_get_bushandle(port),
18950723Scg				off);
19050723Scg}
19129415Sjmg
19250723Scgstatic void
19350723Scgport_wr(struct resource *port, int off, u_int8_t data)
19450723Scg{
19550723Scg	return bus_space_write_1(rman_get_bustag(port),
19650723Scg				 rman_get_bushandle(port),
19750723Scg				 off, data);
19829415Sjmg}
19929415Sjmg
20029415Sjmgstatic int
20150723Scgsb_rd(struct sb_info *sb, int reg)
20229415Sjmg{
20350723Scg	return port_rd(sb->io_base, reg);
20450723Scg}
20529415Sjmg
20650723Scgstatic void
20750723Scgsb_wr(struct sb_info *sb, int reg, u_int8_t val)
20850723Scg{
20950723Scg	port_wr(sb->io_base, reg, val);
21029415Sjmg}
21129415Sjmg
21250723Scgstatic int
21350723Scgsb_dspready(struct sb_info *sb)
21450723Scg{
21550723Scg	return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0);
21650723Scg}
21729415Sjmg
21829415Sjmgstatic int
21950723Scgsb_dspwr(struct sb_info *sb, u_char val)
22029415Sjmg{
22150723Scg    	int  i;
22229415Sjmg
22350723Scg    	for (i = 0; i < 1000; i++) {
22450723Scg		if (sb_dspready(sb)) {
22550723Scg	    		sb_wr(sb, SBDSP_CMD, val);
22650723Scg	    		return 1;
22750723Scg		}
22850723Scg		if (i > 10) DELAY((i > 100)? 1000 : 10);
22950723Scg    	}
23050723Scg    	printf("sb_dspwr(0x%02x) timed out.\n", val);
23150723Scg    	return 0;
23250723Scg}
23329415Sjmg
23450723Scgstatic int
23550723Scgsb_cmd(struct sb_info *sb, u_char val)
23650723Scg{
23750723Scg#if 0
23850723Scg	printf("sb_cmd: %x\n", val);
23950723Scg#endif
24050723Scg    	return sb_dspwr(sb, val);
24150723Scg}
24229415Sjmg
24350723Scgstatic int
24450723Scgsb_cmd1(struct sb_info *sb, u_char cmd, int val)
24550723Scg{
24650723Scg#if 0
24750723Scg    	printf("sb_cmd1: %x, %x\n", cmd, val);
24850723Scg#endif
24950723Scg    	if (sb_dspwr(sb, cmd)) {
25050723Scg		return sb_dspwr(sb, val & 0xff);
25150723Scg    	} else return 0;
25250723Scg}
25329415Sjmg
25450723Scgstatic int
25550723Scgsb_cmd2(struct sb_info *sb, u_char cmd, int val)
25650723Scg{
25750723Scg#if 0
25850723Scg    	printf("sb_cmd2: %x, %x\n", cmd, val);
25950723Scg#endif
26050723Scg    	if (sb_dspwr(sb, cmd)) {
26150723Scg		return sb_dspwr(sb, val & 0xff) &&
26250723Scg		       sb_dspwr(sb, (val >> 8) & 0xff);
26350723Scg    	} else return 0;
26450723Scg}
26529415Sjmg
26650723Scg/*
26750723Scg * in the SB, there is a set of indirect "mixer" registers with
26850723Scg * address at offset 4, data at offset 5
26950723Scg */
27050723Scgstatic void
27150723Scgsb_setmixer(struct sb_info *sb, u_int port, u_int value)
27250723Scg{
27350723Scg    	u_long   flags;
27429415Sjmg
27550723Scg    	flags = spltty();
27650723Scg    	sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
27750723Scg    	DELAY(10);
27850723Scg    	sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff));
27950723Scg    	DELAY(10);
28050723Scg    	splx(flags);
28150723Scg}
28231361Sjmg
28350723Scgstatic int
28450723Scgsb_getmixer(struct sb_info *sb, u_int port)
28550723Scg{
28650723Scg    	int val;
28750723Scg    	u_long flags;
28829415Sjmg
28950723Scg    	flags = spltty();
29050723Scg    	sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
29150723Scg    	DELAY(10);
29250723Scg    	val = sb_rd(sb, SB_MIX_DATA);
29350723Scg    	DELAY(10);
29450723Scg    	splx(flags);
29529415Sjmg
29650723Scg    	return val;
29750723Scg}
29829415Sjmg
29950723Scgstatic u_int
30050723Scgsb_get_byte(struct sb_info *sb)
30150723Scg{
30250723Scg    	int i;
30329415Sjmg
30450723Scg    	for (i = 1000; i > 0; i--) {
30550723Scg		if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80)
30650723Scg			return sb_rd(sb, DSP_READ);
30750723Scg		else
30850723Scg			DELAY(20);
30950723Scg    	}
31050723Scg    	return 0xffff;
31150723Scg}
31229415Sjmg
31350723Scgstatic int
31450723Scgsb_reset_dsp(struct sb_info *sb)
31529415Sjmg{
31650723Scg    	sb_wr(sb, SBDSP_RST, 3);
31750723Scg    	DELAY(100);
31850723Scg    	sb_wr(sb, SBDSP_RST, 0);
31950723Scg    	if (sb_get_byte(sb) != 0xAA) {
32050723Scg        	DEB(printf("sb_reset_dsp 0x%lx failed\n",
32150723Scg			   rman_get_start(d->io_base)));
32250723Scg		return ENXIO;	/* Sorry */
32350723Scg    	}
32455706Scg    	if (sb->bd_flags & BD_F_ESS)
32555706Scg		sb_cmd(sb, 0xc6);
32650723Scg    	return 0;
32729415Sjmg}
32829415Sjmg
32929415Sjmgstatic void
33050723Scgsb_release_resources(struct sb_info *sb, device_t dev)
33129415Sjmg{
33250723Scg    	/* should we bus_teardown_intr here? */
33350723Scg    	if (sb->irq) {
33454462Scg		bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
33550723Scg		sb->irq = 0;
33650723Scg    	}
33750723Scg    	if (sb->drq1) {
33854462Scg		bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1);
33950723Scg		sb->drq1 = 0;
34050723Scg    	}
34150723Scg    	if (sb->drq2) {
34254462Scg		bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2);
34350723Scg		sb->drq2 = 0;
34450723Scg    	}
34550723Scg    	if (sb->io_base) {
34654462Scg		bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
34750723Scg		sb->io_base = 0;
34850723Scg    	}
34950723Scg    	free(sb, M_DEVBUF);
35050723Scg}
35129415Sjmg
35250723Scgstatic int
35350723Scgsb_alloc_resources(struct sb_info *sb, device_t dev)
35450723Scg{
35554462Scg	int rid;
35654462Scg
35754462Scg	rid = 0;
35850723Scg	if (!sb->io_base)
35950723Scg    		sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
36054462Scg						 &rid, 0, ~0, 1,
36150723Scg						 RF_ACTIVE);
36254462Scg	rid = 0;
36350723Scg	if (!sb->irq)
36450723Scg    		sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ,
36554462Scg					     &rid, 0, ~0, 1,
36650723Scg					     RF_ACTIVE);
36754462Scg	rid = 0;
36850723Scg	if (!sb->drq1)
36950723Scg    		sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ,
37054462Scg					      &rid, 0, ~0, 1,
37150723Scg					      RF_ACTIVE);
37254462Scg	rid = 1;
37358756Scg	if (!sb->drq2)
37450723Scg        	sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ,
37554462Scg					      &rid, 0, ~0, 1,
37650723Scg					      RF_ACTIVE);
37729415Sjmg
37850723Scg    	if (sb->io_base && sb->drq1 && sb->irq) {
37958756Scg		int bs = DSP_BUFFSIZE;
38055424Scg
38154462Scg		isa_dma_acquire(rman_get_start(sb->drq1));
38255424Scg		isa_dmainit(rman_get_start(sb->drq1), bs);
38329415Sjmg
38450723Scg		if (sb->drq2) {
38554462Scg			isa_dma_acquire(rman_get_start(sb->drq2));
38655424Scg			isa_dmainit(rman_get_start(sb->drq2), bs);
38754462Scg		}
38850723Scg
38950723Scg		return 0;
39050723Scg	} else return ENXIO;
39129415Sjmg}
39229415Sjmg
39354462Scgstatic void
39454462Scgsb16_swap(void *v, int dir)
39529415Sjmg{
39654462Scg	struct sb_info *sb = v;
39754462Scg	int pb = sb->pch.buffer->dl;
39854462Scg	int rb = sb->rch.buffer->dl;
39954462Scg	int pc = sb->pch.buffer->chan;
40054462Scg	int rc = sb->rch.buffer->chan;
40154462Scg	int swp = 0;
40229415Sjmg
40354462Scg	if (!pb && !rb) {
40455706Scg		if (dir == PCMDIR_PLAY && pc < 4)
40555706Scg			swp = 1;
40655706Scg		else
40755706Scg			if (dir == PCMDIR_REC && rc < 4)
40855706Scg				swp = 1;
40958756Scg	if (swp) {
41054462Scg			int t;
41129415Sjmg
41254462Scg			t = sb->pch.buffer->chan;
41354462Scg			sb->pch.buffer->chan = sb->rch.buffer->chan;
41454462Scg			sb->rch.buffer->chan = t;
41557973Sphk			sb->pch.buffer->dir = ISADMA_WRITE;
41657973Sphk			sb->rch.buffer->dir = ISADMA_READ;
41731361Sjmg		}
41854462Scg	}
41950723Scg}
42041653Sbrian
42150723Scgstatic int
42250723Scgsb_doattach(device_t dev, struct sb_info *sb)
42329415Sjmg{
42450723Scg    	void *ih;
42550723Scg    	char status[SND_STATUSLEN];
42658756Scg	int bs = DSP_BUFFSIZE;
42729415Sjmg
42855706Scg    	if (sb_alloc_resources(sb, dev))
42955706Scg		goto no;
43055706Scg    	if (sb_reset_dsp(sb))
43155706Scg		goto no;
43265340Scg    	mixer_init(dev, &sb_mixer, sb);
43329415Sjmg
43458756Scg	bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih);
43554462Scg    	if ((sb->bd_flags & BD_F_SB16) && !(sb->bd_flags & BD_F_SB16X))
43654462Scg		pcm_setswap(dev, sb16_swap);
43754462Scg    	if (!sb->drq2)
43854462Scg		pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
43929415Sjmg
44050723Scg    	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
44150723Scg			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
44250723Scg			/*highaddr*/BUS_SPACE_MAXADDR,
44350723Scg			/*filter*/NULL, /*filterarg*/NULL,
44455428Scg			/*maxsize*/bs, /*nsegments*/1,
44550723Scg			/*maxsegz*/0x3ffff,
44650723Scg			/*flags*/0, &sb->parent_dmat) != 0) {
44750723Scg		device_printf(dev, "unable to create dma tag\n");
44850723Scg		goto no;
44950723Scg    	}
45029415Sjmg
45154462Scg    	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld",
45250723Scg    	     	rman_get_start(sb->io_base), rman_get_start(sb->irq),
45354462Scg		rman_get_start(sb->drq1));
45455706Scg    	if (sb->drq2)
45555706Scg		snprintf(status + strlen(status), SND_STATUSLEN - strlen(status),
45655706Scg			":%ld", rman_get_start(sb->drq2));
45729415Sjmg
45855706Scg    	if (pcm_register(dev, sb, 1, 1))
45955706Scg		goto no;
46058756Scg	pcm_addchan(dev, PCMDIR_REC, &sb_chantemplate, sb);
46158756Scg	pcm_addchan(dev, PCMDIR_PLAY, &sb_chantemplate, sb);
46250723Scg    	pcm_setstatus(dev, status);
46329415Sjmg
46450723Scg    	return 0;
46529415Sjmg
46650723Scgno:
46750723Scg    	sb_release_resources(sb, dev);
46850723Scg    	return ENXIO;
46950723Scg}
47029415Sjmg
47150723Scgstatic void
47250723Scgsb_intr(void *arg)
47350723Scg{
47450723Scg    	struct sb_info *sb = (struct sb_info *)arg;
47550723Scg    	int reason = 3, c;
47629415Sjmg
47750723Scg    	/*
47850723Scg     	* SB < 4.0 is half duplex and has only 1 bit for int source,
47950723Scg     	* so we fake it. SB 4.x (SB16) has the int source in a separate
48050723Scg     	* register.
48150723Scg     	* The Vibra16X has separate flags for 8 and 16 bit transfers, but
48250723Scg     	* I have no idea how to tell capture from playback interrupts...
48350723Scg     	*/
48450723Scg    	if (sb->bd_flags & BD_F_SB16) {
48550723Scg    		c = sb_getmixer(sb, IRQ_STAT);
48650723Scg    		/* this tells us if the source is 8-bit or 16-bit dma. We
48750723Scg     		* have to check the io channel to map it to read or write...
48850723Scg     		*/
48950723Scg    		reason = 0;
49050723Scg    		if (c & 1) { /* 8-bit dma */
49155706Scg			if (sb->pch.fmt & AFMT_U8)
49255706Scg				reason |= 1;
49355706Scg			if (sb->rch.fmt & AFMT_U8)
49455706Scg				reason |= 2;
49550723Scg    		}
49650723Scg    		if (c & 2) { /* 16-bit dma */
49755706Scg			if (sb->pch.fmt & AFMT_S16_LE)
49855706Scg				reason |= 1;
49955706Scg			if (sb->rch.fmt & AFMT_S16_LE)
50055706Scg				reason |= 2;
50150723Scg    		}
50250723Scg    	} else c = 1;
50350723Scg#if 0
50450723Scg    	printf("sb_intr: reason=%d c=0x%x\n", reason, c);
50550723Scg#endif
50650723Scg    	if ((reason & 1) && (sb->pch.buffer->dl > 0))
50750723Scg		chn_intr(sb->pch.channel);
50850723Scg    	if ((reason & 2) && (sb->rch.buffer->dl > 0))
50950723Scg		chn_intr(sb->rch.channel);
51055706Scg    	if (c & 1)
51155706Scg		sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */
51255706Scg    	if (c & 2)
51355706Scg		sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */
51450723Scg}
51531361Sjmg
51650723Scgstatic int
51755706Scgsb_speed(struct sb_chinfo *ch)
51850723Scg{
51950723Scg    	struct sb_info *sb = ch->parent;
52050723Scg    	int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
52150723Scg    	int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
52255706Scg	int speed = ch->spd;
52329415Sjmg
52450723Scg    	if (sb->bd_flags & BD_F_SB16) {
52550723Scg		RANGE(speed, 5000, 45000);
52650723Scg		sb_cmd(sb, 0x42 - play);
52750723Scg    		sb_cmd(sb, speed >> 8);
52850723Scg		sb_cmd(sb, speed & 0xff);
52950723Scg    	} else {
53050723Scg		u_char tconst;
53150723Scg		int max_speed = 45000, tmp;
53250723Scg        	u_long flags;
53329415Sjmg
53450723Scg    		/* here enforce speed limitations - max 22050 on sb 1.x*/
53555706Scg    		if (sb->bd_id <= 0x200)
53655706Scg			max_speed = 22050;
53729415Sjmg
53850723Scg    		/*
53950723Scg     	 	* SB models earlier than SB Pro have low limit for the
54050723Scg     	 	* input rate. Note that this is only for input, but since
54150723Scg     	 	* we do not support separate values for rec & play....
54250723Scg     	 	*/
54350723Scg		if (!play) {
54455706Scg    			if (sb->bd_id <= 0x200)
54555706Scg				max_speed = 13000;
54655706Scg    			else
54755706Scg				if (sb->bd_id < 0x300)
54855706Scg					max_speed = 15000;
54950723Scg		}
55050723Scg    		RANGE(speed, 4000, max_speed);
55155706Scg    		if (stereo)
55255706Scg			speed <<= 1;
55329415Sjmg
55450723Scg    		/*
55550723Scg     	 	* Now the speed should be valid. Compute the value to be
55650723Scg     	 	* programmed into the board.
55750723Scg     	 	*/
55850723Scg    		if (speed > 22050) { /* High speed mode on 2.01/3.xx */
55950723Scg			tconst = (u_char)
56050723Scg				((65536 - ((256000000 + speed / 2) / speed))
56150723Scg				>> 8);
56250723Scg			sb->bd_flags |= BD_F_HISPEED;
56350723Scg			tmp = 65536 - (tconst << 8);
56450723Scg			speed = (256000000 + tmp / 2) / tmp;
56550723Scg    		} else {
56650723Scg			sb->bd_flags &= ~BD_F_HISPEED;
56750723Scg			tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
56850723Scg			tmp = 256 - tconst;
56950723Scg			speed = (1000000 + tmp / 2) / tmp;
57050723Scg    		}
57150723Scg		flags = spltty();
57250723Scg		sb_cmd1(sb, 0x40, tconst); /* set time constant */
57350723Scg		splx(flags);
57455706Scg    		if (stereo)
57555706Scg			speed >>= 1;
57650723Scg    	}
57755706Scg	ch->spd = speed;
57850723Scg    	return speed;
57929415Sjmg}
58029415Sjmg
58150723Scgstatic int
58250723Scgsb_start(struct sb_chinfo *ch)
58350723Scg{
58450723Scg	struct sb_info *sb = ch->parent;
58550723Scg    	int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
58650723Scg    	int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0;
58750723Scg    	int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
58850723Scg	int l = ch->buffer->dl;
58954462Scg	int dh = ch->buffer->chan > 3;
59054791Scg	u_char i1, i2;
59129415Sjmg
59255706Scg	if (b16 || dh)
59355706Scg		l >>= 1;
59450723Scg	l--;
59555706Scg
59655706Scg	if (play)
59755706Scg		sb_cmd(sb, DSP_CMD_SPKON);
59855706Scg
59950723Scg	if (sb->bd_flags & BD_F_SB16) {
60055706Scg	    	i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON;
60155706Scg	        i1 |= play? DSP_F16_DAC : DSP_F16_ADC;
60255706Scg	    	i1 |= (b16 || dh)? DSP_DMA16 : DSP_DMA8;
60355706Scg	    	i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0);
60455706Scg	    	sb_cmd(sb, i1);
60555706Scg	    	sb_cmd2(sb, i2, l);
60650723Scg	} else {
60755706Scg	    	if (sb->bd_flags & BD_F_HISPEED)
60855706Scg			i1 = play? 0x90 : 0x98;
60955706Scg	    	else
61055706Scg			i1 = play? 0x1c : 0x2c;
61155706Scg	    	sb_setmixer(sb, 0x0e, stereo? 2 : 0);
61255706Scg	    	sb_cmd2(sb, 0x48, l);
61355706Scg       	    	sb_cmd(sb, i1);
61450723Scg	}
61550723Scg	sb->bd_flags |= BD_F_DMARUN << b16;
61650723Scg	return 0;
61750723Scg}
61850723Scg
61950723Scgstatic int
62050723Scgsb_stop(struct sb_chinfo *ch)
62129415Sjmg{
62250723Scg	struct sb_info *sb = ch->parent;
62350723Scg    	int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
62450723Scg    	int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0;
62529415Sjmg
62655706Scg    	if (sb->bd_flags & BD_F_HISPEED)
62755706Scg		sb_reset_dsp(sb);
62850723Scg	else {
62950723Scg		sb_cmd(sb, b16? DSP_CMD_DMAPAUSE_16 : DSP_CMD_DMAPAUSE_8);
63050723Scg	       /*
63150723Scg		* The above seems to have the undocumented side effect of
63250723Scg		* blocking the other side as well. If the other
63350723Scg		* channel was active (SB16) I have to re-enable it :(
63450723Scg		*/
63550723Scg		if (sb->bd_flags & (BD_F_DMARUN << (1 - b16)))
63650723Scg			sb_cmd(sb, b16? 0xd4 : 0xd6 );
63729415Sjmg	}
63855706Scg	if (play)
63955706Scg		sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */
64050723Scg	sb->bd_flags &= ~(BD_F_DMARUN << b16);
64150723Scg	return 0;
64250723Scg}
64329415Sjmg
64455706Scg/* channel interface */
64555706Scgstatic void *
64655706Scgsbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
64755706Scg{
64855706Scg	struct sb_info *sb = devinfo;
64955706Scg	struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch;
65055706Scg	int dch, dl, dh;
65155706Scg
65255706Scg	ch->parent = sb;
65355706Scg	ch->channel = c;
65455706Scg	ch->buffer = b;
65555706Scg	ch->buffer->bufsize = DSP_BUFFSIZE;
65655706Scg	if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1)
65755706Scg		return NULL;
65855706Scg	dch = (dir == PCMDIR_PLAY)? 1 : 0;
65955706Scg	if (sb->bd_flags & BD_F_SB16X)
66055706Scg		dch = !dch;
66155706Scg	dl = rman_get_start(sb->drq1);
66255706Scg	dh = sb->drq2? rman_get_start(sb->drq2) : dl;
66355706Scg	ch->buffer->chan = dch? dh : dl;
66455706Scg	return ch;
66555706Scg}
66655706Scg
66755706Scgstatic int
66855706Scgsbchan_setdir(void *data, int dir)
66955706Scg{
67055706Scg	struct sb_chinfo *ch = data;
67155706Scg
67255706Scg	ch->dir = dir;
67355706Scg	return 0;
67455706Scg}
67555706Scg
67655706Scgstatic int
67755706Scgsbchan_setformat(void *data, u_int32_t format)
67855706Scg{
67955706Scg	struct sb_chinfo *ch = data;
68055706Scg
68155706Scg	ch->fmt = format;
68255706Scg	return 0;
68355706Scg}
68455706Scg
68555706Scgstatic int
68655706Scgsbchan_setspeed(void *data, u_int32_t speed)
68755706Scg{
68855706Scg	struct sb_chinfo *ch = data;
68955706Scg
69055706Scg	ch->spd = speed;
69155706Scg	return sb_speed(ch);
69255706Scg}
69355706Scg
69455706Scgstatic int
69555706Scgsbchan_setblocksize(void *data, u_int32_t blocksize)
69655706Scg{
69755706Scg	return blocksize;
69855706Scg}
69955706Scg
70055706Scgstatic int
70155706Scgsbchan_trigger(void *data, int go)
70255706Scg{
70355706Scg	struct sb_chinfo *ch = data;
70455706Scg
70560958Scg	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
70655706Scg		return 0;
70755706Scg
70855706Scg	buf_isadma(ch->buffer, go);
70955706Scg	if (go == PCMTRIG_START)
71055706Scg		sb_start(ch);
71155706Scg	else
71255706Scg		sb_stop(ch);
71355706Scg	return 0;
71455706Scg}
71555706Scg
71655706Scgstatic int
71755706Scgsbchan_getptr(void *data)
71855706Scg{
71955706Scg	struct sb_chinfo *ch = data;
72055706Scg
72155706Scg	return buf_isadmaptr(ch->buffer);
72255706Scg}
72355706Scg
72455706Scgstatic pcmchan_caps *
72555706Scgsbchan_getcaps(void *data)
72655706Scg{
72755706Scg	struct sb_chinfo *ch = data;
72855706Scg	int p = (ch->dir == PCMDIR_PLAY)? 1 : 0;
72955706Scg
73055706Scg	if (ch->parent->bd_id < 0x300)
73155706Scg		return p? &sb_playcaps : &sb_reccaps;
73255706Scg	else if (ch->parent->bd_id < 0x400)
73355706Scg		return p? &sbpro_playcaps : &sbpro_reccaps;
73455706Scg	else if (ch->parent->bd_flags & BD_F_SB16X)
73555706Scg		return &sb16x_caps;
73655706Scg	else
73755706Scg		return (ch->buffer->chan >= 4)? &sb16_hcaps : &sb16_lcaps;
73855706Scg}
73955706Scg
74050723Scg/************************************************************/
74139899Sluigi
74229415Sjmgstatic int
74350723Scgsbmix_init(snd_mixer *m)
74429415Sjmg{
74550723Scg    	struct sb_info *sb = mix_getdevinfo(m);
74629415Sjmg
74750723Scg    	switch (sb->bd_flags & BD_F_MIX_MASK) {
74850723Scg    	case BD_F_MIX_CT1345: /* SB 3.0 has 1345 mixer */
74950723Scg		mix_setdevs(m, SBPRO_MIXER_DEVICES);
75050723Scg		mix_setrecdevs(m, SBPRO_RECORDING_DEVICES);
75150723Scg		sb_setmixer(sb, 0, 1); /* reset mixer */
75258756Scg		sb_setmixer(sb, MIC_VOL, 0x6); /* mic volume max */
75350723Scg		sb_setmixer(sb, RECORD_SRC, 0x0); /* mic source */
75450723Scg		sb_setmixer(sb, FM_VOL, 0x0); /* no midi */
75550723Scg		break;
75631361Sjmg
75750723Scg    	case BD_F_MIX_CT1745: /* SB16 mixer ... */
75850723Scg		mix_setdevs(m, SB16_MIXER_DEVICES);
75950723Scg		mix_setrecdevs(m, SB16_RECORDING_DEVICES);
76050723Scg		sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */
76150723Scg		sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */
76250723Scg		sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */
76350723Scg    	}
76450723Scg    	return 0;
76550723Scg}
76631361Sjmg
76750723Scgstatic int
76850723Scgsbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
76950723Scg{
77050723Scg    	struct sb_info *sb = mix_getdevinfo(m);
77150723Scg    	int regoffs;
77250723Scg    	u_char   val;
77350723Scg    	mixer_tab *iomap;
77429415Sjmg
77550723Scg    	switch (sb->bd_flags & BD_F_MIX_MASK) {
77650723Scg    	case BD_F_MIX_CT1345:
77758756Scg		iomap = &sbpro_mix;
77850723Scg		break;
77929415Sjmg
78050723Scg    	case BD_F_MIX_CT1745:
78150723Scg		iomap = &sb16_mix;
78250723Scg		break;
78329415Sjmg
78450723Scg    	default:
78550723Scg        	return -1;
78650723Scg    	}
78753126Sdfr
78853126Sdfr	/* Change left channel */
78950723Scg    	regoffs = (*iomap)[dev][LEFT_CHN].regno;
79053126Sdfr    	if (regoffs != 0) {
79153126Sdfr		val = sb_getmixer(sb, regoffs);
79253126Sdfr		change_bits(iomap, &val, dev, LEFT_CHN, left);
79353126Sdfr		sb_setmixer(sb, regoffs, val);
79453126Sdfr	}
79553126Sdfr
79653126Sdfr	/* Change right channel */
79753126Sdfr	regoffs = (*iomap)[dev][RIGHT_CHN].regno;
79853126Sdfr	if (regoffs != 0) {
79953126Sdfr		val = sb_getmixer(sb, regoffs); /* Read the new one */
80053126Sdfr		change_bits(iomap, &val, dev, RIGHT_CHN, right);
80153126Sdfr		sb_setmixer(sb, regoffs, val);
80253126Sdfr	} else
80353126Sdfr		right = left;
80453126Sdfr
80550723Scg    	return left | (right << 8);
80629415Sjmg}
80729415Sjmg
80850723Scgstatic int
80950723Scgsbmix_setrecsrc(snd_mixer *m, u_int32_t src)
81029415Sjmg{
81150723Scg    	struct sb_info *sb = mix_getdevinfo(m);
81250723Scg    	u_char recdev;
81329415Sjmg
81450723Scg    	switch (sb->bd_flags & BD_F_MIX_MASK) {
81550723Scg    	case BD_F_MIX_CT1345:
81655706Scg		if      (src == SOUND_MASK_LINE)
81755706Scg			recdev = 0x06;
81855706Scg		else if (src == SOUND_MASK_CD)
81955706Scg			recdev = 0x02;
82050723Scg		else { /* default: mic */
82150723Scg	    		src = SOUND_MASK_MIC;
82250723Scg	    		recdev = 0;
82350723Scg		}
82450723Scg		sb_setmixer(sb, RECORD_SRC, recdev |
82550723Scg			    (sb_getmixer(sb, RECORD_SRC) & ~0x07));
82650723Scg		break;
82729415Sjmg
82850723Scg    	case BD_F_MIX_CT1745: /* sb16 */
82950723Scg		recdev = 0;
83055706Scg		if (src & SOUND_MASK_MIC)
83155706Scg			recdev |= 0x01; /* mono mic */
83255706Scg		if (src & SOUND_MASK_CD)
83355706Scg			recdev |= 0x06; /* l+r cd */
83455706Scg		if (src & SOUND_MASK_LINE)
83555706Scg			recdev |= 0x18; /* l+r line */
83655706Scg		if (src & SOUND_MASK_SYNTH)
83755706Scg			recdev |= 0x60; /* l+r midi */
83850723Scg		sb_setmixer(sb, SB16_IMASK_L, recdev);
83950723Scg		sb_setmixer(sb, SB16_IMASK_R, recdev);
84050723Scg		/*
84150723Scg	 	* since the same volume controls apply to the input and
84250723Scg	 	* output sections, the best approach to have a consistent
84350723Scg	 	* behaviour among cards would be to disable the output path
84450723Scg	 	* on devices which are used to record.
84550723Scg	 	* However, since users like to have feedback, we only disable
84650723Scg	 	* the mic -- permanently.
84750723Scg	 	*/
84850723Scg        	sb_setmixer(sb, SB16_OMASK, 0x1f & ~1);
84950723Scg		break;
85051768Scg       	}
85150723Scg    	return src;
85229415Sjmg}
85329415Sjmg
85429415Sjmgstatic int
85553553Stanimurasbsbc_probe(device_t dev)
85653553Stanimura{
85754462Scg    	char buf[64];
85855092Sdfr	uintptr_t func, ver, r, f;
85953553Stanimura
86053553Stanimura	/* The parent device has already been probed. */
86154462Scg	r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
86254462Scg	if (func != SCF_PCM)
86353553Stanimura		return (ENXIO);
86453553Stanimura
86554462Scg	r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
86654791Scg	f = (ver & 0xffff0000) >> 16;
86754462Scg	ver &= 0x0000ffff;
86858756Scg	if (f & BD_F_ESS)
86958756Scg		return (ENXIO);
87058756Scg
87158756Scg	snprintf(buf, sizeof buf, "SB DSP %d.%02d%s", (int) ver >> 8, (int) ver & 0xff,
87254793Scg		(f & BD_F_SB16X)? " (ViBRA16X)" : "");
87354462Scg    	device_set_desc_copy(dev, buf);
87453553Stanimura
87553553Stanimura	return 0;
87653553Stanimura}
87753553Stanimura
87853553Stanimurastatic int
87953553Stanimurasbsbc_attach(device_t dev)
88053553Stanimura{
88153553Stanimura    	struct sb_info *sb;
88255092Sdfr	uintptr_t ver;
88353553Stanimura
88453553Stanimura    	sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
88555706Scg    	if (!sb)
88655706Scg		return ENXIO;
88753553Stanimura    	bzero(sb, sizeof *sb);
88853553Stanimura
88954462Scg	BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
89054462Scg	sb->bd_id = ver & 0x0000ffff;
89154462Scg	sb->bd_flags = (ver & 0xffff0000) >> 16;
89254462Scg
89353553Stanimura    	return sb_doattach(dev, sb);
89453553Stanimura}
89553553Stanimura
89653553Stanimurastatic device_method_t sbsbc_methods[] = {
89753553Stanimura	/* Device interface */
89853553Stanimura	DEVMETHOD(device_probe,		sbsbc_probe),
89953553Stanimura	DEVMETHOD(device_attach,	sbsbc_attach),
90053553Stanimura
90153553Stanimura	{ 0, 0 }
90253553Stanimura};
90353553Stanimura
90453553Stanimurastatic driver_t sbsbc_driver = {
90553553Stanimura	"pcm",
90653553Stanimura	sbsbc_methods,
90753553Stanimura	sizeof(snddev_info),
90853553Stanimura};
90953553Stanimura
91062483ScgDRIVER_MODULE(snd_sb, sbc, sbsbc_driver, pcm_devclass, 0, 0);
91162483ScgMODULE_DEPEND(snd_sb, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
91262483ScgMODULE_VERSION(snd_sb, 1);
91353553Stanimura
91454462Scg
91554462Scg
91662483Scg
917