summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@centraldogma>2011-06-07 04:15:00 +0000
committercinap_lenrek <cinap_lenrek@centraldogma>2011-06-07 04:15:00 +0000
commitb932aaeafcb18deea79b4d19e84f527c5018141f (patch)
treeb4970b80975fcfb3aa2e571ee4022f8adbc5b156
parent241cd314480142a7281e677aaeb180f7b65f0664 (diff)
parent0f29bb556d35150445c2bd41ec6875b403bf7fd1 (diff)
downloadplan9front-b932aaeafcb18deea79b4d19e84f527c5018141f.tar.xz
merge
-rw-r--r--sys/games/lib/fortunes1
-rw-r--r--sys/src/9/pc/audiohda.c1356
-rw-r--r--sys/src/9/pc/pcf1
-rw-r--r--sys/src/games/doom/i_system.c6
4 files changed, 1362 insertions, 2 deletions
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; i<Maxwaitup; i++){
+ if((csr8(ctlr, reg) & mask) == set)
+ return 0;
+ microdelay(1);
+ }
+ print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
+ ctlr->no, reg, mask, set);
+ return -1;
+}
+
+static int
+waitup16(Ctlr *ctlr, int reg, ushort mask, ushort set)
+{
+ int i;
+ for(i=0; i<Maxwaitup; i++){
+ if((csr16(ctlr, reg) & mask) == set)
+ return 0;
+ microdelay(1);
+ }
+ print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
+ ctlr->no, reg, mask, set);
+ return -1;
+}
+
+static int
+waitup32(Ctlr *ctlr, int reg, uint mask, uint set)
+{
+ int i;
+ for(i=0; i<Maxwaitup; i++){
+ if((csr32(ctlr, reg) & mask) == set)
+ return 0;
+ microdelay(1);
+ }
+ print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
+ ctlr->no, 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; i<w->nlist; 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; i<w->nlist; 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; i<nlist; i++){
+ if(i * bits % 32 == 0)
+ r = cmd(w->id, 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; i<n; i++)
+ enumwidget(codec->widgets[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; i<n; i++){
+ fg = enumfungroup(codec, newnid(id, base + i));
+ if(fg == nil)
+ continue;
+ fg->next = 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; i<Maxcodecs; i++){
+ id.codec = i;
+ if(enumcodec(&ctlr->codec, 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; i<Nblocks; i++){
+ b->addrlo = 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;