plan9front/sys/src/9/pc/audiosb16.c

867 lines
15 KiB
C
Raw Normal View History

/*
* 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"
2011-05-20 11:30:46 -07:00
#include "../port/audioif.h"
2011-05-18 12:57:31 -07:00
typedef struct Ring Ring;
typedef struct Blaster Blaster;
typedef struct Ctlr Ctlr;
enum
{
2011-05-20 11:30:46 -07:00
Vmaster,
Vaudio,
Vsynth,
Vcd,
Vline,
Vmic,
Vspeaker,
Vtreb,
Vbass,
2011-05-20 11:30:46 -07:00
Vigain,
Vogain,
Vspeed,
2011-07-02 18:42:37 -07:00
Vdelay,
Nvol,
2011-05-18 12:57:31 -07:00
Blocksize = 4096,
Blocks = 65536/Blocksize,
};
2011-05-18 12:57:31 -07:00
struct Ring
{
2011-05-18 12:57:31 -07:00
uchar *buf;
ulong nbuf;
ulong ri;
ulong wi;
};
2011-05-18 12:57:31 -07:00
struct Blaster
{
Lock;
2011-05-18 12:57:31 -07:00
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*);
};
2011-05-18 12:57:31 -07:00
struct Ctlr
{
Rendez vous;
int active; /* boolean dma running */
int major; /* SB16 major version number (sb 4) */
int minor; /* SB16 minor version number */
2011-05-18 12:57:31 -07:00
Ring ring; /* dma ring buffer */
Blaster blaster;
2011-05-20 13:17:46 -07:00
2011-05-20 11:30:46 -07:00
int lvol[Nvol];
int rvol[Nvol];
2011-05-20 13:17:46 -07:00
/* for probe */
2011-05-18 12:57:31 -07:00
Audio *adev;
2011-05-20 13:17:46 -07:00
ISAConf conf;
Ctlr *next;
2011-05-18 12:57:31 -07:00
};
2011-05-20 11:30:46 -07:00
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,
2011-07-02 18:42:37 -07:00
[Vdelay] "delay", 0, 0, Absolute, 0,
2011-05-20 11:30:46 -07:00
0,
2011-05-18 12:57:31 -07:00
};
static char Emajor[] = "soundblaster not responding/wrong version";
static long
buffered(Ring *r)
{
2011-05-18 12:57:31 -07:00
ulong ri, wi;
ri = r->ri;
wi = r->wi;
if(wi >= ri)
return wi - ri;
else
return r->nbuf - (ri - wi);
}
2011-05-18 12:57:31 -07:00
static long
available(Ring *r)
{
long m;
2011-05-18 12:57:31 -07:00
m = (r->nbuf - 1) - buffered(r);
if(m < 0)
m = 0;
return m;
}
2011-05-18 12:57:31 -07:00
static long
readring(Ring *r, uchar *p, long n)
{
2011-05-18 12:57:31 -07:00
long n0, m;
2011-05-18 12:57:31 -07:00
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;
}
2011-05-18 12:57:31 -07:00
static long
writering(Ring *r, uchar *p, long n)
{
long n0, m;
2011-05-18 12:57:31 -07:00
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
2011-05-18 12:57:31 -07:00
sbcmd(Blaster *blaster, int val)
{
int i, s;
for(i=1<<16; i!=0; i--) {
2011-05-18 12:57:31 -07:00
s = inb(blaster->wstatus);
if((s & 0x80) == 0) {
2011-05-18 12:57:31 -07:00
outb(blaster->write, val);
return 0;
}
}
return 1;
}
static int
2011-05-18 12:57:31 -07:00
sbread(Blaster *blaster)
{
int i, s;
for(i=1<<16; i!=0; i--) {
2011-05-18 12:57:31 -07:00
s = inb(blaster->rstatus);
if((s & 0x80) != 0) {
2011-05-18 12:57:31 -07:00
return inb(blaster->read);
}
}
return -1;
}
static int
2011-05-18 12:57:31 -07:00
ess1688w(Blaster *blaster, int reg, int val)
{
2011-05-18 12:57:31 -07:00
if(sbcmd(blaster, reg) || sbcmd(blaster, val))
return 1;
return 0;
}
static int
2011-05-18 12:57:31 -07:00
ess1688r(Blaster *blaster, int reg)
{
2011-05-18 12:57:31 -07:00
if(sbcmd(blaster, 0xC0) || sbcmd(blaster, reg))
return -1;
2011-05-18 12:57:31 -07:00
return sbread(blaster);
}
static int
2011-05-18 12:57:31 -07:00
mxcmd(Blaster *blaster, int addr, int val)
{
2011-05-18 12:57:31 -07:00
outb(blaster->mixaddr, addr);
outb(blaster->mixdata, val);
return 1;
}
static int
2011-05-18 12:57:31 -07:00
mxread(Blaster *blaster, int addr)
{
int s;
2011-05-18 12:57:31 -07:00
outb(blaster->mixaddr, addr);
s = inb(blaster->mixdata);
return s;
}
2011-05-20 11:30:46 -07:00
static int
mxsetvol(Audio *adev, int x, int a[2])
{
2011-05-18 12:57:31 -07:00
Blaster *blaster;
2011-05-20 11:30:46 -07:00
Ctlr *ctlr = adev->ctlr;
Volume *vol;
2011-05-20 11:30:46 -07:00
vol = voltab+x;
2011-05-18 12:57:31 -07:00
blaster = &ctlr->blaster;
ilock(blaster);
2011-05-20 11:30:46 -07:00
switch(vol->type){
2011-07-02 18:42:37 -07:00
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;
2011-05-20 11:30:46 -07:00
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);
2011-05-20 11:30:46 -07:00
return 0;
}
2011-05-20 11:30:46 -07:00
static int
mxgetvol(Audio *adev, int x, int a[2])
{
Ctlr *ctlr = adev->ctlr;
2011-05-20 11:30:46 -07:00
a[0] = ctlr->lvol[x];
a[1] = ctlr->rvol[x];
2011-05-20 11:30:46 -07:00
return 0;
}
static void
2011-05-18 12:57:31 -07:00
contindma(Ctlr *ctlr)
{
2011-05-18 12:57:31 -07:00
Blaster *blaster;
Ring *ring;
2011-05-18 12:57:31 -07:00
blaster = &ctlr->blaster;
ring = &ctlr->ring;
2011-07-02 20:56:53 -07:00
if(buffered(ring) >= Blocksize)
2011-05-20 13:17:46 -07:00
ring->ri = ring->nbuf - dmacount(ctlr->conf.dma);
2011-07-02 20:56:53 -07:00
else{
2011-05-20 13:17:46 -07:00
dmaend(ctlr->conf.dma);
2011-05-18 12:57:31 -07:00
sbcmd(blaster, 0xd9); /* exit at end of count */
sbcmd(blaster, 0xd5); /* pause */
ctlr->active = 0;
}
2011-05-18 12:57:31 -07:00
wakeup(&ctlr->vous);
}
/*
* cause sb to get an interrupt per buffer.
* start first dma
*/
static void
2011-05-18 12:57:31 -07:00
sb16startdma(Ctlr *ctlr)
{
2011-05-18 12:57:31 -07:00
Blaster *blaster;
Ring *ring;
long count;
int speed;
2011-05-18 12:57:31 -07:00
blaster = &ctlr->blaster;
ring = &ctlr->ring;
ilock(blaster);
2011-05-20 13:17:46 -07:00
dmaend(ctlr->conf.dma);
2011-05-20 11:30:46 -07:00
if(0)
sbcmd(blaster, 0x42); /* input sampling rate */
else
sbcmd(blaster, 0x41); /* output sampling rate */
2011-07-02 18:42:37 -07:00
speed = ctlr->adev->speed;
2011-05-18 12:57:31 -07:00
sbcmd(blaster, speed>>8);
sbcmd(blaster, speed);
if(0)
2011-05-20 11:30:46 -07:00
sbcmd(blaster, 0xbe); /* A/D, autoinit */
else
2011-05-20 11:30:46 -07:00
sbcmd(blaster, 0xb6); /* D/A, autoinit */
2011-05-16 15:09:39 -07:00
2011-05-20 11:30:46 -07:00
sbcmd(blaster, 0x30); /* stereo, signed 16 bit */
2011-05-18 12:57:31 -07:00
count = (Blocksize>>1) - 1;
sbcmd(blaster, count);
sbcmd(blaster, count>>8);
2011-05-18 12:57:31 -07:00
ctlr->active = 1;
2011-05-20 13:17:46 -07:00
if(dmasetup(ctlr->conf.dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){
2011-05-18 12:57:31 -07:00
ctlr->active = 0;
print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno);
}
iunlock(blaster);
}
static int
2011-05-18 12:57:31 -07:00
ess1688reset(Blaster *blaster, int ctlrno)
{
int i;
2011-05-18 12:57:31 -07:00
outb(blaster->reset, 3);
2011-05-20 11:30:46 -07:00
delay(1); /* >3 υs */
2011-05-18 12:57:31 -07:00
outb(blaster->reset, 0);
delay(1);
2011-05-18 12:57:31 -07:00
i = sbread(blaster);
if(i != 0xAA) {
2011-05-18 12:57:31 -07:00
print("#A%d: no response %#.2x\n", ctlrno, i);
return 1;
}
2011-05-20 11:30:46 -07:00
if(sbcmd(blaster, 0xC6)){ /* extended mode */
2011-05-18 12:57:31 -07:00
print("#A%d: barf 3\n", ctlrno);
return 1;
}
return 0;
}
static void
2011-05-18 12:57:31 -07:00
ess1688startdma(Ctlr *ctlr)
{
2011-05-18 12:57:31 -07:00
Blaster *blaster;
Ring *ring;
ulong count;
int speed, x;
2011-05-18 12:57:31 -07:00
blaster = &ctlr->blaster;
ring = &ctlr->ring;
ilock(blaster);
2011-05-20 13:17:46 -07:00
dmaend(ctlr->conf.dma);
2011-05-18 12:57:31 -07:00
ess1688reset(blaster, ctlr->adev->ctlrno);
/*
* Set the speed.
*/
2011-07-02 18:42:37 -07:00
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;
2011-05-18 12:57:31 -07:00
ess1688w(blaster, 0xA1, x & 0xFF);
speed = (speed * 9) / 20;
x = 256 - 7160000 / (speed * 82);
2011-05-18 12:57:31 -07:00
ess1688w(blaster, 0xA2, x & 0xFF);
if(0)
2011-05-20 11:30:46 -07:00
ess1688w(blaster, 0xB8, 0x0E); /* A/D, autoinit */
else
2011-05-20 11:30:46 -07:00
ess1688w(blaster, 0xB8, 0x04); /* D/A, autoinit */
2011-05-18 12:57:31 -07:00
x = ess1688r(blaster, 0xA8) & ~0x03;
2011-05-20 11:30:46 -07:00
ess1688w(blaster, 0xA8, x|0x01); /* 2 channels */
ess1688w(blaster, 0xB9, 2); /* demand mode, 4 bytes per request */
if(1)
2011-05-20 11:30:46 -07:00
ess1688w(blaster, 0xB6, 0); /* for output */
2011-05-18 12:57:31 -07:00
ess1688w(blaster, 0xB7, 0x71);
ess1688w(blaster, 0xB7, 0xBC);
2011-05-18 12:57:31 -07:00
x = ess1688r(blaster, 0xB1) & 0x0F;
ess1688w(blaster, 0xB1, x|0x50);
x = ess1688r(blaster, 0xB2) & 0x0F;
ess1688w(blaster, 0xB2, x|0x50);
if(1)
2011-05-20 11:30:46 -07:00
sbcmd(blaster, 0xD1); /* speaker on */
2011-05-18 12:57:31 -07:00
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;
2011-05-20 13:17:46 -07:00
if(dmasetup(ctlr->conf.dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){
2011-05-18 12:57:31 -07:00
ctlr->active = 0;
print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno);
}
iunlock(blaster);
}
static void
2011-05-18 12:57:31 -07:00
sb16intr(Ctlr *ctlr)
{
2011-05-18 12:57:31 -07:00
Blaster *blaster;
2011-05-16 15:09:39 -07:00
int stat;
2011-05-18 12:57:31 -07:00
blaster = &ctlr->blaster;
ilock(blaster);
stat = mxread(blaster, 0x82); /* get irq status */
if(stat & 3){
contindma(ctlr);
2011-05-16 15:09:39 -07:00
if(stat & 2)
2011-05-18 12:57:31 -07:00
inb(blaster->clri16);
2011-05-16 15:09:39 -07:00
else if(stat & 1)
2011-05-18 12:57:31 -07:00
inb(blaster->clri8);
} else if(stat & 4)
inb(blaster->clri401);
iunlock(blaster);
}
static void
2011-05-18 12:57:31 -07:00
ess1688intr(Ctlr *ctlr)
{
2011-05-18 12:57:31 -07:00
Blaster *blaster;
2011-05-18 12:57:31 -07:00
blaster = &ctlr->blaster;
ilock(blaster);
contindma(ctlr);
inb(blaster->clri8);
iunlock(blaster);
}
2011-05-18 12:57:31 -07:00
static void
audiointr(Ureg *, void *arg)
{
2011-05-18 12:57:31 -07:00
Audio *adev;
Ctlr *ctlr;
2011-05-18 12:57:31 -07:00
adev = arg;
ctlr = adev->ctlr;
if(ctlr == nil || ctlr->adev != adev)
return;
2011-05-18 12:57:31 -07:00
if(!ctlr->active){
iprint("#A%d: unexpected %s interrupt\n",
ctlr->adev->ctlrno, ctlr->adev->name);
return;
}
2011-05-18 12:57:31 -07:00
ctlr->blaster.intr(ctlr);
}
static void
2011-05-18 12:57:31 -07:00
setempty(Ctlr *ctlr)
{
2011-05-18 12:57:31 -07:00
ilock(&ctlr->blaster);
ctlr->ring.ri = 0;
ctlr->ring.wi = 0;
iunlock(&ctlr->blaster);
}
static long
2011-05-18 12:57:31 -07:00
audiobuffered(Audio *adev)
{
2011-05-18 12:57:31 -07:00
return buffered(&((Ctlr*)adev->ctlr)->ring);
}
static long
2011-05-20 11:30:46 -07:00
audiostatus(Audio *adev, void *a, long n, vlong)
{
2011-05-20 11:30:46 -07:00
Ctlr *ctlr = adev->ctlr;
2011-07-02 20:56:53 -07:00
return snprint((char*)a, n, "bufsize %6d buffered %6ld\n",
Blocksize, buffered(&ctlr->ring));
}
2011-05-18 12:57:31 -07:00
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);
}
2011-07-02 18:42:37 -07:00
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
2011-05-18 12:57:31 -07:00
audiowrite(Audio *adev, void *vp, long n, vlong)
{
2011-05-18 12:57:31 -07:00
uchar *p, *e;
Ctlr *ctlr;
Ring *ring;
2011-05-18 12:57:31 -07:00
p = vp;
e = p + n;
ctlr = adev->ctlr;
ring = &ctlr->ring;
while(p < e) {
2011-05-20 11:30:46 -07:00
if((n = writering(ring, p, e - p)) <= 0){
2011-05-18 12:57:31 -07:00
if(!ctlr->active && ring->ri == 0)
ctlr->blaster.startdma(ctlr);
2011-05-20 11:30:46 -07:00
if(!ctlr->active)
2011-05-18 12:57:31 -07:00
setempty(ctlr);
2011-05-20 11:30:46 -07:00
else
sleep(&ctlr->vous, anybuf, ctlr);
}
2011-05-20 11:30:46 -07:00
p += n;
}
while(ratebuf(ctlr) == 0)
sleep(&ctlr->vous, ratebuf, ctlr);
2011-05-18 12:57:31 -07:00
return p - (uchar*)vp;
}
static void
audioclose(Audio *adev, int mode)
{
2011-05-18 12:57:31 -07:00
Ctlr *ctlr;
if(mode == OREAD)
return;
2011-05-18 12:57:31 -07:00
ctlr = adev->ctlr;
sleep(&ctlr->vous, inactive, ctlr);
setempty(ctlr);
}
2011-05-20 11:30:46 -07:00
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
2011-05-18 12:57:31 -07:00
ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
{
int i, major, minor;
/*
* Try for ESS1688.
*/
2011-05-18 12:57:31 -07:00
sbcmd(blaster, 0xE7); /* get version */
major = sbread(blaster);
minor = sbread(blaster);
if(major != 0x68 || minor != 0x8B){
2011-05-20 11:30:46 -07:00
print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n",
ctlrno, major, minor);
2011-05-18 12:57:31 -07:00
return -1;
}
2011-05-18 12:57:31 -07:00
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:
2011-05-18 12:57:31 -07:00
print("#A%d: bad ESS1688 irq %d\n", ctlrno, sbconf->irq);
return 1;
}
2011-05-18 12:57:31 -07:00
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:
2011-05-18 12:57:31 -07:00
print("#A%d: bad ESS1688 dma %lud\n", ctlrno, sbconf->dma);
return 1;
}
2011-05-18 12:57:31 -07:00
ess1688w(blaster, 0xB2, i);
2011-05-18 12:57:31 -07:00
ess1688reset(blaster, ctlrno);
2011-05-18 12:57:31 -07:00
blaster->startdma = ess1688startdma;
blaster->intr = ess1688intr;
return 0;
}
static int irqmap[] = {9,5,7,10};
static int
reset1(Audio *adev, Ctlr *ctlr)
{
2011-05-18 12:57:31 -07:00
Blaster *blaster;
int i, x;
2011-05-20 13:17:46 -07:00
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;
}
2011-05-20 13:17:46 -07:00
if(ioalloc(ctlr->conf.port, 0x10, 0, "audio") < 0){
2011-05-20 11:30:46 -07:00
print("#A%d: cannot ioalloc range %lux+0x10\n",
adev->ctlrno, (ulong)ctlr->conf.port);
return -1;
}
2011-05-20 13:17:46 -07:00
if(ioalloc(ctlr->conf.port+0x100, 1, 0, "audio.mpu401") < 0){
iofree(ctlr->conf.port);
2011-05-20 11:30:46 -07:00
print("#A%d: cannot ioalloc range %lux+0x01\n",
adev->ctlrno, (ulong)ctlr->conf.port+0x100);
return -1;
}
2011-05-18 12:57:31 -07:00
blaster = &ctlr->blaster;
2011-05-20 13:17:46 -07:00
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;
2011-05-18 12:57:31 -07:00
blaster->startdma = sb16startdma;
blaster->intr = sb16intr;
outb(blaster->reset, 1);
delay(1); /* >3 υs */
outb(blaster->reset, 0);
delay(1);
2011-05-18 12:57:31 -07:00
i = sbread(blaster);
if(i != 0xaa) {
2011-05-18 12:57:31 -07:00
print("#A%d: no response #%.2x\n", adev->ctlrno, i);
2011-05-20 11:30:46 -07:00
Errout:
2011-05-20 13:17:46 -07:00
iofree(ctlr->conf.port);
iofree(ctlr->conf.port+0x100);
return -1;
}
2011-05-18 12:57:31 -07:00
sbcmd(blaster, 0xe1); /* get version */
ctlr->major = sbread(blaster);
ctlr->minor = sbread(blaster);
2011-05-18 12:57:31 -07:00
if(ctlr->major != 4) {
2011-05-20 11:30:46 -07:00
if(ctlr->major != 3 || ctlr->minor != 1 ||
2011-05-20 13:17:46 -07:00
ess1688(&ctlr->conf, blaster, adev->ctlrno)){
print("#A%d: model %#.2x %#.2x; not SB 16 compatible\n",
2011-05-18 12:57:31 -07:00
adev->ctlrno, ctlr->major, ctlr->minor);
2011-05-20 11:30:46 -07:00
goto Errout;
}
2011-05-18 12:57:31 -07:00
ctlr->major = 4;
}
/*
* initialize the mixer
*/
2011-05-18 12:57:31 -07:00
mxcmd(blaster, 0x00, 0); /* Reset mixer */
2011-05-20 11:30:46 -07:00
for(i=0; i<Nvol; i++){
int a[2];
a[0] = 0;
a[1] = 0;
mxsetvol(adev, i, a);
}
2011-05-18 12:57:31 -07:00
/* set irq */
for(i=0; i<nelem(irqmap); i++){
if(ctlr->conf.irq == irqmap[i]){
2011-05-18 12:57:31 -07:00
mxcmd(blaster, 0x80, 1<<i);
break;
}
2011-05-16 15:09:39 -07:00
}
2011-05-18 12:57:31 -07:00
x = mxread(blaster, 0x80);
for(i=0; i<nelem(irqmap); i++){
if(x & (1<<i)){
ctlr->conf.irq = irqmap[i];
break;
}
2011-05-18 12:57:31 -07:00
}
2011-05-18 12:57:31 -07:00
for(;;){
/* set 16bit dma */
2011-05-20 13:17:46 -07:00
if(ctlr->conf.dma>=5 && ctlr->conf.dma<=7){
2011-05-18 12:57:31 -07:00
x = mxread(blaster, 0x81);
2011-05-20 13:17:46 -07:00
mxcmd(blaster, 0x81, (1<<ctlr->conf.dma) & 0xF0 | (x & 0x0F));
2011-05-18 12:57:31 -07:00
}
x = mxread(blaster, 0x81);
for(i=5; i<=7; i++){
if(x & (1<<i)){
2011-05-20 13:17:46 -07:00
ctlr->conf.dma = i;
2011-05-16 15:09:39 -07:00
break;
}
}
2011-05-20 13:17:46 -07:00
if(ctlr->conf.dma>=5)
2011-05-20 11:30:46 -07:00
break;
2011-05-20 13:17:46 -07:00
ctlr->conf.dma = 7;
2011-05-18 12:57:31 -07:00
}
2011-05-16 15:09:39 -07:00
2011-05-20 13:17:46 -07:00
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);
2011-05-18 12:57:31 -07:00
ctlr->ring.nbuf = Blocks*Blocksize;
2011-05-20 13:17:46 -07:00
if(dmainit(ctlr->conf.dma, ctlr->ring.nbuf))
2011-05-20 11:30:46 -07:00
goto Errout;
2011-05-20 13:17:46 -07:00
ctlr->ring.buf = dmabva(ctlr->conf.dma);
print("#A%d: %s dma buffer %p-%p\n", adev->ctlrno, adev->name,
2011-05-20 11:30:46 -07:00
ctlr->ring.buf, ctlr->ring.buf+ctlr->ring.nbuf);
2011-05-16 09:30:07 -07:00
2011-05-18 12:57:31 -07:00
setempty(ctlr);
2011-05-16 09:30:07 -07:00
adev->ctlr = ctlr;
2011-05-16 15:09:39 -07:00
adev->write = audiowrite;
adev->close = audioclose;
2011-05-20 11:30:46 -07:00
adev->volread = audiovolread;
adev->volwrite = audiovolwrite;
2011-05-16 15:09:39 -07:00
adev->status = audiostatus;
adev->buffered = audiobuffered;
2011-05-20 13:17:46 -07:00
intrenable(ctlr->conf.irq, audiointr, adev, BUSUNKNOWN, adev->name);
2011-05-20 11:30:46 -07:00
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);
2011-05-18 12:57:31 -07:00
addaudiocard("ess1688", audioprobe);
}