diff options
author | cinap_lenrek <cinap_lenrek@localhost> | 2011-05-16 12:16:43 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@localhost> | 2011-05-16 12:16:43 +0000 |
commit | d642d726babfc8e6a04a9ea2280655b0c5ff8f98 (patch) | |
tree | 7fe39211e5f6d17622c9f6f0c9eb53aa8351576a /sys/src | |
parent | 1a81fb86eb713f3e98a71522259d843e8cc14995 (diff) |
add ac97 driver
Diffstat (limited to 'sys/src')
-rw-r--r-- | sys/src/9/pc/audio.h | 15 | ||||
-rw-r--r-- | sys/src/9/pc/audioac97.c | 609 | ||||
-rw-r--r-- | sys/src/9/pc/audioac97mix.c | 365 | ||||
-rw-r--r-- | sys/src/9/pc/pcf | 2 | ||||
-rw-r--r-- | sys/src/9/port/devaudio.c | 1302 |
5 files changed, 1098 insertions, 1195 deletions
diff --git a/sys/src/9/pc/audio.h b/sys/src/9/pc/audio.h deleted file mode 100644 index d06c7bccb..000000000 --- a/sys/src/9/pc/audio.h +++ /dev/null @@ -1,15 +0,0 @@ -enum -{ - Bufsize = 1024, /* 5.8 ms each, must be power of two */ - Nbuf = 128, /* .74 seconds total */ - Dma = 6, - IrqAUDIO = 7, - SBswab = 0, -}; - -#define seteisadma(a, b) dmainit(a, Bufsize); -#define CACHELINESZ 8 -#define UNCACHED(type, v) (type*)((ulong)(v)) - -#define Int0vec -#define setvec(v, f, a) intrenable(v, f, a, BUSUNKNOWN, "audio") diff --git a/sys/src/9/pc/audioac97.c b/sys/src/9/pc/audioac97.c new file mode 100644 index 000000000..0dc38f6cb --- /dev/null +++ b/sys/src/9/pc/audioac97.c @@ -0,0 +1,609 @@ +#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/audio.h" + +typedef struct Hwdesc Hwdesc; +typedef struct Ctlr Ctlr; +static uint sis7012 = 0; + +enum { + Ioc = 1<<31, + Bup = 1<<30, +}; + +struct Hwdesc { + ulong addr; + ulong size; +}; + +enum { + Ndesc = 32, + Nts = 33, + Bufsize = 32768, /* bytes, must be divisible by ndesc */ + Maxbusywait = 500000, /* microseconds, roughly */ + BytesPerSample = 4, +}; + +struct Ctlr { + /* keep these first, they want to be 8-aligned */ + Hwdesc indesc[Ndesc]; + Hwdesc outdesc[Ndesc]; + Hwdesc micdesc[Ndesc]; + + Lock; + Rendez outr; + + ulong port; + ulong mixport; + + char *out; + char *in; + char *mic; + + char *outp; + char *inp; + char *micp; + + /* shared variables, ilock to access */ + int outavail; + int inavail; + int micavail; + + /* interrupt handler alone */ + int outciv; + int inciv; + int micciv; + + int tsouti; + uvlong tsoutp; + ulong tsout[Nts]; + int tsoutb[Nts]; + + ulong civstat[Ndesc]; + ulong lvistat[Ndesc]; + + int targetrate; + int hardrate; + + int attachok; +}; + +#define iorl(c, r) (inl((c)->port+(r))) +#define iowl(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +enum { + In = 0x00, + Out = 0x10, + Mic = 0x20, + Bar = 0x00, /* Base address register, 8-byte aligned */ + /* a 32-bit read at 0x04 can be used to get civ:lvi:sr in one step */ + Civ = 0x04, /* current index value (desc being processed) */ + Lvi = 0x05, /* Last valid index (index of first unused entry!) */ + Sr = 0x06, /* status register */ + Fifoe = 1<<4, /* fifo error (r/wc) */ + Bcis = 1<<3, /* buffer completion interrupt status (r/wc) */ + Lvbci = 1<<2, /* last valid buffer completion(in)/fetched(out) interrupt (r/wc) */ + Celv = 1<<1, /* current equals last valid (ro) */ + Dch = 1<<0, /* dma controller halted (ro) */ + Picb = 0x08, /* position in current buffer */ + Piv = 0x0a, /* prefetched index value */ + Cr = 0x0b, /* control register */ + Ioce = 1<<4, /* interrupt on buffer completion (if bit set in hwdesc.size) (rw) */ + Feie = 1<<3, /* fifo error interrupt enable (rw) */ + Lvbie = 1<<2, /* last valid buffer interrupt enable (rw) */ + RR = 1<<1, /* reset busmaster related regs, excl. ioce,feie,lvbie (rw) */ + Rpbm = 1<<0, /* run/pause busmaster. 0 stops, 1 starts (rw) */ + Cnt = 0x2c, /* global control */ + Ena16bit = 0x0<<22, + Ena20bit = 0x1<<22, + Ena2chan = 0x0<<20, + Ena4chan = 0x1<<20, + Enam6chan = 0x2<<20, + EnaRESER = 0x3<<20, + Sr2ie = 1<<6, /* sdin2 interrupt enable (rw) */ + Srie = 1<<5, /* sdin1 interrupt enable (rw) */ + Prie = 1<<4, /* sdin0 interrupt enable (rw) */ + Aclso = 1<<3, /* ac link shut-off (rw) */ + Acwr = 1<<2, /* ac 97 warm reset (rw) */ + Accr = 1<<1, /* ac 97 cold reset (rw) */ + GPIie = 1<<0, /* GPI interrupt enable (rw) */ + Sta = 0x30, /* global status */ + Cap6chan = 1<<21, + Cap4chan = 1<<20, + Md3 = 1<<17, /* modem powerdown semaphore */ + Ad3 = 1<<16, /* audio powerdown semaphore */ + Rcs = 1<<15, /* read completion status (r/wc) */ + S2ri = 1<<29, /* sdin2 resume interrupt (r/wc) */ + Sri = 1<<11, /* sdin1 resume interrupt (r/wc) */ + Pri = 1<<10, /* sdin0 resume interrupt (r/wc) */ + S2cr = 1<<28, /* sdin2 codec ready (ro) */ + Scr = 1<<9, /* sdin1 codec ready (ro) */ + Pcr = 1<<8, /* sdin0 codec ready (ro) */ + Mint = 1<<7, /* microphone in inetrrupt (ro) */ + Point = 1<<6, /* pcm out interrupt (ro) */ + Piint = 1<<5, /* pcm in interrupt (ro) */ + Moint = 1<<2, /* modem out interrupt (ro) */ + Miint = 1<<1, /* modem in interrupt (ro) */ + Gsci = 1<<0, /* GPI status change interrupt */ + Cas = 0x34, /* codec access semaphore */ + Casp = 1<<0, /* set to 1 on read if zero, cleared by hardware */ +}; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w))) + +static void +ac97waitcodec(Audio *adev) +{ + Ctlr *ctlr; + int i; + ctlr = adev->ctlr; + for(i = 0; i < Maxbusywait/10; i++){ + if((csr8r(ctlr, Cas) & Casp) == 0) + break; + microdelay(10); + } + if(i == Maxbusywait) + print("#A%d: ac97 exhausted waiting codec access\n", adev->ctlrno); +} + +static void +ac97mixw(Audio *adev, int port, ushort val) +{ + Ctlr *ctlr; + ac97waitcodec(adev); + ctlr = adev->ctlr; + outs(ctlr->mixport+port, val); +} + +static ushort +ac97mixr(Audio *adev, int port) +{ + Ctlr *ctlr; + ac97waitcodec(adev); + ctlr = adev->ctlr; + return ins(ctlr->mixport+port); +} + +static int +outavail(void *arg) +{ + Ctlr *ctlr; + ctlr = arg; + return ctlr->outavail > 0; +} + +static void +ac97interrupt(Ureg *, void *arg) +{ + Audio *adev; + Ctlr *ctlr; + int civ, n, i; + ulong stat; + uvlong now; + + adev = arg; + ctlr = adev->ctlr; + stat = csr32r(ctlr, Sta); + + stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci; + + ilock(ctlr); + if(stat & Point){ + if(sis7012) + csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch); + else + csr16w(ctlr, Out + Sr, csr16r(ctlr, Out + Sr) & ~Dch); + + civ = csr8r(ctlr, Out + Civ); + n = 0; + while(ctlr->outciv != civ){ + ctlr->civstat[ctlr->outciv++]++; + if(ctlr->outciv == Ndesc) + ctlr->outciv = 0; + n += Bufsize/Ndesc; + } + + now = fastticks(0); + i = ctlr->tsouti; + ctlr->tsoutb[i] = n; + ctlr->tsout[i] = now - ctlr->tsoutp; + ctlr->tsouti = (i + 1) % Nts; + ctlr->tsoutp = now; + ctlr->outavail += n; + + if(ctlr->outavail > Bufsize/2) + wakeup(&ctlr->outr); + stat &= ~Point; + } + iunlock(ctlr); + if(stat) /* have seen 0x400, which is sdin0 resume */ + print("#A%d: ac97 unhandled interrupt(s): stat 0x%lux\n", adev->ctlrno, stat); +} + +static int +off2lvi(char *base, char *p) +{ + int lvi; + lvi = p - base; + return lvi / (Bufsize/Ndesc); +} + +static long +ac97medianoutrate(Audio *adev) +{ + ulong ts[Nts], t; + uvlong hz; + int i, j; + Ctlr *ctlr; + ctlr = adev->ctlr; + fastticks(&hz); + for(i = 0; i < Nts; i++) + if(ctlr->tsout[i] > 0) + ts[i] = (ctlr->tsoutb[i] * hz) / ctlr->tsout[i]; + else + ts[i] = 0; + for(i = 1; i < Nts; i++){ + t = ts[i]; + j = i; + while(j > 0 && ts[j-1] > t){ + ts[j] = ts[j-1]; + j--; + } + ts[j] = t; + } + return ts[Nts/2] / BytesPerSample; +} + +static void +ac97volume(Audio *adev, char *msg) +{ + adev->volwrite(adev, msg, strlen(msg), 0); +} + +static void +ac97attach(Audio *adev) +{ + Ctlr *ctlr; + ctlr = adev->ctlr; + if(!ctlr->attachok){ + ac97hardrate(adev, ctlr->hardrate); + ac97volume(adev, "audio 75"); + ac97volume(adev, "head 100"); + ac97volume(adev, "master 100"); + ctlr->attachok = 1; + } +} + +static long +ac97status(Audio *adev, void *a, long n, vlong off) +{ + Ctlr *ctlr; + char *buf; + long i, l; + ctlr = adev->ctlr; + l = 0; + buf = malloc(READSTR); + l += snprint(buf + l, READSTR - l, "rate %d\n", ctlr->targetrate); + l += snprint(buf + l, READSTR - l, "median rate %lud\n", ac97medianoutrate(adev)); + l += snprint(buf + l, READSTR - l, "hard rate %d\n", ac97hardrate(adev, -1)); + + l += snprint(buf + l, READSTR - l, "civ stats"); + for(i = 0; i < Ndesc; i++) + l += snprint(buf + l, READSTR - l, " %lud", ctlr->civstat[i]); + l += snprint(buf + l, READSTR - l, "\n"); + + l += snprint(buf + l, READSTR - l, "lvi stats"); + for(i = 0; i < Ndesc; i++) + l += snprint(buf + l, READSTR - l, " %lud", ctlr->lvistat[i]); + snprint(buf + l, READSTR - l, "\n"); + + n = readstr(off, a, n, buf); + free(buf); + return n; +} + +static long +ac97buffered(Audio *adev) +{ + Ctlr *ctlr; + ctlr = adev->ctlr; + return Bufsize - Bufsize/Ndesc - ctlr->outavail; +} + +static long +ac97ctl(Audio *adev, void *a, long n, vlong) +{ + Ctlr *ctlr; + char *tok[2], *p; + int ntok; + long t; + + ctlr = adev->ctlr; + if(n > READSTR) + n = READSTR - 1; + p = malloc(READSTR); + + if(waserror()){ + free(p); + nexterror(); + } + memmove(p, a, n); + p[n] = 0; + ntok = tokenize(p, tok, nelem(tok)); + if(ntok > 1 && !strcmp(tok[0], "rate")){ + t = strtol(tok[1], 0, 10); + if(t < 8000 || t > 48000) + error("rate must be between 8000 and 48000"); + ctlr->targetrate = t; + ctlr->hardrate = t; + ac97hardrate(adev, ctlr->hardrate); + poperror(); + free(p); + return n; + } + error("invalid ctl"); + return n; /* shut up, you compiler you */ +} + +static void +ac97kick(Ctlr *ctlr, long reg) +{ + csr8w(ctlr, reg+Cr, Ioce | Rpbm); +} + +static long +ac97write(Audio *adev, void *a, long nwr, vlong) +{ + Ctlr *ctlr; + char *p, *sp, *ep; + int len, lvi, olvi; + int t; + long n; + + ctlr = adev->ctlr; + ilock(ctlr); + p = ctlr->outp; + sp = a; + ep = ctlr->out + Bufsize; + olvi = off2lvi(ctlr->out, p); + n = nwr; + while(n > 0){ + len = ep - p; + if(ctlr->outavail < len) + len = ctlr->outavail; + if(n < len) + len = n; + ctlr->outavail -= len; + iunlock(ctlr); + memmove(p, sp, len); + ilock(ctlr); + p += len; + sp += len; + n -= len; + if(p == ep) + p = ctlr->out; + lvi = off2lvi(ctlr->out, p); + if(olvi != lvi){ + t = olvi; + while(t != lvi){ + t = (t + 1) % Ndesc; + ctlr->lvistat[t]++; + csr8w(ctlr, Out+Lvi, t); + ac97kick(ctlr, Out); + } + olvi = lvi; + } + if(ctlr->outavail == 0){ + ctlr->outp = p; + iunlock(ctlr); + sleep(&ctlr->outr, outavail, ctlr); + ilock(ctlr); + } + } + ctlr->outp = p; + iunlock(ctlr); + return nwr; +} + +static Pcidev* +ac97match(Pcidev *p) +{ + /* not all of the matched devices have been tested */ + while(p = pcimatch(p, 0, 0)) + switch(p->vid){ + default: + break; + case 0x1039: + switch(p->did){ + default: + break; + case 0x7012: + sis7012 = 1; + return p; + } + case 0x1022: + switch(p->did){ + default: + break; + case 0x746d: + case 0x7445: + return p; + } + case 0x10de: + switch(p->did){ + default: + break; + case 0x01b1: + case 0x006a: + case 0x00da: + case 0x00ea: + return p; + } + case 0x8086: + switch(p->did){ + default: + break; + case 0x2415: + case 0x2425: + case 0x2445: + case 0x2485: + case 0x24c5: + case 0x24d5: + case 0x25a6: + case 0x266e: + case 0x7195: + return p; + } + } + return nil; +} + +static void +sethwp(Ctlr *ctlr, long off, void *ptr) +{ + csr8w(ctlr, off+Cr, RR); + csr32w(ctlr, off+Bar, PCIWADDR(ptr)); + csr8w(ctlr, off+Lvi, 0); +} + +static int +ac97reset(Audio *adev) +{ + static int ncards = 1; + int i, irq, tbdf; + Pcidev *p; + Ctlr *ctlr; + ulong ctl, stat = 0; + + p = nil; + for(i = 0; i < ncards; i++) + if((p = ac97match(p)) == nil) + return -1; + ncards++; + + ctlr = xspanalloc(sizeof(Ctlr), 8, 0); + memset(ctlr, 0, sizeof(Ctlr)); + adev->ctlr = ctlr; + ctlr->targetrate = 44100; + ctlr->hardrate = 44100; + + if(p->mem[0].size == 64){ + ctlr->port = p->mem[0].bar & ~3; + ctlr->mixport = p->mem[1].bar & ~3; + } else if(p->mem[1].size == 64){ + ctlr->port = p->mem[1].bar & ~3; + ctlr->mixport = p->mem[0].bar & ~3; + } else if(p->mem[0].size == 256){ /* sis7012 */ + ctlr->port = p->mem[1].bar & ~3; + ctlr->mixport = p->mem[0].bar & ~3; + } else if(p->mem[1].size == 256){ + ctlr->port = p->mem[0].bar & ~3; + ctlr->mixport = p->mem[1].bar & ~3; + } + + irq = p->intl; + tbdf = p->tbdf; + + print("#A%d: ac97 port 0x%04lux mixport 0x%04lux irq %d\n", + adev->ctlrno, ctlr->port, ctlr->mixport, irq); + + pcisetbme(p); + pcisetioe(p); + + ctlr->mic = xspanalloc(Bufsize, 8, 0); + ctlr->in = xspanalloc(Bufsize, 8, 0); + ctlr->out = xspanalloc(Bufsize, 8, 0); + + for(i = 0; i < Ndesc; i++){ + int size, off = i * (Bufsize/Ndesc); + + if(sis7012) + size = (Bufsize/Ndesc); + else + size = (Bufsize/Ndesc) / 2; + + ctlr->micdesc[i].addr = PCIWADDR(ctlr->mic + off); + ctlr->micdesc[i].size = Ioc | size; + ctlr->indesc[i].addr = PCIWADDR(ctlr->in + off); + ctlr->indesc[i].size = Ioc | size; + ctlr->outdesc[i].addr = PCIWADDR(ctlr->out + off); + ctlr->outdesc[i].size = Ioc | size; + } + + ctlr->outavail = Bufsize - Bufsize/Ndesc; + ctlr->outp = ctlr->out; + + ctl = csr32r(ctlr, Cnt); + ctl &= ~(EnaRESER | Aclso); + + if((ctl & Accr) == 0){ + print("#A%d: ac97 cold reset\n", adev->ctlrno); + ctl |= Accr; + }else{ + print("#A%d: ac97 warm reset\n", adev->ctlrno); + ctl |= Acwr; + } + + csr32w(ctlr, Cnt, ctl); + for(i = 0; i < Maxbusywait; i++){ + if((csr32r(ctlr, Cnt) & Acwr) == 0) + break; + microdelay(1); + } + if(i == Maxbusywait) + print("#A%d: ac97 gave up waiting Acwr to go down\n", adev->ctlrno); + + for(i = 0; i < Maxbusywait; i++){ + if((stat = csr32r(ctlr, Sta)) & (Pcr | Scr | S2cr)) + break; + microdelay(1); + } + if(i == Maxbusywait) + print("#A%d: ac97 gave up waiting codecs become ready\n", adev->ctlrno); + + print("#A%d: ac97 codecs ready:%s%s%s\n", adev->ctlrno, + (stat & Pcr) ? " sdin0" : "", + (stat & Scr) ? " sdin1" : "", + (stat & S2cr) ? " sdin2" : ""); + + print("#A%d: ac97 codecs resumed:%s%s%s\n", adev->ctlrno, + (stat & Pri) ? " sdin0" : "", + (stat & Sri) ? " sdin1" : "", + (stat & S2ri) ? " sdin2" : ""); + + sethwp(ctlr, In, ctlr->indesc); + sethwp(ctlr, Out, ctlr->outdesc); + sethwp(ctlr, Mic, ctlr->micdesc); + + csr8w(ctlr, In+Cr, Ioce); /* | Lvbie | Feie */ + csr8w(ctlr, Out+Cr, Ioce); /* | Lvbie | Feie */ + csr8w(ctlr, Mic+Cr, Ioce); /* | Lvbie | Feie */ + + adev->attach = ac97attach; + adev->write = ac97write; + adev->status = ac97status; + adev->ctl = ac97ctl; + adev->buffered = ac97buffered; + + ac97mixreset(adev, ac97mixw, ac97mixr); + + intrenable(irq, ac97interrupt, adev, tbdf, adev->name); + + return 0; +} + +void +audioac97link(void) +{ + addaudiocard("ac97audio", ac97reset); +} diff --git a/sys/src/9/pc/audioac97mix.c b/sys/src/9/pc/audioac97mix.c new file mode 100644 index 000000000..09fbe6ebf --- /dev/null +++ b/sys/src/9/pc/audioac97mix.c @@ -0,0 +1,365 @@ +#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/audio.h" + +typedef ushort (*ac97rdfn)(Audio *, int); +typedef void (*ac97wrfn)(Audio *, int, ushort); + +typedef struct Mixer Mixer; +typedef struct Volume Volume; + +struct Mixer { + QLock; + ac97wrfn wr; + ac97rdfn rr; + int vra; +}; + +enum { Maxbusywait = 500000 }; + +enum { + Reset = 0x0, + Capmic = 0x1, + Captonectl = 0x4, + Capsimstereo = 0x8, + Capheadphones = 0x10, + Caploudness = 0x20, + Capdac18 = 0x40, + Capdac20 = 0x80, + Capadc18 = 0x100, + Capadc20 = 0x200, + Capenh = 0xfc00, + Master = 0x02, + Headphone = 0x04, + Monomaster = 0x06, + Mastertone = 0x08, + Pcbeep = 0x0A, + Phone = 0x0C, + Mic = 0x0E, + Line = 0x10, + Cd = 0x12, + Video = 0x14, + Aux = 0x16, + Pcmout = 0x18, + Mute = 0x8000, + Recsel = 0x1A, + Recgain = 0x1C, + Micgain = 0x1E, + General = 0x20, + ThreeDctl = 0x22, + Ac97RESER = 0x24, + Powerdowncsr = 0x26, + Adcpower = 0x1, + Dacpower = 0x2, + Anlpower = 0x4, + Refpower = 0x8, + Inpower = 0x100, + Outpower = 0x200, + Mixpower = 0x400, + Mixvrefpower = 0x800, + Aclinkpower = 0x1000, + Clkpower = 0x2000, + Auxpower = 0x4000, + Eamppower = 0x8000, + Extid = 0x28, + Extcsr = 0x2A, + Extvra = 1<<0, + Extdra = 1<<1, + Extspdif = 1<<2, + Extvrm = 1<<3, + Extiddsa0 = 0<<4, /* extid only */ + Extiddsa1 = 1<<4, /* extid only */ + Extiddsa2 = 2<<4, /* extid only */ + Extiddsa3 = 3<<4, /* extid only */ + Extcsrspsa34 = 0<<4, /* extcsr only */ + Extcsrspsa78 = 1<<4, /* extcsr only */ + Extcsrspsa69 = 2<<4, /* extcsr only */ + ExtcsrspsaAB = 3<<4, /* extcsr only */ + Extcdac = 1<<6, + Extsdac = 1<<7, + Extldac = 1<<8, + Extidamap = 1<<9, /* extid only */ + Extidrev11 = 0<<10, /* extid only */ + Extidrev22 = 1<<10, /* extid only */ + Extidrev23 = 2<<10, /* extid only */ + Extidprim = 0<<14, /* extid only */ + Extidsec0 = 1<<14, /* extid only */ + Extidsec1 = 2<<14, /* extid only */ + Extidsec2 = 3<<14, /* extid only */ + Extcsrmadc = 1<<9, /* extcsr only */ + Extcsrspcv = 1<<10, /* extcsr only */ + Extcsrpri = 1<<11, /* extcsr only */ + Extcsrprj = 1<<12, /* extcsr only */ + Extcsrprk = 1<<13, /* extcsr only */ + Extcsrprl = 1<<14, /* extcsr only */ + Extcsrvcfg = 1<<15, /* extcsr only */ + Pcmfrontdacrate = 0x2C, + Pcmsurrounddacrate = 0x2E, + Pcmlfedacrate = 0x30, + Pcmadcrate = 0x32, + Pcmmicadcrate = 0x34, + CenterLfe = 0x36, + LrSurround = 0x38, + Spdifcsr = 0x3a, + Spdifpro = 1<<0, + Spdifnonaudio = 1<<1, + Spdifcopy = 1<<2, + Spdifpre = 1<<3, + SpdifCC = 0x7f<<4, + Spdifl = 1<<11, + Spdif44k1 = 0<<12, + Spdif32k = 1<<12, + Spdif48k = 2<<12, + Spdifdsr = 1<<14, + Spdifv = 1<<15, + VID1 = 0x7c, + VID2 = 0x7e, + Speed = 0x1234567, +}; + +enum { + Left, + Right, + Stereo, + Absolute, +}; + +enum { + Vmaster, + Vhead, + Vaudio, + Vcd, + Vbass, + Vtreb, + Vbeep, + Vphone, + Vmic, + Vline, + Vvideo, + Vaux, + Vrecgain, + Vmicgain, +}; + +struct Volume { + int reg; + int range; + int type; + int cap; + char *name; +}; + +struct Topology { + Volume *this; + Volume *next[2]; +}; + +Volume vol[] = { +[Vmaster] {Master, 63, Stereo, 0, "master"}, +[Vaudio] {Pcmout, 31, Stereo, 0, "audio"}, +[Vhead] {Headphone, 31, Stereo, Capheadphones, "head"}, +[Vbass] {Mastertone, 15, Left, Captonectl, "bass"}, +[Vtreb] {Mastertone, 15, Right, Captonectl, "treb"}, +[Vbeep] {Pcbeep, 31, Right, 0, "beep"}, +[Vphone] {Phone, 31, Right, 0, "phone"}, +[Vmic] {Mic, 31, Right, Capmic, "mic"}, +[Vline] {Line, 31, Stereo, 0, "line"}, +[Vcd] {Cd, 31, Stereo, 0, "cd"}, +[Vvideo] {Video, 31, Stereo, 0, "video"}, +[Vaux] {Aux, 63, Stereo, 0, "aux"}, +[Vrecgain] {Recgain, 15, Stereo, 0, "recgain"}, +[Vmicgain] {Micgain, 15, Right, Capmic, "micgain"}, + {0, 0, 0, 0, 0}, +}; + +long +ac97mixtopology(Audio *adev, void *a, long n, vlong off) +{ + Mixer *m; + char *buf; + long l; + ulong caps; + m = adev->mixer; + qlock(m); + caps = m->rr(adev, Reset); + caps |= m->rr(adev, Extid) << 16; + l = 0; + buf = malloc(READSTR); + l += snprint(buf+l, READSTR-l, "not implemented. have fun.\n"); + USED(caps); + USED(l); + qunlock(m); + n = readstr(off, a, n, buf); + free(buf); + return n; +} + +long +ac97mixread(Audio *adev, void *a, long n, vlong off) +{ + Mixer *m; + char *nam, *buf; + long l; + ushort v; + ulong caps; + int i, rang, le, ri; + buf = malloc(READSTR); + m = adev->mixer; + qlock(m); + l = 0; + caps = m->rr(adev, Reset); + caps |= m->rr(adev, Extid) << 16; + for(i = 0; vol[i].name != 0; ++i){ + if(vol[i].cap && ((vol[i].cap & caps) == 0)) + continue; + v = m->rr(adev, vol[i].reg); + nam = vol[i].name; + rang = vol[i].range; + if(vol[i].type == Absolute){ + l += snprint(buf+l, READSTR-l, "%s %d", nam, v); + } else { + ri = ((rang-(v&rang)) * 100) / rang; + le = ((rang-((v>>8)&rang)) * 100) / rang; + if(vol[i].type == Stereo) + l += snprint(buf+l, READSTR-l, "%s %d %d", nam, le, ri); + if(vol[i].type == Left) + l += snprint(buf+l, READSTR-l, "%s %d", nam, le); + if(vol[i].type == Right) + l += snprint(buf+l, READSTR-l, "%s %d", nam, ri); + if(v&Mute) + l += snprint(buf+l, READSTR-l, " mute"); + } + l += snprint(buf+l, READSTR-l, "\n"); + } + qunlock(m); + n = readstr(off, a, n, buf); + free(buf); + return n; +} + +long +ac97mixwrite(Audio *adev, void *a, long n, vlong) +{ + Mixer *m; + char *tok[4]; + int ntok, i, left, right, rang, reg; + ushort v; + m = adev->mixer; + qlock(m); + ntok = tokenize(a, tok, 4); + for(i = 0; vol[i].name != 0; ++i){ + if(!strcmp(vol[i].name, tok[0])){ + rang = vol[i].range; + reg = vol[i].reg; + left = right = 0; + if(ntok > 1) + left = right = atoi(tok[1]); + if(ntok > 2) + right = atoi(tok[2]); + + if(vol[i].type == Absolute){ + m->wr(adev, reg, left); + } else { + left = rang - ((left*rang)) / 100; + right = rang - ((right*rang)) / 100; + switch(vol[i].type){ + default: + break; + case Left: + v = m->rr(adev, reg); + v = (v & 0x007f) | (left << 8); + m->wr(adev, reg, v); + break; + case Right: + v = m->rr(adev, reg); + v = (v & 0x7f00) | right; + m->wr(adev, reg, v); + break; + case Stereo: + v = (left<<8) | right; + m->wr(adev, reg, v); + break; + } + } + qunlock(m); + return n; + } + } + if(vol[i].name == nil){ + char *p; + for(p = tok[0]; *p; ++p) + if(*p < '0' || *p > '9') { + qunlock(m); + error("no such volume setting"); + } + rang = vol[0].range; + reg = vol[0].reg; + left = right = rang - ((atoi(tok[0])*rang)) / 100; + v = (left<<8) | right; + m->wr(adev, reg, v); + } + qunlock(m); + + return n; +} + +int +ac97hardrate(Audio *adev, int rate) +{ + Mixer *m; + int oldrate; + m = adev->mixer; + oldrate = m->rr(adev, Pcmfrontdacrate); + if(rate > 0) + m->wr(adev, Pcmfrontdacrate, rate); + return oldrate; +} + +void +ac97mixreset(Audio *adev, ac97wrfn wr, ac97rdfn rr) +{ + Mixer *m; + int i; + ushort t; + if(adev->mixer == nil) + adev->mixer = malloc(sizeof(Mixer)); + m = adev->mixer; + m->wr = wr; + m->rr = rr; + adev->volread = ac97mixread; + adev->volwrite = ac97mixwrite; + m->wr(adev, Reset, 0); + m->wr(adev, Powerdowncsr, 0); + + t = (Adcpower | Dacpower | Anlpower | Refpower); + for(i = 0; i < Maxbusywait; i++){ + if((m->rr(adev, Powerdowncsr) & t) == t) + break; + microdelay(1); + } + if(i == Maxbusywait) + print("#A%d: ac97 exhausted waiting powerup\n", adev->ctlrno); + + t = m->rr(adev, Extid); + print("#A%d: ac97 codec ext:%s%s%s%s%s%s%s\n", adev->ctlrno, + (t & Extvra) ? " vra" : "", + (t & Extdra) ? " dra" : "", + (t & Extspdif) ? " spdif" : "", + (t & Extvrm) ? " vrm" : "", + (t & Extcdac) ? " cdac" : "", + (t & Extsdac) ? " sdac" : "", + (t & Extldac) ? " ldac" : ""); + + if(t & Extvra){ + m->wr(adev, Extcsr, Extvra); + m->vra = 1; + } else { + print("#A%d: ac97 vra extension not supported\n", adev->ctlrno); + m->vra = 0; + } +} diff --git a/sys/src/9/pc/pcf b/sys/src/9/pc/pcf index 900b6ed1f..c578c7bb5 100644 --- a/sys/src/9/pc/pcf +++ b/sys/src/9/pc/pcf @@ -73,6 +73,8 @@ link usbohci usbehci usbehcipc + audioac97 audioac97mix + misc archmp mp apic mtrr diff --git a/sys/src/9/port/devaudio.c b/sys/src/9/port/devaudio.c index 690fc39ba..e34b8f916 100644 --- a/sys/src/9/port/devaudio.c +++ b/sys/src/9/port/devaudio.c @@ -1,873 +1,163 @@ -/* - * SB 16 driver - */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" -#include "../port/error.h" #include "io.h" -#include "audio.h" +#include "../port/error.h" +#include "../port/audio.h" -typedef struct AQueue AQueue; -typedef struct Buf Buf; +typedef struct Audioprobe Audioprobe; +struct Audioprobe { + char *name; + int (*probe)(Audio*); +}; -enum -{ - Qdir = 0, +enum { + Qdir = 0, Qaudio, + Qaudioctl, + Qaudiostatus, Qvolume, - Qstatus, - - Fmono = 1, - Fin = 2, - Fout = 4, - - Aclosed = 0, - Aread, - Awrite, - - Vaudio = 0, - Vsynth, - Vcd, - Vline, - Vmic, - Vspeaker, - Vtreb, - Vbass, - Vspeed, - Nvol, - - Speed = 44100, - Ncmd = 50, /* max volume command words */ -}; - -Dirtab -audiodir[] = -{ - ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, - "audio", {Qaudio}, 0, 0666, - "volume", {Qvolume}, 0, 0666, - "audiostat",{Qstatus}, 0, 0444, + Maxaudioprobes = 8, }; -struct Buf -{ - uchar* virt; - ulong phys; - Buf* next; -}; -struct AQueue -{ - Lock; - Buf* first; - Buf* last; -}; -static struct -{ - QLock; - Rendez vous; - int buffered; /* number of bytes en route */ - int bufinit; /* boolean if buffers allocated */ - int curcount; /* how much data in current buffer */ - int active; /* boolean dma running */ - int intr; /* boolean an interrupt has happened */ - int amode; /* Aclosed/Aread/Awrite for /audio */ - int rivol[Nvol]; /* right/left input/output volumes */ - int livol[Nvol]; - int rovol[Nvol]; - int lovol[Nvol]; - int major; /* SB16 major version number (sb 4) */ - int minor; /* SB16 minor version number */ - ulong totcount; /* how many bytes processed since open */ - vlong tottime; /* time at which totcount bytes were processed */ - - Buf buf[Nbuf]; /* buffers and queues */ - AQueue empty; - AQueue full; - Buf* current; - Buf* filling; -} audio; +static int naudioprobes; +static Audioprobe audioprobes[Maxaudioprobes]; +static Audio *audiodevs; -static struct -{ - char* name; - int flag; - int ilval; /* initial values */ - int irval; -} volumes[] = -{ -[Vaudio] "audio", Fout, 50, 50, -[Vsynth] "synth", Fin|Fout, 0, 0, -[Vcd] "cd", Fin|Fout, 0, 0, -[Vline] "line", Fin|Fout, 0, 0, -[Vmic] "mic", Fin|Fout|Fmono, 0, 0, -[Vspeaker] "speaker", Fout|Fmono, 0, 0, - -[Vtreb] "treb", Fout, 50, 50, -[Vbass] "bass", Fout, 50, 50, - -[Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed, - 0 +static Dirtab audiodir[] = { + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "audio", {Qaudio}, 0, 0666, + "audioctl", {Qaudioctl}, 0, 0666, + "audiostat", {Qaudiostatus}, 0, 0666, + "volume", {Qvolume}, 0, 0666, }; -static struct -{ - Lock; - int reset; /* io ports to the sound blaster */ - int read; - int write; - int wstatus; - int rstatus; - int mixaddr; - int mixdata; - int clri8; - int clri16; - int clri401; - int dma; - - void (*startdma)(void); - void (*intr)(void); -} blaster; - -static void swab(uchar*); - -static char Emajor[] = "soundblaster not responding/wrong version"; -static char Emode[] = "illegal open mode"; -static char Evolume[] = "illegal volume specifier"; - -static int -sbcmd(int val) -{ - int i, s; - - for(i=1<<16; i!=0; i--) { - s = inb(blaster.wstatus); - if((s & 0x80) == 0) { - outb(blaster.write, val); - return 0; - } - } -/* print("#A: sbcmd (%#.2x) timeout\n", val); /**/ - return 1; -} - -static int -sbread(void) -{ - int i, s; - - for(i=1<<16; i!=0; i--) { - s = inb(blaster.rstatus); - if((s & 0x80) != 0) { - return inb(blaster.read); - } - } -/* print("#A: sbread did not respond\n"); /**/ - return -1; -} - -static int -ess1688w(int reg, int val) -{ - if(sbcmd(reg) || sbcmd(val)) - return 1; - - return 0; -} - -static int -ess1688r(int reg) -{ - if(sbcmd(0xC0) || sbcmd(reg)) - return -1; - - return sbread(); -} - -static int -mxcmd(int addr, int val) -{ - - outb(blaster.mixaddr, addr); - outb(blaster.mixdata, val); - return 1; -} - -static int -mxread(int addr) -{ - int s; - - outb(blaster.mixaddr, addr); - s = inb(blaster.mixdata); - return s; -} - -static void -mxcmds(int s, int v) -{ - - if(v > 100) - v = 100; - if(v < 0) - v = 0; - mxcmd(s, (v*255)/100); -} - -static void -mxcmdt(int s, int v) -{ - - if(v > 100) - v = 100; - if(v <= 0) - mxcmd(s, 0); - else - mxcmd(s, 255-100+v); -} - -static void -mxcmdu(int s, int v) -{ - - if(v > 100) - v = 100; - if(v <= 0) - v = 0; - mxcmd(s, 128-50+v); -} - -static void -mxvolume(void) -{ - int *left, *right; - int source; - - if(audio.amode == Aread){ - left = audio.livol; - right = audio.rivol; - }else{ - left = audio.lovol; - right = audio.rovol; - } - - ilock(&blaster); - - mxcmd(0x30, 255); /* left master */ - mxcmd(0x31, 255); /* right master */ - mxcmd(0x3f, 0); /* left igain */ - mxcmd(0x40, 0); /* right igain */ - mxcmd(0x41, 0); /* left ogain */ - mxcmd(0x42, 0); /* right ogain */ - - mxcmds(0x32, left[Vaudio]); - mxcmds(0x33, right[Vaudio]); - - mxcmds(0x34, left[Vsynth]); - mxcmds(0x35, right[Vsynth]); - - mxcmds(0x36, left[Vcd]); - mxcmds(0x37, right[Vcd]); - - mxcmds(0x38, left[Vline]); - mxcmds(0x39, right[Vline]); - - mxcmds(0x3a, left[Vmic]); - mxcmds(0x3b, left[Vspeaker]); - - mxcmdu(0x44, left[Vtreb]); - mxcmdu(0x45, right[Vtreb]); - - mxcmdu(0x46, left[Vbass]); - mxcmdu(0x47, right[Vbass]); - - source = 0; - if(left[Vsynth]) - source |= 1<<6; - if(right[Vsynth]) - source |= 1<<5; - if(left[Vaudio]) - source |= 1<<4; - if(right[Vaudio]) - source |= 1<<3; - if(left[Vcd]) - source |= 1<<2; - if(right[Vcd]) - source |= 1<<1; - if(left[Vmic]) - source |= 1<<0; - if(audio.amode == Aread) - mxcmd(0x3c, 0); /* output switch */ - else - mxcmd(0x3c, source); - mxcmd(0x3d, source); /* input left switch */ - mxcmd(0x3e, source); /* input right switch */ - iunlock(&blaster); -} - -static Buf* -getbuf(AQueue *q) -{ - Buf *b; - - ilock(q); - b = q->first; - if(b) - q->first = b->next; - iunlock(q); - - return b; -} - -static void -putbuf(AQueue *q, Buf *b) -{ - - ilock(q); - b->next = 0; - if(q->first) - q->last->next = b; - else - q->first = b; - q->last = b; - iunlock(q); -} - -/* - * move the dma to the next buffer - */ -static void -contindma(void) -{ - Buf *b; - - if(!audio.active) - goto shutdown; - - b = audio.current; - if(b){ - audio.totcount += Bufsize; - audio.tottime = todget(nil); - } - if(audio.amode == Aread) { - if(b){ - putbuf(&audio.full, b); - audio.buffered += Bufsize; - } - b = getbuf(&audio.empty); - } else { - if(b){ - putbuf(&audio.empty, b); - audio.buffered -= Bufsize; - } - b = getbuf(&audio.full); - } - audio.current = b; - if(b == 0) - goto shutdown; - - if(dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread) >= 0) - return; - print("#A: dmasetup fail\n"); - putbuf(&audio.empty, b); - -shutdown: - dmaend(blaster.dma); - sbcmd(0xd9); /* exit at end of count */ - sbcmd(0xd5); /* pause */ - audio.curcount = 0; - audio.active = 0; -} - -/* - * cause sb to get an interrupt per buffer. - * start first dma - */ -static void -sb16startdma(void) -{ - ulong count; - int speed; - - ilock(&blaster); - dmaend(blaster.dma); - if(audio.amode == Aread) { - sbcmd(0x42); /* input sampling rate */ - speed = audio.livol[Vspeed]; - } else { - sbcmd(0x41); /* output sampling rate */ - speed = audio.lovol[Vspeed]; - } - sbcmd(speed>>8); - sbcmd(speed); - - count = (Bufsize >> 1) - 1; - if(audio.amode == Aread) - sbcmd(0xbe); /* A/D, autoinit */ - else - sbcmd(0xb6); /* D/A, autoinit */ - sbcmd(0x30); /* stereo, 16 bit */ - sbcmd(count); - sbcmd(count>>8); - - audio.active = 1; - contindma(); - iunlock(&blaster); -} - -static int -ess1688reset(void) -{ - int i; - - outb(blaster.reset, 3); - delay(1); /* >3 υs */ - outb(blaster.reset, 0); - delay(1); - - i = sbread(); - if(i != 0xAA) { - print("#A: no response %#.2x\n", i); - return 1; - } - - if(sbcmd(0xC6)){ /* extended mode */ - print("#A: barf 3\n"); - return 1; - } - - return 0; -} - -static void -ess1688startdma(void) -{ - ulong count; - int speed, x; - - ilock(&blaster); - dmaend(blaster.dma); - - if(audio.amode == Awrite) - ess1688reset(); - if(audio.amode == Aread) - sbcmd(0xD3); /* speaker off */ - - /* - * Set the speed. - */ - if(audio.amode == Aread) - speed = audio.livol[Vspeed]; - else - speed = audio.lovol[Vspeed]; - if(speed < 4000) - speed = 4000; - else if(speed > 48000) - speed = 48000; - - if(speed > 22000) - x = 0x80|(256-(795500+speed/2)/speed); - else - x = 128-(397700+speed/2)/speed; - ess1688w(0xA1, x & 0xFF); - - speed = (speed * 9) / 20; - x = 256 - 7160000 / (speed * 82); - ess1688w(0xA2, x & 0xFF); - - if(audio.amode == Aread) - ess1688w(0xB8, 0x0E); /* A/D, autoinit */ - else - ess1688w(0xB8, 0x04); /* D/A, autoinit */ - x = ess1688r(0xA8) & ~0x03; - ess1688w(0xA8, x|0x01); /* 2 channels */ - ess1688w(0xB9, 2); /* demand mode, 4 bytes per request */ - - if(audio.amode == Awrite) - ess1688w(0xB6, 0); - ess1688w(0xB7, 0x71); - ess1688w(0xB7, 0xBC); - - x = ess1688r(0xB1) & 0x0F; - ess1688w(0xB1, x|0x50); - x = ess1688r(0xB2) & 0x0F; - ess1688w(0xB2, x|0x50); - if(audio.amode == Awrite) - sbcmd(0xD1); /* speaker on */ - - count = -Bufsize; - ess1688w(0xA4, count & 0xFF); - ess1688w(0xA5, (count>>8) & 0xFF); - x = ess1688r(0xB8); - ess1688w(0xB8, x|0x05); - - audio.active = 1; - contindma(); - iunlock(&blaster); -} - -/* - * if audio is stopped, - * start it up again. - */ -static void -pokeaudio(void) -{ - if(!audio.active) - blaster.startdma(); -} - -static void -sb16intr(void) -{ - int stat, dummy; - - stat = mxread(0x82) & 7; /* get irq status */ - if(stat) { - dummy = 0; - if(stat & 2) { - ilock(&blaster); - dummy = inb(blaster.clri16); - contindma(); - iunlock(&blaster); - audio.intr = 1; - wakeup(&audio.vous); - } - if(stat & 1) { - dummy = inb(blaster.clri8); - } - if(stat & 4) { - dummy = inb(blaster.clri401); - } - USED(dummy); - } -} - -static void -ess1688intr(void) -{ - int dummy; - - if(audio.active){ - ilock(&blaster); - contindma(); - dummy = inb(blaster.clri8); - iunlock(&blaster); - audio.intr = 1; - wakeup(&audio.vous); - USED(dummy); - } - else - print("#A: unexpected ess1688 interrupt\n"); -} - void -audiosbintr(void) +addaudiocard(char *name, int (*probefn)(Audio *)) { - /* - * Carrera interrupt interface. - */ - blaster.intr(); + Audioprobe *probe; + probe = &audioprobes[naudioprobes++]; + probe->name = name; + probe->probe = probefn; } static void -pcaudiosbintr(Ureg*, void*) +audioreset(void) { - /* - * x86 interrupt interface. - */ - blaster.intr(); -} - -void -audiodmaintr(void) -{ -/* print("#A: dma interrupt\n"); /**/ -} - -static int -anybuf(void*) -{ - return audio.intr; -} - -/* - * wait for some output to get - * empty buffers back. - */ -static void -waitaudio(void) -{ - - audio.intr = 0; - pokeaudio(); - tsleep(&audio.vous, anybuf, 0, 10000); - if(audio.intr == 0) { -/* print("#A: audio timeout\n"); /**/ - audio.active = 0; - pokeaudio(); - } -} - -static void -sbbufinit(void) -{ - int i; - uchar *p; - - p = (uchar*)(((ulong)xalloc((Nbuf+1) * Bufsize) + Bufsize-1) & - ~(Bufsize-1)); - if (p == nil) - panic("sbbufinit: no memory"); - for(i=0; i<Nbuf; i++) { - dcflush(p, Bufsize); - audio.buf[i].virt = UNCACHED(uchar, p); - audio.buf[i].phys = (ulong)PADDR(p); - p += Bufsize; + int i, ctlrno = 0; + Audio **pp; + Audioprobe *probe; + pp = &audiodevs; + *pp = malloc(sizeof(Audio)); + (*pp)->ctlrno = ctlrno++; + for(i = 0; i < naudioprobes; i++){ + probe = &audioprobes[i]; + (*pp)->name = probe->name; + while(!probe->probe(*pp)){ + pp = &(*pp)->next; + *pp = malloc(sizeof(Audio)); + (*pp)->ctlrno = ctlrno++; + (*pp)->name = probe->name; + } } + free(*pp); + *pp = nil; } -static void -setempty(void) -{ - int i; - - ilock(&blaster); - audio.empty.first = 0; - audio.empty.last = 0; - audio.full.first = 0; - audio.full.last = 0; - audio.current = 0; - audio.filling = 0; - audio.buffered = 0; - for(i=0; i<Nbuf; i++) - putbuf(&audio.empty, &audio.buf[i]); - audio.totcount = 0; - audio.tottime = 0LL; - iunlock(&blaster); -} - -static void -resetlevel(void) +static Chan* +audioattach(char *spec) { + Chan *c; + Audio *p; int i; - - for(i=0; volumes[i].name; i++) { - audio.lovol[i] = volumes[i].ilval; - audio.rovol[i] = volumes[i].irval; - audio.livol[i] = volumes[i].ilval; - audio.rivol[i] = volumes[i].irval; - } + if(spec != nil && *spec != '\0') + i = strtol(spec, 0, 10); + else + i = 0; + for(p = audiodevs; p; p = p->next) + if(i-- == 0) + break; + if(p == nil) + error(Enodev); + c = devattach('A', spec); + c->qid.path = Qdir; + c->aux = p; + if(p->attach) + p->attach(p); + return c; } -static int -ess1688(ISAConf* sbconf) +static long +audioread(Chan *c, void *a, long n, vlong off) { - int i, major, minor; - - /* - * Try for ESS1688. - */ - sbcmd(0xE7); /* get version */ - major = sbread(); - minor = sbread(); - if(major != 0x68 || minor != 0x8B){ - print("#A: model %#.2x %#.2x; not ESS1688 compatible\n", major, minor); - return 1; - } - - ess1688reset(); - - switch(sbconf->irq){ - case 2: - case 9: - i = 0x50|(0<<2); + Audio *adev; + long (*fn)(Audio *, void *, long, vlong); + adev = c->aux; + switch((ulong)c->qid.path){ + default: + error("audio bugger (rd)"); + case Qaudioctl: + fn = adev->ctl; break; - case 5: - i = 0x50|(1<<2); + case Qdir: + if(adev->buffered) + audiodir[Qaudio].length = adev->buffered(adev); + return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); + case Qaudio: + fn = adev->read; break; - case 7: - i = 0x50|(2<<2); + case Qaudiostatus: + fn = adev->status; break; - case 10: - i = 0x50|(3<<2); + case Qvolume: + fn = adev->volread; break; - default: - print("#A: bad ESS1688 irq %d\n", sbconf->irq); - return 1; } - ess1688w(0xB1, i); + if(fn == nil) + error("not implemented"); + return fn(adev, a, n, off); +} - switch(sbconf->dma){ - case 0: - i = 0x50|(1<<2); +static long +audiowrite(Chan *c, void *a, long n, vlong off) +{ + Audio *adev; + long (*fn)(Audio *, void *, long, vlong); + adev = c->aux; + switch((ulong)c->qid.path){ + default: + error("audio bugger (wr)"); + case Qaudio: + fn = adev->write; break; - case 1: - i = 0xF0|(2<<2); + case Qaudioctl: + fn = adev->ctl; break; - case 3: - i = 0x50|(3<<2); + case Qvolume: + fn = adev->volwrite; break; - default: - print("#A: bad ESS1688 dma %lud\n", sbconf->dma); - return 1; } - ess1688w(0xB2, i); - - ess1688reset(); - - blaster.startdma = ess1688startdma; - blaster.intr = ess1688intr; - - return 0; + if(fn == nil) + error("not implemented"); + return fn(adev, a, n, off); } static void -audioinit(void) +audioclose(Chan *c) { - ISAConf sbconf; - int i, x; - static int irq[] = {2,5,7,10}; - - sbconf.port = 0x220; - sbconf.dma = Dma; - sbconf.irq = IrqAUDIO; - if(isaconfig("audio", 0, &sbconf) == 0) - return; - if(sbconf.type == nil || - (cistrcmp(sbconf.type, "sb16") != 0 && - cistrcmp(sbconf.type, "ess1688") != 0)) - return; - switch(sbconf.port){ - case 0x220: - case 0x240: - case 0x260: - case 0x280: - break; - default: - print("#A: bad port %#lux\n", sbconf.port); - return; - } - - if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){ - print("#A: cannot ioalloc range %lux+0x10\n", sbconf.port); - return; - } - if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){ - iofree(sbconf.port); - print("#A: cannot ioalloc range %lux+0x01\n", sbconf.port+0x100); - return; - } - - switch(sbconf.irq){ - case 2: - case 5: - case 7: - case 9: - case 10: - break; + Audio *adev; + adev = c->aux; + switch((ulong)c->qid.path){ default: - print("#A: bad irq %d\n", sbconf.irq); - iofree(sbconf.port); - iofree(sbconf.port+0x100); - return; - } - - blaster.reset = sbconf.port + 0x6; - blaster.read = sbconf.port + 0xa; - blaster.write = sbconf.port + 0xc; - blaster.wstatus = sbconf.port + 0xc; - blaster.rstatus = sbconf.port + 0xe; - blaster.mixaddr = sbconf.port + 0x4; - blaster.mixdata = sbconf.port + 0x5; - blaster.clri8 = sbconf.port + 0xe; - blaster.clri16 = sbconf.port + 0xf; - blaster.clri401 = sbconf.port + 0x100; - blaster.dma = sbconf.dma; - - blaster.startdma = sb16startdma; - blaster.intr = sb16intr; - - audio.amode = Aclosed; - resetlevel(); - - outb(blaster.reset, 1); - delay(1); /* >3 υs */ - outb(blaster.reset, 0); - delay(1); - - i = sbread(); - if(i != 0xaa) { - print("#A: no response #%.2x\n", i); - iofree(sbconf.port); - iofree(sbconf.port+0x100); return; - } - - sbcmd(0xe1); /* get version */ - audio.major = sbread(); - audio.minor = sbread(); - - if(audio.major != 4) { - if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){ - print("#A: model %#.2x %#.2x; not SB 16 compatible\n", - audio.major, audio.minor); - iofree(sbconf.port); - iofree(sbconf.port+0x100); + case Qaudio: + if(adev->close == nil) return; - } - audio.major = 4; + adev->close(adev); + return; } - - /* - * initialize the mixer - */ - mxcmd(0x00, 0); /* Reset mixer */ - mxvolume(); - - /* - * Attempt to set IRQ/DMA channels. - * On old ISA boards, these registers are writable. - * On Plug-n-Play boards, these are read-only. - * - * To accomodate both, we write to the registers, - * but then use the contents in case the write is - * disallowed. - */ - mxcmd(0x80, /* irq */ - (sbconf.irq==2)? 1: - (sbconf.irq==5)? 2: - (sbconf.irq==7)? 4: - (sbconf.irq==9)? 1: - (sbconf.irq==10)? 8: - 0); - - mxcmd(0x81, 1<<blaster.dma); /* dma */ - - x = mxread(0x81); - for(i=5; i<=7; i++) - if(x & (1<<i)){ - blaster.dma = i; - break; - } - - x = mxread(0x80); - for(i=0; i<=3; i++) - if(x & (1<<i)){ - sbconf.irq = irq[i]; - break; - } - - seteisadma(blaster.dma, audiodmaintr); - setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0); -} - -static Chan* -audioattach(char *param) -{ - return devattach('A', param); } static Walkqid* @@ -877,374 +167,26 @@ audiowalk(Chan *c, Chan *nc, char **name, int nname) } static int -audiostat(Chan *c, uchar *db, int n) +audiostat(Chan *c, uchar *dp, int n) { - audiodir[Qaudio].length = audio.buffered; - return devstat(c, db, n, audiodir, nelem(audiodir), devgen); + Audio *adev; + adev = c->aux; + if(adev->buffered && (ulong)c->qid.path == Qaudio) + audiodir[Qaudio].length = adev->buffered(adev); + return devstat(c, dp, n, audiodir, nelem(audiodir), devgen); } static Chan* audioopen(Chan *c, int omode) { - int amode; - - if(audio.major != 4) - error(Emajor); - - switch((ulong)c->qid.path) { - default: - error(Eperm); - break; - - case Qstatus: - if((omode&7) != OREAD) - error(Eperm); - case Qvolume: - case Qdir: - break; - - case Qaudio: - amode = Awrite; - if((omode&7) == OREAD) - amode = Aread; - qlock(&audio); - if(audio.amode != Aclosed){ - qunlock(&audio); - error(Einuse); - } - if(audio.bufinit == 0) { - audio.bufinit = 1; - sbbufinit(); - } - audio.amode = amode; - setempty(); - audio.curcount = 0; - qunlock(&audio); - mxvolume(); - break; - } - c = devopen(c, omode, audiodir, nelem(audiodir), devgen); - c->mode = openmode(omode); - c->flag |= COPEN; - c->offset = 0; - - return c; -} - -static void -audioclose(Chan *c) -{ - Buf *b; - - switch((ulong)c->qid.path) { - default: - error(Eperm); - break; - - case Qdir: - case Qvolume: - case Qstatus: - break; - - case Qaudio: - if(c->flag & COPEN) { - qlock(&audio); - if(audio.amode == Awrite) { - /* flush out last partial buffer */ - b = audio.filling; - if(b) { - audio.filling = 0; - memset(b->virt+audio.curcount, 0, Bufsize-audio.curcount); - audio.buffered += Bufsize-audio.curcount; - swab(b->virt); - putbuf(&audio.full, b); - } - if(!audio.active && audio.full.first) - pokeaudio(); - } - audio.amode = Aclosed; - if(waserror()){ - qunlock(&audio); - nexterror(); - } - while(audio.active) - waitaudio(); - setempty(); - poperror(); - qunlock(&audio); - } - break; - } -} - -static long -audioread(Chan *c, void *v, long n, vlong off) -{ - int liv, riv, lov, rov; - long m, n0; - char buf[300]; - Buf *b; - int j; - ulong offset = off; - char *a; - - n0 = n; - a = v; - switch((ulong)c->qid.path) { - default: - error(Eperm); - break; - - case Qdir: - return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); - - case Qaudio: - if(audio.amode != Aread) - error(Emode); - qlock(&audio); - if(waserror()){ - qunlock(&audio); - nexterror(); - } - while(n > 0) { - b = audio.filling; - if(b == 0) { - b = getbuf(&audio.full); - if(b == 0) { - waitaudio(); - continue; - } - audio.filling = b; - swab(b->virt); - audio.curcount = 0; - } - m = Bufsize-audio.curcount; - if(m > n) - m = n; - memmove(a, b->virt+audio.curcount, m); - - audio.curcount += m; - n -= m; - a += m; - audio.buffered -= m; - if(audio.curcount >= Bufsize) { - audio.filling = 0; - putbuf(&audio.empty, b); - } - } - poperror(); - qunlock(&audio); - break; - - case Qstatus: - buf[0] = 0; - snprint(buf, sizeof(buf), "bufsize %6d buffered %6d offset %10lud time %19lld\n", - Bufsize, audio.buffered, audio.totcount, audio.tottime); - return readstr(offset, a, n, buf); - - case Qvolume: - j = 0; - buf[0] = 0; - for(m=0; volumes[m].name; m++){ - liv = audio.livol[m]; - riv = audio.rivol[m]; - lov = audio.lovol[m]; - rov = audio.rovol[m]; - j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); - if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ - if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) - j += snprint(buf+j, sizeof(buf)-j, " %d", liv); - else{ - if(volumes[m].flag & Fin) - j += snprint(buf+j, sizeof(buf)-j, - " in %d", liv); - if(volumes[m].flag & Fout) - j += snprint(buf+j, sizeof(buf)-j, - " out %d", lov); - } - }else{ - if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && - liv==lov && riv==rov) - j += snprint(buf+j, sizeof(buf)-j, - " left %d right %d", - liv, riv); - else{ - if(volumes[m].flag & Fin) - j += snprint(buf+j, sizeof(buf)-j, - " in left %d right %d", - liv, riv); - if(volumes[m].flag & Fout) - j += snprint(buf+j, sizeof(buf)-j, - " out left %d right %d", - lov, rov); - } - } - j += snprint(buf+j, sizeof(buf)-j, "\n"); - } - return readstr(offset, a, n, buf); - } - return n0-n; -} - -static long -audiowrite(Chan *c, void *vp, long n, vlong) -{ - long m, n0; - int i, v, left, right, in, out; - Cmdbuf *cb; - Buf *b; - char *a; - - a = vp; - n0 = n; - switch((ulong)c->qid.path) { - default: - error(Eperm); - break; - - case Qvolume: - v = Vaudio; - left = 1; - right = 1; - in = 1; - out = 1; - cb = parsecmd(vp, n); - if(waserror()){ - free(cb); - nexterror(); - } - - for(i = 0; i < cb->nf; i++){ - /* - * a number is volume - */ - if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') { - m = strtoul(cb->f[i], 0, 10); - if(left && out) - audio.lovol[v] = m; - if(left && in) - audio.livol[v] = m; - if(right && out) - audio.rovol[v] = m; - if(right && in) - audio.rivol[v] = m; - mxvolume(); - goto cont0; - } - - for(m=0; volumes[m].name; m++) { - if(strcmp(cb->f[i], volumes[m].name) == 0) { - v = m; - in = 1; - out = 1; - left = 1; - right = 1; - goto cont0; - } - } - - if(strcmp(cb->f[i], "reset") == 0) { - resetlevel(); - mxvolume(); - goto cont0; - } - if(strcmp(cb->f[i], "in") == 0) { - in = 1; - out = 0; - goto cont0; - } - if(strcmp(cb->f[i], "out") == 0) { - in = 0; - out = 1; - goto cont0; - } - if(strcmp(cb->f[i], "left") == 0) { - left = 1; - right = 0; - goto cont0; - } - if(strcmp(cb->f[i], "right") == 0) { - left = 0; - right = 1; - goto cont0; - } - error(Evolume); - break; - cont0:; - } - free(cb); - poperror(); - break; - - case Qaudio: - if(audio.amode != Awrite) - error(Emode); - qlock(&audio); - if(waserror()){ - qunlock(&audio); - nexterror(); - } - while(n > 0) { - b = audio.filling; - if(b == 0) { - b = getbuf(&audio.empty); - if(b == 0) { - waitaudio(); - continue; - } - audio.filling = b; - audio.curcount = 0; - } - - m = Bufsize-audio.curcount; - if(m > n) - m = n; - memmove(b->virt+audio.curcount, a, m); - - audio.curcount += m; - n -= m; - a += m; - audio.buffered += m; - if(audio.curcount >= Bufsize) { - audio.filling = 0; - swab(b->virt); - putbuf(&audio.full, b); - pokeaudio(); - } - } - poperror(); - qunlock(&audio); - break; - } - return n0 - n; -} - -static void -swab(uchar *a) -{ - ulong *p, *ep, b; - - if(!SBswab){ - USED(a); - return; - } - p = (ulong*)a; - ep = p + (Bufsize>>2); - while(p < ep) { - b = *p; - b = (b>>24) | (b<<24) | - ((b&0xff0000) >> 8) | - ((b&0x00ff00) << 8); - *p++ = b; - } + return devopen(c, omode, audiodir, nelem(audiodir), devgen); } Dev audiodevtab = { 'A', "audio", - - devreset, - audioinit, + audioreset, + devinit, devshutdown, audioattach, audiowalk, |