ref: 9e28bf1a630f97b4fc1a72392cddf5789b592847
parent: 4ed4ae495dbb4fd8d8bbcc879d70fac09025c30e
author: cinap_lenrek <[email protected]>
date: Sun Feb 19 14:43:49 EST 2012
audiohda: make it work with sb600 onboard sound
--- a/sys/src/9/pc/audiohda.c
+++ b/sys/src/9/pc/audiohda.c
@@ -32,7 +32,6 @@
Intsts = 0x24,
Gis = 1<<31,
Cis = 1<<30,
- Sismask = 0xff,
Walclk = 0x30,
Corblbase = 0x40,
Corbubase = 0x44,
@@ -63,17 +62,8 @@
Immstat = 0x68,
Dplbase = 0x70,
Dpubase = 0x74,
- /* stream register base */
- Sdinput0 = 0x80,
- Sdinput1 = 0xa0,
- Sdinput2 = 0xc0,
- Sdinput3 = 0xe0,
- Sdoutput0 = 0x100,
- Sdoutput1 = 0x120,
- Sdoutput2 = 0x140,
- Sdoutput3 = 0x160,
/* Warning: Sdctl is 24bit register */
- Sdctl = Sdoutput0 + 0x00,
+ Sdctl0 = 0x80,
Srst = 1<<0,
Srun = 1<<1,
Scie = 1<<2,
@@ -80,18 +70,17 @@
Seie = 1<<3,
Sdie = 1<<4,
Stagbit = 20,
- Stagmask = 0xf00000,
- Sdsts = Sdoutput0 + 0x03,
+ Sdsts = 0x03,
Scompl = 1<<2,
Sfifoerr = 1<<3,
Sdescerr = 1<<4,
Sfifordy = 1<<5,
- Sdlpib = Sdoutput0 + 0x04,
- Sdcbl = Sdoutput0 + 0x08,
- Sdlvi = Sdoutput0 + 0x0c,
- Sdfifow = Sdoutput0 + 0x0e,
- Sdfifos = Sdoutput0 + 0x10,
- Sdfmt = Sdoutput0 + 0x12,
+ Sdlpib = 0x04,
+ Sdcbl = 0x08,
+ Sdlvi = 0x0c,
+ Sdfifow = 0x0e,
+ Sdfifos = 0x10,
+ Sdfmt = 0x12,
Fmtmono = 0,
Fmtstereo = 1,
Fmtsampw = 1<<4,
@@ -100,8 +89,8 @@
Fmtmul1 = 0<<11,
Fmtbase441 = 1<<14,
Fmtbase48 = 0<<14,
- Sdbdplo = Sdoutput0 + 0x18,
- Sdbdphi = Sdoutput0 + 0x1c,
+ Sdbdplo = 0x18,
+ Sdbdphi = 0x1c,
};
enum {
@@ -108,8 +97,8 @@
Bufsize = 64 * 1024 * 4,
Nblocks = 256,
Blocksize = Bufsize / Nblocks,
- Streamtag = 1,
- Streamno = 4,
+ BytesPerSample = 4,
+
Maxrirbwait = 1000, /* microseconds */
Maxwaitup = 500, /* microseconds */
Codecdelay = 1000, /* microseconds */
@@ -220,14 +209,13 @@
};
struct Ring {
- uint rp, wp, cp;
- uint size, blocksize;
- uchar *buf;
-};
+ Rendez r;
-struct Bld {
- uint addrlo, addrhi;
- uint len, flags;
+ uchar *buf;
+ ulong nbuf;
+
+ ulong ri;
+ ulong wi;
};
struct Id {
@@ -270,6 +258,15 @@
Fungroup *fgroup;
};
+/* hardware structures */
+
+struct Bld {
+ ulong addrlo;
+ ulong addrhi;
+ ulong len;
+ ulong flags;
+};
+
struct Ctlr {
Ctlr *next;
uint no;
@@ -276,7 +273,6 @@
Lock; /* interrupt lock */
QLock; /* command lock */
- Rendez outr;
Audio *adev;
Pcidev *pcidev;
@@ -290,12 +286,22 @@
ulong *rirb;
ulong rirbsize;
+ uint sdctl;
+ uint sdintr;
+ uint sdnum;
+
Bld *blds;
+
Ring ring;
-
- Codec codec;
+
+ uint iss, oss, bss;
+
+ uint codecmask;
+ Codec *codec[Maxcodecs];
+
Widget *amp, *src;
uint pin;
+ uint cad;
int active;
uint afmt, atag;
@@ -614,30 +620,52 @@
}
static void
+addconn(Widget *w, uint nid)
+{
+ Widget *src;
+
+ if(nid >= Maxwidgets)
+ return;
+ if((src = w->fg->codec->widgets[nid]) == nil)
+ return;
+ for(nid=0; nid<w->nlist; nid++)
+ if(w->list[nid] == src)
+ return;
+ if((w->nlist % 16) == 0){
+ void *p;
+
+ if((p = realloc(w->list, sizeof(Widget*) * (w->nlist+16))) == nil){
+ print("hda: no memory for Widgetlist\n");
+ return;
+ }
+ w->list = p;
+ }
+ w->list[w->nlist++] = src;
+}
+
+static void
enumconns(Widget *w)
{
- uint r, i, j, mask, bits, nlist;
- Widget **ws, **list;
-
- ws = w->fg->codec->widgets;
+ uint r, f, b, m, i, n, x, y;
+
r = cmd(w->id, Getparm, Connlistlen);
- bits = (r & 0x80) == 0 ? 8 : 16;
- nlist = r & 0x7f;
- mask = (1 << bits) - 1;
- list = mallocz(sizeof *list * nlist, 1);
- if(list == nil){
- print("hda: no memory for Widget list\n");
- nlist = 0;
- }
- for(i=0; i<nlist; i++){
- if(i * bits % 32 == 0)
+ n = r & 0x7f;
+ b = (r & 0x80) ? 16 : 8;
+ m = (1<<b)-1;
+ f = (32/b)-1;
+ x = 0;
+ for(i=0; i<n; i++){
+ if(i & f)
+ r >>= b;
+ else
r = cmd(w->id, Getconnlist, i);
- j = (r >> (i * bits % 32)) & mask;
- if(j < Maxwidgets)
- list[i] = ws[j];
+ y = r & (m>>1);
+ if(i && ((r & m) != y))
+ while(++x < y)
+ addconn(w, x);
+ addconn(w, y);
+ x = y;
}
- w->nlist = nlist;
- w->list = list;
}
static void
@@ -645,14 +673,14 @@
{
w->cap = cmd(w->id, Getparm, Widgetcap);
w->type = (w->cap >> 20) & 0x7;
-
+
enumconns(w);
switch(w->type){
- case Wpin:
- w->pin = cmd(w->id, Getdefault, 0);
- w->pincap = cmd(w->id, Getparm, Pincap);
- break;
+ case Wpin:
+ w->pin = cmd(w->id, Getdefault, 0);
+ w->pincap = cmd(w->id, Getparm, Pincap);
+ break;
}
}
@@ -660,7 +688,7 @@
enumfungroup(Codec *codec, Id id)
{
Fungroup *fg;
- Widget *w, *next;
+ Widget *w, **tail;
uint i, r, n, base;
r = cmd(id, Getparm, Fungrtype) & 0x7f;
@@ -679,18 +707,19 @@
r = cmd(id, Getparm, Subnodecnt);
n = r & 0xff;
- base = (r >> 8) & 0xff;
+ base = (r >> 16) & 0xff;
if(base + n > Maxwidgets){
free(fg);
return nil;
}
-
- for(i=n, next=nil; i--; next=w){
+
+ tail = &fg->first;
+ for(i=0; i<n; i++){
w = mallocz(sizeof(Widget), 1);
if(w == nil){
- while(w = next){
- next = w->next;
+ while(w = fg->first){
+ fg->first = w->next;
codec->widgets[w->id.nid] = nil;
free(w);
}
@@ -699,10 +728,10 @@
}
w->id = newnid(id, base + i);
w->fg = fg;
- w->next = next;
codec->widgets[base + i] = w;
+ *tail = w;
+ tail = &w->next;
}
- fg->first = next;
for(i=0; i<n; i++)
enumwidget(codec->widgets[base + i]);
@@ -718,7 +747,6 @@
uint i, r, n, base;
uint vid, rid;
-
if(cmderr(id, Getparm, Vendorid, &vid) < 0)
return -1;
if(cmderr(id, Getparm, Revid, &rid) < 0)
@@ -728,10 +756,13 @@
codec->vid = vid;
codec->rid = rid;
+ print("#A%d: codec #%d, vendor %08x, rev %08x\n",
+ id.ctlr->no, codec->id.codec, codec->vid, codec->rid);
+
r = cmd(id, Getparm, Subnodecnt);
n = r & 0xff;
base = (r >> 16) & 0xff;
-
+
for(i=0; i<n; i++){
fg = enumfungroup(codec, newnid(id, base + i));
if(fg == nil)
@@ -747,25 +778,42 @@
static int
enumdev(Ctlr *ctlr)
{
+ Codec *codec;
+ int ret;
Id id;
int i;
-
+
+ ret = -1;
id.ctlr = ctlr;
id.nid = 0;
for(i=0; i<Maxcodecs; i++){
+ if(((1<<i) & ctlr->codecmask) == 0)
+ continue;
+ codec = mallocz(sizeof(Codec), 1);
+ if(codec == nil){
+ print("hda: no memory for Codec\n");
+ break;
+ }
id.codec = i;
- if(enumcodec(&ctlr->codec, id) == 0)
- return 0;
+ ctlr->codec[i] = codec;
+ if(enumcodec(codec, id) < 0){
+ ctlr->codec[i] = nil;
+ free(codec);
+ continue;
+ }
+ ret++;
}
- return -1;
+ return ret;
}
static int
-connectpin(Ctlr *ctlr, uint pin)
+connectpin(Ctlr *ctlr, uint pin, uint cad)
{
Widget *src, *dst;
-
- src = ctlr->codec.widgets[pin];
+
+ if(cad >= Maxcodecs || pin >= Maxwidgets || ctlr->codec[cad] == nil)
+ return -1;
+ src = ctlr->codec[cad]->widgets[pin];
if(src == nil)
return -1;
if(src->type != Wpin)
@@ -775,45 +823,51 @@
dst = findpath(src);
if(!dst)
return -1;
-
- connectpath(src, dst, Streamtag);
+ connectpath(src, dst, ctlr->atag);
ctlr->amp = dst;
ctlr->src = src;
ctlr->pin = pin;
+ ctlr->cad = cad;
return 0;
}
static int
-bestpin(Ctlr *ctlr)
+bestpin(Ctlr *ctlr, int *pcad)
{
Fungroup *fg;
Widget *w;
int best, pin, score;
uint r;
-
+ int i;
+
pin = -1;
best = -1;
- for(fg=ctlr->codec.fgroup; fg; fg=fg->next){
- for(w=fg->first; w; w=w->next){
- if(w->type != Wpin)
- continue;
- if((w->pincap & Pout) == 0)
- continue;
- score = 0;
- r = w->pin;
- if(((r >> 12) & 0xf) == 4) /* green */
- score |= 32;
- if(((r >> 24) & 0xf) == 1) /* rear */
- score |= 16;
- if(((r >> 28) & 0x3) == 0) /* ext */
- score |= 8;
- if(((r >> 20) & 0xf) == 2) /* hpout */
- score |= 4;
- if(((r >> 20) & 0xf) == 0) /* lineout */
- score |= 4;
- if(score >= best){
- best = score;
- pin = w->id.nid;
+ for(i=0; i<Maxcodecs; i++){
+ if(ctlr->codec[i] == nil)
+ continue;
+ for(fg=ctlr->codec[i]->fgroup; fg; fg=fg->next){
+ for(w=fg->first; w; w=w->next){
+ if(w->type != Wpin)
+ continue;
+ if((w->pincap & Pout) == 0)
+ continue;
+ score = 0;
+ r = w->pin;
+ if(((r >> 12) & 0xf) == 4) /* green */
+ score |= 32;
+ if(((r >> 24) & 0xf) == 1) /* rear */
+ score |= 16;
+ if(((r >> 28) & 0x3) == 0) /* ext */
+ score |= 8;
+ if(((r >> 20) & 0xf) == 2) /* hpout */
+ score |= 4;
+ if(((r >> 20) & 0xf) == 0) /* lineout */
+ score |= 4;
+ if(score >= best){
+ best = score;
+ pin = w->id.nid;
+ *pcad = i;
+ }
}
}
}
@@ -820,246 +874,152 @@
return pin;
}
-static void
-ringreset(Ring *r)
+static long
+buffered(Ring *r)
{
- memset(r->buf, 0, r->size);
- r->rp = 0;
- r->wp = 0;
- r->cp = 0;
-}
+ ulong ri, wi;
-static uint
-ringused(Ring *r)
-{
- return (r->wp - r->rp) % r->size;
+ ri = r->ri;
+ wi = r->wi;
+ if(wi >= ri)
+ return wi - ri;
+ else
+ return r->nbuf - (ri - wi);
}
-static uint
-ringavail(Ring *r)
+static long
+available(Ring *r)
{
- return r->size - r->blocksize - ringused(r);
-}
+ long m;
-static uint
-ringdirty(Ring *r)
-{
- return (r->rp - r->cp) % r->size;
+ m = (r->nbuf - BytesPerSample) - buffered(r);
+ if(m < 0)
+ m = 0;
+ return m;
}
-static void
-ringalign(Ring *r)
+static long
+writering(Ring *r, uchar *p, long n)
{
- r->wp += r->blocksize - 1;
- r->wp -= r->wp % r->blocksize;
- r->wp %= r->size;
-}
+ long n0, m;
-static uint
-ringwrite(Ring *r, uchar *ap, uint n)
-{
- uchar *p;
- uint a, c;
-
- p = ap;
- a = ringavail(r);
- if(n > a)
- n = a;
-
- c = ringdirty(r);
- while(c > 0){
- a = r->size - r->cp;
- if(a > c)
- a = c;
- memset(r->buf + r->cp, 0, a);
- r->cp = (r->cp + a) % r->size;
- c -= a;
- }
-
+ n0 = n;
while(n > 0){
- a = r->size - r->wp;
- if(a > n)
- a = n;
- memmove(r->buf + r->wp, p, a);
- r->wp = (r->wp + a) % r->size;
- p += a;
- n -= a;
+ 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 p - ap;
+ return n0 - n;
}
-
static int
-ringupdate(Ring *r, uint np)
-{
- uint rp, wp, bs, s;
-
- rp = r->rp;
- bs = r->blocksize;
- s = r->size;
-
- np += bs / 2;
- np %= s;
- np -= np % bs;
- wp = r->wp;
- wp -= wp % bs;
- r->rp = np;
- if((np - rp) % s >= (wp - rp) % s)
- return 1;
- return 0;
-}
-
-static void
streamalloc(Ctlr *ctlr)
{
- uchar *p;
- Bld *b;
- uint i;
Ring *r;
-
+ int i;
+
r = &ctlr->ring;
- r->size = Bufsize;
- r->blocksize = Blocksize;
- r->buf = xspanalloc(r->size, 128, 0);
- ringreset(r);
-
- ctlr->active = 0;
- ctlr->atag = Streamtag;
- ctlr->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 |
- Fmtmul1 | Fmtbase441;
-
+ memset(r, 0, sizeof(*r));
+ r->buf = xspanalloc(r->nbuf = Bufsize, 128, 0);
ctlr->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
- b = ctlr->blds;
- p = r->buf;
+ if(r->buf == nil || ctlr->blds == nil){
+ print("hda: no memory for stream\n");
+ return -1;
+ }
for(i=0; i<Nblocks; i++){
- b->addrlo = PADDR(p);
- b->addrhi = 0;
- b->flags = ~0;
- b->len = Blocksize;
- p += Blocksize;
- b++;
+ ctlr->blds[i].addrlo = PADDR(r->buf) + i*Blocksize;
+ ctlr->blds[i].addrhi = 0;
+ ctlr->blds[i].len = Blocksize;
+ ctlr->blds[i].flags = 0x01; /* interrupt on completion */
}
-}
-static void
-streamstart(Ctlr *ctlr)
-{
- Ring *r = &ctlr->ring;
-
+ /* output dma engine starts after inputs */
+ ctlr->sdnum = ctlr->iss;
+ ctlr->sdctl = Sdctl0 + ctlr->sdnum*0x20;
+ ctlr->sdintr = 1<<ctlr->sdnum;
+ ctlr->atag = ctlr->sdnum+1;
+ ctlr->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 | Fmtmul1 | Fmtbase441;
+ ctlr->active = 0;
+
/* perform reset */
- csr8(ctlr, Sdctl) = Srst;
- waitup8(ctlr, Sdctl, Srst, Srst);
- csr8(ctlr, Sdctl) = 0;
- waitup8(ctlr, Sdctl, Srst, 0);
-
+ csr8(ctlr, ctlr->sdctl) &= ~(Srst | Srun | Scie | Seie | Sdie);
+ csr8(ctlr, ctlr->sdctl) |= Srst;
+ microdelay(Codecdelay);
+ waitup8(ctlr, ctlr->sdctl, Srst, Srst);
+ csr8(ctlr, ctlr->sdctl) &= ~Srst;
+ microdelay(Codecdelay);
+ waitup8(ctlr, ctlr->sdctl, Srst, 0);
+
+ /* set stream number */
+ csr32(ctlr, ctlr->sdctl) = (ctlr->atag << Stagbit) |
+ (csr32(ctlr, ctlr->sdctl) & ~(0xF << Stagbit));
+
+ /* set stream format */
+ csr16(ctlr, Sdfmt+ctlr->sdctl) = ctlr->afmt;
+
/* program stream DMA & parms */
- csr32(ctlr, Sdcbl) = r->size;
- csr16(ctlr, Sdlvi) = (r->size / r->blocksize - 1) & 0xff;
- csr32(ctlr, Sdfmt) = ctlr->afmt;
- csr32(ctlr, Sdbdplo) = PADDR(ctlr->blds);
- csr32(ctlr, Sdbdphi) = 0;
-
+ csr32(ctlr, Sdbdplo+ctlr->sdctl) = PADDR(ctlr->blds);
+ csr32(ctlr, Sdbdphi+ctlr->sdctl) = 0;
+ csr32(ctlr, Sdcbl+ctlr->sdctl) = r->nbuf;
+ csr16(ctlr, Sdlvi+ctlr->sdctl) = (Nblocks - 1) & 0xff;
+
+ /* mask out ints */
+ csr8(ctlr, Sdsts+ctlr->sdctl) = Scompl | Sfifoerr | Sdescerr;
+
/* enable global intrs for this stream */
- csr32(ctlr, Intctl) |= (1 << Streamno);
-
- /* enable stream intrs */
- csr32(ctlr, Sdctl) = (ctlr->atag << Stagbit) | Srun | Scie | Seie | Sdie;
- waitup32(ctlr, Sdctl, Srun, Srun);
-
- /* mark as running */
- ctlr->active = 1;
+ csr32(ctlr, Intctl) |= ctlr->sdintr;
+ csr8(ctlr, ctlr->sdctl) |= Scie | Seie | Sdie;
+
+ return 0;
}
static void
-streamstop(Ctlr *ctlr)
+streamstart(Ctlr *ctlr)
{
- /* disble stream intrs */
- csr32(ctlr, Sdctl) = 0;
+ ctlr->active = 1;
- /* disable global intrs for this stream */
- csr32(ctlr, Intctl) &= ~(1 << Streamno);
-
- /* mark as stopped */
- ctlr->active = 0;
+ csr8(ctlr, ctlr->sdctl) |= Srun;
+ waitup8(ctlr, ctlr->sdctl, Srun, Srun);
}
-
static void
-streamupdate(Ctlr *ctlr)
+streamstop(Ctlr *ctlr)
{
- uint pos;
- Ring *r;
-
- r = &ctlr->ring;
-
- /* ack interrupt and wake writer */
- csr8(ctlr, Sdsts) |= 0x4;
- wakeup(&ctlr->outr);
- pos = csr32(ctlr, Sdlpib);
-
- /* underrun? */
- if(ringupdate(r, pos) == 1)
- streamstop(ctlr);
-}
+ csr8(ctlr, ctlr->sdctl) &= ~Srun;
+ waitup8(ctlr, ctlr->sdctl, Srun, 0);
-static int
-outavail(void *arg)
-{
- return ringavail(arg) > 0;
+ ctlr->active = 0;
}
-static int
-outrate(void *arg)
+static uint
+streampos(Ctlr *ctlr)
{
- Ctlr *ctlr = arg;
- int delay = ctlr->adev->delay*4;
- return (delay <= 0) || (ringused(&ctlr->ring) <= delay) || (ctlr->active == 0);
-}
-
-static int
-checkptr(Ctlr *ctlr)
-{
Ring *r;
-
+ uint p;
+
r = &ctlr->ring;
- if(ctlr->active == 1)
- return 1;
- if(r->rp == 0)
- return 1;
- ringreset(r);
- return 0;
+ p = csr32(ctlr, Sdlpib+ctlr->sdctl);
+ if(p >= r->nbuf)
+ p = 0;
+ return p;
}
-static void
-hdakick(Ctlr *ctlr)
-{
- Ring *r = &ctlr->ring;
-
- ilock(ctlr);
- if(ctlr->active == 0){
- if(ringused(r) >= r->blocksize){
- iunlock(ctlr);
- streamstart(ctlr);
- return;
- }
- }
- iunlock(ctlr);
-}
-
static long
-hdabuffered(Audio *adev)
-{
- Ctlr *ctlr;
- ctlr = adev->ctlr;
- return ringused(&ctlr->ring);
-}
-
-static long
hdactl(Audio *adev, void *va, long n, vlong)
{
char *p, *e, *x, *tok[4];
int ntok;
Ctlr *ctlr;
+ uint pin, cad;
ctlr = adev->ctlr;
p = va;
@@ -1073,8 +1033,12 @@
ntok = tokenize(p, tok, 4);
if(ntok <= 0)
continue;
- if(cistrcmp(tok[0], "pin") == 0 && ntok == 2){
- connectpin(ctlr, strtoul(tok[1], 0, 0));
+ if(cistrcmp(tok[0], "pin") == 0 && ntok >= 2){
+ cad = ctlr->cad;
+ pin = strtoul(tok[1], 0, 0);
+ if(ntok > 2)
+ cad = strtoul(tok[2], 0, 0);
+ connectpin(ctlr, pin, cad);
}else
error(Ebadctl);
}
@@ -1081,35 +1045,60 @@
return n;
}
+static int
+outavail(void *arg)
+{
+ Ctlr *ctlr = arg;
+ return available(&ctlr->ring) > 0;
+}
+
+static int
+outrate(void *arg)
+{
+ Ctlr *ctlr = arg;
+ int delay = ctlr->adev->delay*BytesPerSample;
+ return (delay <= 0) || (buffered(&ctlr->ring) <= delay) || (ctlr->active == 0);
+}
+
static long
-hdawrite(Audio *adev, void *vp, long vn, vlong)
+hdabuffered(Audio *adev)
{
- uchar *p;
- uint n, k;
- Ring *r;
Ctlr *ctlr;
-
+ ctlr = adev->ctlr;
+ return buffered(&ctlr->ring);
+}
+
+static void
+hdakick(Ctlr *ctlr)
+{
+ if(ctlr->active)
+ return;
+ if(buffered(&ctlr->ring) > Blocksize)
+ streamstart(ctlr);
+}
+
+static long
+hdawrite(Audio *adev, void *vp, long n, vlong)
+{
+ uchar *p, *e;
+ Ctlr *ctlr;
+ Ring *ring;
+
p = vp;
- n = vn;
+ e = p + n;
ctlr = adev->ctlr;
- r = &ctlr->ring;
-
- checkptr(ctlr);
- while(n > 0){
- k = ringwrite(r, p, n);
- if(checkptr(ctlr) == 0)
- continue;
- if(k == 0){
+ ring = &ctlr->ring;
+ while(p < e) {
+ if((n = writering(ring, p, e - p)) <= 0){
hdakick(ctlr);
- sleep(&ctlr->outr, outavail, r);
- }else{
- p += k;
- n -= k;
+ sleep(&ring->r, outavail, ctlr);
+ continue;
}
+ p += n;
}
hdakick(ctlr);
- sleep(&ctlr->outr, outrate, ctlr);
- return vn;
+ sleep(&ring->r, outrate, ctlr);
+ return p - (uchar*)vp;
}
static void
@@ -1116,9 +1105,16 @@
hdaclose(Audio *adev)
{
Ctlr *ctlr;
+ uchar z[1];
+ Ring *r;
+
ctlr = adev->ctlr;
- ringalign(&ctlr->ring);
- hdakick(ctlr);
+ if(!ctlr->active)
+ return;
+ z[0] = 0;
+ r = &ctlr->ring;
+ while(r->wi % Blocksize)
+ hdawrite(adev, z, sizeof(z), 0);
}
enum {
@@ -1205,14 +1201,24 @@
Ctlr *ctlr;
Audio *adev;
uint sts;
-
+ Ring *r;
+
adev = arg;
ctlr = adev->ctlr;
ilock(ctlr);
sts = csr32(ctlr, Intsts);
- if(sts & Sismask)
- streamupdate(ctlr);
+ if(sts & ctlr->sdintr){
+ /* ack interrupt */
+ csr8(ctlr, Sdsts+ctlr->sdctl) |= Scompl;
+ r = &ctlr->ring;
+ r->ri = streampos(ctlr);
+ if(ctlr->active && buffered(r) < Blocksize){
+ streamstop(ctlr);
+ r->ri = r->wi = streampos(ctlr);
+ }
+ wakeup(&r->r);
+ }
iunlock(ctlr);
}
@@ -1220,32 +1226,35 @@
hdastatus(Audio *adev, void *a, long n, vlong)
{
Ctlr *ctlr = adev->ctlr;
+ Codec *codec;
Fungroup *fg;
Widget *w;
uint r;
- int k;
+ int k, i;
char *s;
s = a;
- k = snprint(s, n, "bufsize %6d buffered %6ud\ncodec %2d pin %3d\n",
- ctlr->ring.blocksize, ringused(&ctlr->ring),
- ctlr->codec.id.codec, ctlr->pin);
-
- for(fg=ctlr->codec.fgroup; fg; fg=fg->next){
- for(w=fg->first; w; w=w->next){
- if(w->type != Wpin)
- continue;
- r = w->pin;
- k += snprint(s+k, n-k, "pin %3d %s %s %s %s %s %s\n",
- w->id.nid,
- (w->pincap & Pout) != 0 ? "out" : "in",
- pinport[(r >> 30) & 0x3],
- pinloc2[(r >> 28) & 0x3],
- pinloc[(r >> 24) & 0xf],
- pinfunc[(r >> 20) & 0xf],
- pincol[(r >> 12) & 0xf]
- );
-
+ k = snprint(s, n, "bufsize %6d buffered %6ld\n", Blocksize, buffered(&ctlr->ring));
+ for(i=0; i<Maxcodecs; i++){
+ if((codec = ctlr->codec[i]) == nil)
+ continue;
+ k += snprint(s+k, n-k, "codec %2d pin %3d\n",
+ codec->id.codec, ctlr->pin);
+ for(fg=codec->fgroup; fg; fg=fg->next){
+ for(w=fg->first; w; w=w->next){
+ if(w->type != Wpin)
+ continue;
+ r = w->pin;
+ k += snprint(s+k, n-k, "pin %3d %s %s %s %s %s %s\n",
+ w->id.nid,
+ (w->pincap & Pout) != 0 ? "out" : "in",
+ pinport[(r >> 30) & 0x3],
+ pinloc2[(r >> 28) & 0x3],
+ pinloc[(r >> 24) & 0xf],
+ pinfunc[(r >> 20) & 0xf],
+ pincol[(r >> 12) & 0xf]
+ );
+ }
}
}
return k;
@@ -1257,7 +1266,37 @@
{
static int cmdbufsize[] = { 2, 16, 256, 2048 };
int n, size;
+ uint cap;
+ /* reset controller */
+ csr32(ctlr, Gctl) &= ~Rst;
+ waitup32(ctlr, Gctl, Rst, 0);
+ microdelay(Codecdelay);
+ csr32(ctlr, Gctl) |= Rst;
+ if(waitup32(ctlr, Gctl, Rst, Rst) &&
+ waitup32(ctlr, Gctl, Rst, Rst)){
+ print("#A%d: hda failed to reset\n", ctlr->no);
+ return -1;
+ }
+ microdelay(Codecdelay);
+
+ ctlr->codecmask = csr16(ctlr, Statests);
+ if(ctlr->codecmask == 0){
+ print("#A%d: hda no codecs\n", ctlr->no);
+ return -1;
+ }
+
+ cap = csr16(ctlr, Gcap);
+ ctlr->bss = (cap>>3) & 0x1F;
+ ctlr->iss = (cap>>8) & 0xF;
+ ctlr->oss = (cap>>12) & 0xF;
+
+ csr8(ctlr, Corbctl) = 0;
+ waitup8(ctlr, Corbctl, Corbdma, 0);
+
+ csr8(ctlr, Rirbctl) = 0;
+ waitup8(ctlr, Rirbctl, Rirbdma, 0);
+
/* alloc command buffers */
size = csr8(ctlr, Corbsz);
n = cmdbufsize[size & 3];
@@ -1270,22 +1309,7 @@
ctlr->rirb = xspanalloc(n * 8, 128, 0);
memset(ctlr->rirb, 0, n * 8);
ctlr->rirbsize = n;
-
- /* stop command buffers */
- csr16(ctlr, Wakeen) = 0;
- csr32(ctlr, Intctl) = 0;
- csr8(ctlr, Corbctl) = 0;
- csr8(ctlr, Rirbctl) = 0;
- waitup8(ctlr, Corbctl, Corbdma, 0);
- waitup8(ctlr, Rirbctl, Rirbdma, 0);
-
- /* reset controller */
- csr32(ctlr, Gctl) = 0;
- waitup32(ctlr, Gctl, Rst, 0);
- microdelay(Codecdelay);
- csr32(ctlr, Gctl) = Rst;
- waitup32(ctlr, Gctl, Rst, Rst);
-
+
/* setup controller */
csr32(ctlr, Dplbase) = 0;
csr32(ctlr, Dpubase) = 0;
@@ -1311,7 +1335,7 @@
waitup8(ctlr, Rirbctl, Rirbdma, Rirbdma);
/* enable interrupts */
- csr32(ctlr, Intctl) = Gie | Cie;
+ csr32(ctlr, Intctl) |= Gie | Cie;
return 0;
}
@@ -1322,6 +1346,8 @@
while(p = pcimatch(p, 0, 0))
switch((p->vid << 16) | p->did){
case (0x8086 << 16) | 0x27d8:
+ case (0x1002 << 16) | 0x4383: /* ATI */
+ case (0x1002 << 16) | 0x7919: /* ATI HDMI */
return p;
}
return nil;
@@ -1370,11 +1396,11 @@
hdareset(Audio *adev)
{
static Ctlr *cards = nil;
- Pcidev *p;
- int irq, tbdf, best;
+ int irq, tbdf, best, cad;
Ctlr *ctlr;
+ Pcidev *p;
- /* make a list of all ac97 cards if not already done */
+ /* make a list of all cards if not already done */
if(cards == nil){
p = nil;
while(p = hdamatch(p)){
@@ -1402,9 +1428,13 @@
irq = p->intl;
tbdf = p->tbdf;
+ /* magic for ATI */
+ if(p->vid == 0x1002)
+ pcicfgw8(p, 0x42, pcicfgr8(p, 0x42) | 2);
+
pcisetbme(p);
pcisetpms(p, 0);
-
+
ctlr->no = adev->ctlrno;
ctlr->size = p->mem[0].size;
ctlr->q = qopen(256, 0, 0, 0);
@@ -1419,20 +1449,20 @@
print("#A%d: unable to start hda\n", ctlr->no);
return -1;
}
- streamalloc(ctlr);
+ if(streamalloc(ctlr) < 0){
+ print("#A%d: streamalloc failed\n", ctlr->no);
+ return -1;
+ }
if(enumdev(ctlr) < 0){
print("#A%d: no audio codecs found\n", ctlr->no);
return -1;
}
- print("#A%d: using codec #%d, vendor %08x\n",
- ctlr->no, ctlr->codec.id.codec, ctlr->codec.vid);
-
- best = bestpin(ctlr);
+ best = bestpin(ctlr, &cad);
if(best < 0){
print("#A%d: no output pins found!\n", ctlr->no);
return -1;
}
- if(connectpin(ctlr, best) < 0){
+ if(connectpin(ctlr, best, cad) < 0){
print("#A%d: error connecting pin\n", ctlr->no);
return -1;
}