typedef struct Ctlr Ctlr;
typedef struct Bld Bld;
typedef struct Ring Ring;
+typedef struct Stream Stream;
+
typedef struct Id Id;
typedef struct Widget Widget;
typedef struct Codec Codec;
ulong wi;
};
+struct Stream {
+ Ring ring;
+
+ Bld *blds;
+
+ uint sdctl;
+ uint sdintr;
+ uint sdnum;
+
+ uint afmt;
+ uint atag;
+ int active;
+
+ uint pin;
+ uint cad;
+
+ Widget *conv; /* DAC or ADC */
+ Widget *jack; /* the pin jack */
+};
+
struct Id {
Ctlr *ctlr;
uint codec, nid;
uint convrate, convfmt;
};
};
- Widget *next;
- Widget *from;
+ Widget *next; /* next in function group */
+ Widget *path; /* next in audio path */
+
+ Widget *link; /* temporary for findpath */
};
struct Fungroup {
Codec *codec;
uint type;
Widget *first;
- Widget *mixer;
- Widget *src, *dst;
Fungroup *next;
};
ulong *rirb;
ulong rirbsize;
- uint sdctl;
- uint sdintr;
- uint sdnum;
-
- Bld *blds;
-
- Ring ring;
+ Stream sout;
+ Stream sin;
uint iss, oss, bss;
uint codecmask;
Codec *codec[Maxcodecs];
-
- Widget *amp, *src;
- uint pin;
- uint cad;
-
- int active;
- uint afmt, atag;
};
#define csr32(c, r) (*(ulong *)&(c)->mem[r])
getoutamprange(Widget *w)
{
uint r;
- r = cmd(w->id, Getparm, Outampcap);
+
+ if((w->cap & Woutampcap) == 0)
+ return 0;
+ if((w->cap & Wampovrcap) == 0)
+ r = cmd(w->fg->id, Getparm, Outampcap);
+ else
+ r = cmd(w->id, Getparm, Outampcap);
return (r >> 8) & 0x7f;
}
if((w->cap & Woutampcap) == 0)
return;
-
- r = cmd(w->id, Getparm, Outampcap);
+ if((w->cap & Wampovrcap) == 0)
+ r = cmd(w->fg->id, Getparm, Outampcap);
+ else
+ r = cmd(w->id, Getparm, Outampcap);
zerodb = r & 0x7f;
for(i=0; i<2; i++){
}
}
+static uint
+getinamprange(Widget *w)
+{
+ uint r;
+
+ if((w->cap & Winampcap) == 0)
+ return 0;
+ if((w->cap & Wampovrcap) == 0)
+ r = cmd(w->fg->id, Getparm, Inampcap);
+ else
+ r = cmd(w->id, Getparm, Inampcap);
+ return (r >> 8) & 0x7f;
+}
+
+static void
+getinamp(Widget *w, int vol[2])
+{
+ vol[0] = vol[1] = 0;
+ if((w->cap & Winampcap) == 0)
+ return;
+ vol[0] = cmd(w->id, Getamp, Agetin | Agetleft) & Againmask;
+ vol[1] = cmd(w->id, Getamp, Agetin | Agetright) & Againmask;
+}
+
/* vol is 0...range or nil for 0dB; mute is 0/1; in is widget or nil for all */
static void
setinamp(Widget *w, Widget *in, int mute, int *vol)
if((w->cap & Winampcap) == 0)
return;
-
- r = cmd(w->id, Getparm, Inampcap);
+ if((w->cap & Wampovrcap) == 0)
+ r = cmd(w->fg->id, Getparm, Inampcap);
+ else
+ r = cmd(w->id, Getparm, Inampcap);
zerodb = r & 0x7f;
for(i=0; i<2; i++){
}
static Widget *
-findpath(Widget *src)
+findpath(Widget *jack, int type, char *route)
{
Widget *q[Maxwidgets];
uint l, r, i;
- Widget *w, *v;
-
+ Widget *w, *to;
+ Fungroup *fg;
+
+ fg = jack->fg;
+
l = r = 0;
- q[r++] = src;
- for(w=src->fg->first; w; w=w->next)
- w->from = nil;
- src->from = src;
+ for(w=fg->first; w != nil; w = w->next)
+ w->link = nil;
+
+ if(route != nil && *route != 0){
+ w = jack;
+ while(*route++ == ','){
+ i = strtoul(route, &route, 0);
+ if(i >= Maxwidgets)
+ return nil;
+ to = fg->codec->widgets[i];
+ if(to == nil || to->fg != fg || to->link != nil)
+ return nil;
+ if(type == Waout)
+ to->link = w;
+ else
+ w->link = to;
+ w = to;
+ }
+ if(w == jack || w->type != type)
+ w = nil;
+ return w;
+ }
+
+ if(type == Waout){
+ q[r++] = jack;
+ jack->link = jack;
+ } else {
+ for(w=fg->first; w != nil; w = w->next)
+ if(w->type == type){
+ q[r++] = w;
+ w->link = w;
+ }
+ }
while(l < r){
w = q[l++];
- if(w->type == Waout)
+ if(type == Waout){
+ if(w->type == type)
+ return w;
+ } else if(w == jack){
+ for(w = jack->link; w != nil; w = w->link)
+ if(w->type == type)
+ return w;
break;
+ }
for(i=0; i<w->nlist; i++){
- v = w->list[i];
- if(v == nil || v->from)
+ to = w->list[i];
+ if(to == nil || to->link)
continue;
- v->from = w;
- q[r++] = v;
+ to->link = w;
+ q[r++] = to;
}
}
- if(w->type != Waout)
- return nil;
- return w;
+
+ return nil;
}
static void
-connectpath(Widget *src, Widget *dst)
+disconnectpath(Widget *from, Widget *to)
{
- Widget *w, *v;
- uint i;
+ Widget *next;
- for(w=dst; w != src; w=v){
- v = w->from;
- setoutamp(w, 0, nil);
- setinamp(v, w, 0, nil);
- if(v->nlist == 1)
+ for(; from != nil && from != to; from = next){
+ next = from->path;
+ from->path = nil;
+ setoutamp(from, 1, nil);
+ if(next != nil)
+ setinamp(next, from, 1, nil);
+ }
+ setoutamp(to, 1, nil);
+}
+
+static void
+muteall(Ctlr *ctlr)
+{
+ Fungroup *fg;
+ Widget *w;
+ int i;
+
+ for(i=0; i<Maxcodecs; i++){
+ if(ctlr->codec[i] == nil)
continue;
- for(i=0; i < v->nlist; i++)
- if(v->list[i] == w){
- cmd(v->id, Setconn, i);
- break;
+ for(fg=ctlr->codec[i]->fgroup; fg; fg=fg->next){
+ for(w=fg->first; w != nil; w=w->next){
+ setinamp(w, nil, 1, nil);
+ setoutamp(w, 1, nil);
+ switch(w->type){
+ case Wain:
+ case Waout:
+ cmd(w->id, Setstream, 0);
+ break;
+ case Wpin:
+ cmd(w->id, Setpinctl, 0);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+connectpath(Widget *from, Widget *to)
+{
+ Widget *next;
+ uint i;
+
+ for(; from != nil && from != to; from = next){
+ next = from->link;
+ from->path = next;
+ setoutamp(from, 0, nil);
+ if(next != nil){
+ setinamp(next, from, 0, nil);
+ for(i=0; i < next->nlist; i++){
+ if(next->list[i] == from){
+ cmd(next->id, Setconn, i);
+ break;
+ }
}
+ }
}
- setoutamp(src, 0, nil);
- cmd(src, Setpinctl, Pinctlout);
+ setoutamp(to, 0, nil);
}
static void
w->list = p;
}
w->list[w->nlist++] = src;
- return;
}
static void
{
w->cap = cmd(w->id, Getparm, Widgetcap);
w->type = (w->cap >> 20) & 0x7;
- if(w->cap & Wpwrcap)
+ if(w->cap & Wpwrcap){
cmd(w->id, Setpower, 0);
-
- enumconns(w);
-
+ delay(10);
+ }
switch(w->type){
case Wpin:
w->pin = cmd(w->id, Getdefault, 0);
/* open eyes */
cmd(id, Setpower, 0);
- microdelay(100);
+ delay(10);
+
+ r = cmd(id, Getparm, Subnodecnt);
+ n = r & 0xff;
+ base = (r >> 16) & 0xff;
+ if(base >= Maxwidgets){
+ print("hda: enumfungroup: base %d out of range\n", base);
+ return nil;
+ }
+ if(base+n > Maxwidgets){
+ print("hda: enumfungroup: widgets %d - %d out of range\n", base, base+n);
+ n = Maxwidgets - base;
+ }
fg = mallocz(sizeof *fg, 1);
if(fg == nil){
fg->id = id;
fg->type = r;
- r = cmd(id, Getparm, Subnodecnt);
- n = r & 0xff;
- base = (r >> 16) & 0xff;
-
- if(base + n > Maxwidgets){
- free(fg);
- return nil;
- }
-
tail = &fg->first;
for(i=0; i<n; i++){
+ if(codec->widgets[base + i] != nil){
+ print("hda: enumfungroup: duplicate widget %d\n", base + i);
+ continue;
+ }
w = mallocz(sizeof(Widget), 1);
if(w == nil){
while(w = fg->first){
for(i=0; i<n; i++)
enumwidget(codec->widgets[base + i]);
+ for(i=0; i<n; i++)
+ enumconns(codec->widgets[base + i]);
return fg;
}
-
static int
enumcodec(Codec *codec, Id id)
{
if(codec->fgroup == nil)
return -1;
- print("#A%d: codec #%d, vendor %08x, rev %08x\n",
+ print("#A%d: codec #%d, vendor %08ux, rev %08ux\n",
id.ctlr->no, codec->id.codec, codec->vid, codec->rid);
return 0;
}
static int
-connectpin(Ctlr *ctlr, uint pin, uint cad)
+connectpin(Ctlr *ctlr, Stream *s, int type, uint pin, uint cad, char *route)
{
- Widget *w, *src, *dst;
+ Widget *jack, *conv;
- if(cad >= Maxcodecs || pin >= Maxwidgets || ctlr->codec[cad] == nil)
+ if(s->atag == 0)
return -1;
- src = ctlr->codec[cad]->widgets[pin];
- if(src == nil)
+ if(cad >= Maxcodecs || pin >= Maxwidgets || ctlr->codec[cad] == nil)
return -1;
- if(src->type != Wpin)
+ jack = ctlr->codec[cad]->widgets[pin];
+ if(jack == nil)
return -1;
- if((src->pincap & Pout) == 0)
+ if(jack->type != Wpin)
return -1;
- dst = findpath(src);
- if(!dst)
+ conv = findpath(jack, type, route);
+ if(conv == nil)
return -1;
- /* mute all widgets, clear stream */
- for(w=src->fg->first; w != nil; w=w->next){
- setoutamp(w, 1, nil);
- setinamp(w, nil, 1, nil);
- cmd(w->id, Setstream, 0);
+ if(s->conv != nil && s->jack != nil){
+ if(s->conv->type == Waout)
+ disconnectpath(s->conv, s->jack);
+ else
+ disconnectpath(s->jack, s->conv);
+ cmd(s->conv->id, Setstream, 0);
+ cmd(s->jack->id, Setpinctl, 0);
+ }
+
+ if(type == Waout){
+ connectpath(conv, jack);
+ cmd(jack->id, Setpinctl, Pinctlout);
+ } else {
+ connectpath(jack, conv);
+ cmd(jack->id, Setpinctl, Pinctlin);
}
- connectpath(src, dst);
+ cmd(conv->id, Setconvfmt, s->afmt);
+ cmd(conv->id, Setstream, (s->atag << 4) | 0);
+ cmd(conv->id, Setchancnt, 1);
- cmd(dst->id, Setconvfmt, ctlr->afmt);
- cmd(dst->id, Setstream, (ctlr->atag << 4) | 0);
- cmd(dst->id, Setchancnt, 1);
+ s->conv = conv;
+ s->jack = jack;
+ s->pin = pin;
+ s->cad = cad;
- ctlr->amp = dst;
- ctlr->src = src;
- ctlr->pin = pin;
- ctlr->cad = cad;
return 0;
}
static int
-bestpin(Ctlr *ctlr, int *pcad)
+scoreout(Widget *w)
+{
+ int score;
+ uint r;
+
+ if((w->pincap & Pout) == 0)
+ return -1;
+ if(w->id.ctlr->sin.jack == w)
+ return -1;
+
+ score = 0;
+ r = w->pin;
+ if(((r >> 30) & 0x3) >= 2) /* fix or fix+jack */
+ score |= 32;
+ 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;
+ return score;
+}
+
+static int
+scorein(Widget *w)
+{
+ int score;
+ uint r;
+
+ if((w->pincap & Pin) == 0)
+ return -1;
+ if(w->id.ctlr->sout.jack == w)
+ return -1;
+
+ score = 0;
+ r = w->pin;
+ if(((r >> 30) & 0x3) >= 2) /* fix or fix+jack */
+ score |= 4;
+ return score;
+}
+
+static int
+bestpin(Ctlr *ctlr, int *pcad, int (*fscore)(Widget *))
{
Fungroup *fg;
Widget *w;
int best, pin, score;
- uint r;
int i;
pin = -1;
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){
+ for(fg=ctlr->codec[i]->fgroup; fg != nil; fg=fg->next){
+ for(w=fg->first; w != nil; 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){
+ score = (*fscore)(w);
+ if(score >= 0 && score >= best){
best = score;
pin = w->id.nid;
*pcad = i;
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)
{
}
static int
-streamalloc(Ctlr *ctlr)
+streamalloc(Ctlr *ctlr, Stream *s, int num)
{
Ring *r;
int i;
- r = &ctlr->ring;
- memset(r, 0, sizeof(*r));
+ r = &s->ring;
r->buf = xspanalloc(r->nbuf = Bufsize, 128, 0);
- ctlr->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
- if(r->buf == nil || ctlr->blds == nil){
+ s->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
+ if(r->buf == nil || s->blds == nil){
print("hda: no memory for stream\n");
return -1;
}
for(i=0; i<Nblocks; i++){
- 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 */
+ s->blds[i].addrlo = PADDR(r->buf) + i*Blocksize;
+ s->blds[i].addrhi = 0;
+ s->blds[i].len = Blocksize;
+ s->blds[i].flags = 0x01; /* interrupt on completion */
}
- /* 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;
+ s->sdnum = num;
+ s->sdctl = Sdctl0 + s->sdnum*0x20;
+ s->sdintr = 1<<s->sdnum;
+ s->atag = s->sdnum+1;
+ s->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 | Fmtmul1 | Fmtbase441;
+ s->active = 0;
/* perform reset */
- csr8(ctlr, ctlr->sdctl) &= ~(Srst | Srun | Scie | Seie | Sdie);
- csr8(ctlr, ctlr->sdctl) |= Srst;
+ csr8(ctlr, s->sdctl) &= ~(Srst | Srun | Scie | Seie | Sdie);
+ csr8(ctlr, s->sdctl) |= Srst;
microdelay(Codecdelay);
- waitup8(ctlr, ctlr->sdctl, Srst, Srst);
- csr8(ctlr, ctlr->sdctl) &= ~Srst;
+ waitup8(ctlr, s->sdctl, Srst, Srst);
+ csr8(ctlr, s->sdctl) &= ~Srst;
microdelay(Codecdelay);
- waitup8(ctlr, ctlr->sdctl, Srst, 0);
+ waitup8(ctlr, s->sdctl, Srst, 0);
/* set stream number */
- csr32(ctlr, ctlr->sdctl) = (ctlr->atag << Stagbit) |
- (csr32(ctlr, ctlr->sdctl) & ~(0xF << Stagbit));
+ csr32(ctlr, s->sdctl) = (s->atag << Stagbit) |
+ (csr32(ctlr, s->sdctl) & ~(0xF << Stagbit));
/* set stream format */
- csr16(ctlr, Sdfmt+ctlr->sdctl) = ctlr->afmt;
+ csr16(ctlr, Sdfmt+s->sdctl) = s->afmt;
/* program stream DMA & parms */
- 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;
+ csr32(ctlr, Sdbdplo+s->sdctl) = PADDR(s->blds);
+ csr32(ctlr, Sdbdphi+s->sdctl) = 0;
+ csr32(ctlr, Sdcbl+s->sdctl) = r->nbuf;
+ csr16(ctlr, Sdlvi+s->sdctl) = (Nblocks - 1) & 0xff;
/* mask out ints */
- csr8(ctlr, Sdsts+ctlr->sdctl) = Scompl | Sfifoerr | Sdescerr;
+ csr8(ctlr, Sdsts+s->sdctl) = Scompl | Sfifoerr | Sdescerr;
/* enable global intrs for this stream */
- csr32(ctlr, Intctl) |= ctlr->sdintr;
- csr8(ctlr, ctlr->sdctl) |= Scie | Seie | Sdie;
+ csr32(ctlr, Intctl) |= s->sdintr;
+ csr8(ctlr, s->sdctl) |= Scie | Seie | Sdie;
return 0;
}
static void
-streamstart(Ctlr *ctlr)
+streamstart(Ctlr *ctlr, Stream *s)
{
- ctlr->active = 1;
-
- csr8(ctlr, ctlr->sdctl) |= Srun;
- waitup8(ctlr, ctlr->sdctl, Srun, Srun);
+ s->active = 1;
+ csr8(ctlr, s->sdctl) |= Srun;
+ waitup8(ctlr, s->sdctl, Srun, Srun);
}
static void
-streamstop(Ctlr *ctlr)
+streamstop(Ctlr *ctlr, Stream *s)
{
- csr8(ctlr, ctlr->sdctl) &= ~Srun;
- waitup8(ctlr, ctlr->sdctl, Srun, 0);
-
- ctlr->active = 0;
+ csr8(ctlr, s->sdctl) &= ~Srun;
+ waitup8(ctlr, s->sdctl, Srun, 0);
+ s->active = 0;
}
static uint
-streampos(Ctlr *ctlr)
+streampos(Ctlr *ctlr, Stream *s)
{
- Ring *r;
uint p;
- r = &ctlr->ring;
- p = csr32(ctlr, Sdlpib+ctlr->sdctl);
- if(p >= r->nbuf)
+ p = csr32(ctlr, Sdlpib+s->sdctl);
+ if(p >= s->ring.nbuf)
p = 0;
return p;
}
static long
hdactl(Audio *adev, void *va, long n, vlong)
{
- char *p, *e, *x, *tok[4];
+ char *p, *e, *x, *route, *tok[4];
int ntok;
Ctlr *ctlr;
uint pin, cad;
e = p + n;
for(; p < e; p = x){
+ route = nil;
if(x = strchr(p, '\n'))
*x++ = 0;
else
if(ntok <= 0)
continue;
if(cistrcmp(tok[0], "pin") == 0 && ntok >= 2){
- cad = ctlr->cad;
- pin = strtoul(tok[1], 0, 0);
+ cad = ctlr->sout.cad;
+ pin = strtoul(tok[1], &route, 0);
if(ntok > 2)
cad = strtoul(tok[2], 0, 0);
- connectpin(ctlr, pin, cad);
+ if(connectpin(ctlr, &ctlr->sout, Waout, pin, cad, route) < 0)
+ error("connectpin failed");
+ }else
+ if(cistrcmp(tok[0], "inpin") == 0 && ntok >= 2){
+ cad = ctlr->sin.cad;
+ pin = strtoul(tok[1], &route, 0);
+ if(ntok > 2)
+ cad = strtoul(tok[2], 0, 0);
+ if(connectpin(ctlr, &ctlr->sin, Wain, pin, cad, route) < 0)
+ error("connectpin failed");
}else
error(Ebadctl);
}
return n;
}
+static int
+inavail(void *arg)
+{
+ Ring *r = arg;
+ return buffered(r) > 0;
+}
+
static int
outavail(void *arg)
{
- Ctlr *ctlr = arg;
- return available(&ctlr->ring) > 0;
+ Ring *r = arg;
+ return available(r) > 0;
}
static int
{
Ctlr *ctlr = arg;
int delay = ctlr->adev->delay*BytesPerSample;
- return (delay <= 0) || (buffered(&ctlr->ring) <= delay) || (ctlr->active == 0);
+ return (delay <= 0) || (buffered(&ctlr->sout.ring) <= delay) || (ctlr->sout.active == 0);
}
static long
{
Ctlr *ctlr;
ctlr = adev->ctlr;
- return buffered(&ctlr->ring);
+ return buffered(&ctlr->sout.ring);
}
static void
hdakick(Ctlr *ctlr)
{
- if(ctlr->active)
+ int delay;
+
+ if(ctlr->sout.active)
return;
- if(buffered(&ctlr->ring) > Blocksize)
- streamstart(ctlr);
+ delay = ctlr->adev->delay*BytesPerSample;
+ if(buffered(&ctlr->sout.ring) >= delay)
+ streamstart(ctlr, &ctlr->sout);
+}
+
+static long
+hdaread(Audio *adev, void *vp, long n, vlong)
+{
+ uchar *p, *e;
+ Ctlr *ctlr;
+ Ring *ring;
+
+ p = vp;
+ e = p + n;
+ ctlr = adev->ctlr;
+ ring = &ctlr->sin.ring;
+ if(ring->buf == nil || ctlr->sin.conv == nil)
+ return 0;
+ while(p < e) {
+ if((n = readring(ring, p, e - p)) <= 0){
+ if(!ctlr->sin.active)
+ streamstart(ctlr, &ctlr->sin);
+ sleep(&ring->r, inavail, ring);
+ continue;
+ }
+ p += n;
+ }
+ return p - (uchar*)vp;
}
static long
p = vp;
e = p + n;
ctlr = adev->ctlr;
- ring = &ctlr->ring;
+ ring = &ctlr->sout.ring;
+ if(ring->buf == nil || ctlr->sout.conv == nil)
+ return 0;
while(p < e) {
if((n = writering(ring, p, e - p)) <= 0){
hdakick(ctlr);
- sleep(&ring->r, outavail, ctlr);
+ sleep(&ring->r, outavail, ring);
continue;
}
p += n;
}
hdakick(ctlr);
- sleep(&ring->r, outrate, ctlr);
+ while(outrate(ctlr) == 0)
+ sleep(&ring->r, outrate, ctlr);
return p - (uchar*)vp;
}
static void
-hdaclose(Audio *adev)
+hdaclose(Audio *adev, int mode)
{
Ctlr *ctlr;
- uchar z[1];
- Ring *r;
+ Ring *ring;
ctlr = adev->ctlr;
- if(!ctlr->active)
- return;
- z[0] = 0;
- r = &ctlr->ring;
- while(r->wi % Blocksize)
- hdawrite(adev, z, sizeof(z), 0);
+ if(mode == OREAD || mode == ORDWR){
+ if(ctlr->sin.active)
+ streamstop(ctlr, &ctlr->sin);
+ }
+ if(mode == OWRITE || mode == ORDWR){
+ ring = &ctlr->sout.ring;
+ while(ring->wi % Blocksize)
+ if(writering(ring, (uchar*)"", 1) <= 0)
+ break;
+ }
}
enum {
Vmaster,
+ Vrecord,
Vspeed,
Vdelay,
Nvol,
static Volume voltab[] = {
[Vmaster] "master", 0, 0x7f, Stereo, 0,
+ [Vrecord] "recgain", 0, 0x7f, Stereo, 0,
[Vspeed] "speed", 0, 0, Absolute, 0,
[Vdelay] "delay", 0, 0, Absolute, 0,
0
};
+static Widget*
+findoutamp(Stream *s)
+{
+ Widget *w;
+
+ for(w = s->conv; w != nil; w = w->path){
+ if(w->cap & Woutampcap)
+ return w;
+ if(w == s->jack)
+ break;
+ }
+ return nil;
+}
+
+static Widget*
+findinamp(Stream *s)
+{
+ Widget *w, *p, *a;
+
+ a = nil;
+ for(p = nil, w = s->jack; w != nil; p = w, w = w->path){
+ w->link = p; /* for setinamp */
+ if(w->cap & Winampcap)
+ a = w;
+ if(w == s->conv)
+ break;
+ }
+ return a;
+}
+
static int
hdagetvol(Audio *adev, int x, int a[2])
{
Ctlr *ctlr = adev->ctlr;
+ Widget *w;
switch(x){
case Vmaster:
- if(ctlr->amp != nil)
- getoutamp(ctlr->amp, a);
+ if((w = findoutamp(&ctlr->sout)) != nil)
+ getoutamp(w, a);
+ break;
+ case Vrecord:
+ if((w = findinamp(&ctlr->sin)) != nil)
+ getinamp(w, a);
break;
case Vspeed:
a[0] = adev->speed;
hdasetvol(Audio *adev, int x, int a[2])
{
Ctlr *ctlr = adev->ctlr;
+ Widget *w;
switch(x){
case Vmaster:
- if(ctlr->amp != nil)
- setoutamp(ctlr->amp, 0, a);
+ if((w = findoutamp(&ctlr->sout)) != nil)
+ setoutamp(w, 0, a);
+ break;
+ case Vrecord:
+ if((w = findinamp(&ctlr->sin)) != nil)
+ setinamp(w, w->link, 0, a);
break;
case Vspeed:
adev->speed = a[0];
break;
case Vdelay:
- adev->delay = a[0];
+ if(a[0] < Blocksize/BytesPerSample) {
+ adev->delay = Blocksize/BytesPerSample;
+ } else if(a[0] > (ctlr->sout.ring.nbuf/BytesPerSample)-1) {
+ adev->delay = (ctlr->sout.ring.nbuf/BytesPerSample)-1;
+ } else {
+ adev->delay = a[0];
+ }
break;
}
return 0;
static void
fillvoltab(Ctlr *ctlr, Volume *vt)
{
+ Widget *w;
+
memmove(vt, voltab, sizeof(voltab));
- if(ctlr->amp != nil)
- vt[Vmaster].range = getoutamprange(ctlr->amp);
+ if((w = findoutamp(&ctlr->sout)) != nil)
+ vt[Vmaster].range = getoutamprange(w);
+ if((w = findinamp(&ctlr->sin)) != nil)
+ vt[Vrecord].range = getinamprange(w);
}
static long
{
Ctlr *ctlr;
Audio *adev;
- uint sts;
Ring *r;
+ uint sts;
adev = arg;
ctlr = adev->ctlr;
-
ilock(ctlr);
sts = csr32(ctlr, Intsts);
- 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);
+ if(sts & ctlr->sout.sdintr){
+ csr8(ctlr, Sdsts+ctlr->sout.sdctl) |= Scompl;
+
+ r = &ctlr->sout.ring;
+ r->ri = streampos(ctlr, &ctlr->sout);
+ if(ctlr->sout.active && buffered(r) < Blocksize){
+ streamstop(ctlr, &ctlr->sout);
+ r->ri = r->wi = streampos(ctlr, &ctlr->sout);
+ }
+ wakeup(&r->r);
+ }
+ if(sts & ctlr->sin.sdintr){
+ csr8(ctlr, Sdsts+ctlr->sin.sdctl) |= Scompl;
+
+ r = &ctlr->sin.ring;
+ r->wi = streampos(ctlr, &ctlr->sin);
+ if(ctlr->sin.active && available(r) < Blocksize){
+ streamstop(ctlr, &ctlr->sin);
+ r->ri = r->wi = streampos(ctlr, &ctlr->sin);
}
wakeup(&r->r);
}
{
Ctlr *ctlr = adev->ctlr;
Codec *codec;
- Fungroup *fg;
Widget *w;
uint r;
- int k, i;
- char *s;
+ int i, j, k;
+ char *s, *e;
s = a;
- k = snprint(s, n, "bufsize %6d buffered %6ld\n", Blocksize, buffered(&ctlr->ring));
+ e = s + n;
+ s = seprint(s, e, "bufsize %6d buffered %6ld\n", Blocksize, buffered(&ctlr->sout.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;
+ s = seprint(s, e, "codec %d pin %d inpin %d\n",
+ codec->id.codec, ctlr->sout.pin, ctlr->sin.pin);
+ for(j=0; j<Maxwidgets; j++){
+ if((w = codec->widgets[j]) == nil)
+ continue;
+ switch(w->type){
+ case Wpin:
r = w->pin;
- k += snprint(s+k, n-k, "pin %3d %s %s %s %s %s %s%s%s\n",
- w->id.nid,
- (w->pincap & Pout) != 0 ? "out" : "in",
+ s = seprint(s, e, "%s %d %s%s %s %s %s %s %s%s%s",
+ widtype[w->type&7], w->id.nid,
+ (w->pincap & Pin) != 0 ? "in" : "",
+ (w->pincap & Pout) != 0 ? "out" : "",
pinport[(r >> 30) & 0x3],
pinloc2[(r >> 28) & 0x3],
pinloc[(r >> 24) & 0xf],
(w->pincap & Phdmi) ? " hdmi" : "",
(w->pincap & Peapd) ? " eapd" : ""
);
+ break;
+ default:
+ s = seprint(s, e, "%s %d %lux",
+ widtype[w->type&7], w->id.nid,
+ (ulong)w->cap);
+ }
+ if(w->nlist > 0){
+ s = seprint(s, e, " ← ");
+ for(k=0; k<w->nlist; k++){
+ if(k > 0)
+ s = seprint(s, e, ", ");
+ if(w->list[k] != nil)
+ s = seprint(s, e, "%s %d", widtype[w->list[k]->type&7], w->list[k]->id.nid);
+ }
}
+ s = seprint(s, e, "\n");
+ }
+ }
+
+ if(ctlr->sout.conv != nil && ctlr->sout.jack != nil){
+ s = seprint(s, e, "outpath ");
+ for(w=ctlr->sout.conv; w != nil; w = w->path){
+ s = seprint(s, e, "%s %d", widtype[w->type&7], w->id.nid);
+ if(w == ctlr->sout.jack)
+ break;
+ s = seprint(s, e, " → ");
}
+ s = seprint(s, e, "\n");
+ if((w = findoutamp(&ctlr->sout)) != nil)
+ s = seprint(s, e, "outamp %s %d\n", widtype[w->type&7], w->id.nid);
}
- return k;
+
+ if(ctlr->sin.conv != nil && ctlr->sin.jack != nil){
+ s = seprint(s, e, "inpath ");
+ for(w=ctlr->sin.jack; w != nil; w = w->path){
+ s = seprint(s, e, "%s %d", widtype[w->type&7], w->id.nid);
+ if(w == ctlr->sin.conv)
+ break;
+ s = seprint(s, e, " → ");
+ }
+ s = seprint(s, e, "\n");
+ if((w = findinamp(&ctlr->sin)) != nil)
+ s = seprint(s, e, "inamp %s %d\n", widtype[w->type&7], w->id.nid);
+ }
+
+ return s - (char*)a;
}
case (0x8086 << 16) | 0x284b: /* Intel ICH8 */
case (0x8086 << 16) | 0x293f: /* Intel ICH9 (untested) */
case (0x8086 << 16) | 0x293e: /* Intel P35 (untested) */
+ case (0x8086 << 16) | 0x3b56: /* Intel P55 (Ibex Peak) */
+ case (0x8086 << 16) | 0x811b: /* Intel SCH (Poulsbo) */
+ case (0x8086 << 16) | 0x080a: /* Intel SCH (Oaktrail) */
+ case (0x8086 << 16) | 0x1c20: /* Intel PCH */
+ case (0x8086 << 16) | 0x1e20: /* Intel (Thinkpad x230t) */
+ case (0x8086 << 16) | 0x8c20: /* Intel 8 Series/C220 Series */
+ case (0x8086 << 16) | 0x8ca0: /* Intel 9 Series */
+ case (0x8086 << 16) | 0x9c20: /* Intel 8 Series Lynx Point */
+ case (0x8086 << 16) | 0x9ca0: /* Intel Wildcat Point */
+ case (0x8086 << 16) | 0xa170: /* Intel Sunrise Point-H */
+ case (0x8086 << 16) | 0x3a6e: /* Intel ICH10 */
case (0x10de << 16) | 0x026c: /* NVidia MCP51 (untested) */
case (0x10de << 16) | 0x0371: /* NVidia MCP55 (untested) */
case (0x10de << 16) | 0x03f0: /* NVidia MCP61A (untested) */
case (0x10de << 16) | 0x044a: /* NVidia MCP65 (untested) */
case (0x10de << 16) | 0x055c: /* NVidia MCP67 (untested) */
+ case (0x10de << 16) | 0x0fbb: /* NVidia GM204 (untested) */
case (0x1002 << 16) | 0x437b: /* ATI SB450 (untested) */
case (0x1002 << 16) | 0x4383: /* ATI SB600 */
+ case (0x1002 << 16) | 0xaa55: /* ATI HDMI (8500 series) */
case (0x1002 << 16) | 0x7919: /* ATI HDMI */
case (0x1106 << 16) | 0x3288: /* VIA (untested) */
case (0x1039 << 16) | 0x7502: /* SIS (untested) */
case (0x10b9 << 16) | 0x5461: /* ULI (untested) */
+
+ case (0x1022 << 16) | 0x780d: /* AMD FCH Azalia Controller */
+
+ case (0x15ad << 16) | 0x1977: /* Vmware */
return p;
}
return nil;
if(cards == nil){
p = nil;
while(p = hdamatch(p)){
- ctlr = xspanalloc(sizeof(Ctlr), 8, 0);
- memset(ctlr, 0, sizeof(Ctlr));
+ ctlr = mallocz(sizeof(Ctlr), 1);
+ if(ctlr == nil){
+ print("hda: can't allocate memory\n");
+ return -1;
+ }
ctlr->pcidev = p;
ctlr->next = cards;
cards = ctlr;
}
/* pick a card from the list */
- for(ctlr = cards; ctlr; ctlr = ctlr->next){
+ for(ctlr = cards; ctlr != nil; ctlr = ctlr->next){
if(p = ctlr->pcidev){
ctlr->pcidev = nil;
goto Found;
pcicfgw16(p, 0x40, pcicfgr16(p, 0x40) | 0x10);
pcicfgw32(p, PciBAR1, 0);
}
+ if(p->vid == 0x8086){
+ /* magic for Intel */
+ switch(p->did){
+ case 0x1c20: /* PCH */
+ case 0x1e20:
+ case 0x811b: /* SCH */
+ case 0x080a:
+ case 0x8c20:
+ case 0x8ca0:
+ case 0x9c20:
+ case 0x9ca0:
+ case 0xa170:
+ pcicfgw16(p, 0x78, pcicfgr16(p, 0x78) & ~0x800);
+ }
+ }
if(p->vid == 0x1002){
/* magic for ATI */
pcicfgw8(p, 0x42, pcicfgr8(p, 0x42) | 0x02);
print("#A%d: unable to start hda\n", ctlr->no);
return -1;
}
- if(streamalloc(ctlr) < 0){
- print("#A%d: streamalloc failed\n", ctlr->no);
- return -1;
+
+ /* iss + oss + bss */
+ if(streamalloc(ctlr, &ctlr->sout, ctlr->iss) < 0)
+ print("#A%d: output streamalloc failed\n", ctlr->no);
+ if(ctlr->iss > 0){
+ if(streamalloc(ctlr, &ctlr->sin, 0) < 0)
+ print("#A%d: input streamalloc failed\n", ctlr->no);
+ }
+ else if(ctlr->bss > 0){
+ if(ctlr->oss > 0){
+ if(streamalloc(ctlr, &ctlr->sin, ctlr->oss) < 0)
+ print("#A%d: input streamalloc failed\n", ctlr->no);
+ } else if(ctlr->bss > 1) {
+ if(streamalloc(ctlr, &ctlr->sin, 1) < 0)
+ print("#A%d: input streamalloc failed\n", ctlr->no);
+ }
}
+
if(enumdev(ctlr) < 0){
print("#A%d: no audio codecs found\n", ctlr->no);
return -1;
}
- best = bestpin(ctlr, &cad);
- if(best < 0){
- print("#A%d: no output pins found!\n", ctlr->no);
- return -1;
- }
- if(connectpin(ctlr, best, cad) < 0){
- print("#A%d: error connecting pin\n", ctlr->no);
- return -1;
- }
+ muteall(ctlr);
+
+ best = bestpin(ctlr, &cad, scoreout);
+ if(best < 0)
+ print("#A%d: no output pins found\n", ctlr->no);
+ else if(connectpin(ctlr, &ctlr->sout, Waout, best, cad, nil) < 0)
+ print("#A%d: error connecting output pin\n", ctlr->no);
+
+ best = bestpin(ctlr, &cad, scorein);
+ if(best < 0)
+ print("#A%d: no input pins found\n", ctlr->no);
+ else if(connectpin(ctlr, &ctlr->sin, Wain, best, cad, nil) < 0)
+ print("#A%d: error connecting input pin\n", ctlr->no);
+ adev->read = hdaread;
adev->write = hdawrite;
adev->close = hdaclose;
adev->buffered = hdabuffered;