diff --git a/sys/games/lib/fortunes b/sys/games/lib/fortunes index 834144537..55ced02db 100644 --- a/sys/games/lib/fortunes +++ b/sys/games/lib/fortunes @@ -1668,6 +1668,7 @@ What use is magic if it can't save a unicorn? -Peter S. Beagle What's in a name? That which we call a rose/By any other name would smell as sweet. What's it all about? We're all tools. Life's just a crock. What's the point to combing your hair when it's grey and thinning? +What's wrong with plan9? (...) I see there is a problem almost in every piece of plan9 code. - 9fans When all else fails, read the instructions. When all is said and done, a lot more is said than done. When an Okie moves to California, he raises the IQ of both states. -Will Rogers diff --git a/sys/src/9/pc/audiohda.c b/sys/src/9/pc/audiohda.c new file mode 100644 index 000000000..7b978f117 --- /dev/null +++ b/sys/src/9/pc/audiohda.c @@ -0,0 +1,1356 @@ +#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 Codec Codec; +typedef struct Ctlr Ctlr; +typedef struct Bld Bld; +typedef struct Ring Ring; +typedef struct Id Id; +typedef struct Widget Widget; +typedef struct Codec Codec; +typedef struct Fungroup Fungroup; +typedef struct Pinprop Pinprop; + +enum { + Gcap = 0x00, + Gctl = 0x08, + Rst = 1, + Flush = 2, + Acc = 1<<8, + Wakeen = 0x0c, + Statests = 0x0e, + Sdiwake = 1 | 2 | 4, + Intctl = 0x20, + Gie = 1<<31, + Cie = 1<<30, + Intsts = 0x24, + Gis = 1<<31, + Cis = 1<<30, + Sismask = 0xff, + Walclk = 0x30, + Corblbase = 0x40, + Corbubase = 0x44, + Corbwp = 0x48, + Corbrp = 0x4a, + Corbptrrst = 1<<15, + Corbctl = 0x4c, + Corbdma = 2, + Corbint = 1, + Corbsts = 0x4d, + Cmei = 1, + Corbsz = 0x4e, + Rirblbase = 0x50, + Rirbubase = 0x54, + Rirbwp = 0x58, + Rirbptrrst = 1<<15, + Rintcnt = 0x5a, + Rirbctl = 0x5c, + Rirbover = 4, + Rirbdma = 2, + Rirbint = 1, + Rirbsts = 0x5d, + Rirbrover = 4, + Rirbrint = 1, + Rirbsz = 0x5e, + Immcmd = 0x60, + Immresp = 0x64, + 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, + Srst = 1<<0, + Srun = 1<<1, + Scie = 1<<2, + Seie = 1<<3, + Sdie = 1<<4, + Stagbit = 20, + Stagmask = 0xf00000, + Sdsts = Sdoutput0 + 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, + Fmtmono = 0, + Fmtstereo = 1, + Fmtsampw = 1<<4, + Fmtsampb = 0<<4, + Fmtdiv1 = 0<<8, + Fmtmul1 = 0<<11, + Fmtbase441 = 1<<14, + Fmtbase48 = 0<<14, + Sdbdplo = Sdoutput0 + 0x18, + Sdbdphi = Sdoutput0 + 0x1c, +}; + +enum { + Bufsize = 64 * 1024 * 4, + Nblocks = 256, + Blocksize = Bufsize / Nblocks, + Streamtag = 1, + Streamno = 4, + Maxrirbwait = 1000, /* microseconds */ + Maxwaitup = 500, /* microseconds */ + Codecdelay = 1000, /* microseconds */ +}; + +enum { + /* 12-bit cmd + 8-bit payload */ + Getparm = 0xf00, + Vendorid = 0x00, + Revid = 0x02, + Subnodecnt = 0x04, + Fungrtype = 0x05, + Graudio = 0x01, + Grmodem = 0x02, + Fungrcap = 0x08, + Widgetcap = 0x09, + Waout = 0, + Wain = 1, + Wamix = 2, + Wasel = 3, + Wpin = 4, + Wpower = 5, + Wknob = 6, + Wbeep = 7, + Winampcap = 0x0002, + Woutampcap = 0x0004, + Wampovrcap = 0x0008, + Wfmtovrcap = 0x0010, + Wstripecap = 0x0020, + Wproccap = 0x0040, + Wunsolcap = 0x0080, + Wconncap = 0x0100, + Wdigicap = 0x0200, + Wpwrcap = 0x0400, + Wlrcap = 0x0800, + Wcpcap = 0x1000, + Streamrate = 0x0a, + Streamfmt = 0x0b, + Pincap = 0x0c, + Psense = 1<<0, + Ptrigreq = 1<<1, + Pdetect = 1<<2, + Pheadphone = 1<<3, + Pout = 1<<4, + Pin = 1<<5, + Pbalanced = 1<<6, + Phdmi = 1<<7, + Inampcap = 0x0d, + Outampcap = 0x12, + Connlistlen = 0x0e, + Powerstates = 0x0f, + Processcap = 0x10, + Gpiocount = 0x11, + Knobcap = 0x13, + Getconn = 0xf01, + Setconn = 0x701, + Getconnlist = 0xf02, + Getstate = 0xf03, + Setstate = 0x703, + Getstream = 0xf06, + Setstream = 0x706, + Getpinctl = 0xf07, + Setpinctl = 0x707, + Pinctlin = 1<<5, + Pinctlout = 1<<6, + Getunsolresp = 0xf08, + Setunsolresp = 0x708, + Getpinsense = 0xf09, + Exepinsense = 0x709, + Getgpi = 0xf10, + Setgpi = 0x710, + Getbeep = 0xf0a, + Setbeep = 0x70a, + Getknob = 0xf0f, + Setknob = 0x70f, + Getdefault = 0xf1c, + Funreset = 0x7ff, + Getchancnt = 0xf2d, + Setchancnt = 0x72d, + + /* 4-bit cmd + 16-bit payload */ + Getcoef = 0xd, + Setcoef = 0x5, + Getproccoef = 0xc, + Setproccoef = 0x4, + Getamp = 0xb, + Setamp = 0x3, + Asetout = 1<<15, + Asetin = 1<<14, + Asetleft = 1<<13, + Asetright = 1<<12, + Asetmute = 1<<7, + Aidx = 8, + Again = 0, + Getconvfmt = 0xa, + Setconvfmt = 0x2, +}; + +enum { + Maxcodecs = 16, + Maxwidgets = 256, +}; + +struct Ring { + uint rp, wp, cp; + uint size, blocksize; + uchar *buf; +}; + +struct Bld { + uint addrlo, addrhi; + uint len, flags; +}; + +struct Id { + Ctlr *ctlr; + uint codec, nid; +}; + +struct Widget { + Id id; + Fungroup *fg; + uint cap, type; + uint nlist; + Widget **list; + union { + struct { + uint pin, pincap; + }; + struct { + uint convrate, convfmt; + }; + }; + Widget *next; + Widget *from; +}; + +struct Fungroup { + Id id; + Codec *codec; + uint type; + Widget *first; + Widget *mixer; + Widget *src, *dst; + Fungroup *next; +}; + +struct Codec { + Id id; + uint vid, rid; + Widget *widgets[Maxwidgets]; + Fungroup *fgroup; +}; + +struct Ctlr { + Ctlr *next; + uint no; + + Lock; /* interrupt lock */ + QLock; /* command lock */ + Rendez outr; + + Pcidev *pcidev; + + uchar *mem; + ulong size; + + ulong *corb; + ulong corbsize; + + ulong *rirb; + ulong rirbsize; + + Bld *blds; + Ring ring; + + Codec codec; + Widget *amp, *src; + uint pin; + + int active; + uint afmt, atag; +}; + +#define csr32(c, r) (*(ulong *)&(c)->mem[r]) +#define csr16(c, r) (*(ushort *)&(c)->mem[r]) +#define csr8(c, r) (*(uchar *)&(c)->mem[r]) + +static char *pinport[] = { + "jack", + "nothing", + "fix", + "jack+fix", +}; + +static char *pinfunc[] = { + "lineout", + "speaker", + "hpout", + "cd", + "spdifout", + "digiout", + "modemline", + "modemhandset", + "linein", + "aux", + "micin", + "telephony", + "spdifin", + "digiin", + "resvd", + "other", +}; + + +static char *pincol[] = { + "?", + "black", + "grey", + "blue", + "green", + "red", + "orange", + "yellow", + "purple", + "pink", + "resvd", + "resvd", + "resvd", + "resvd", + "white", + "other", +}; + +static char *pinloc[] = { + "N/A", + "rear", + "front", + "left", + "right", + "top", + "bottom", + "special", + "special", + "special", + "resvd", + "resvd", + "resvd", + "resvd", + "resvd", + "resvd", +}; + +static char *pinloc2[] = { + "ext", + "int", + "sep", + "other", +}; + +static int +waitup8(Ctlr *ctlr, int reg, uchar mask, uchar set) +{ + int i; + for(i=0; ino, reg, mask, set); + return -1; +} + +static int +waitup16(Ctlr *ctlr, int reg, ushort mask, ushort set) +{ + int i; + for(i=0; ino, reg, mask, set); + return -1; +} + +static int +waitup32(Ctlr *ctlr, int reg, uint mask, uint set) +{ + int i; + for(i=0; ino, reg, mask, set); + return -1; +} + +static int +hdacmd(Ctlr *ctlr, uint request, uint reply[2]) +{ + uint rp, wp; + uint re; + int wait; + + re = csr16(ctlr, Rirbwp); + rp = csr16(ctlr, Corbrp); + wp = (csr16(ctlr, Corbwp) + 1) % ctlr->corbsize; + if(rp == wp){ + print("#A%d: corb full\n", ctlr->no); + return -1; + } + ctlr->corb[wp] = request; + coherence(); + csr16(ctlr, Corbwp) = wp; + for(wait=0; wait < Maxrirbwait; wait++){ + if(csr16(ctlr, Rirbwp) != re){ + re = (re + 1) % ctlr->rirbsize; + memmove(reply, &ctlr->rirb[re*2], 8); + return 8; + } + microdelay(1); + } + return 0; +} + +static int +cmderr(Id id, uint verb, uint par, uint *ret) +{ + uint q, w[2]; + q = (id.codec << 28) | (id.nid << 20); + if((verb & 0x700) == 0x700) + q |= (verb << 8) | par; + else + q |= (verb << 16) | par; + if(hdacmd(id.ctlr, q, w) != 8) + return -1; + *ret = w[0]; + return 0; +} + +static uint +cmd(Id id, uint verb, uint par) +{ + uint w[2]; + if(cmderr(id, verb, par, w) == -1) + return ~0; + return w[0]; +} + +static Id +newnid(Id id, uint nid) +{ + id.nid = nid; + return id; +} + +/* vol is 0...127 or ~0 for 0dB; mute is 0/1 */ +static void +setoutamp(Widget *w, int mute, uint vol) +{ + uint q, r; + uint zerodb, range; + + if((w->cap & Woutampcap) == 0) + return; + + r = cmd(w->id, Getparm, Outampcap); + range = (r >> 8) & 0x7f; + zerodb = r & 127; + + q = Asetout | Asetleft | Asetright; + if(mute) + q |= Asetmute; + else if(vol == ~0) + q |= zerodb << Again; + else if(range > 0) + q |= (range * vol / 128) << Again; + + cmd(w->id, Setamp, q); +} + +static void +getoutamp(Widget *w, int v[2]) +{ + uint q, r, range; + + v[0] = 0; + v[1] = 0; + + if((w->cap & Woutampcap) == 0) + return; + + r = cmd(w->id, Getparm, Outampcap); + range = (r >> 8) & 0x7f; + + q = (1 << 15) | (1 << 13); + r = cmd(w->id, Getamp, q); + v[0] = (r & 0x7f) * 128 / range; + + q = (1 << 15) | (0 << 13); + r = cmd(w->id, Getamp, q); + v[1] = (r & 0x7f) * 128 / range; +} + +/* vol is 0...127 or ~0 for 0dB; mute is 0/1; in is widget or nil for all */ +static void +setinamp(Widget *w, Widget *in, int mute, uint vol) +{ + uint q, r, i; + uint zerodb, range; + + if((w->cap & Winampcap) == 0) + return; + + r = cmd(w->id, Getparm, Inampcap); + range = (r >> 8) & 0x7f; + zerodb = r & 127; + + q = Asetin | Asetleft | Asetright; + if(mute) + q |= Asetmute; + else if(vol == ~0) + q |= zerodb << Again; + else if(range > 0) + q |= (range * vol / 128) << Again; + for(i=0; inlist; i++){ + if(in == nil || w->list[i] == in) + cmd(w->id, Setamp, q | (i << Aidx)); + } +} + +static Widget * +findpath(Widget *src) +{ + Widget *q[Maxwidgets]; + uint l, r, i; + Widget *w, *v; + + l = r = 0; + q[r++] = src; + for(w=src->fg->first; w; w=w->next) + w->from = nil; + src->from = src; + + while(l < r){ + w = q[l++]; + if(w->type == Waout) + break; + for(i=0; inlist; i++){ + v = w->list[i]; + if(v->from) + continue; + v->from = w; + q[r++] = v; + } + } + if(w->type != Waout) + return nil; + return w; +} + +static void +connectpath(Widget *src, Widget *dst, uint stream) +{ + Widget *w, *v; + uint i; + + for(w=src->fg->first; w != nil; w=w->next){ + setoutamp(w, 1, 0); + setinamp(w, nil, 1, 0); + cmd(w->id, Setstream, 0); + } + for(w=dst; w != src; w=v){ + v = w->from; + setoutamp(w, 0, ~0); + setinamp(v, w, 0, ~0); + if(v->type == Waout || v->type == Wamix) + continue; + if(v->nlist == 1) + continue; + for(i=0; i < v->nlist && v->list[i] != w; i++) + ; + cmd(v->id, Setconn, i); + } + setoutamp(src, 0, ~0); + cmd(src->id, Setpinctl, Pinctlout); + cmd(dst->id, Setstream, (stream << 4) | 0); + cmd(dst->id, Setconvfmt, (1 << 14) | (1 << 4) | 1); + cmd(dst->id, Setchancnt, 1); +} + +static void +enumconns(Widget *w) +{ + uint r, i, mask, bits, nlist; + Widget **ws, **list; + + ws = w->fg->codec->widgets; + r = cmd(w->id, Getparm, Connlistlen); + bits = (r & 0x80) == 0 ? 8 : 16; + nlist = r & 0x7f; + mask = (1 << bits) - 1; + list = malloc(sizeof *list * nlist); + for(i=0; iid, Getconnlist, i); + list[i] = ws[(r >> (i * bits % 32)) & mask]; + } + w->nlist = nlist; + w->list = list; +} + +static void +enumwidget(Widget *w) +{ + 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; + } +} + +static Fungroup * +enumfungroup(Codec *codec, Id id) +{ + Fungroup *fg; + Widget *w, *next; + uint i, r, n, base; + + r = cmd(id, Getparm, Fungrtype) & 0x7f; + if(r != Graudio) + return nil; + + fg = mallocz(sizeof *fg, 1); + fg->codec = codec; + fg->id = id; + fg->type = r; + + r = cmd(id, Getparm, Subnodecnt); + n = r & 0xff; + base = (r >> 8) & 0xff; + + if(base + n > Maxwidgets) + return nil; + + for(i=n, next=nil; i--; next=w){ + w = mallocz(sizeof(Widget), 1); + w->id = newnid(id, base + i); + w->fg = fg; + w->next = next; + codec->widgets[base + i] = w; + } + fg->first = next; + + for(i=0; iwidgets[base + i]); + + return fg; +} + + +static int +enumcodec(Codec *codec, Id id) +{ + Fungroup *fg; + uint i, r, n, base; + uint vid, rid; + + + if(cmderr(id, Getparm, Vendorid, &vid) < 0) + return -1; + if(cmderr(id, Getparm, Revid, &rid) < 0) + return -1; + + codec->id = id; + codec->vid = vid; + codec->rid = rid; + + r = cmd(id, Getparm, Subnodecnt); + n = r & 0xff; + base = (r >> 16) & 0xff; + + for(i=0; inext = codec->fgroup; + codec->fgroup = fg; + } + if(codec->fgroup == nil) + return -1; + return 0; +} + +static int +enumdev(Ctlr *ctlr) +{ + Id id; + int i; + + id.ctlr = ctlr; + id.nid = 0; + for(i=0; icodec, id) == 0) + return 0; + } + return -1; +} + +static int +connectpin(Ctlr *ctlr, uint pin) +{ + Widget *src, *dst; + + src = ctlr->codec.widgets[pin]; + if(src == nil) + return -1; + if(src->type != Wpin) + return -1; + if((src->pincap & Pout) == 0) + return -1; + dst = findpath(src); + if(!dst) + return -1; + + connectpath(src, dst, Streamtag); + ctlr->amp = dst; + ctlr->src = src; + ctlr->pin = pin; + return 0; +} + +static int +bestpin(Ctlr *ctlr) +{ + Fungroup *fg; + Widget *w; + int best, pin, score; + uint r; + + 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; + } + } + } + return pin; +} + +static void +ringreset(Ring *r) +{ + memset(r->buf, 0, r->size); + r->rp = 0; + r->wp = 0; + r->cp = 0; +} + +static uint +ringused(Ring *r) +{ + return (r->wp - r->rp) % r->size; +} + +static uint +ringavail(Ring *r) +{ + return r->size - r->blocksize - ringused(r); +} + +static uint +ringdirty(Ring *r) +{ + return (r->rp - r->cp) % r->size; +} + +static void +ringalign(Ring *r) +{ + r->wp += r->blocksize - 1; + r->wp -= r->wp % r->blocksize; + r->wp %= r->size; +} + +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; + } + + 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; + } + return p - ap; +} + + +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 int +streamalloc(Ctlr *ctlr) +{ + uchar *p; + Bld *b; + uint i; + Ring *r; + + r = &ctlr->ring; + r->size = Bufsize; + r->blocksize = Blocksize; + r->buf = xspanalloc(r->size, 128, 0); + if(r->buf == nil) + return -1; + ringreset(r); + + ctlr->active = 0; + ctlr->atag = Streamtag; + ctlr->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 | + Fmtmul1 | Fmtbase441; + + ctlr->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0); + if(ctlr->blds == nil) + return -1; + b = ctlr->blds; + p = r->buf; + for(i=0; iaddrlo = PADDR(p); + b->addrhi = 0; + b->flags = ~0; + b->len = Blocksize; + p += Blocksize; + b++; + } + return 0; +} + +static void +streamstart(Ctlr *ctlr) +{ + Ring *r = &ctlr->ring; + + /* perform reset */ + csr8(ctlr, Sdctl) = Srst; + waitup8(ctlr, Sdctl, Srst, Srst); + csr8(ctlr, Sdctl) = 0; + waitup8(ctlr, Sdctl, Srst, 0); + + /* 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; + + /* 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; +} + +static void +streamstop(Ctlr *ctlr) +{ + /* disble stream intrs */ + csr32(ctlr, Sdctl) = 0; + + /* disable global intrs for this stream */ + csr32(ctlr, Intctl) &= ~(1 << Streamno); + + /* mark as stopped */ + ctlr->active = 0; +} + + +static void +streamupdate(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); +} + +static int +outavail(void *arg) +{ + return ringavail(arg) > 0; +} + +static int +checkptr(Ctlr *ctlr) +{ + Ring *r; + + r = &ctlr->ring; + if(ctlr->active == 1) + return 1; + if(r->rp == 0) + return 1; + ringreset(r); + return 0; +} + +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; + + ctlr = adev->ctlr; + p = va; + e = p + n; + + for(; p < e; p = x){ + if(x = strchr(p, '\n')) + *x++ = 0; + else + x = e; + ntok = tokenize(p, tok, 4); + if(ntok <= 0) + continue; + if(cistrcmp(tok[0], "pin") == 0 && ntok == 2){ + qlock(ctlr); + connectpin(ctlr, strtoul(tok[1], 0, 0)); + qunlock(ctlr); + }else + error(Ebadctl); + } + return n; +} + +static long +hdawrite(Audio *adev, void *vp, long vn, vlong) +{ + uchar *p; + uint n, k; + Ring *r; + Ctlr *ctlr; + + p = vp; + n = vn; + ctlr = adev->ctlr; + r = &ctlr->ring; + + checkptr(ctlr); + while(n > 0){ + k = ringwrite(r, p, n); + if(checkptr(ctlr) == 0) + continue; + if(k == 0){ + hdakick(ctlr); + sleep(&ctlr->outr, outavail, r); + }else{ + p += k; + n -= k; + } + } + hdakick(ctlr); + return vn; +} + +static void +hdaclose(Audio *adev) +{ + Ctlr *ctlr; + ctlr = adev->ctlr; + ringalign(&ctlr->ring); + hdakick(ctlr); +} + +static Volume voltab[] = { + [0] "master", 0, 0x7f, Stereo, 0, + 0 +}; + +static int +hdagetvol(Audio *adev, int, int a[2]) +{ + Ctlr *ctlr = adev->ctlr; + + if(ctlr->amp == nil) + return -1; + qlock(ctlr); + getoutamp(ctlr->amp, a); + qunlock(ctlr); + return 0; +} + +static int +hdasetvol(Audio *adev, int, int a[2]) +{ + Ctlr *ctlr = adev->ctlr; + + if(ctlr->amp == nil) + return -1; + qlock(ctlr); + setoutamp(ctlr->amp, 0, a[0]); + qunlock(ctlr); + return 0; +} + +static long +hdavolread(Audio *adev, void *a, long n, vlong) +{ + return genaudiovolread(adev, a, n, 0, voltab, hdagetvol, 0); +} + +static long +hdavolwrite(Audio *adev, void *a, long n, vlong) +{ + return genaudiovolwrite(adev, a, n, 0, voltab, hdasetvol, 0); +} + +static void +hdainterrupt(Ureg *, void *arg) +{ + Ctlr *ctlr; + Audio *adev; + uint sts; + + adev = arg; + ctlr = adev->ctlr; + + ilock(ctlr); + sts = csr32(ctlr, Intsts); + if(sts & Sismask){ + streamupdate(ctlr); + }else{ + iprint("#A%d: hda unhandled interrupt\n", ctlr->no); + } + iunlock(ctlr); +} + +static long +hdastatus(Audio *adev, void *a, long n, vlong) +{ + Ctlr *ctlr = adev->ctlr; + Fungroup *fg; + Widget *w; + uint r; + int k; + char *s; + + s = a; + k = snprint(s, n, + "bufsize %6d buffered %6ud codec %2d pin %3d\n", + Bufsize, 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] + ); + + } + } + return k; +} + + +static int +hdastart(Ctlr *ctlr) +{ + static int cmdbufsize[] = { 2, 16, 256, 2048 }; + int n, size; + + /* alloc command buffers */ + size = csr8(ctlr, Corbsz); + n = cmdbufsize[size & 3]; + ctlr->corb = xspanalloc(n * 4, 128, 0); + memset(ctlr->corb, 0, n * 4); + ctlr->corbsize = n; + + size = csr8(ctlr, Rirbsz); + n = cmdbufsize[size & 3]; + 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; + csr16(ctlr, Statests) = csr16(ctlr, Statests); + csr8(ctlr, Rirbsts) = csr8(ctlr, Rirbsts); + + /* setup CORB */ + csr32(ctlr, Corblbase) = PADDR(ctlr->corb); + csr32(ctlr, Corbubase) = 0; + csr16(ctlr, Corbwp) = 0; + csr16(ctlr, Corbrp) = Corbptrrst; + waitup16(ctlr, Corbrp, Corbptrrst, Corbptrrst); + csr16(ctlr, Corbrp) = 0; + waitup16(ctlr, Corbrp, Corbptrrst, 0); + csr8(ctlr, Corbctl) = Corbdma; + waitup8(ctlr, Corbctl, Corbdma, Corbdma); + + /* setup RIRB */ + csr32(ctlr, Rirblbase) = PADDR(ctlr->rirb); + csr32(ctlr, Rirbubase) = 0; + csr16(ctlr, Rirbwp) = Rirbptrrst; + csr8(ctlr, Rirbctl) = Rirbdma; + waitup8(ctlr, Rirbctl, Rirbdma, Rirbdma); + + /* enable interrupts */ + csr32(ctlr, Intctl) = Gie | Cie; + + return 0; +} + +static Pcidev* +hdamatch(Pcidev *p) +{ + while(p = pcimatch(p, 0, 0)) + switch((p->vid << 16) | p->did){ + case (0x8086 << 16) | 0x27d8: + return p; + } + return nil; +} + +static int +hdareset(Audio *adev) +{ + static Ctlr *cards = nil; + Pcidev *p; + int irq, tbdf, best; + Ctlr *ctlr; + + /* make a list of all ac97 cards if not already done */ + if(cards == nil){ + p = nil; + while(p = hdamatch(p)){ + ctlr = xspanalloc(sizeof(Ctlr), 8, 0); + memset(ctlr, 0, sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->next = cards; + cards = ctlr; + } + } + + /* pick a card from the list */ + for(ctlr = cards; ctlr; ctlr = ctlr->next){ + if(p = ctlr->pcidev){ + ctlr->pcidev = nil; + goto Found; + } + } + return -1; + +Found: + adev->ctlr = ctlr; + + irq = p->intl; + tbdf = p->tbdf; + + pcisetbme(p); + pcisetpms(p, 0); + + ctlr->no = adev->ctlrno; + ctlr->size = p->mem[0].size; + ctlr->mem = vmap(p->mem[0].bar & ~0x0F, ctlr->size); + if(ctlr->mem == nil){ + print("#A%d: can't map %.8lux\n", ctlr->no, p->mem[0].bar); + return -1; + } + print("#A%d: hda mem %p irq %d\n", ctlr->no, ctlr->mem, irq); + + if(hdastart(ctlr) < 0){ + print("#A%d: unable to start hda\n", ctlr->no); + return -1; + } + if(streamalloc(ctlr) < 0){ + print("#A%d: unable to allocate stream buffer\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); + if(best < 0){ + print("#A%d: no output pins found!\n", ctlr->no); + return -1; + } + if(connectpin(ctlr, best) < 0){ + print("#A%d: error connecting pin\n", ctlr->no); + return -1; + } + + adev->write = hdawrite; + adev->close = hdaclose; + adev->buffered = hdabuffered; + adev->volread = hdavolread; + adev->volwrite = hdavolwrite; + adev->status = hdastatus; + adev->ctl = hdactl; + + intrenable(irq, hdainterrupt, adev, tbdf, "hda"); + + return 0; +} + +void +audiohdalink(void) +{ + addaudiocard("hda", hdareset); +} diff --git a/sys/src/9/pc/pcf b/sys/src/9/pc/pcf index cbdf0e3b7..7feb36052 100644 --- a/sys/src/9/pc/pcf +++ b/sys/src/9/pc/pcf @@ -75,6 +75,7 @@ link audiosb16 dma audioac97 audioac97mix + audiohda misc archmp mp apic diff --git a/sys/src/games/doom/i_system.c b/sys/src/games/doom/i_system.c index 7e8c8173b..8e17da76b 100644 --- a/sys/src/games/doom/i_system.c +++ b/sys/src/games/doom/i_system.c @@ -140,12 +140,14 @@ char* I_IdentifyWAD(char *wadname) { char path[1024]; - /* /sys/lib/doom/... */ snprintf(path, sizeof path, "/sys/lib/doom/%s", wadname); if (I_FileExists (path)) return path; - /* $home/lib/doom/... */ + snprintf(path, sizeof path, "/sys/games/lib/doom/%s", wadname); + if (I_FileExists (path)) + return path; + snprintf(path, sizeof path, "%s/lib/doom/%s", getenv("home"), wadname); if (I_FileExists (path)) return path;