866 lines
15 KiB
C
866 lines
15 KiB
C
/*
|
||
* SB 16 driver
|
||
*/
|
||
#include "u.h"
|
||
#include "../port/lib.h"
|
||
#include "mem.h"
|
||
#include "dat.h"
|
||
#include "fns.h"
|
||
#include "io.h"
|
||
#include "../port/error.h"
|
||
#include "../port/audioif.h"
|
||
|
||
typedef struct Ring Ring;
|
||
typedef struct Blaster Blaster;
|
||
typedef struct Ctlr Ctlr;
|
||
|
||
enum
|
||
{
|
||
Vmaster,
|
||
Vaudio,
|
||
Vsynth,
|
||
Vcd,
|
||
Vline,
|
||
Vmic,
|
||
Vspeaker,
|
||
Vtreb,
|
||
Vbass,
|
||
Vigain,
|
||
Vogain,
|
||
Vspeed,
|
||
Vdelay,
|
||
Nvol,
|
||
|
||
Blocksize = 4096,
|
||
Blocks = 65536/Blocksize,
|
||
};
|
||
|
||
struct Ring
|
||
{
|
||
uchar *buf;
|
||
ulong nbuf;
|
||
|
||
ulong ri;
|
||
ulong wi;
|
||
};
|
||
|
||
struct Blaster
|
||
{
|
||
Lock;
|
||
int reset; /* io ports to the sound blaster */
|
||
int read;
|
||
int write;
|
||
int wstatus;
|
||
int rstatus;
|
||
int mixaddr;
|
||
int mixdata;
|
||
int clri8;
|
||
int clri16;
|
||
int clri401;
|
||
|
||
void (*startdma)(Ctlr*);
|
||
void (*intr)(Ctlr*);
|
||
};
|
||
|
||
struct Ctlr
|
||
{
|
||
Rendez vous;
|
||
int active; /* boolean dma running */
|
||
int major; /* SB16 major version number (sb 4) */
|
||
int minor; /* SB16 minor version number */
|
||
Ring ring; /* dma ring buffer */
|
||
Blaster blaster;
|
||
|
||
int lvol[Nvol];
|
||
int rvol[Nvol];
|
||
|
||
/* for probe */
|
||
Audio *adev;
|
||
ISAConf conf;
|
||
Ctlr *next;
|
||
};
|
||
|
||
static Volume voltab[] = {
|
||
[Vmaster] "master", 0x30, 0xff, Stereo, 0,
|
||
[Vaudio] "audio", 0x32, 0xff, Stereo, 0,
|
||
[Vsynth] "synth", 0x34, 0xff, Stereo, 0,
|
||
[Vcd] "cd", 0x36, 0xff, Stereo, 0,
|
||
[Vline] "line", 0x38, 0xff, Stereo, 0,
|
||
[Vmic] "mic", 0x3a, 0xff, Mono, 0,
|
||
[Vspeaker] "speaker", 0x3b, 0xff, Mono, 0,
|
||
[Vtreb] "treb", 0x44, 0xff, Stereo, 0,
|
||
[Vbass] "bass", 0x46, 0xff, Stereo, 0,
|
||
[Vigain] "recgain", 0x3f, 0xff, Stereo, 0,
|
||
[Vogain] "outgain", 0x41, 0xff, Stereo, 0,
|
||
[Vspeed] "speed", 0, 0, Absolute, 0,
|
||
[Vdelay] "delay", 0, 0, Absolute, 0,
|
||
0,
|
||
};
|
||
|
||
static char Emajor[] = "soundblaster not responding/wrong version";
|
||
|
||
static long
|
||
buffered(Ring *r)
|
||
{
|
||
ulong ri, wi;
|
||
|
||
ri = r->ri;
|
||
wi = r->wi;
|
||
if(wi >= ri)
|
||
return wi - ri;
|
||
else
|
||
return r->nbuf - (ri - wi);
|
||
}
|
||
|
||
static long
|
||
available(Ring *r)
|
||
{
|
||
long m;
|
||
|
||
m = (r->nbuf - 1) - buffered(r);
|
||
if(m < 0)
|
||
m = 0;
|
||
return m;
|
||
}
|
||
|
||
static long
|
||
readring(Ring *r, uchar *p, long n)
|
||
{
|
||
long n0, m;
|
||
|
||
n0 = n;
|
||
while(n > 0){
|
||
if((m = buffered(r)) <= 0)
|
||
break;
|
||
if(m > n)
|
||
m = n;
|
||
if(p){
|
||
if(r->ri + m > r->nbuf)
|
||
m = r->nbuf - r->ri;
|
||
memmove(p, r->buf + r->ri, m);
|
||
p += m;
|
||
}
|
||
r->ri = (r->ri + m) % r->nbuf;
|
||
n -= m;
|
||
}
|
||
return n0 - n;
|
||
}
|
||
|
||
static long
|
||
writering(Ring *r, uchar *p, long n)
|
||
{
|
||
long n0, m;
|
||
|
||
n0 = n;
|
||
while(n > 0){
|
||
if((m = available(r)) <= 0)
|
||
break;
|
||
if(m > n)
|
||
m = n;
|
||
if(p){
|
||
if(r->wi + m > r->nbuf)
|
||
m = r->nbuf - r->wi;
|
||
memmove(r->buf + r->wi, p, m);
|
||
p += m;
|
||
}
|
||
r->wi = (r->wi + m) % r->nbuf;
|
||
n -= m;
|
||
}
|
||
return n0 - n;
|
||
}
|
||
|
||
static int
|
||
sbcmd(Blaster *blaster, int val)
|
||
{
|
||
int i, s;
|
||
|
||
for(i=1<<16; i!=0; i--) {
|
||
s = inb(blaster->wstatus);
|
||
if((s & 0x80) == 0) {
|
||
outb(blaster->write, val);
|
||
return 0;
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
sbread(Blaster *blaster)
|
||
{
|
||
int i, s;
|
||
|
||
for(i=1<<16; i!=0; i--) {
|
||
s = inb(blaster->rstatus);
|
||
if((s & 0x80) != 0) {
|
||
return inb(blaster->read);
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static int
|
||
ess1688w(Blaster *blaster, int reg, int val)
|
||
{
|
||
if(sbcmd(blaster, reg) || sbcmd(blaster, val))
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
ess1688r(Blaster *blaster, int reg)
|
||
{
|
||
if(sbcmd(blaster, 0xC0) || sbcmd(blaster, reg))
|
||
return -1;
|
||
return sbread(blaster);
|
||
}
|
||
|
||
static int
|
||
mxcmd(Blaster *blaster, int addr, int val)
|
||
{
|
||
outb(blaster->mixaddr, addr);
|
||
outb(blaster->mixdata, val);
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
mxread(Blaster *blaster, int addr)
|
||
{
|
||
int s;
|
||
|
||
outb(blaster->mixaddr, addr);
|
||
s = inb(blaster->mixdata);
|
||
return s;
|
||
}
|
||
|
||
static int
|
||
mxsetvol(Audio *adev, int x, int a[2])
|
||
{
|
||
Blaster *blaster;
|
||
Ctlr *ctlr = adev->ctlr;
|
||
Volume *vol;
|
||
|
||
vol = voltab+x;
|
||
blaster = &ctlr->blaster;
|
||
ilock(blaster);
|
||
switch(vol->type){
|
||
case Absolute:
|
||
switch(x){
|
||
case Vdelay:
|
||
adev->delay = a[0];
|
||
break;
|
||
case Vspeed:
|
||
adev->speed = a[0];
|
||
break;
|
||
}
|
||
ctlr->lvol[x] = ctlr->rvol[x] = a[0];
|
||
break;
|
||
case Stereo:
|
||
ctlr->rvol[x] = a[1];
|
||
mxcmd(blaster, vol->reg+1, a[1]);
|
||
/* no break */
|
||
case Mono:
|
||
ctlr->lvol[x] = a[0];
|
||
mxcmd(blaster, vol->reg, a[0]);
|
||
}
|
||
iunlock(blaster);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
mxgetvol(Audio *adev, int x, int a[2])
|
||
{
|
||
Ctlr *ctlr = adev->ctlr;
|
||
|
||
a[0] = ctlr->lvol[x];
|
||
a[1] = ctlr->rvol[x];
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
contindma(Ctlr *ctlr)
|
||
{
|
||
Blaster *blaster;
|
||
Ring *ring;
|
||
|
||
blaster = &ctlr->blaster;
|
||
ring = &ctlr->ring;
|
||
if(buffered(ring) >= Blocksize)
|
||
ring->ri = ring->nbuf - dmacount(ctlr->conf.dma);
|
||
else{
|
||
dmaend(ctlr->conf.dma);
|
||
sbcmd(blaster, 0xd9); /* exit at end of count */
|
||
sbcmd(blaster, 0xd5); /* pause */
|
||
ctlr->active = 0;
|
||
}
|
||
wakeup(&ctlr->vous);
|
||
}
|
||
|
||
/*
|
||
* cause sb to get an interrupt per buffer.
|
||
* start first dma
|
||
*/
|
||
static void
|
||
sb16startdma(Ctlr *ctlr)
|
||
{
|
||
Blaster *blaster;
|
||
Ring *ring;
|
||
long count;
|
||
int speed;
|
||
|
||
blaster = &ctlr->blaster;
|
||
ring = &ctlr->ring;
|
||
ilock(blaster);
|
||
dmaend(ctlr->conf.dma);
|
||
if(0)
|
||
sbcmd(blaster, 0x42); /* input sampling rate */
|
||
else
|
||
sbcmd(blaster, 0x41); /* output sampling rate */
|
||
speed = ctlr->adev->speed;
|
||
sbcmd(blaster, speed>>8);
|
||
sbcmd(blaster, speed);
|
||
|
||
if(0)
|
||
sbcmd(blaster, 0xbe); /* A/D, autoinit */
|
||
else
|
||
sbcmd(blaster, 0xb6); /* D/A, autoinit */
|
||
|
||
sbcmd(blaster, 0x30); /* stereo, signed 16 bit */
|
||
|
||
count = (Blocksize>>1) - 1;
|
||
sbcmd(blaster, count);
|
||
sbcmd(blaster, count>>8);
|
||
|
||
ctlr->active = 1;
|
||
if(dmasetup(ctlr->conf.dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){
|
||
ctlr->active = 0;
|
||
print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno);
|
||
}
|
||
iunlock(blaster);
|
||
}
|
||
|
||
static int
|
||
ess1688reset(Blaster *blaster, int ctlrno)
|
||
{
|
||
int i;
|
||
|
||
outb(blaster->reset, 3);
|
||
delay(1); /* >3 υs */
|
||
outb(blaster->reset, 0);
|
||
delay(1);
|
||
|
||
i = sbread(blaster);
|
||
if(i != 0xAA) {
|
||
print("#A%d: no response %#.2x\n", ctlrno, i);
|
||
return 1;
|
||
}
|
||
|
||
if(sbcmd(blaster, 0xC6)){ /* extended mode */
|
||
print("#A%d: barf 3\n", ctlrno);
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
ess1688startdma(Ctlr *ctlr)
|
||
{
|
||
Blaster *blaster;
|
||
Ring *ring;
|
||
ulong count;
|
||
int speed, x;
|
||
|
||
blaster = &ctlr->blaster;
|
||
ring = &ctlr->ring;
|
||
|
||
ilock(blaster);
|
||
dmaend(ctlr->conf.dma);
|
||
|
||
ess1688reset(blaster, ctlr->adev->ctlrno);
|
||
|
||
/*
|
||
* Set the speed.
|
||
*/
|
||
speed = ctlr->adev->speed;
|
||
if(speed < 4000)
|
||
speed = 4000;
|
||
else if(speed > 48000)
|
||
speed = 48000;
|
||
if(speed > 22000)
|
||
x = 0x80|(256-(795500+speed/2)/speed);
|
||
else
|
||
x = 128-(397700+speed/2)/speed;
|
||
ess1688w(blaster, 0xA1, x & 0xFF);
|
||
|
||
speed = (speed * 9) / 20;
|
||
x = 256 - 7160000 / (speed * 82);
|
||
ess1688w(blaster, 0xA2, x & 0xFF);
|
||
|
||
if(0)
|
||
ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */
|
||
else
|
||
ess1688w(blaster, 0xB8, 0x04); /* D/A, autoinit */
|
||
x = ess1688r(blaster, 0xA8) & ~0x03;
|
||
ess1688w(blaster, 0xA8, x|0x01); /* 2 channels */
|
||
ess1688w(blaster, 0xB9, 2); /* demand mode, 4 bytes per request */
|
||
|
||
if(1)
|
||
ess1688w(blaster, 0xB6, 0); /* for output */
|
||
|
||
ess1688w(blaster, 0xB7, 0x71);
|
||
ess1688w(blaster, 0xB7, 0xBC);
|
||
|
||
x = ess1688r(blaster, 0xB1) & 0x0F;
|
||
ess1688w(blaster, 0xB1, x|0x50);
|
||
x = ess1688r(blaster, 0xB2) & 0x0F;
|
||
ess1688w(blaster, 0xB2, x|0x50);
|
||
|
||
if(1)
|
||
sbcmd(blaster, 0xD1); /* speaker on */
|
||
|
||
count = -Blocksize;
|
||
ess1688w(blaster, 0xA4, count & 0xFF);
|
||
ess1688w(blaster, 0xA5, (count>>8) & 0xFF);
|
||
x = ess1688r(blaster, 0xB8);
|
||
ess1688w(blaster, 0xB8, x|0x05);
|
||
|
||
ctlr->active = 1;
|
||
if(dmasetup(ctlr->conf.dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){
|
||
ctlr->active = 0;
|
||
print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno);
|
||
}
|
||
iunlock(blaster);
|
||
}
|
||
|
||
static void
|
||
sb16intr(Ctlr *ctlr)
|
||
{
|
||
Blaster *blaster;
|
||
int stat;
|
||
|
||
blaster = &ctlr->blaster;
|
||
ilock(blaster);
|
||
stat = mxread(blaster, 0x82); /* get irq status */
|
||
if(stat & 3){
|
||
contindma(ctlr);
|
||
if(stat & 2)
|
||
inb(blaster->clri16);
|
||
else if(stat & 1)
|
||
inb(blaster->clri8);
|
||
} else if(stat & 4)
|
||
inb(blaster->clri401);
|
||
iunlock(blaster);
|
||
}
|
||
|
||
static void
|
||
ess1688intr(Ctlr *ctlr)
|
||
{
|
||
Blaster *blaster;
|
||
|
||
blaster = &ctlr->blaster;
|
||
ilock(blaster);
|
||
contindma(ctlr);
|
||
inb(blaster->clri8);
|
||
iunlock(blaster);
|
||
}
|
||
|
||
static void
|
||
audiointr(Ureg *, void *arg)
|
||
{
|
||
Audio *adev;
|
||
Ctlr *ctlr;
|
||
|
||
adev = arg;
|
||
ctlr = adev->ctlr;
|
||
if(ctlr == nil || ctlr->adev != adev)
|
||
return;
|
||
if(!ctlr->active){
|
||
iprint("#A%d: unexpected %s interrupt\n",
|
||
ctlr->adev->ctlrno, ctlr->adev->name);
|
||
return;
|
||
}
|
||
ctlr->blaster.intr(ctlr);
|
||
}
|
||
|
||
static void
|
||
setempty(Ctlr *ctlr)
|
||
{
|
||
ilock(&ctlr->blaster);
|
||
ctlr->ring.ri = 0;
|
||
ctlr->ring.wi = 0;
|
||
iunlock(&ctlr->blaster);
|
||
}
|
||
|
||
static long
|
||
audiobuffered(Audio *adev)
|
||
{
|
||
return buffered(&((Ctlr*)adev->ctlr)->ring);
|
||
}
|
||
|
||
static long
|
||
audiostatus(Audio *adev, void *a, long n, vlong)
|
||
{
|
||
Ctlr *ctlr = adev->ctlr;
|
||
return snprint((char*)a, n, "bufsize %6d buffered %6ld\n",
|
||
Blocksize, buffered(&ctlr->ring));
|
||
}
|
||
|
||
static int
|
||
inactive(void *arg)
|
||
{
|
||
Ctlr *ctlr = arg;
|
||
return !ctlr->active;
|
||
}
|
||
|
||
static int
|
||
anybuf(void *arg)
|
||
{
|
||
Ctlr *ctlr = arg;
|
||
return available(&ctlr->ring) || inactive(ctlr);
|
||
}
|
||
|
||
static int
|
||
ratebuf(void *arg)
|
||
{
|
||
Ctlr *ctlr = arg;
|
||
int delay = ctlr->adev->delay*4;
|
||
return (delay <= 0) || (buffered(&ctlr->ring) <= delay) || inactive(ctlr);
|
||
}
|
||
|
||
static long
|
||
audiowrite(Audio *adev, void *vp, long n, vlong)
|
||
{
|
||
uchar *p, *e;
|
||
Ctlr *ctlr;
|
||
Ring *ring;
|
||
|
||
p = vp;
|
||
e = p + n;
|
||
ctlr = adev->ctlr;
|
||
ring = &ctlr->ring;
|
||
while(p < e) {
|
||
if((n = writering(ring, p, e - p)) <= 0){
|
||
if(!ctlr->active && ring->ri == 0)
|
||
ctlr->blaster.startdma(ctlr);
|
||
if(!ctlr->active)
|
||
setempty(ctlr);
|
||
else
|
||
sleep(&ctlr->vous, anybuf, ctlr);
|
||
}
|
||
p += n;
|
||
}
|
||
while(ratebuf(ctlr) == 0)
|
||
sleep(&ctlr->vous, ratebuf, ctlr);
|
||
return p - (uchar*)vp;
|
||
}
|
||
|
||
static void
|
||
audioclose(Audio *adev, int mode)
|
||
{
|
||
Ctlr *ctlr;
|
||
|
||
if(mode == OREAD)
|
||
return;
|
||
ctlr = adev->ctlr;
|
||
sleep(&ctlr->vous, inactive, ctlr);
|
||
setempty(ctlr);
|
||
}
|
||
|
||
static long
|
||
audiovolread(Audio *adev, void *a, long n, vlong)
|
||
{
|
||
return genaudiovolread(adev, a, n, 0, voltab, mxgetvol, 0);
|
||
}
|
||
|
||
static long
|
||
audiovolwrite(Audio *adev, void *a, long n, vlong)
|
||
{
|
||
Blaster *blaster;
|
||
Ctlr *ctlr;
|
||
int source;
|
||
|
||
ctlr = adev->ctlr;
|
||
blaster = &ctlr->blaster;
|
||
|
||
n = genaudiovolwrite(adev, a, n, 0, voltab, mxsetvol, 0);
|
||
|
||
source = 0;
|
||
if(ctlr->lvol[Vsynth])
|
||
source |= 1<<6;
|
||
if(ctlr->rvol[Vsynth])
|
||
source |= 1<<5;
|
||
if(ctlr->lvol[Vaudio])
|
||
source |= 1<<4;
|
||
if(ctlr->rvol[Vaudio])
|
||
source |= 1<<3;
|
||
if(ctlr->lvol[Vcd])
|
||
source |= 1<<2;
|
||
if(ctlr->rvol[Vcd])
|
||
source |= 1<<1;
|
||
if(ctlr->lvol[Vmic])
|
||
source |= 1<<0;
|
||
|
||
ilock(blaster);
|
||
mxcmd(blaster, 0x3c, source); /* output switch */
|
||
mxcmd(blaster, 0x3d, source); /* input left switch */
|
||
mxcmd(blaster, 0x3e, source); /* input right switch */
|
||
iunlock(blaster);
|
||
|
||
return n;
|
||
}
|
||
|
||
static int
|
||
ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
|
||
{
|
||
int i, major, minor;
|
||
|
||
/*
|
||
* Try for ESS1688.
|
||
*/
|
||
sbcmd(blaster, 0xE7); /* get version */
|
||
major = sbread(blaster);
|
||
minor = sbread(blaster);
|
||
if(major != 0x68 || minor != 0x8B){
|
||
print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n",
|
||
ctlrno, major, minor);
|
||
return -1;
|
||
}
|
||
|
||
ess1688reset(blaster, ctlrno);
|
||
|
||
switch(sbconf->irq){
|
||
case 2:
|
||
case 9:
|
||
i = 0x50|(0<<2);
|
||
break;
|
||
case 5:
|
||
i = 0x50|(1<<2);
|
||
break;
|
||
case 7:
|
||
i = 0x50|(2<<2);
|
||
break;
|
||
case 10:
|
||
i = 0x50|(3<<2);
|
||
break;
|
||
default:
|
||
print("#A%d: bad ESS1688 irq %d\n", ctlrno, sbconf->irq);
|
||
return 1;
|
||
}
|
||
ess1688w(blaster, 0xB1, i);
|
||
|
||
switch(sbconf->dma){
|
||
case 0:
|
||
i = 0x50|(1<<2);
|
||
break;
|
||
case 1:
|
||
i = 0xF0|(2<<2);
|
||
break;
|
||
case 3:
|
||
i = 0x50|(3<<2);
|
||
break;
|
||
default:
|
||
print("#A%d: bad ESS1688 dma %lud\n", ctlrno, sbconf->dma);
|
||
return 1;
|
||
}
|
||
ess1688w(blaster, 0xB2, i);
|
||
|
||
ess1688reset(blaster, ctlrno);
|
||
|
||
blaster->startdma = ess1688startdma;
|
||
blaster->intr = ess1688intr;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int irqmap[] = {9,5,7,10};
|
||
|
||
static int
|
||
reset1(Audio *adev, Ctlr *ctlr)
|
||
{
|
||
Blaster *blaster;
|
||
int i, x;
|
||
|
||
switch(ctlr->conf.port){
|
||
case 0x220:
|
||
case 0x240:
|
||
case 0x260:
|
||
case 0x280:
|
||
break;
|
||
default:
|
||
print("#A%d: bad port %lux\n", adev->ctlrno, (ulong)ctlr->conf.port);
|
||
return -1;
|
||
}
|
||
|
||
if(ioalloc(ctlr->conf.port, 0x10, 0, "audio") < 0){
|
||
print("#A%d: cannot ioalloc range %lux+0x10\n",
|
||
adev->ctlrno, (ulong)ctlr->conf.port);
|
||
return -1;
|
||
}
|
||
if(ioalloc(ctlr->conf.port+0x100, 1, 0, "audio.mpu401") < 0){
|
||
iofree(ctlr->conf.port);
|
||
print("#A%d: cannot ioalloc range %lux+0x01\n",
|
||
adev->ctlrno, (ulong)ctlr->conf.port+0x100);
|
||
return -1;
|
||
}
|
||
|
||
blaster = &ctlr->blaster;
|
||
blaster->reset = ctlr->conf.port + 0x6;
|
||
blaster->read = ctlr->conf.port + 0xa;
|
||
blaster->write = ctlr->conf.port + 0xc;
|
||
blaster->wstatus = ctlr->conf.port + 0xc;
|
||
blaster->rstatus = ctlr->conf.port + 0xe;
|
||
blaster->mixaddr = ctlr->conf.port + 0x4;
|
||
blaster->mixdata = ctlr->conf.port + 0x5;
|
||
blaster->clri8 = ctlr->conf.port + 0xe;
|
||
blaster->clri16 = ctlr->conf.port + 0xf;
|
||
blaster->clri401 = ctlr->conf.port + 0x100;
|
||
|
||
blaster->startdma = sb16startdma;
|
||
blaster->intr = sb16intr;
|
||
|
||
outb(blaster->reset, 1);
|
||
delay(1); /* >3 υs */
|
||
outb(blaster->reset, 0);
|
||
delay(1);
|
||
|
||
i = sbread(blaster);
|
||
if(i != 0xaa) {
|
||
print("#A%d: no response #%.2x\n", adev->ctlrno, i);
|
||
Errout:
|
||
iofree(ctlr->conf.port);
|
||
iofree(ctlr->conf.port+0x100);
|
||
return -1;
|
||
}
|
||
|
||
sbcmd(blaster, 0xe1); /* get version */
|
||
ctlr->major = sbread(blaster);
|
||
ctlr->minor = sbread(blaster);
|
||
|
||
if(ctlr->major != 4) {
|
||
if(ctlr->major != 3 || ctlr->minor != 1 ||
|
||
ess1688(&ctlr->conf, blaster, adev->ctlrno)){
|
||
print("#A%d: model %#.2x %#.2x; not SB 16 compatible\n",
|
||
adev->ctlrno, ctlr->major, ctlr->minor);
|
||
goto Errout;
|
||
}
|
||
ctlr->major = 4;
|
||
}
|
||
|
||
/*
|
||
* initialize the mixer
|
||
*/
|
||
mxcmd(blaster, 0x00, 0); /* Reset mixer */
|
||
|
||
for(i=0; i<Nvol; i++){
|
||
int a[2];
|
||
|
||
a[0] = 0;
|
||
a[1] = 0;
|
||
mxsetvol(adev, i, a);
|
||
}
|
||
|
||
/* set irq */
|
||
for(i=0; i<nelem(irqmap); i++){
|
||
if(ctlr->conf.irq == irqmap[i]){
|
||
mxcmd(blaster, 0x80, 1<<i);
|
||
break;
|
||
}
|
||
}
|
||
x = mxread(blaster, 0x80);
|
||
for(i=0; i<nelem(irqmap); i++){
|
||
if(x & (1<<i)){
|
||
ctlr->conf.irq = irqmap[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
for(;;){
|
||
/* set 16bit dma */
|
||
if(ctlr->conf.dma>=5 && ctlr->conf.dma<=7){
|
||
x = mxread(blaster, 0x81);
|
||
mxcmd(blaster, 0x81, (1<<ctlr->conf.dma) & 0xF0 | (x & 0x0F));
|
||
}
|
||
x = mxread(blaster, 0x81);
|
||
for(i=5; i<=7; i++){
|
||
if(x & (1<<i)){
|
||
ctlr->conf.dma = i;
|
||
break;
|
||
}
|
||
}
|
||
if(ctlr->conf.dma>=5)
|
||
break;
|
||
ctlr->conf.dma = 7;
|
||
}
|
||
|
||
print("#A%d: %s port 0x%04lux irq %d dma %lud\n", adev->ctlrno, adev->name,
|
||
(ulong)ctlr->conf.port, ctlr->conf.irq, ctlr->conf.dma);
|
||
|
||
ctlr->ring.nbuf = Blocks*Blocksize;
|
||
if(dmainit(ctlr->conf.dma, ctlr->ring.nbuf))
|
||
goto Errout;
|
||
ctlr->ring.buf = dmabva(ctlr->conf.dma);
|
||
print("#A%d: %s dma buffer %p-%p\n", adev->ctlrno, adev->name,
|
||
ctlr->ring.buf, ctlr->ring.buf+ctlr->ring.nbuf);
|
||
|
||
setempty(ctlr);
|
||
|
||
adev->ctlr = ctlr;
|
||
adev->write = audiowrite;
|
||
adev->close = audioclose;
|
||
adev->volread = audiovolread;
|
||
adev->volwrite = audiovolwrite;
|
||
adev->status = audiostatus;
|
||
adev->buffered = audiobuffered;
|
||
|
||
intrenable(ctlr->conf.irq, audiointr, adev, BUSUNKNOWN, adev->name);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
audioprobe(Audio *adev)
|
||
{
|
||
static Ctlr *cards = nil;
|
||
Ctlr *ctlr;
|
||
int i;
|
||
|
||
/* make a list of audio isa cards if not already done */
|
||
if(cards == nil){
|
||
for(i=0; i<nelem(irqmap); i++){
|
||
ctlr = mallocz(sizeof(Ctlr), 1);
|
||
if(ctlr == nil){
|
||
print("sb16: can't allocate memory\n");
|
||
break;
|
||
}
|
||
ctlr->conf.port = 0x220 + i*0x10;
|
||
ctlr->conf.irq = irqmap[i];
|
||
ctlr->conf.dma = 0;
|
||
if(!isaconfig("audio", i, &ctlr->conf)){
|
||
free(ctlr);
|
||
break;
|
||
}
|
||
ctlr->next = cards;
|
||
cards = ctlr;
|
||
}
|
||
}
|
||
|
||
/* pick a card */
|
||
for(ctlr = cards; ctlr; ctlr = ctlr->next){
|
||
if(ctlr->adev == nil && strcmp(adev->name, ctlr->conf.type) == 0){
|
||
ctlr->adev = adev;
|
||
if(reset1(adev, ctlr) == 0)
|
||
return 0;
|
||
ctlr->adev = (void*)-1;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
void
|
||
audiosb16link(void)
|
||
{
|
||
addaudiocard("sb16", audioprobe);
|
||
addaudiocard("ess1688", audioprobe);
|
||
}
|