ref: d642d726babfc8e6a04a9ea2280655b0c5ff8f98
parent: 1a81fb86eb713f3e98a71522259d843e8cc14995
author: cinap_lenrek <cinap_lenrek@localhost>
date: Mon May 16 08:16:43 EDT 2011
add ac97 driver
--- a/sys/src/9/pc/audio.h
+++ /dev/null
@@ -1,15 +1,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")
--- /dev/null
+++ b/sys/src/9/pc/audioac97.c
@@ -1,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);
+}
--- /dev/null
+++ b/sys/src/9/pc/audioac97mix.c
@@ -1,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;
+ }
+}
--- a/sys/src/9/pc/pcf
+++ b/sys/src/9/pc/pcf
@@ -73,6 +73,8 @@
usbohci
usbehci usbehcipc
+ audioac97 audioac97mix
+
misc
archmp mp apic
mtrr
--- a/sys/src/9/port/devaudio.c
+++ b/sys/src/9/port/devaudio.c
@@ -1,875 +1,165 @@
-/*
- * 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 */
+ Maxaudioprobes = 8,
};
-Dirtab
-audiodir[] =
-{
- ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
- "audio", {Qaudio}, 0, 0666,
- "volume", {Qvolume}, 0, 0666,
- "audiostat",{Qstatus}, 0, 0444,
-};
+static int naudioprobes;
+static Audioprobe audioprobes[Maxaudioprobes];
+static Audio *audiodevs;
-struct Buf
-{
- uchar* virt;
- ulong phys;
- Buf* next;
+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,
};
-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 struct
+void
+addaudiocard(char *name, int (*probefn)(Audio *))
{
- 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 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);
+ Audioprobe *probe;
+ probe = &audioprobes[naudioprobes++];
+ probe->name = name;
+ probe->probe = probefn;
}
-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)
+audioreset(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);
+ 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;
}
- if(stat & 1) {
- dummy = inb(blaster.clri8);
- }
- if(stat & 4) {
- dummy = inb(blaster.clri401);
- }
- USED(dummy);
}
+ free(*pp);
+ *pp = nil;
}
-static void
-ess1688intr(void)
+static Chan*
+audioattach(char *spec)
{
- int dummy;
-
- if(audio.active){
- ilock(&blaster);
- contindma();
- dummy = inb(blaster.clri8);
- iunlock(&blaster);
- audio.intr = 1;
- wakeup(&audio.vous);
- USED(dummy);
- }
+ Chan *c;
+ Audio *p;
+ int i;
+ if(spec != nil && *spec != '\0')
+ i = strtol(spec, 0, 10);
else
- print("#A: unexpected ess1688 interrupt\n");
+ 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;
}
-void
-audiosbintr(void)
+static long
+audioread(Chan *c, void *a, long n, vlong off)
{
- /*
- * Carrera interrupt interface.
- */
- blaster.intr();
-}
-
-static void
-pcaudiosbintr(Ureg*, 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;
- }
-}
-
-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)
-{
- 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;
- }
-}
-
-static int
-ess1688(ISAConf* sbconf)
-{
- 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;
+ Audio *adev;
+ adev = c->aux;
+ switch((ulong)c->qid.path){
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);
+ case Qaudio:
+ if(adev->close == nil)
+ return;
+ adev->close(adev);
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;
- 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);
- return;
- }
- audio.major = 4;
- }
-
- /*
- * 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*
audiowalk(Chan *c, Chan *nc, char **name, int nname)
{
@@ -877,374 +167,26 @@
}
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;
+ return devopen(c, omode, audiodir, nelem(audiodir), devgen);
}
-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;
- }
-}
-
Dev audiodevtab = {
'A',
"audio",
-
- devreset,
- audioinit,
+ audioreset,
+ devinit,
devshutdown,
audioattach,
audiowalk,