make sb dependant on sbc

add support for non-pnp cards to sbc
move card identification to sbc
channel-swapping code is in sb now instead of dsp
vibra16x support is still broken, but will be fixed soon

note: sbc is now compulsory for sb cards

for pnp cards use:
device sbc0

for non-pnp cards eg:
device sbc0 at isa? port 0x240 irq 5 drq 3 flags 0x15
(hints as oldpcm)

both in addition to:
device pcm0

Reviewed by:	tanimura,dfr
Said he liked it: peter
This commit is contained in:
Cameron Grant 1999-12-12 02:30:19 +00:00
parent fefaab10f0
commit d4a72b067c
5 changed files with 629 additions and 1209 deletions

View File

@ -60,6 +60,7 @@ static int esschan_setblocksize(void *data, u_int32_t blocksize);
static int esschan_trigger(void *data, int go);
static int esschan_getptr(void *data);
static pcmchan_caps *esschan_getcaps(void *data);
static pcmchan_caps sb_playcaps = {
4000, 22050,
AFMT_U8,
@ -84,18 +85,24 @@ static pcmchan_caps sbpro_reccaps = {
AFMT_STEREO | AFMT_U8
};
static pcmchan_caps sb16_playcaps = {
static pcmchan_caps sb16_hcaps = {
5000, 45000,
AFMT_STEREO | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcmchan_caps sb16_reccaps = {
static pcmchan_caps sb16_lcaps = {
5000, 45000,
AFMT_STEREO | AFMT_U8,
AFMT_STEREO | AFMT_U8
};
static pcmchan_caps sb16x_caps = {
5000, 49000,
AFMT_STEREO | AFMT_U8 /* | AFMT_S16_LE */,
AFMT_STEREO | AFMT_U8 /* AFMT_S16_LE */
};
static pcmchan_caps ess_playcaps = {
5000, 49000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
@ -144,16 +151,11 @@ struct sb_chinfo {
struct sb_info {
struct resource *io_base; /* I/O address for the board */
int io_rid;
struct resource *irq;
int irq_rid;
struct resource *drq1; /* play */
int drq1_rid;
struct resource *drq2; /* rec */
int drq2_rid;
struct resource *drq1;
struct resource *drq2;
bus_dma_tag_t parent_dmat;
int dma16, dma8;
int bd_id;
u_long bd_flags; /* board-specific flags */
struct sb_chinfo pch, rch;
@ -166,31 +168,25 @@ static int sb_cmd(struct sb_info *sb, u_char val);
static int sb_cmd1(struct sb_info *sb, u_char cmd, int val);
static int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
static u_int sb_get_byte(struct sb_info *sb);
static int ess_write(struct sb_info *sb, u_char reg, int val);
static int ess_read(struct sb_info *sb, u_char reg);
/*
* in the SB, there is a set of indirect "mixer" registers with
* address at offset 4, data at offset 5
*/
static void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
static int sb_getmixer(struct sb_info *sb, u_int port);
static void sb_intr(void *arg);
static void ess_intr(void *arg);
static int sb_init(device_t dev, struct sb_info *sb);
static int sb_reset_dsp(struct sb_info *sb);
static void sb_intr(void *arg);
static int sb_format(struct sb_chinfo *ch, u_int32_t format);
static int sb_speed(struct sb_chinfo *ch, int speed);
static int sb_start(struct sb_chinfo *ch);
static int sb_stop(struct sb_chinfo *ch);
static int ess_write(struct sb_info *sb, u_char reg, int val);
static int ess_read(struct sb_info *sb, u_char reg);
static void ess_intr(void *arg);
static int ess_format(struct sb_chinfo *ch, u_int32_t format);
static int ess_speed(struct sb_chinfo *ch, int speed);
static int ess_start(struct sb_chinfo *ch);
static int ess_stop(struct sb_chinfo *ch);
static int ess_abort(struct sb_chinfo *ch);
static int sbmix_init(snd_mixer *m);
static int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
static int sbmix_setrecsrc(snd_mixer *m, u_int32_t src);
@ -377,20 +373,19 @@ sb_release_resources(struct sb_info *sb, device_t dev)
{
/* should we bus_teardown_intr here? */
if (sb->irq) {
bus_release_resource(dev, SYS_RES_IRQ, sb->irq_rid, sb->irq);
bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
sb->irq = 0;
}
if (sb->drq1) {
bus_release_resource(dev, SYS_RES_DRQ, sb->drq1_rid, sb->drq1);
bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1);
sb->drq1 = 0;
}
if (sb->drq2) {
bus_release_resource(dev, SYS_RES_DRQ, sb->drq2_rid, sb->drq2);
bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2);
sb->drq2 = 0;
}
if (sb->io_base) {
bus_release_resource(dev, SYS_RES_IOPORT, sb->io_rid,
sb->io_base);
bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
sb->io_base = 0;
}
free(sb, M_DEVBUF);
@ -399,184 +394,66 @@ sb_release_resources(struct sb_info *sb, device_t dev)
static int
sb_alloc_resources(struct sb_info *sb, device_t dev)
{
int rid;
rid = 0;
if (!sb->io_base)
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
rid = 0;
if (!sb->irq)
sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ,
&sb->irq_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
rid = 0;
if (!sb->drq1)
sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ,
&sb->drq1_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
if (!sb->drq2 && sb->drq2_rid > 0)
rid = 1;
if (!sb->drq2)
sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ,
&sb->drq2_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
if (sb->io_base && sb->drq1 && sb->irq) {
sb->dma8 = rman_get_start(sb->drq1);
isa_dma_acquire(sb->dma8);
isa_dmainit(sb->dma8, DSP_BUFFSIZE);
isa_dma_acquire(rman_get_start(sb->drq1));
isa_dmainit(rman_get_start(sb->drq1), DSP_BUFFSIZE);
if (sb->drq2) {
sb->dma16 = rman_get_start(sb->drq2);
isa_dma_acquire(sb->dma16);
isa_dmainit(sb->dma16, DSP_BUFFSIZE);
} else sb->dma16 = sb->dma8;
if (sb->dma8 > sb->dma16) {
int tmp = sb->dma16;
sb->dma16 = sb->dma8;
sb->dma8 = tmp;
isa_dma_acquire(rman_get_start(sb->drq2));
isa_dmainit(rman_get_start(sb->drq2), DSP_BUFFSIZE);
}
return 0;
} else return ENXIO;
}
static int
sb_identify_board(device_t dev, struct sb_info *sb)
static void
sb16_swap(void *v, int dir)
{
char *fmt = NULL;
static char buf[64];
int essver = 0;
struct sb_info *sb = v;
int pb = sb->pch.buffer->dl;
int rb = sb->rch.buffer->dl;
int pc = sb->pch.buffer->chan;
int rc = sb->rch.buffer->chan;
int swp = 0;
sb_cmd(sb, DSP_CMD_GETVER); /* Get version */
sb->bd_id = (sb_get_byte(sb) << 8) | sb_get_byte(sb);
if (!pb && !rb) {
if (dir == PCMDIR_PLAY && pc < 4) swp = 1;
else if (dir == PCMDIR_REC && rc < 4) swp = 1;
if (sb->bd_flags & BD_F_SB16X) swp = !swp;
if (swp) {
int t;
switch (sb->bd_id >> 8) {
case 1: /* old sound blaster has nothing... */
case 2:
fmt = "SoundBlaster %d.%d" ; /* default */
break;
case 3:
fmt = "SoundBlaster Pro %d.%d";
if (sb->bd_id == 0x301) {
int rev;
/* Try to detect ESS chips. */
sb_cmd(sb, DSP_CMD_GETID); /* Return ident. bytes. */
essver = (sb_get_byte(sb) << 8) | sb_get_byte(sb);
rev = essver & 0x000f;
essver &= 0xfff0;
if (essver == 0x4880) {
/* the ESS488 can be treated as an SBPRO */
fmt = "SoundBlaster Pro (ESS488 rev %d)";
} else if (essver == 0x6880) {
if (rev < 8) fmt = "ESS688 rev %d";
else fmt = "ESS1868 rev %d";
sb->bd_flags |= BD_F_ESS;
} else return ENXIO;
sb->bd_id &= 0xff00;
sb->bd_id |= ((essver & 0xf000) >> 8) | rev;
t = sb->pch.buffer->chan;
sb->pch.buffer->chan = sb->rch.buffer->chan;
sb->rch.buffer->chan = t;
sb->pch.buffer->dir = B_WRITE;
sb->rch.buffer->dir = B_READ;
}
break;
case 4:
sb->bd_flags |= BD_F_SB16;
if (sb->bd_flags & BD_F_SB16X) fmt = "SB16 ViBRA16X %d.%d";
else fmt = "SoundBlaster 16 %d.%d";
break;
default:
device_printf(dev, "failed to get SB version (%x)\n",
sb->bd_id);
return ENXIO;
}
if (essver) snprintf(buf, sizeof buf, fmt, sb->bd_id & 0x000f);
else snprintf(buf, sizeof buf, fmt, sb->bd_id >> 8, sb->bd_id & 0xff);
device_set_desc_copy(dev, buf);
return sb_reset_dsp(sb);
}
static int
sb_init(device_t dev, struct sb_info *sb)
{
int x, irq;
sb->bd_flags &= ~BD_F_MIX_MASK;
/* do various initializations depending on board id. */
switch (sb->bd_id >> 8) {
case 1: /* old sound blaster has nothing... */
break;
case 2:
sb->bd_flags |= BD_F_DUP_MIDI;
if (sb->bd_id > 0x200) sb->bd_flags |= BD_F_MIX_CT1335;
break;
case 3:
sb->bd_flags |= BD_F_DUP_MIDI | BD_F_MIX_CT1345;
break;
case 4:
sb->bd_flags |= BD_F_SB16 | BD_F_MIX_CT1745;
if (sb->dma16 != sb->dma8) sb->bd_flags |= BD_F_DUPLEX;
/* soft irq/dma configuration */
x = -1;
irq = rman_get_start(sb->irq);
if (irq == 5) x = 2;
else if (irq == 7) x = 4;
else if (irq == 9) x = 1;
else if (irq == 10) x = 8;
if (x == -1) device_printf(dev,
"bad irq %d (5/7/9/10 valid)\n",
irq);
else sb_setmixer(sb, IRQ_NR, x);
sb_setmixer(sb, DMA_NR, (1 << sb->dma16) | (1 << sb->dma8));
break;
}
return 0;
}
static int
sb_probe(device_t dev)
{
snddev_info *d = device_get_softc(dev);
struct sb_info *sb;
int allocated, i;
int error;
if (isa_get_vendorid(dev)) return ENXIO; /* not yet */
device_set_desc(dev, "SoundBlaster");
bzero(d, sizeof *d);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
allocated = 0;
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid,
0, ~0, 16, RF_ACTIVE);
if (!sb->io_base) {
BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n"));
allocated = 1;
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x220, 0x22f,
16, RF_ACTIVE);
if (!sb->io_base) {
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x240,
0x24f, 16, RF_ACTIVE);
}
}
if (!sb->io_base) return ENXIO;
error = sb_reset_dsp(sb);
if (error) goto no;
error = sb_identify_board(dev, sb);
if (error) goto no;
no:
i = sb->io_rid;
sb_release_resources(sb, dev);
if (allocated) bus_delete_resource(dev, SYS_RES_IOPORT, i);
return error;
}
}
static int
@ -584,29 +461,21 @@ sb_doattach(device_t dev, struct sb_info *sb)
{
snddev_info *d = device_get_softc(dev);
void *ih;
int error;
char status[SND_STATUSLEN];
sb->irq_rid = 0;
sb->drq1_rid = 0;
sb->drq2_rid = 1;
if (sb_alloc_resources(sb, dev)) goto no;
error = sb_reset_dsp(sb);
if (error) goto no;
error = sb_identify_board(dev, sb);
if (error) goto no;
sb_init(dev, sb);
if (sb_reset_dsp(sb)) goto no;
mixer_init(d, &sb_mixer, sb);
if (sb->bd_flags & BD_F_ESS)
bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, ess_intr, sb, &ih);
else
bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih);
if (sb->bd_flags & BD_F_SB16)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_EVILSB16);
if (sb->dma16 == sb->dma8)
if ((sb->bd_flags & BD_F_SB16) && !(sb->bd_flags & BD_F_SB16X))
pcm_setswap(dev, sb16_swap);
if (!sb->drq2)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
@ -618,11 +487,11 @@ sb_doattach(device_t dev, struct sb_info *sb)
goto no;
}
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d",
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld",
rman_get_start(sb->io_base), rman_get_start(sb->irq),
sb->dma8);
if (sb->dma16 != sb->dma8) snprintf(status + strlen(status),
SND_STATUSLEN - strlen(status), ":%d", sb->dma16);
rman_get_start(sb->drq1));
if (sb->drq2) snprintf(status + strlen(status), SND_STATUSLEN - strlen(status),
":%ld", rman_get_start(sb->drq2));
if (pcm_register(dev, sb, 1, 1)) goto no;
if (sb->bd_flags & BD_F_ESS) {
@ -641,57 +510,6 @@ no:
return ENXIO;
}
static int
sb_attach(device_t dev)
{
struct sb_info *sb;
int flags = device_get_flags(dev);
if (flags & DV_F_DUAL_DMA) {
bus_set_resource(dev, SYS_RES_DRQ, 1,
flags & DV_F_DRQ_MASK, 1);
}
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
/* XXX in probe should set io resource to right val instead of this */
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid,
0, ~0, 16, RF_ACTIVE);
if (!sb->io_base) {
BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n"));
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x220, 0x22f,
16, RF_ACTIVE);
if (!sb->io_base) {
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x240,
0x24f, 16, RF_ACTIVE);
}
}
if (!sb->io_base) return ENXIO;
return sb_doattach(dev, sb);
}
static device_method_t sb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sb_probe),
DEVMETHOD(device_attach, sb_attach),
{ 0, 0 }
};
static driver_t sb_driver = {
"pcm",
sb_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sb, isa, sb_driver, pcm_devclass, 0, 0);
static void
sb_intr(void *arg)
{
@ -829,6 +647,7 @@ sb_start(struct sb_chinfo *ch)
int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0;
int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
int l = ch->buffer->dl;
int dh = ch->buffer->chan > 3;
u_char i1, i2 = 0;
if (b16) l >>= 1;
@ -837,7 +656,7 @@ sb_start(struct sb_chinfo *ch)
if (sb->bd_flags & BD_F_SB16) {
i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON |
(play? DSP_F16_DAC : DSP_F16_ADC);
i1 |= (b16 && (sb->bd_flags & BD_F_DUPLEX))? DSP_DMA16 : DSP_DMA8;
i1 |= (b16 || dh)? DSP_DMA16 : DSP_DMA8;
i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0);
sb_cmd(sb, i1);
sb_cmd2(sb, i2, l);
@ -999,7 +818,8 @@ sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
ch->buffer = b;
ch->buffer->bufsize = DSP_BUFFSIZE;
if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) return NULL;
ch->buffer->chan = (dir == PCMDIR_PLAY)? sb->dma16 : sb->dma8;
ch->buffer->chan = (dir == PCMDIR_PLAY)? rman_get_start(sb->drq2)
: rman_get_start(sb->drq1);
return ch;
}
@ -1053,12 +873,14 @@ sbchan_getcaps(void *data)
{
struct sb_chinfo *ch = data;
int p = (ch->dir == PCMDIR_PLAY)? 1 : 0;
if (ch->parent->bd_id <= 0x200)
if (ch->parent->bd_id < 0x300)
return p? &sb_playcaps : &sb_reccaps;
else if (ch->parent->bd_id >= 0x400)
return p? &sb16_playcaps : &sb16_reccaps;
else
else if (ch->parent->bd_id < 0x400)
return p? &sbpro_playcaps : &sbpro_reccaps;
else if (ch->parent->bd_flags & BD_F_SB16X)
return &sb16x_caps;
else
return (ch->buffer->chan >= 4)? &sb16_hcaps : &sb16_lcaps;
}
/* channel interface for ESS18xx */
#ifdef notyet
@ -1252,102 +1074,22 @@ sbmix_setrecsrc(snd_mixer *m, u_int32_t src)
return src;
}
static int
sbpnp_probe(device_t dev)
{
char *s = NULL;
u_int32_t logical_id = isa_get_logicalid(dev);
switch(logical_id) {
case 0x01000000: /* @@@0001 */
s = "Avance Asound 100";
break;
case 0x01100000: /* @@@1001 */
s = "Avance Asound 110";
break;
case 0x01200000: /* @@@2001 */
s = "Avance Logic ALS120";
break;
case 0x68187316: /* ESS1868 */
s = "ESS1868";
break;
case 0x69187316: /* ESS1869 */
case 0xacb0110e: /* Compaq's Presario 1621 ESS1869 */
s = "ESS1869";
break;
case 0x79187316: /* ESS1879 */
s = "ESS1879";
break;
case 0x88187316: /* ESS1888 */
s = "ESS1888";
break;
}
if (s) {
device_set_desc(dev, s);
return (0);
}
return ENXIO;
}
static int
sbpnp_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id = isa_get_vendorid(dev);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
return sb_doattach(dev, sb);
}
static device_method_t sbpnp_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbpnp_probe),
DEVMETHOD(device_attach, sbpnp_attach),
{ 0, 0 }
};
static driver_t sbpnp_driver = {
"pcm",
sbpnp_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0);
#if NSBC > 0
#define DESCSTR " PCM Audio"
static int
sbsbc_probe(device_t dev)
{
char *s = NULL;
struct sndcard_func *func;
char buf[64];
u_int32_t func, ver, r;
/* The parent device has already been probed. */
func = device_get_ivars(dev);
if (func == NULL || func->func != SCF_PCM)
r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
if (func != SCF_PCM)
return (ENXIO);
s = "SB PCM Audio";
r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
ver &= 0x0000ffff;
snprintf(buf, sizeof buf, "SB DSP %d.%02d", ver >> 8, ver & 0xff);
device_set_desc_copy(dev, buf);
device_set_desc(dev, s);
return 0;
}
@ -1355,22 +1097,16 @@ static int
sbsbc_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id;
device_t sbc;
u_int32_t ver;
sbc = device_get_parent(dev);
vend_id = isa_get_vendorid(sbc);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
sb->bd_id = ver & 0x0000ffff;
sb->bd_flags = (ver & 0xffff0000) >> 16;
return sb_doattach(dev, sb);
}
@ -1390,4 +1126,5 @@ static driver_t sbsbc_driver = {
DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0);
#endif /* NSBC > 0 */

View File

@ -3,11 +3,6 @@
* $FreeBSD$
*/
typedef struct _sbdev_info {
} sbdev_info ;
extern int sbc_major, sbc_minor ;
/*
* sound blaster registers
*/
@ -150,6 +145,47 @@ extern int sbc_major, sbc_minor ;
#define BD_F_DMARUN 0x2000
#define BD_F_DMARUN2 0x4000
#define BD_F_DUPLEX 0x8000
/*
* Mixer registers of SB Pro
*/
#define VOC_VOL 0x04
#define MIC_VOL 0x0A
#define MIC_MIX 0x0A
#define RECORD_SRC 0x0C
#define IN_FILTER 0x0C
#define OUT_FILTER 0x0E
#define MASTER_VOL 0x22
#define FM_VOL 0x26
#define CD_VOL 0x28
#define LINE_VOL 0x2E
#define IRQ_NR 0x80
#define DMA_NR 0x81
#define IRQ_STAT 0x82
/*
* Additional registers on the SG NX Pro
*/
#define COVOX_VOL 0x42
#define TREBLE_LVL 0x44
#define BASS_LVL 0x46
#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
#define FILT_OFF (1 << 5)
#define MONO_DAC 0x00
#define STEREO_DAC 0x02
/*
* Mixer registers of SB16
*/
#define SB16_IMASK_L 0x3d
#define SB16_IMASK_R 0x3e
#define SB16_OMASK 0x3c
#ifndef SB_NOMIXER
/*
* sound/sb_mixer.h
*
@ -210,52 +246,6 @@ extern int sbc_major, sbc_minor ;
SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)
/*
* Mixer registers
*
* NOTE! RECORD_SRC == IN_FILTER
*/
/*
* Mixer registers of SB Pro
*/
#define VOC_VOL 0x04
#define MIC_VOL 0x0A
#define MIC_MIX 0x0A
#define RECORD_SRC 0x0C
#define IN_FILTER 0x0C
#define OUT_FILTER 0x0E
#define MASTER_VOL 0x22
#define FM_VOL 0x26
#define CD_VOL 0x28
#define LINE_VOL 0x2E
#define IRQ_NR 0x80
#define DMA_NR 0x81
#define IRQ_STAT 0x82
/*
* Additional registers on the SG NX Pro
*/
#define COVOX_VOL 0x42
#define TREBLE_LVL 0x44
#define BASS_LVL 0x46
#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
#define FILT_OFF (1 << 5)
#define MONO_DAC 0x00
#define STEREO_DAC 0x02
/*
* Mixer registers of SB16
*/
#define SB16_IMASK_L 0x3d
#define SB16_IMASK_R 0x3e
#define SB16_OMASK 0x3c
#ifndef __SB_MIXER_C__
mixer_tab sbpro_mix;
mixer_tab ess_mix;
@ -379,4 +369,4 @@ static u_char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
#define SRC_CD 3 /* Select CD recording source */
#define SRC_LINE 7 /* Use Line-in for recording source */
#endif

View File

@ -60,6 +60,7 @@ static int esschan_setblocksize(void *data, u_int32_t blocksize);
static int esschan_trigger(void *data, int go);
static int esschan_getptr(void *data);
static pcmchan_caps *esschan_getcaps(void *data);
static pcmchan_caps sb_playcaps = {
4000, 22050,
AFMT_U8,
@ -84,18 +85,24 @@ static pcmchan_caps sbpro_reccaps = {
AFMT_STEREO | AFMT_U8
};
static pcmchan_caps sb16_playcaps = {
static pcmchan_caps sb16_hcaps = {
5000, 45000,
AFMT_STEREO | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcmchan_caps sb16_reccaps = {
static pcmchan_caps sb16_lcaps = {
5000, 45000,
AFMT_STEREO | AFMT_U8,
AFMT_STEREO | AFMT_U8
};
static pcmchan_caps sb16x_caps = {
5000, 49000,
AFMT_STEREO | AFMT_U8 /* | AFMT_S16_LE */,
AFMT_STEREO | AFMT_U8 /* AFMT_S16_LE */
};
static pcmchan_caps ess_playcaps = {
5000, 49000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
@ -144,16 +151,11 @@ struct sb_chinfo {
struct sb_info {
struct resource *io_base; /* I/O address for the board */
int io_rid;
struct resource *irq;
int irq_rid;
struct resource *drq1; /* play */
int drq1_rid;
struct resource *drq2; /* rec */
int drq2_rid;
struct resource *drq1;
struct resource *drq2;
bus_dma_tag_t parent_dmat;
int dma16, dma8;
int bd_id;
u_long bd_flags; /* board-specific flags */
struct sb_chinfo pch, rch;
@ -166,31 +168,25 @@ static int sb_cmd(struct sb_info *sb, u_char val);
static int sb_cmd1(struct sb_info *sb, u_char cmd, int val);
static int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
static u_int sb_get_byte(struct sb_info *sb);
static int ess_write(struct sb_info *sb, u_char reg, int val);
static int ess_read(struct sb_info *sb, u_char reg);
/*
* in the SB, there is a set of indirect "mixer" registers with
* address at offset 4, data at offset 5
*/
static void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
static int sb_getmixer(struct sb_info *sb, u_int port);
static void sb_intr(void *arg);
static void ess_intr(void *arg);
static int sb_init(device_t dev, struct sb_info *sb);
static int sb_reset_dsp(struct sb_info *sb);
static void sb_intr(void *arg);
static int sb_format(struct sb_chinfo *ch, u_int32_t format);
static int sb_speed(struct sb_chinfo *ch, int speed);
static int sb_start(struct sb_chinfo *ch);
static int sb_stop(struct sb_chinfo *ch);
static int ess_write(struct sb_info *sb, u_char reg, int val);
static int ess_read(struct sb_info *sb, u_char reg);
static void ess_intr(void *arg);
static int ess_format(struct sb_chinfo *ch, u_int32_t format);
static int ess_speed(struct sb_chinfo *ch, int speed);
static int ess_start(struct sb_chinfo *ch);
static int ess_stop(struct sb_chinfo *ch);
static int ess_abort(struct sb_chinfo *ch);
static int sbmix_init(snd_mixer *m);
static int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
static int sbmix_setrecsrc(snd_mixer *m, u_int32_t src);
@ -377,20 +373,19 @@ sb_release_resources(struct sb_info *sb, device_t dev)
{
/* should we bus_teardown_intr here? */
if (sb->irq) {
bus_release_resource(dev, SYS_RES_IRQ, sb->irq_rid, sb->irq);
bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
sb->irq = 0;
}
if (sb->drq1) {
bus_release_resource(dev, SYS_RES_DRQ, sb->drq1_rid, sb->drq1);
bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1);
sb->drq1 = 0;
}
if (sb->drq2) {
bus_release_resource(dev, SYS_RES_DRQ, sb->drq2_rid, sb->drq2);
bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2);
sb->drq2 = 0;
}
if (sb->io_base) {
bus_release_resource(dev, SYS_RES_IOPORT, sb->io_rid,
sb->io_base);
bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
sb->io_base = 0;
}
free(sb, M_DEVBUF);
@ -399,184 +394,66 @@ sb_release_resources(struct sb_info *sb, device_t dev)
static int
sb_alloc_resources(struct sb_info *sb, device_t dev)
{
int rid;
rid = 0;
if (!sb->io_base)
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
rid = 0;
if (!sb->irq)
sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ,
&sb->irq_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
rid = 0;
if (!sb->drq1)
sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ,
&sb->drq1_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
if (!sb->drq2 && sb->drq2_rid > 0)
rid = 1;
if (!sb->drq2)
sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ,
&sb->drq2_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
if (sb->io_base && sb->drq1 && sb->irq) {
sb->dma8 = rman_get_start(sb->drq1);
isa_dma_acquire(sb->dma8);
isa_dmainit(sb->dma8, DSP_BUFFSIZE);
isa_dma_acquire(rman_get_start(sb->drq1));
isa_dmainit(rman_get_start(sb->drq1), DSP_BUFFSIZE);
if (sb->drq2) {
sb->dma16 = rman_get_start(sb->drq2);
isa_dma_acquire(sb->dma16);
isa_dmainit(sb->dma16, DSP_BUFFSIZE);
} else sb->dma16 = sb->dma8;
if (sb->dma8 > sb->dma16) {
int tmp = sb->dma16;
sb->dma16 = sb->dma8;
sb->dma8 = tmp;
isa_dma_acquire(rman_get_start(sb->drq2));
isa_dmainit(rman_get_start(sb->drq2), DSP_BUFFSIZE);
}
return 0;
} else return ENXIO;
}
static int
sb_identify_board(device_t dev, struct sb_info *sb)
static void
sb16_swap(void *v, int dir)
{
char *fmt = NULL;
static char buf[64];
int essver = 0;
struct sb_info *sb = v;
int pb = sb->pch.buffer->dl;
int rb = sb->rch.buffer->dl;
int pc = sb->pch.buffer->chan;
int rc = sb->rch.buffer->chan;
int swp = 0;
sb_cmd(sb, DSP_CMD_GETVER); /* Get version */
sb->bd_id = (sb_get_byte(sb) << 8) | sb_get_byte(sb);
if (!pb && !rb) {
if (dir == PCMDIR_PLAY && pc < 4) swp = 1;
else if (dir == PCMDIR_REC && rc < 4) swp = 1;
if (sb->bd_flags & BD_F_SB16X) swp = !swp;
if (swp) {
int t;
switch (sb->bd_id >> 8) {
case 1: /* old sound blaster has nothing... */
case 2:
fmt = "SoundBlaster %d.%d" ; /* default */
break;
case 3:
fmt = "SoundBlaster Pro %d.%d";
if (sb->bd_id == 0x301) {
int rev;
/* Try to detect ESS chips. */
sb_cmd(sb, DSP_CMD_GETID); /* Return ident. bytes. */
essver = (sb_get_byte(sb) << 8) | sb_get_byte(sb);
rev = essver & 0x000f;
essver &= 0xfff0;
if (essver == 0x4880) {
/* the ESS488 can be treated as an SBPRO */
fmt = "SoundBlaster Pro (ESS488 rev %d)";
} else if (essver == 0x6880) {
if (rev < 8) fmt = "ESS688 rev %d";
else fmt = "ESS1868 rev %d";
sb->bd_flags |= BD_F_ESS;
} else return ENXIO;
sb->bd_id &= 0xff00;
sb->bd_id |= ((essver & 0xf000) >> 8) | rev;
t = sb->pch.buffer->chan;
sb->pch.buffer->chan = sb->rch.buffer->chan;
sb->rch.buffer->chan = t;
sb->pch.buffer->dir = B_WRITE;
sb->rch.buffer->dir = B_READ;
}
break;
case 4:
sb->bd_flags |= BD_F_SB16;
if (sb->bd_flags & BD_F_SB16X) fmt = "SB16 ViBRA16X %d.%d";
else fmt = "SoundBlaster 16 %d.%d";
break;
default:
device_printf(dev, "failed to get SB version (%x)\n",
sb->bd_id);
return ENXIO;
}
if (essver) snprintf(buf, sizeof buf, fmt, sb->bd_id & 0x000f);
else snprintf(buf, sizeof buf, fmt, sb->bd_id >> 8, sb->bd_id & 0xff);
device_set_desc_copy(dev, buf);
return sb_reset_dsp(sb);
}
static int
sb_init(device_t dev, struct sb_info *sb)
{
int x, irq;
sb->bd_flags &= ~BD_F_MIX_MASK;
/* do various initializations depending on board id. */
switch (sb->bd_id >> 8) {
case 1: /* old sound blaster has nothing... */
break;
case 2:
sb->bd_flags |= BD_F_DUP_MIDI;
if (sb->bd_id > 0x200) sb->bd_flags |= BD_F_MIX_CT1335;
break;
case 3:
sb->bd_flags |= BD_F_DUP_MIDI | BD_F_MIX_CT1345;
break;
case 4:
sb->bd_flags |= BD_F_SB16 | BD_F_MIX_CT1745;
if (sb->dma16 != sb->dma8) sb->bd_flags |= BD_F_DUPLEX;
/* soft irq/dma configuration */
x = -1;
irq = rman_get_start(sb->irq);
if (irq == 5) x = 2;
else if (irq == 7) x = 4;
else if (irq == 9) x = 1;
else if (irq == 10) x = 8;
if (x == -1) device_printf(dev,
"bad irq %d (5/7/9/10 valid)\n",
irq);
else sb_setmixer(sb, IRQ_NR, x);
sb_setmixer(sb, DMA_NR, (1 << sb->dma16) | (1 << sb->dma8));
break;
}
return 0;
}
static int
sb_probe(device_t dev)
{
snddev_info *d = device_get_softc(dev);
struct sb_info *sb;
int allocated, i;
int error;
if (isa_get_vendorid(dev)) return ENXIO; /* not yet */
device_set_desc(dev, "SoundBlaster");
bzero(d, sizeof *d);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
allocated = 0;
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid,
0, ~0, 16, RF_ACTIVE);
if (!sb->io_base) {
BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n"));
allocated = 1;
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x220, 0x22f,
16, RF_ACTIVE);
if (!sb->io_base) {
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x240,
0x24f, 16, RF_ACTIVE);
}
}
if (!sb->io_base) return ENXIO;
error = sb_reset_dsp(sb);
if (error) goto no;
error = sb_identify_board(dev, sb);
if (error) goto no;
no:
i = sb->io_rid;
sb_release_resources(sb, dev);
if (allocated) bus_delete_resource(dev, SYS_RES_IOPORT, i);
return error;
}
}
static int
@ -584,29 +461,21 @@ sb_doattach(device_t dev, struct sb_info *sb)
{
snddev_info *d = device_get_softc(dev);
void *ih;
int error;
char status[SND_STATUSLEN];
sb->irq_rid = 0;
sb->drq1_rid = 0;
sb->drq2_rid = 1;
if (sb_alloc_resources(sb, dev)) goto no;
error = sb_reset_dsp(sb);
if (error) goto no;
error = sb_identify_board(dev, sb);
if (error) goto no;
sb_init(dev, sb);
if (sb_reset_dsp(sb)) goto no;
mixer_init(d, &sb_mixer, sb);
if (sb->bd_flags & BD_F_ESS)
bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, ess_intr, sb, &ih);
else
bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih);
if (sb->bd_flags & BD_F_SB16)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_EVILSB16);
if (sb->dma16 == sb->dma8)
if ((sb->bd_flags & BD_F_SB16) && !(sb->bd_flags & BD_F_SB16X))
pcm_setswap(dev, sb16_swap);
if (!sb->drq2)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
@ -618,11 +487,11 @@ sb_doattach(device_t dev, struct sb_info *sb)
goto no;
}
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d",
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld",
rman_get_start(sb->io_base), rman_get_start(sb->irq),
sb->dma8);
if (sb->dma16 != sb->dma8) snprintf(status + strlen(status),
SND_STATUSLEN - strlen(status), ":%d", sb->dma16);
rman_get_start(sb->drq1));
if (sb->drq2) snprintf(status + strlen(status), SND_STATUSLEN - strlen(status),
":%ld", rman_get_start(sb->drq2));
if (pcm_register(dev, sb, 1, 1)) goto no;
if (sb->bd_flags & BD_F_ESS) {
@ -641,57 +510,6 @@ no:
return ENXIO;
}
static int
sb_attach(device_t dev)
{
struct sb_info *sb;
int flags = device_get_flags(dev);
if (flags & DV_F_DUAL_DMA) {
bus_set_resource(dev, SYS_RES_DRQ, 1,
flags & DV_F_DRQ_MASK, 1);
}
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
/* XXX in probe should set io resource to right val instead of this */
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid,
0, ~0, 16, RF_ACTIVE);
if (!sb->io_base) {
BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n"));
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x220, 0x22f,
16, RF_ACTIVE);
if (!sb->io_base) {
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x240,
0x24f, 16, RF_ACTIVE);
}
}
if (!sb->io_base) return ENXIO;
return sb_doattach(dev, sb);
}
static device_method_t sb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sb_probe),
DEVMETHOD(device_attach, sb_attach),
{ 0, 0 }
};
static driver_t sb_driver = {
"pcm",
sb_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sb, isa, sb_driver, pcm_devclass, 0, 0);
static void
sb_intr(void *arg)
{
@ -829,6 +647,7 @@ sb_start(struct sb_chinfo *ch)
int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0;
int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
int l = ch->buffer->dl;
int dh = ch->buffer->chan > 3;
u_char i1, i2 = 0;
if (b16) l >>= 1;
@ -837,7 +656,7 @@ sb_start(struct sb_chinfo *ch)
if (sb->bd_flags & BD_F_SB16) {
i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON |
(play? DSP_F16_DAC : DSP_F16_ADC);
i1 |= (b16 && (sb->bd_flags & BD_F_DUPLEX))? DSP_DMA16 : DSP_DMA8;
i1 |= (b16 || dh)? DSP_DMA16 : DSP_DMA8;
i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0);
sb_cmd(sb, i1);
sb_cmd2(sb, i2, l);
@ -999,7 +818,8 @@ sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
ch->buffer = b;
ch->buffer->bufsize = DSP_BUFFSIZE;
if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) return NULL;
ch->buffer->chan = (dir == PCMDIR_PLAY)? sb->dma16 : sb->dma8;
ch->buffer->chan = (dir == PCMDIR_PLAY)? rman_get_start(sb->drq2)
: rman_get_start(sb->drq1);
return ch;
}
@ -1053,12 +873,14 @@ sbchan_getcaps(void *data)
{
struct sb_chinfo *ch = data;
int p = (ch->dir == PCMDIR_PLAY)? 1 : 0;
if (ch->parent->bd_id <= 0x200)
if (ch->parent->bd_id < 0x300)
return p? &sb_playcaps : &sb_reccaps;
else if (ch->parent->bd_id >= 0x400)
return p? &sb16_playcaps : &sb16_reccaps;
else
else if (ch->parent->bd_id < 0x400)
return p? &sbpro_playcaps : &sbpro_reccaps;
else if (ch->parent->bd_flags & BD_F_SB16X)
return &sb16x_caps;
else
return (ch->buffer->chan >= 4)? &sb16_hcaps : &sb16_lcaps;
}
/* channel interface for ESS18xx */
#ifdef notyet
@ -1252,102 +1074,22 @@ sbmix_setrecsrc(snd_mixer *m, u_int32_t src)
return src;
}
static int
sbpnp_probe(device_t dev)
{
char *s = NULL;
u_int32_t logical_id = isa_get_logicalid(dev);
switch(logical_id) {
case 0x01000000: /* @@@0001 */
s = "Avance Asound 100";
break;
case 0x01100000: /* @@@1001 */
s = "Avance Asound 110";
break;
case 0x01200000: /* @@@2001 */
s = "Avance Logic ALS120";
break;
case 0x68187316: /* ESS1868 */
s = "ESS1868";
break;
case 0x69187316: /* ESS1869 */
case 0xacb0110e: /* Compaq's Presario 1621 ESS1869 */
s = "ESS1869";
break;
case 0x79187316: /* ESS1879 */
s = "ESS1879";
break;
case 0x88187316: /* ESS1888 */
s = "ESS1888";
break;
}
if (s) {
device_set_desc(dev, s);
return (0);
}
return ENXIO;
}
static int
sbpnp_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id = isa_get_vendorid(dev);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
return sb_doattach(dev, sb);
}
static device_method_t sbpnp_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbpnp_probe),
DEVMETHOD(device_attach, sbpnp_attach),
{ 0, 0 }
};
static driver_t sbpnp_driver = {
"pcm",
sbpnp_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0);
#if NSBC > 0
#define DESCSTR " PCM Audio"
static int
sbsbc_probe(device_t dev)
{
char *s = NULL;
struct sndcard_func *func;
char buf[64];
u_int32_t func, ver, r;
/* The parent device has already been probed. */
func = device_get_ivars(dev);
if (func == NULL || func->func != SCF_PCM)
r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
if (func != SCF_PCM)
return (ENXIO);
s = "SB PCM Audio";
r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
ver &= 0x0000ffff;
snprintf(buf, sizeof buf, "SB DSP %d.%02d", ver >> 8, ver & 0xff);
device_set_desc_copy(dev, buf);
device_set_desc(dev, s);
return 0;
}
@ -1355,22 +1097,16 @@ static int
sbsbc_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id;
device_t sbc;
u_int32_t ver;
sbc = device_get_parent(dev);
vend_id = isa_get_vendorid(sbc);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
sb->bd_id = ver & 0x0000ffff;
sb->bd_flags = (ver & 0xffff0000) >> 16;
return sb_doattach(dev, sb);
}
@ -1390,4 +1126,5 @@ static driver_t sbsbc_driver = {
DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0);
#endif /* NSBC > 0 */

View File

@ -60,6 +60,7 @@ static int esschan_setblocksize(void *data, u_int32_t blocksize);
static int esschan_trigger(void *data, int go);
static int esschan_getptr(void *data);
static pcmchan_caps *esschan_getcaps(void *data);
static pcmchan_caps sb_playcaps = {
4000, 22050,
AFMT_U8,
@ -84,18 +85,24 @@ static pcmchan_caps sbpro_reccaps = {
AFMT_STEREO | AFMT_U8
};
static pcmchan_caps sb16_playcaps = {
static pcmchan_caps sb16_hcaps = {
5000, 45000,
AFMT_STEREO | AFMT_S16_LE,
AFMT_STEREO | AFMT_S16_LE
};
static pcmchan_caps sb16_reccaps = {
static pcmchan_caps sb16_lcaps = {
5000, 45000,
AFMT_STEREO | AFMT_U8,
AFMT_STEREO | AFMT_U8
};
static pcmchan_caps sb16x_caps = {
5000, 49000,
AFMT_STEREO | AFMT_U8 /* | AFMT_S16_LE */,
AFMT_STEREO | AFMT_U8 /* AFMT_S16_LE */
};
static pcmchan_caps ess_playcaps = {
5000, 49000,
AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
@ -144,16 +151,11 @@ struct sb_chinfo {
struct sb_info {
struct resource *io_base; /* I/O address for the board */
int io_rid;
struct resource *irq;
int irq_rid;
struct resource *drq1; /* play */
int drq1_rid;
struct resource *drq2; /* rec */
int drq2_rid;
struct resource *drq1;
struct resource *drq2;
bus_dma_tag_t parent_dmat;
int dma16, dma8;
int bd_id;
u_long bd_flags; /* board-specific flags */
struct sb_chinfo pch, rch;
@ -166,31 +168,25 @@ static int sb_cmd(struct sb_info *sb, u_char val);
static int sb_cmd1(struct sb_info *sb, u_char cmd, int val);
static int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
static u_int sb_get_byte(struct sb_info *sb);
static int ess_write(struct sb_info *sb, u_char reg, int val);
static int ess_read(struct sb_info *sb, u_char reg);
/*
* in the SB, there is a set of indirect "mixer" registers with
* address at offset 4, data at offset 5
*/
static void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
static int sb_getmixer(struct sb_info *sb, u_int port);
static void sb_intr(void *arg);
static void ess_intr(void *arg);
static int sb_init(device_t dev, struct sb_info *sb);
static int sb_reset_dsp(struct sb_info *sb);
static void sb_intr(void *arg);
static int sb_format(struct sb_chinfo *ch, u_int32_t format);
static int sb_speed(struct sb_chinfo *ch, int speed);
static int sb_start(struct sb_chinfo *ch);
static int sb_stop(struct sb_chinfo *ch);
static int ess_write(struct sb_info *sb, u_char reg, int val);
static int ess_read(struct sb_info *sb, u_char reg);
static void ess_intr(void *arg);
static int ess_format(struct sb_chinfo *ch, u_int32_t format);
static int ess_speed(struct sb_chinfo *ch, int speed);
static int ess_start(struct sb_chinfo *ch);
static int ess_stop(struct sb_chinfo *ch);
static int ess_abort(struct sb_chinfo *ch);
static int sbmix_init(snd_mixer *m);
static int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
static int sbmix_setrecsrc(snd_mixer *m, u_int32_t src);
@ -377,20 +373,19 @@ sb_release_resources(struct sb_info *sb, device_t dev)
{
/* should we bus_teardown_intr here? */
if (sb->irq) {
bus_release_resource(dev, SYS_RES_IRQ, sb->irq_rid, sb->irq);
bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
sb->irq = 0;
}
if (sb->drq1) {
bus_release_resource(dev, SYS_RES_DRQ, sb->drq1_rid, sb->drq1);
bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1);
sb->drq1 = 0;
}
if (sb->drq2) {
bus_release_resource(dev, SYS_RES_DRQ, sb->drq2_rid, sb->drq2);
bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2);
sb->drq2 = 0;
}
if (sb->io_base) {
bus_release_resource(dev, SYS_RES_IOPORT, sb->io_rid,
sb->io_base);
bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
sb->io_base = 0;
}
free(sb, M_DEVBUF);
@ -399,184 +394,66 @@ sb_release_resources(struct sb_info *sb, device_t dev)
static int
sb_alloc_resources(struct sb_info *sb, device_t dev)
{
int rid;
rid = 0;
if (!sb->io_base)
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
rid = 0;
if (!sb->irq)
sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ,
&sb->irq_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
rid = 0;
if (!sb->drq1)
sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ,
&sb->drq1_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
if (!sb->drq2 && sb->drq2_rid > 0)
rid = 1;
if (!sb->drq2)
sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ,
&sb->drq2_rid, 0, ~0, 1,
&rid, 0, ~0, 1,
RF_ACTIVE);
if (sb->io_base && sb->drq1 && sb->irq) {
sb->dma8 = rman_get_start(sb->drq1);
isa_dma_acquire(sb->dma8);
isa_dmainit(sb->dma8, DSP_BUFFSIZE);
isa_dma_acquire(rman_get_start(sb->drq1));
isa_dmainit(rman_get_start(sb->drq1), DSP_BUFFSIZE);
if (sb->drq2) {
sb->dma16 = rman_get_start(sb->drq2);
isa_dma_acquire(sb->dma16);
isa_dmainit(sb->dma16, DSP_BUFFSIZE);
} else sb->dma16 = sb->dma8;
if (sb->dma8 > sb->dma16) {
int tmp = sb->dma16;
sb->dma16 = sb->dma8;
sb->dma8 = tmp;
isa_dma_acquire(rman_get_start(sb->drq2));
isa_dmainit(rman_get_start(sb->drq2), DSP_BUFFSIZE);
}
return 0;
} else return ENXIO;
}
static int
sb_identify_board(device_t dev, struct sb_info *sb)
static void
sb16_swap(void *v, int dir)
{
char *fmt = NULL;
static char buf[64];
int essver = 0;
struct sb_info *sb = v;
int pb = sb->pch.buffer->dl;
int rb = sb->rch.buffer->dl;
int pc = sb->pch.buffer->chan;
int rc = sb->rch.buffer->chan;
int swp = 0;
sb_cmd(sb, DSP_CMD_GETVER); /* Get version */
sb->bd_id = (sb_get_byte(sb) << 8) | sb_get_byte(sb);
if (!pb && !rb) {
if (dir == PCMDIR_PLAY && pc < 4) swp = 1;
else if (dir == PCMDIR_REC && rc < 4) swp = 1;
if (sb->bd_flags & BD_F_SB16X) swp = !swp;
if (swp) {
int t;
switch (sb->bd_id >> 8) {
case 1: /* old sound blaster has nothing... */
case 2:
fmt = "SoundBlaster %d.%d" ; /* default */
break;
case 3:
fmt = "SoundBlaster Pro %d.%d";
if (sb->bd_id == 0x301) {
int rev;
/* Try to detect ESS chips. */
sb_cmd(sb, DSP_CMD_GETID); /* Return ident. bytes. */
essver = (sb_get_byte(sb) << 8) | sb_get_byte(sb);
rev = essver & 0x000f;
essver &= 0xfff0;
if (essver == 0x4880) {
/* the ESS488 can be treated as an SBPRO */
fmt = "SoundBlaster Pro (ESS488 rev %d)";
} else if (essver == 0x6880) {
if (rev < 8) fmt = "ESS688 rev %d";
else fmt = "ESS1868 rev %d";
sb->bd_flags |= BD_F_ESS;
} else return ENXIO;
sb->bd_id &= 0xff00;
sb->bd_id |= ((essver & 0xf000) >> 8) | rev;
t = sb->pch.buffer->chan;
sb->pch.buffer->chan = sb->rch.buffer->chan;
sb->rch.buffer->chan = t;
sb->pch.buffer->dir = B_WRITE;
sb->rch.buffer->dir = B_READ;
}
break;
case 4:
sb->bd_flags |= BD_F_SB16;
if (sb->bd_flags & BD_F_SB16X) fmt = "SB16 ViBRA16X %d.%d";
else fmt = "SoundBlaster 16 %d.%d";
break;
default:
device_printf(dev, "failed to get SB version (%x)\n",
sb->bd_id);
return ENXIO;
}
if (essver) snprintf(buf, sizeof buf, fmt, sb->bd_id & 0x000f);
else snprintf(buf, sizeof buf, fmt, sb->bd_id >> 8, sb->bd_id & 0xff);
device_set_desc_copy(dev, buf);
return sb_reset_dsp(sb);
}
static int
sb_init(device_t dev, struct sb_info *sb)
{
int x, irq;
sb->bd_flags &= ~BD_F_MIX_MASK;
/* do various initializations depending on board id. */
switch (sb->bd_id >> 8) {
case 1: /* old sound blaster has nothing... */
break;
case 2:
sb->bd_flags |= BD_F_DUP_MIDI;
if (sb->bd_id > 0x200) sb->bd_flags |= BD_F_MIX_CT1335;
break;
case 3:
sb->bd_flags |= BD_F_DUP_MIDI | BD_F_MIX_CT1345;
break;
case 4:
sb->bd_flags |= BD_F_SB16 | BD_F_MIX_CT1745;
if (sb->dma16 != sb->dma8) sb->bd_flags |= BD_F_DUPLEX;
/* soft irq/dma configuration */
x = -1;
irq = rman_get_start(sb->irq);
if (irq == 5) x = 2;
else if (irq == 7) x = 4;
else if (irq == 9) x = 1;
else if (irq == 10) x = 8;
if (x == -1) device_printf(dev,
"bad irq %d (5/7/9/10 valid)\n",
irq);
else sb_setmixer(sb, IRQ_NR, x);
sb_setmixer(sb, DMA_NR, (1 << sb->dma16) | (1 << sb->dma8));
break;
}
return 0;
}
static int
sb_probe(device_t dev)
{
snddev_info *d = device_get_softc(dev);
struct sb_info *sb;
int allocated, i;
int error;
if (isa_get_vendorid(dev)) return ENXIO; /* not yet */
device_set_desc(dev, "SoundBlaster");
bzero(d, sizeof *d);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
allocated = 0;
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid,
0, ~0, 16, RF_ACTIVE);
if (!sb->io_base) {
BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n"));
allocated = 1;
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x220, 0x22f,
16, RF_ACTIVE);
if (!sb->io_base) {
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x240,
0x24f, 16, RF_ACTIVE);
}
}
if (!sb->io_base) return ENXIO;
error = sb_reset_dsp(sb);
if (error) goto no;
error = sb_identify_board(dev, sb);
if (error) goto no;
no:
i = sb->io_rid;
sb_release_resources(sb, dev);
if (allocated) bus_delete_resource(dev, SYS_RES_IOPORT, i);
return error;
}
}
static int
@ -584,29 +461,21 @@ sb_doattach(device_t dev, struct sb_info *sb)
{
snddev_info *d = device_get_softc(dev);
void *ih;
int error;
char status[SND_STATUSLEN];
sb->irq_rid = 0;
sb->drq1_rid = 0;
sb->drq2_rid = 1;
if (sb_alloc_resources(sb, dev)) goto no;
error = sb_reset_dsp(sb);
if (error) goto no;
error = sb_identify_board(dev, sb);
if (error) goto no;
sb_init(dev, sb);
if (sb_reset_dsp(sb)) goto no;
mixer_init(d, &sb_mixer, sb);
if (sb->bd_flags & BD_F_ESS)
bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, ess_intr, sb, &ih);
else
bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih);
if (sb->bd_flags & BD_F_SB16)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_EVILSB16);
if (sb->dma16 == sb->dma8)
if ((sb->bd_flags & BD_F_SB16) && !(sb->bd_flags & BD_F_SB16X))
pcm_setswap(dev, sb16_swap);
if (!sb->drq2)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
@ -618,11 +487,11 @@ sb_doattach(device_t dev, struct sb_info *sb)
goto no;
}
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d",
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld",
rman_get_start(sb->io_base), rman_get_start(sb->irq),
sb->dma8);
if (sb->dma16 != sb->dma8) snprintf(status + strlen(status),
SND_STATUSLEN - strlen(status), ":%d", sb->dma16);
rman_get_start(sb->drq1));
if (sb->drq2) snprintf(status + strlen(status), SND_STATUSLEN - strlen(status),
":%ld", rman_get_start(sb->drq2));
if (pcm_register(dev, sb, 1, 1)) goto no;
if (sb->bd_flags & BD_F_ESS) {
@ -641,57 +510,6 @@ no:
return ENXIO;
}
static int
sb_attach(device_t dev)
{
struct sb_info *sb;
int flags = device_get_flags(dev);
if (flags & DV_F_DUAL_DMA) {
bus_set_resource(dev, SYS_RES_DRQ, 1,
flags & DV_F_DRQ_MASK, 1);
}
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
/* XXX in probe should set io resource to right val instead of this */
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid,
0, ~0, 16, RF_ACTIVE);
if (!sb->io_base) {
BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n"));
sb->io_rid = 0;
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x220, 0x22f,
16, RF_ACTIVE);
if (!sb->io_base) {
sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sb->io_rid, 0x240,
0x24f, 16, RF_ACTIVE);
}
}
if (!sb->io_base) return ENXIO;
return sb_doattach(dev, sb);
}
static device_method_t sb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sb_probe),
DEVMETHOD(device_attach, sb_attach),
{ 0, 0 }
};
static driver_t sb_driver = {
"pcm",
sb_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sb, isa, sb_driver, pcm_devclass, 0, 0);
static void
sb_intr(void *arg)
{
@ -829,6 +647,7 @@ sb_start(struct sb_chinfo *ch)
int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0;
int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
int l = ch->buffer->dl;
int dh = ch->buffer->chan > 3;
u_char i1, i2 = 0;
if (b16) l >>= 1;
@ -837,7 +656,7 @@ sb_start(struct sb_chinfo *ch)
if (sb->bd_flags & BD_F_SB16) {
i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON |
(play? DSP_F16_DAC : DSP_F16_ADC);
i1 |= (b16 && (sb->bd_flags & BD_F_DUPLEX))? DSP_DMA16 : DSP_DMA8;
i1 |= (b16 || dh)? DSP_DMA16 : DSP_DMA8;
i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0);
sb_cmd(sb, i1);
sb_cmd2(sb, i2, l);
@ -999,7 +818,8 @@ sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
ch->buffer = b;
ch->buffer->bufsize = DSP_BUFFSIZE;
if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) return NULL;
ch->buffer->chan = (dir == PCMDIR_PLAY)? sb->dma16 : sb->dma8;
ch->buffer->chan = (dir == PCMDIR_PLAY)? rman_get_start(sb->drq2)
: rman_get_start(sb->drq1);
return ch;
}
@ -1053,12 +873,14 @@ sbchan_getcaps(void *data)
{
struct sb_chinfo *ch = data;
int p = (ch->dir == PCMDIR_PLAY)? 1 : 0;
if (ch->parent->bd_id <= 0x200)
if (ch->parent->bd_id < 0x300)
return p? &sb_playcaps : &sb_reccaps;
else if (ch->parent->bd_id >= 0x400)
return p? &sb16_playcaps : &sb16_reccaps;
else
else if (ch->parent->bd_id < 0x400)
return p? &sbpro_playcaps : &sbpro_reccaps;
else if (ch->parent->bd_flags & BD_F_SB16X)
return &sb16x_caps;
else
return (ch->buffer->chan >= 4)? &sb16_hcaps : &sb16_lcaps;
}
/* channel interface for ESS18xx */
#ifdef notyet
@ -1252,102 +1074,22 @@ sbmix_setrecsrc(snd_mixer *m, u_int32_t src)
return src;
}
static int
sbpnp_probe(device_t dev)
{
char *s = NULL;
u_int32_t logical_id = isa_get_logicalid(dev);
switch(logical_id) {
case 0x01000000: /* @@@0001 */
s = "Avance Asound 100";
break;
case 0x01100000: /* @@@1001 */
s = "Avance Asound 110";
break;
case 0x01200000: /* @@@2001 */
s = "Avance Logic ALS120";
break;
case 0x68187316: /* ESS1868 */
s = "ESS1868";
break;
case 0x69187316: /* ESS1869 */
case 0xacb0110e: /* Compaq's Presario 1621 ESS1869 */
s = "ESS1869";
break;
case 0x79187316: /* ESS1879 */
s = "ESS1879";
break;
case 0x88187316: /* ESS1888 */
s = "ESS1888";
break;
}
if (s) {
device_set_desc(dev, s);
return (0);
}
return ENXIO;
}
static int
sbpnp_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id = isa_get_vendorid(dev);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
return sb_doattach(dev, sb);
}
static device_method_t sbpnp_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbpnp_probe),
DEVMETHOD(device_attach, sbpnp_attach),
{ 0, 0 }
};
static driver_t sbpnp_driver = {
"pcm",
sbpnp_methods,
sizeof(snddev_info),
};
DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0);
#if NSBC > 0
#define DESCSTR " PCM Audio"
static int
sbsbc_probe(device_t dev)
{
char *s = NULL;
struct sndcard_func *func;
char buf[64];
u_int32_t func, ver, r;
/* The parent device has already been probed. */
func = device_get_ivars(dev);
if (func == NULL || func->func != SCF_PCM)
r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
if (func != SCF_PCM)
return (ENXIO);
s = "SB PCM Audio";
r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
ver &= 0x0000ffff;
snprintf(buf, sizeof buf, "SB DSP %d.%02d", ver >> 8, ver & 0xff);
device_set_desc_copy(dev, buf);
device_set_desc(dev, s);
return 0;
}
@ -1355,22 +1097,16 @@ static int
sbsbc_attach(device_t dev)
{
struct sb_info *sb;
u_int32_t vend_id;
device_t sbc;
u_int32_t ver;
sbc = device_get_parent(dev);
vend_id = isa_get_vendorid(sbc);
sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT);
if (!sb) return ENXIO;
bzero(sb, sizeof *sb);
switch(vend_id) {
case 0xf0008c0e:
case 0x10019305:
case 0x20019305:
/* XXX add here the vend_id for other vibra16X cards... */
sb->bd_flags = BD_F_SB16X;
}
BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
sb->bd_id = ver & 0x0000ffff;
sb->bd_flags = (ver & 0xffff0000) >> 16;
return sb_doattach(dev, sb);
}
@ -1390,4 +1126,5 @@ static driver_t sbsbc_driver = {
DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0);
#endif /* NSBC > 0 */

View File

@ -28,114 +28,298 @@
#include "isa.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/soundcard.h>
#include <dev/sound/chip.h>
#if NISA > 0
#include <isa/isavar.h>
#include <isa/isa_common.h>
#ifdef __alpha__ /* XXX workaround a stupid warning */
#include <alpha/isa/isavar.h>
#endif
#endif /* NISA > 0 */
#include <dev/sound/pcm/sound.h>
#define __SB_MIXER_C__ /* XXX warning... */
#define SB_NOMIXER
#include <dev/sound/isa/sb.h>
/* Here is the parameter structure per a device. */
struct sbc_softc {
device_t dev; /* device */
int io_rid[3]; /* io port rids */
struct resource *io[3]; /* io port resources */
int io_alloced[3]; /* io port alloc flag */
int irq_rid; /* irq rids */
struct resource *irq; /* irq resources */
int irq_alloced; /* irq alloc flag */
int drq_rid[2]; /* drq rids */
struct resource *drq[2]; /* drq resources */
int drq_alloced[2]; /* drq alloc flag */
u_int32_t bd_ver;
};
typedef struct sbc_softc *sc_p;
#if NISA > 0
static int sbc_probe(device_t dev);
static int sbc_attach(device_t dev);
#endif /* NISA > 0 */
static struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags);
u_long start, u_long end, u_long count, u_int flags);
static int sbc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r);
struct resource *r);
static int alloc_resource(sc_p scp);
static int release_resource(sc_p scp);
static int alloc_resource(struct sbc_softc *scp);
static int release_resource(struct sbc_softc *scp);
static devclass_t sbc_devclass;
#if NISA > 0
static int io_range[3] = {0x10, 0x2, 0x4};
static int sb_rd(struct resource *io, int reg);
static void sb_wr(struct resource *io, int reg, u_int8_t val);
static int sb_dspready(struct resource *io);
static int sb_cmd(struct resource *io, u_char val);
static u_int sb_get_byte(struct resource *io);
static void sb_setmixer(struct resource *io, u_int port, u_int value);
static int
sb_rd(struct resource *io, int reg)
{
return bus_space_read_1(rman_get_bustag(io),
rman_get_bushandle(io),
reg);
}
static void
sb_wr(struct resource *io, int reg, u_int8_t val)
{
return bus_space_write_1(rman_get_bustag(io),
rman_get_bushandle(io),
reg, val);
}
static int
sb_dspready(struct resource *io)
{
return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0);
}
static int
sb_dspwr(struct resource *io, u_char val)
{
int i;
for (i = 0; i < 1000; i++) {
if (sb_dspready(io)) {
sb_wr(io, SBDSP_CMD, val);
return 1;
}
if (i > 10) DELAY((i > 100)? 1000 : 10);
}
printf("sb_dspwr(0x%02x) timed out.\n", val);
return 0;
}
static int
sb_cmd(struct resource *io, u_char val)
{
return sb_dspwr(io, val);
}
static void
sb_setmixer(struct resource *io, u_int port, u_int value)
{
u_long flags;
flags = spltty();
sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff));
DELAY(10);
splx(flags);
}
static u_int
sb_get_byte(struct resource *io)
{
int i;
for (i = 1000; i > 0; i--) {
if (sb_rd(io, DSP_DATA_AVAIL) & 0x80)
return sb_rd(io, DSP_READ);
else
DELAY(20);
}
return 0xffff;
}
static int
sb_reset_dsp(struct resource *io)
{
sb_wr(io, SBDSP_RST, 3);
DELAY(100);
sb_wr(io, SBDSP_RST, 0);
return (sb_get_byte(io) == 0xAA)? 0 : ENXIO;
}
static int
sb_identify_board(struct resource *io)
{
int ver, essver, rev;
sb_cmd(io, DSP_CMD_GETVER); /* Get version */
ver = (sb_get_byte(io) << 8) | sb_get_byte(io);
if (ver < 0x100 || ver > 0x4ff) return 0;
if (ver == 0x0301) {
/* Try to detect ESS chips. */
sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */
essver = (sb_get_byte(io) << 8) | sb_get_byte(io);
rev = essver & 0x000f;
essver &= 0xfff0;
if (essver == 0x4880) ver |= 0x1000;
else if (essver == 0x6880) ver = 0x0500 | rev;
}
return ver;
}
static struct isa_pnp_id sbc_ids[] = {
#if notdef
{0x0000630e, "CS423x"},
#endif
{0x01008c0e, "Creative ViBRA16C PnP"},
{0x41008c0e, "Creative ViBRA16CL PnP"},
{0x43008c0e, "Creative ViBRA16X PnP"},
{0x31008c0e, "Creative SB16 PnP/SB32"},
{0x01008c0e, "Creative ViBRA16C"},
{0x31008c0e, "Creative SB16/SB32"},
{0x41008c0e, "Creative SB16/SB32"},
{0x42008c0e, "Creative SB AWE64"}, /* CTL00c1 */
{0x43008c0e, "Creative ViBRA16X"},
{0x44008c0e, "Creative SB AWE64 Gold"},
{0x45008c0e, "Creative SB AWE64"}, /* CTL0045 */
#if notdef
{0x01200001, "Avance Logic ALS120"},
{0x01000000, "Avance Asound 100"},
{0x01100001, "Avance Asound 110"},
{0x68187316, "ESS ES1868 Plug and Play AudioDrive"}, /* ESS1868 */
{0x79187316, "ESS ES1879 Plug and Play AudioDrive"}, /* ESS1879 */
{0x2100a865, "Yamaha OPL3-SA2/SAX Sound Board"},
{0x80719304, "Terratec Soundsystem Base 1"},
#endif
{0x01200001, "Avance Logic ALS120"},
{0x68187316, "ESS ES1868"}, /* ESS1868 */
{0x69187316, "ESS ES1869"}, /* ESS1869 */
{0xacb0110e, "ESS ES1869 (Compaq OEM)"},
{0x79187316, "ESS ES1879"}, /* ESS1879 */
{0x88187316, "ESS ES1888"}, /* ESS1888 */
{0}
};
static int
sbc_probe(device_t dev)
{
int error;
char *s = NULL;
u_int32_t logical_id = isa_get_logicalid(dev);
if (logical_id) {
/* Check pnp ids */
return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids);
} else {
int rid = 0, ver;
struct resource *io;
/* Check pnp ids */
error = ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids);
if (error)
return error;
else
return -100;
io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
0, ~0, 16, RF_ACTIVE);
if (!io) goto bad;
if (sb_reset_dsp(io)) goto bad2;
ver = sb_identify_board(io);
if (ver == 0) goto bad2;
switch ((ver & 0x00000f00) >> 8) {
case 1:
case 2:
s = "Soundblaster";
break;
case 3:
s = (ver & 0x0000f000)? "ESS 488" : "Soundblaster Pro";
break;
case 4:
s = "Soundblaster 16";
break;
case 5:
s = (ver & 0x00000008)? "ESS 688" : "ESS 1688";
break;
}
if (s) device_set_desc(dev, s);
bad2: bus_release_resource(dev, SYS_RES_IOPORT, rid, io);
bad: return s? 0 : ENXIO;
}
}
static int
sbc_attach(device_t dev)
{
sc_p scp;
device_t child;
int unit;
struct sbc_softc *scp;
struct sndcard_func *func;
device_t child;
u_int32_t logical_id = isa_get_logicalid(dev);
int flags = device_get_flags(dev);
int f = 0, dh = -1, dl = -1;
if (!logical_id && (flags & DV_F_DUAL_DMA)) {
bus_set_resource(dev, SYS_RES_DRQ, 1,
flags & DV_F_DRQ_MASK, 1);
}
scp = device_get_softc(dev);
unit = device_get_unit(dev);
bzero(scp, sizeof(*scp));
scp->dev = dev;
if (alloc_resource(scp)) {
release_resource(scp);
return (ENXIO);
if (alloc_resource(scp)) goto bad;
if (sb_reset_dsp(scp->io[0])) goto bad;
scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff;
if (scp->bd_ver == 0) goto bad;
switch ((scp->bd_ver & 0x0f00) >> 8) {
case 1: /* old sound blaster has nothing... */
break;
case 2:
f |= BD_F_DUP_MIDI;
if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335;
break;
case 5:
f |= BD_F_ESS;
scp->bd_ver = 0x0301;
case 3:
f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345;
break;
case 4:
f |= BD_F_SB16 | BD_F_MIX_CT1745;
if (scp->drq[0]) dl = rman_get_start(scp->drq[0]);
if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl;
if (dh != dl) f |= BD_F_DUPLEX;
if (dh < dl) {
struct resource *r;
r = scp->drq[0];
scp->drq[0] = scp->drq[1];
scp->drq[1] = r;
dl = rman_get_start(scp->drq[0]);
dh = rman_get_start(scp->drq[1]);
}
if (!logical_id) {
/* soft irq/dma configuration */
int x = -1, irq = rman_get_start(scp->irq);
if (irq == 5) x = 2;
else if (irq == 7) x = 4;
else if (irq == 9) x = 1;
else if (irq == 10) x = 8;
if (x == -1) device_printf(dev,
"bad irq %d (5/7/9/10 valid)\n",
irq);
else sb_setmixer(scp->io[0], IRQ_NR, x);
sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl));
device_printf(dev, "setting non-pnp card to irq %d, drq %d", irq, dl);
if (dl != dh) printf(", %d", dh);
printf("\n");
}
break;
}
switch (logical_id) {
case 0x01008c0e: /* CTL0001 */
case 0x43008c0e: /* CTL0043 */
f |= BD_F_SB16X;
break;
}
scp->bd_ver |= f << 16;
/* PCM Audio */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
if (func == NULL) goto bad;
bzero(func, sizeof(*func));
func->func = SCF_PCM;
child = device_add_child(dev, "pcm", -1);
@ -144,8 +328,7 @@ sbc_attach(device_t dev)
#if notyet
/* Midi Interface */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
if (func == NULL) goto bad;
bzero(func, sizeof(*func));
func->func = SCF_MIDI;
child = device_add_child(dev, "midi", -1);
@ -153,25 +336,27 @@ sbc_attach(device_t dev)
/* OPL FM Synthesizer */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
return (ENOMEM);
if (func == NULL) goto bad;
bzero(func, sizeof(*func));
func->func = SCF_SYNTH;
child = device_add_child(dev, "midi", -1);
device_set_ivars(child, func);
#endif /* notyet */
/* probe/attach kids */
bus_generic_attach(dev);
return (0);
bad: release_resource(scp);
return (ENXIO);
}
#endif /* NISA > 0 */
static struct resource *
sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
sc_p scp;
struct sbc_softc *scp;
int *alloced, rid_max, alloced_max;
struct resource **res;
@ -183,18 +368,18 @@ sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
rid_max = 2;
alloced_max = 1;
break;
case SYS_RES_IRQ:
alloced = &scp->irq_alloced;
res = &scp->irq;
rid_max = 0;
alloced_max = 2; /* pcm and mpu may share the irq. */
break;
case SYS_RES_DRQ:
alloced = scp->drq_alloced;
res = scp->drq;
rid_max = 1;
alloced_max = 1;
break;
case SYS_RES_IRQ:
alloced = &scp->irq_alloced;
res = &scp->irq;
rid_max = 0;
alloced_max = 2; /* pcm and mpu may share the irq. */
break;
default:
return (NULL);
}
@ -203,14 +388,14 @@ sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
return (NULL);
alloced[*rid]++;
return (res[*rid]);
return (res[*rid]);
}
static int
sbc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
sc_p scp;
struct sbc_softc *scp;
int *alloced, rid_max;
scp = device_get_softc(bus);
@ -219,14 +404,14 @@ sbc_release_resource(device_t bus, device_t child, int type, int rid,
alloced = scp->io_alloced;
rid_max = 2;
break;
case SYS_RES_IRQ:
alloced = &scp->irq_alloced;
rid_max = 0;
break;
case SYS_RES_DRQ:
alloced = scp->drq_alloced;
rid_max = 1;
break;
case SYS_RES_IRQ:
alloced = &scp->irq_alloced;
rid_max = 0;
break;
default:
return (1);
}
@ -238,9 +423,44 @@ sbc_release_resource(device_t bus, device_t child, int type, int rid,
return (0);
}
static int io_range[3] = {0x10, 0x4, 0x4};
static int
alloc_resource(sc_p scp)
sbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result)
{
struct sbc_softc *scp = device_get_softc(bus);
struct sndcard_func *func = device_get_ivars(dev);
switch (index) {
case 0:
*result = func->func;
break;
case 1:
*result = scp->bd_ver;
break;
default:
return ENOENT;
}
return 0;
}
static int
sbc_write_ivar(device_t bus, device_t dev,
int index, uintptr_t value)
{
switch (index) {
case 0:
case 1:
return EINVAL;
default:
return (ENOENT);
}
}
static int
alloc_resource(struct sbc_softc *scp)
{
int i;
@ -249,34 +469,34 @@ alloc_resource(sc_p scp)
scp->io_rid[i] = i;
scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i],
0, ~0, io_range[i], RF_ACTIVE);
if (scp->io[i] == NULL)
if (i == 0 && scp->io[i] == NULL)
return (1);
scp->io_alloced[i] = 0;
}
}
if (scp->irq == NULL) {
scp->irq_rid = 0;
scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid,
0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
if (scp->irq == NULL)
return (1);
scp->irq_alloced = 0;
}
for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) {
if (scp->drq[i] == NULL) {
scp->drq_rid[i] = i;
scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i],
0, ~0, 1, RF_ACTIVE);
if (scp->drq[i] == NULL)
if (i == 0 && scp->drq[i] == NULL)
return (1);
scp->drq_alloced[i] = 0;
}
}
if (scp->irq == NULL) {
scp->irq_rid = 0;
scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid,
0, ~0, 1, RF_ACTIVE);
if (scp->irq == NULL)
return (1);
scp->irq_alloced = 0;
}
return (0);
}
static int
release_resource(sc_p scp)
release_resource(struct sbc_softc *scp)
{
int i;
@ -299,7 +519,6 @@ release_resource(sc_p scp)
return (0);
}
#if NISA > 0
static device_method_t sbc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbc_probe),
@ -310,6 +529,8 @@ static device_method_t sbc_methods[] = {
DEVMETHOD(device_resume, bus_generic_resume),
/* Bus interface */
DEVMETHOD(bus_read_ivar, sbc_read_ivar),
DEVMETHOD(bus_write_ivar, sbc_write_ivar),
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_alloc_resource, sbc_alloc_resource),
DEVMETHOD(bus_release_resource, sbc_release_resource),
@ -327,8 +548,6 @@ static driver_t sbc_driver = {
sizeof(struct sbc_softc),
};
/*
* sbc can be attached to an isa bus.
*/
/* sbc can be attached to an isa bus. */
DRIVER_MODULE(sbc, isa, sbc_driver, sbc_devclass, 0, 0);
#endif /* NISA > 0 */