diff options
author | cg <cg@FreeBSD.org> | 2000-06-13 23:24:40 +0000 |
---|---|---|
committer | cg <cg@FreeBSD.org> | 2000-06-13 23:24:40 +0000 |
commit | a6f4815b7e46b3d433952cdbb92603ad5247c777 (patch) | |
tree | bea1e3c7e80ff913c071b3fffe5329709d576a2b /sys/dev/sound/pci/emu10k1.c | |
parent | 1c4f19e2d7a7e3e4add331342fa4970d86169d19 (diff) | |
download | FreeBSD-src-a6f4815b7e46b3d433952cdbb92603ad5247c777.zip FreeBSD-src-a6f4815b7e46b3d433952cdbb92603ad5247c777.tar.gz |
add alpha-quality recording code and handle pci error interrupts - this may
prevent the card generating an nmi on ecc systems. for now a message is
printed on every pci error and it seems every time we start playng we get one
Diffstat (limited to 'sys/dev/sound/pci/emu10k1.c')
-rw-r--r-- | sys/dev/sound/pci/emu10k1.c | 424 |
1 files changed, 353 insertions, 71 deletions
diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c index ddfceea..c814ada 100644 --- a/sys/dev/sound/pci/emu10k1.c +++ b/sys/dev/sound/pci/emu10k1.c @@ -38,6 +38,7 @@ #define EMU10K1_PCI_ID 0x00021102 #define EMU_BUFFSIZE 4096 +#define EMU_CHANS 4 #undef EMUDEBUG struct emu_memblk { @@ -55,7 +56,7 @@ struct emu_mem { struct emu_voice { int vnum; - int b16:1, stereo:1, busy:1, running:1, ismaster:1, istracker:1; + int b16:1, stereo:1, busy:1, ismaster:1, istracker:1; int speed; int start, end, vol; u_int32_t buf; @@ -66,14 +67,22 @@ struct emu_voice { struct sc_info; /* channel registers */ -struct sc_chinfo { - int spd, dir, fmt; +struct sc_pchinfo { + int spd, fmt, run; struct emu_voice *master, *slave, *tracker; snd_dbuf *buffer; pcm_channel *channel; struct sc_info *parent; }; +struct sc_rchinfo { + int spd, fmt, run, num; + u_int32_t idxreg, basereg, sizereg, setupreg, irqmask; + snd_dbuf *buffer; + pcm_channel *channel; + struct sc_info *parent; +}; + /* device private data */ struct sc_info { device_t dev; @@ -88,9 +97,11 @@ struct sc_info { int regtype, regid, irqid; void *ih; + int pnum, rnum; struct emu_mem mem; struct emu_voice voice[64]; - struct sc_chinfo pch, rch; + struct sc_pchinfo pch[EMU_CHANS]; + struct sc_rchinfo rch[2]; }; /* -------------------------------------------------------------------- */ @@ -100,14 +111,24 @@ struct sc_info { */ /* channel interface */ -static void *emuchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int emuchan_setdir(void *data, int dir); -static int emuchan_setformat(void *data, u_int32_t format); -static int emuchan_setspeed(void *data, u_int32_t speed); -static int emuchan_setblocksize(void *data, u_int32_t blocksize); -static int emuchan_trigger(void *data, int go); -static int emuchan_getptr(void *data); -static pcmchan_caps *emuchan_getcaps(void *data); +static void *emupchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int emupchan_setdir(void *data, int dir); +static int emupchan_setformat(void *data, u_int32_t format); +static int emupchan_setspeed(void *data, u_int32_t speed); +static int emupchan_setblocksize(void *data, u_int32_t blocksize); +static int emupchan_trigger(void *data, int go); +static int emupchan_getptr(void *data); +static pcmchan_caps *emupchan_getcaps(void *data); + +/* channel interface */ +static void *emurchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int emurchan_setdir(void *data, int dir); +static int emurchan_setformat(void *data, u_int32_t format); +static int emurchan_setspeed(void *data, u_int32_t speed); +static int emurchan_setblocksize(void *data, u_int32_t blocksize); +static int emurchan_trigger(void *data, int go); +static int emurchan_getptr(void *data); +static pcmchan_caps *emurchan_getcaps(void *data); /* talk to the codec - called from ac97.c */ static u_int32_t emu_rdcd(void *, int); @@ -132,10 +153,10 @@ static void emu_wr(struct sc_info *, int, u_int32_t, int); /* -------------------------------------------------------------------- */ -static pcmchan_caps emu_reccaps = { - 4000, 48000, - AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE +static pcmchan_caps emu_reccaps[3] = { + {8000, 48000, AFMT_STEREO | AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE}, + {8000, 8000, AFMT_U8, AFMT_U8}, + {48000, 48000, AFMT_STEREO | AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE}, }; static pcmchan_caps emu_playcaps = { @@ -145,16 +166,29 @@ static pcmchan_caps emu_playcaps = { }; static pcm_channel emu_chantemplate = { - emuchan_init, - emuchan_setdir, - emuchan_setformat, - emuchan_setspeed, - emuchan_setblocksize, - emuchan_trigger, - emuchan_getptr, - emuchan_getcaps, + emupchan_init, + emupchan_setdir, + emupchan_setformat, + emupchan_setspeed, + emupchan_setblocksize, + emupchan_trigger, + emupchan_getptr, + emupchan_getcaps, +}; + +static pcm_channel emur_chantemplate = { + emurchan_init, + emurchan_setdir, + emurchan_setformat, + emurchan_setspeed, + emurchan_setblocksize, + emurchan_trigger, + emurchan_getptr, + emurchan_getcaps, }; +static int adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}; + /* -------------------------------------------------------------------- */ /* Hardware */ static u_int32_t @@ -271,6 +305,16 @@ emu_enastop(struct sc_info *sc, char channel, int enable) emu_wrptr(sc, 0, reg, enable); } +static int +emu_recval(int speed) { + int val; + + val = 0; + while (val < 7 && speed < adcspeed[val]) + val++; + return val; +} + /* ac97 codec */ static u_int32_t emu_rdcd(void *devinfo, int regno) @@ -371,13 +415,16 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, struct e buf = emu_memalloc(sc, sz); if (buf == NULL) return -1; + if (c != NULL) { + c->buffer.buf = buf; + c->buffer.bufsize = sz; + } m->start = emu_memstart(sc, buf) * EMUPAGESIZE; m->end = m->start + sz; m->channel = NULL; m->speed = 0; m->b16 = 0; m->stereo = 0; - m->running = 0; m->ismaster = 1; m->istracker = 0; m->vol = 0xff; @@ -391,7 +438,6 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, struct e s->speed = 0; s->b16 = 0; s->stereo = 0; - s->running = 0; s->ismaster = 0; s->istracker = 0; s->vol = m->vol; @@ -406,7 +452,6 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, struct e t->speed = 0; t->b16 = 0; t->stereo = 0; - t->running = 0; t->ismaster = 0; t->istracker = 1; t->vol = 0; @@ -414,15 +459,11 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, struct e t->slave = NULL; t->tracker = NULL; } - if (c != NULL) { - c->buffer.buf = buf; - c->buffer.bufsize = sz; - } return 0; } static void -emu_vsetup(struct sc_chinfo *ch) +emu_vsetup(struct sc_pchinfo *ch) { struct emu_voice *v = ch->master; @@ -520,6 +561,7 @@ emu_vwrite(struct sc_info *sc, struct emu_voice *v) emu_wrptr(sc, v->vnum, Z2, 0); silent_page = ((u_int32_t)v->buf << 1) | (v->start / EMUPAGESIZE); + /* silent_page = ((u_int32_t)vtophys(sc->mem.silent_page) << 1) | MAP_PTI_MASK; */ emu_wrptr(sc, v->vnum, MAPA, silent_page); emu_wrptr(sc, v->vnum, MAPB, silent_page); @@ -579,9 +621,11 @@ emu_vtrigger(struct sc_info *sc, struct emu_voice *v, int go) static int emu_vpos(struct sc_info *sc, struct emu_voice *v) { - int s; + int s, ptr; + s = (v->b16? 1 : 0) + (v->stereo? 1 : 0); - return ((emu_rdptr(sc, v->vnum, CCCA_CURRADDR) >> s) - v->start); + ptr = (emu_rdptr(sc, v->vnum, CCCA_CURRADDR) << s) - v->start; + return ptr; } #ifdef EMUDEBUG @@ -612,12 +656,13 @@ emu_vdump(struct sc_info *sc, struct emu_voice *v) /* channel interface */ void * -emuchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +emupchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct sc_info *sc = devinfo; - struct sc_chinfo *ch; + struct sc_pchinfo *ch; - ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; + KASSERT(dir == PCMDIR_PLAY, ("emupchan_init: bad direction")); + ch = &sc->pch[sc->pnum++]; ch->buffer = b; ch->parent = sc; ch->channel = c; @@ -631,42 +676,39 @@ emuchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) } static int -emuchan_setdir(void *data, int dir) +emupchan_setdir(void *data, int dir) { - struct sc_chinfo *ch = data; - - ch->dir = dir; return 0; } static int -emuchan_setformat(void *data, u_int32_t format) +emupchan_setformat(void *data, u_int32_t format) { - struct sc_chinfo *ch = data; + struct sc_pchinfo *ch = data; ch->fmt = format; return 0; } static int -emuchan_setspeed(void *data, u_int32_t speed) +emupchan_setspeed(void *data, u_int32_t speed) { - struct sc_chinfo *ch = data; + struct sc_pchinfo *ch = data; ch->spd = speed; return ch->spd; } static int -emuchan_setblocksize(void *data, u_int32_t blocksize) +emupchan_setblocksize(void *data, u_int32_t blocksize) { return blocksize; } static int -emuchan_trigger(void *data, int go) +emupchan_trigger(void *data, int go) { - struct sc_chinfo *ch = data; + struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) @@ -684,25 +726,172 @@ emuchan_trigger(void *data, int go) emu_vdump(sc, ch->slave); #endif } - emu_vtrigger(sc, ch->master, (go == PCMTRIG_START)? 1 : 0); + ch->run = (go == PCMTRIG_START)? 1 : 0; + emu_vtrigger(sc, ch->master, ch->run); return 0; } static int -emuchan_getptr(void *data) +emupchan_getptr(void *data) { - struct sc_chinfo *ch = data; + struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; return emu_vpos(sc, ch->master); } static pcmchan_caps * -emuchan_getcaps(void *data) +emupchan_getcaps(void *data) +{ + return &emu_playcaps; +} + +/* channel interface */ +static void * +emurchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +{ + struct sc_info *sc = devinfo; + struct sc_rchinfo *ch; + + KASSERT(dir == PCMDIR_REC, ("emupchan_init: bad direction")); + ch = &sc->rch[sc->rnum]; + ch->buffer = b; + ch->buffer->bufsize = EMU_BUFFSIZE; + ch->parent = sc; + ch->channel = c; + ch->fmt = AFMT_U8; + ch->spd = 8000; + ch->num = sc->rnum; + switch(sc->rnum) { + case 0: + ch->idxreg = ADCIDX; + ch->basereg = ADCBA; + ch->sizereg = ADCBS; + ch->setupreg = ADCCR; + ch->irqmask = INTE_ADCBUFENABLE; + break; + + case 1: + ch->idxreg = MICIDX; + ch->basereg = MICBA; + ch->sizereg = MICBS; + ch->setupreg = 0; + ch->irqmask = INTE_MICBUFENABLE; + break; + + case 2: + ch->idxreg = FXIDX; + ch->basereg = FXBA; + ch->sizereg = FXBS; + ch->setupreg = FXWC; + ch->irqmask = INTE_EFXBUFENABLE; + break; + } + sc->rnum++; + if (chn_allocbuf(ch->buffer, sc->parent_dmat) == -1) + return NULL; + else { + emu_wrptr(sc, 0, ch->basereg, vtophys(ch->buffer->buf)); + emu_wrptr(sc, 0, ch->sizereg, 0); /* off */ + return ch; + } +} + +static int +emurchan_setdir(void *data, int dir) +{ + return 0; +} + +static int +emurchan_setformat(void *data, u_int32_t format) +{ + struct sc_rchinfo *ch = data; + + ch->fmt = format; + return 0; +} + +static int +emurchan_setspeed(void *data, u_int32_t speed) +{ + struct sc_rchinfo *ch = data; + + if (ch->num == 0) + speed = adcspeed[emu_recval(speed)]; + if (ch->num == 1) + speed = 8000; + if (ch->num == 2) + speed = 48000; + ch->spd = speed; + return ch->spd; +} + +static int +emurchan_setblocksize(void *data, u_int32_t blocksize) +{ + return blocksize; +} + +/* semantic note: must start at beginning of buffer */ +static int +emurchan_trigger(void *data, int go) +{ + struct sc_rchinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t val; + + switch(go) { + case PCMTRIG_START: + ch->run = 1; + emu_wrptr(sc, 0, ch->sizereg, ADCBS_BUFSIZE_4096); + if (ch->num == 0) { + val = ADCCR_LCHANENABLE; + if (ch->fmt & AFMT_STEREO) + val |= ADCCR_RCHANENABLE; + val |= emu_recval(ch->spd); + emu_wrptr(sc, 0, ch->setupreg, val); + } + val = emu_rd(sc, INTE, 4); + val |= ch->irqmask; + emu_wr(sc, INTE, val, 4); + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + ch->run = 0; + emu_wrptr(sc, 0, ch->sizereg, 0); + if (ch->setupreg) + emu_wrptr(sc, 0, ch->setupreg, 0); + val = emu_rd(sc, INTE, 4); + val &= ~ch->irqmask; + emu_wr(sc, INTE, val, 4); + break; + + case PCMTRIG_EMLDMAWR: + case PCMTRIG_EMLDMARD: + default: + break; + } + + return 0; +} + +static int +emurchan_getptr(void *data) +{ + struct sc_rchinfo *ch = data; + struct sc_info *sc = ch->parent; + + return emu_rdptr(sc, 0, ch->idxreg) & 0x0000ffff; +} + +static pcmchan_caps * +emurchan_getcaps(void *data) { - struct sc_chinfo *ch = data; + struct sc_rchinfo *ch = data; - return (ch->dir == PCMDIR_PLAY)? &emu_playcaps : &emu_reccaps; + return &emu_reccaps[ch->num]; } /* The interrupt handler */ @@ -710,36 +899,84 @@ static void emu_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; - u_int32_t stat, i; + u_int32_t stat, ack, i; - do { + while (1) { stat = emu_rd(sc, IPR, 4); + if (stat == 0) + break; + ack = 0; /* process irq */ - for (i = 0; i < 64; i++) { - if (emu_testint(sc, i)) { - if (sc->voice[i].channel) - chn_intr(sc->voice[i].channel); - else - device_printf(sc->dev, "bad irq voice %d\n", i); - emu_clrint(sc, i); + if (stat & IPR_CHANNELLOOP) { + ack |= IPR_CHANNELLOOP | (stat & IPR_CHANNELNUMBERMASK); + for (i = 0; i < 64; i++) { + if (emu_testint(sc, i)) { + if (sc->voice[i].channel) + chn_intr(sc->voice[i].channel); + else { + device_printf(sc->dev, "bad irq voice %d\n", i); + emu_enaint(sc, i, 0); + } + emu_clrint(sc, i); + } } } + if (stat & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL)) { + ack |= stat & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL); + if (sc->rch[0].channel) + chn_intr(sc->rch[0].channel); + } + if (stat & (IPR_MICBUFFULL | IPR_MICBUFHALFFULL)) { + ack |= stat & (IPR_MICBUFFULL | IPR_MICBUFHALFFULL); + if (sc->rch[1].channel) + chn_intr(sc->rch[1].channel); + } + if (stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL)) { + ack |= stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL); + if (sc->rch[2].channel) + chn_intr(sc->rch[2].channel); + } + if (stat & IPR_PCIERROR) { + ack |= IPR_PCIERROR; + device_printf(sc->dev, "pci error\n"); + } + + if (stat & ~ack) + device_printf(sc->dev, "dodgy irq: %x\n", stat & ~ack); + emu_wr(sc, IPR, stat, 4); - } while (stat); + } } /* -------------------------------------------------------------------- */ +static void +emu_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + void **phys = arg; + + *phys = error? 0 : (void *)segs->ds_addr; + + if (bootverbose) { + printf("emu: setmap (%lx, %lx), nseg=%d, error=%d\n", + (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, + nseg, error); + } +} + static void * emu_malloc(struct sc_info *sc, u_int32_t sz) { - void *buf; + void *buf, *phys = 0; bus_dmamap_t map; if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map)) return NULL; + if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, emu_setmap, &phys, 0) + || !phys) + return NULL; return buf; } @@ -950,7 +1187,7 @@ emu_init(struct sc_info *sc) emu_wrptr(sc, 0, ADCBA, 0); /* disable channel interrupt */ - emu_wr(sc, INTE, DISABLE, 4); + emu_wr(sc, INTE, INTE_SAMPLERATETRACKER | INTE_PCIERRORENABLE, 4); emu_wrptr(sc, 0, CLIEL, 0); emu_wrptr(sc, 0, CLIEH, 0); emu_wrptr(sc, 0, SOLEL, 0); @@ -991,8 +1228,10 @@ emu_init(struct sc_info *sc) sc->voice[ch].vnum = ch; sc->voice[ch].slave = NULL; + sc->voice[ch].tracker = NULL; sc->voice[ch].busy = 0; - sc->voice[ch].running = 0; + sc->voice[ch].ismaster = 0; + sc->voice[ch].istracker = 0; sc->voice[ch].b16 = 0; sc->voice[ch].stereo = 0; sc->voice[ch].speed = 0; @@ -1000,6 +1239,7 @@ emu_init(struct sc_info *sc) sc->voice[ch].end = 0; sc->voice[ch].channel = NULL; } + sc->pnum = sc->rnum = 0; /* * Init to 0x02109204 : @@ -1111,6 +1351,7 @@ emu_pci_attach(device_t dev) } bzero(sc, sizeof(*sc)); + sc->dev = dev; sc->type = pci_get_devid(dev); sc->rev = pci_get_revid(dev); @@ -1120,7 +1361,6 @@ emu_pci_attach(device_t dev) data = pci_read_config(dev, PCIR_COMMAND, 2); mapped = 0; - /* Xemu dfr: is this strictly necessary? */ for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { sc->regid = PCIR_MAPS + i*4; sc->regtype = SYS_RES_MEMORY; @@ -1176,9 +1416,11 @@ emu_pci_attach(device_t dev) (sc->regtype == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(sc->reg), rman_get_start(sc->irq)); - if (pcm_register(dev, sc, 1, 0)) goto bad; - pcm_addchan(dev, PCMDIR_PLAY, &emu_chantemplate, sc); - /* pcm_addchan(dev, PCMDIR_REC, &emu_chantemplate, sc); */ + if (pcm_register(dev, sc, EMU_CHANS, 3)) goto bad; + for (i = 0; i < EMU_CHANS; i++) + pcm_addchan(dev, PCMDIR_PLAY, &emu_chantemplate, sc); + for (i = 0; i < 3; i++) + pcm_addchan(dev, PCMDIR_REC, &emur_chantemplate, sc); pcm_setstatus(dev, status); @@ -1209,3 +1451,43 @@ static driver_t emu_driver = { static devclass_t pcm_devclass; DRIVER_MODULE(emu, pci, emu_driver, pcm_devclass, 0, 0); + +static int +emujoy_pci_probe(device_t dev) +{ + char *s = NULL; + + switch (pci_get_devid(dev)) { + case 0x70021102: + s = "Creative EMU10K1 Joystick"; + device_quiet(dev); + break; + } + + if (s) device_set_desc(dev, s); + return s? 0 : ENXIO; +} + +static int +emujoy_pci_attach(device_t dev) +{ + return 0; +} + +static device_method_t emujoy_methods[] = { + DEVMETHOD(device_probe, emujoy_pci_probe), + DEVMETHOD(device_attach, emujoy_pci_attach), + + { 0, 0 } +}; + +static driver_t emujoy_driver = { + "emujoy", + emujoy_methods, + 8, +}; + +static devclass_t emujoy_devclass; + +DRIVER_MODULE(emujoy, pci, emujoy_driver, emujoy_devclass, 0, 0); + |