diff options
author | obrien <obrien@FreeBSD.org> | 2004-01-11 10:30:56 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 2004-01-11 10:30:56 +0000 |
commit | b10112e3d11f879afddde834c51c190d019f5a78 (patch) | |
tree | cd62df122e30b81b6d5507d68d31c09cf8547943 /sys/dev/sound | |
parent | 032a5ac8ac2af81974aa6ae4fa2e114e80b9e9e9 (diff) | |
download | FreeBSD-src-b10112e3d11f879afddde834c51c190d019f5a78.zip FreeBSD-src-b10112e3d11f879afddde834c51c190d019f5a78.tar.gz |
Add Audigy support.
I started with a year-old patch by Orlando Bassotto
<orlando.bassotto@ieo-research.it>, and ported it to 5.2-CURRENT along with
fixing the problems working with pre-Audigy cards.
Diffstat (limited to 'sys/dev/sound')
-rw-r--r-- | sys/dev/sound/pci/emu10k1.c | 614 |
1 files changed, 521 insertions, 93 deletions
diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c index c82e9e2..2c44d52 100644 --- a/sys/dev/sound/pci/emu10k1.c +++ b/sys/dev/sound/pci/emu10k1.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004 David O'Brien <obrien@FreeBSD.org> + * Copyright (c) 2003 Orlando Bassotto <orlando.bassotto@ieo-research.it> * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> * All rights reserved. * @@ -28,6 +29,7 @@ #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> #include <gnu/dev/sound/pci/emu10k1.h> +#include <gnu/dev/sound/pci/emu10k1-alsa%diked.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> @@ -40,14 +42,30 @@ SND_DECLARE_FILE("$FreeBSD$"); #define NUM_G 64 /* use all channels */ #define WAVEOUT_MAXBUFSIZE 32768 #define EMUPAGESIZE 4096 /* don't change */ -#define MAXPAGES (WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE) -#define EMU10K1_PCI_ID 0x00021102 -#define EMU10K2_PCI_ID 0x00041102 +#define EMUMAXPAGES (WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE) +#define EMU10K1_PCI_ID 0x00021102 /* 1102 => Creative Labs Vendor ID */ +#define EMU10K2_PCI_ID 0x00041102 #define EMU_DEFAULT_BUFSZ 4096 +#define EMU_MAX_CHANS 8 #define EMU_CHANS 4 + +#define MAXREQVOICES 8 +#define RESERVED 0 +#define NUM_MIDI 16 +#define NUM_FXSENDS 4 + +#define TMEMSIZE 256*1024 +#define TMEMSIZEREG 4 + +#define ENABLE 0xffffffff +#define DISABLE 0x00000000 #define ENV_ON DCYSUSV_CHANNELENABLE_MASK #define ENV_OFF 0x00 /* XXX: should this be 1? */ +#define A_IOCFG_GPOUT_A 0x40 /* Analog Output */ +#define A_IOCFG_GPOUT_D 0x04 /* Digital Output */ +#define A_IOCFG_GPOUT_AD (A_IOCFG_GPOUT_A|A_IOCFG_GPOUT_D) /* A_IOCFG_GPOUT0 */ + struct emu_memblk { SLIST_ENTRY(emu_memblk) link; void *buf; @@ -56,7 +74,7 @@ struct emu_memblk { }; struct emu_mem { - u_int8_t bmap[MAXPAGES / 8]; + u_int8_t bmap[EMUMAXPAGES / 8]; u_int32_t *ptb_pages; void *silent_page; bus_addr_t silent_page_addr; @@ -69,6 +87,8 @@ struct emu_voice { int b16:1, stereo:1, busy:1, running:1, ismaster:1; int speed; int start, end, vol; + int fxrt1; /* FX routing */ + int fxrt2; /* FX routing (only for audigy) */ u_int32_t buf; struct emu_voice *slave; struct pcm_channel *channel; @@ -97,7 +117,8 @@ struct sc_rchinfo { struct sc_info { device_t dev; u_int32_t type, rev; - u_int32_t tos_link:1, APS:1; + u_int32_t tos_link:1, APS:1, audigy:1, audigy2:1; + u_int32_t addrmask; /* wider if audigy */ bus_space_tag_t st; bus_space_handle_t sh; @@ -110,9 +131,10 @@ struct sc_info { unsigned int bufsz; int timer, timerinterval; int pnum, rnum; + int nchans; struct emu_mem mem; struct emu_voice voice[64]; - struct sc_pchinfo pch[EMU_CHANS]; + struct sc_pchinfo pch[EMU_MAX_CHANS]; struct sc_rchinfo rch[3]; }; @@ -172,6 +194,10 @@ static u_int32_t emu_pfmt[] = { static struct pcmchan_caps emu_playcaps = {4000, 48000, emu_pfmt, 0}; static int adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}; +/* audigy supports 12kHz. */ +static int audigy_adcspeed[9] = { + 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000 +}; /* -------------------------------------------------------------------- */ /* Hardware */ @@ -211,7 +237,7 @@ emu_rdptr(struct sc_info *sc, int chn, int reg) { u_int32_t ptr, val, mask, size, offset; - ptr = ((reg << 16) & PTR_ADDRESS_MASK) | (chn & PTR_CHANNELNUM_MASK); + ptr = ((reg << 16) & sc->addrmask) | (chn & PTR_CHANNELNUM_MASK); emu_wr(sc, PTR, ptr, 4); val = emu_rd(sc, DATA, 4); if (reg & 0xff000000) { @@ -229,7 +255,7 @@ emu_wrptr(struct sc_info *sc, int chn, int reg, u_int32_t data) { u_int32_t ptr, mask, size, offset; - ptr = ((reg << 16) & PTR_ADDRESS_MASK) | (chn & PTR_CHANNELNUM_MASK); + ptr = ((reg << 16) & sc->addrmask) | (chn & PTR_CHANNELNUM_MASK); emu_wr(sc, PTR, ptr, 4); if (reg & 0xff000000) { size = (reg >> 24) & 0x3f; @@ -245,7 +271,8 @@ emu_wrptr(struct sc_info *sc, int chn, int reg, u_int32_t data) static void emu_wrefx(struct sc_info *sc, unsigned int pc, unsigned int data) { - emu_wrptr(sc, 0, MICROCODEBASE + pc, data); + pc += sc->audigy ? AUDIGY_CODEBASE : MICROCODEBASE; + emu_wrptr(sc, 0, pc, data); } /* -------------------------------------------------------------------- */ @@ -288,10 +315,11 @@ emu_settimer(struct sc_info *sc) int i, tmp, rate; rate = 0; - for (i = 0; i < EMU_CHANS; i++) { + for (i = 0; i < sc->nchans; i++) { pch = &sc->pch[i]; if (pch->buffer) { - tmp = (pch->spd * sndbuf_getbps(pch->buffer)) / pch->blksz; + tmp = (pch->spd * sndbuf_getbps(pch->buffer)) + / pch->blksz; if (tmp > rate) rate = tmp; } @@ -300,7 +328,8 @@ emu_settimer(struct sc_info *sc) for (i = 0; i < 3; i++) { rch = &sc->rch[i]; if (rch->buffer) { - tmp = (rch->spd * sndbuf_getbps(rch->buffer)) / rch->blksz; + tmp = (rch->spd * sndbuf_getbps(rch->buffer)) + / rch->blksz; if (tmp > rate) rate = tmp; } @@ -351,6 +380,16 @@ emu_recval(int speed) { return val; } +static int +audigy_recval(int speed) { + int val; + + val = 0; + while (val < 8 && speed < audigy_adcspeed[val]) + val++; + return val; +} + static u_int32_t emu_rate_to_pitch(u_int32_t rate) { @@ -453,6 +492,16 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, m->vol = 0xff; m->buf = tmp_addr; m->slave = s; + if (sc->audigy) { + m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_LEFT << 8 | + FXBUS_PCM_RIGHT << 16 | FXBUS_MIDI_REVERB << 24; + m->fxrt2 = 0x3f3f3f3f; /* No effects on second route */ + } else { + m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_LEFT << 4 | + FXBUS_PCM_RIGHT << 8 | FXBUS_MIDI_REVERB << 12; + m->fxrt2 = 0; + } + if (s != NULL) { s->start = m->start; s->end = m->end; @@ -464,6 +513,8 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, s->ismaster = 0; s->vol = m->vol; s->buf = m->buf; + s->fxrt1 = m->fxrt1; + s->fxrt2 = m->fxrt2; s->slave = NULL; } return 0; @@ -512,7 +563,13 @@ emu_vwrite(struct sc_info *sc, struct emu_voice *v) val *= v->b16 ? 1 : 2; start = sa + val; - emu_wrptr(sc, v->vnum, FXRT, 0xd01c0000); + if (sc->audigy) { + emu_wrptr(sc, v->vnum, A_FXRT1, v->fxrt1); + emu_wrptr(sc, v->vnum, A_FXRT2, v->fxrt2); + emu_wrptr(sc, v->vnum, A_SENDAMOUNTS, 0); + } + else + emu_wrptr(sc, v->vnum, FXRT, v->fxrt1 << 16); emu_wrptr(sc, v->vnum, PTRX, (x << 8) | r); emu_wrptr(sc, v->vnum, DSL, ea | (y << 24)); @@ -522,7 +579,8 @@ emu_vwrite(struct sc_info *sc, struct emu_voice *v) emu_wrptr(sc, v->vnum, Z1, 0); emu_wrptr(sc, v->vnum, Z2, 0); - silent_page = ((u_int32_t)(sc->mem.silent_page_addr) << 1) | MAP_PTI_MASK; + silent_page = ((u_int32_t)(sc->mem.silent_page_addr) << 1) + | MAP_PTI_MASK; emu_wrptr(sc, v->vnum, MAPA, silent_page); emu_wrptr(sc, v->vnum, MAPB, silent_page); @@ -537,7 +595,8 @@ emu_vwrite(struct sc_info *sc, struct emu_voice *v) emu_wrptr(sc, v->vnum, FM2FRQ2, 0); emu_wrptr(sc, v->vnum, ENVVAL, 0x8000); - emu_wrptr(sc, v->vnum, ATKHLDV, ATKHLDV_HOLDTIME_MASK | ATKHLDV_ATTACKTIME_MASK); + emu_wrptr(sc, v->vnum, ATKHLDV, + ATKHLDV_HOLDTIME_MASK | ATKHLDV_ATTACKTIME_MASK); emu_wrptr(sc, v->vnum, ENVVOL, 0x8000); emu_wrptr(sc, v->vnum, PEFE_FILTERAMOUNT, 0x7f); @@ -613,6 +672,12 @@ emu_vdump(struct sc_info *sc, struct emu_voice *v) "ip", "ifatn", "pefe", "fmmod", "tremfrq", "fmfrq2", "tempenv" }; + char *regname2[] = { + "mudata1", "mustat1", "mudata2", "mustat2", + "fxwc1", "fxwc2", "spdrate", NULL, NULL, + NULL, NULL, NULL, "fxrt2", "sndamnt", "fxrt1", + NULL, NULL + }; int i, x; printf("voice number %d\n", v->vnum); @@ -625,13 +690,28 @@ emu_vdump(struct sc_info *sc, struct emu_voice *v) if (x > 2) x = 0; } + + /* Print out audigy extra registers */ + if (sc->audigy) { + for (i = 0; i <= 0xe; i++) { + if (regname2[i] == NULL) + continue; + printf("%s\t[%08x]", regname2[i], + emu_rdptr(sc, v->vnum, i + 0x70)); + printf("%s", (x == 2)? "\n" : "\t"); + x++; + if (x > 2) + x = 0; + } + } printf("\n\n"); } #endif /* channel interface */ static void * -emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_pchinfo *ch; @@ -649,7 +729,8 @@ emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->master = emu_valloc(sc); ch->slave = emu_valloc(sc); snd_mtxunlock(sc->lock); - r = (emu_vinit(sc, ch->master, ch->slave, sc->bufsz, ch->buffer)) ? NULL : ch; + r = (emu_vinit(sc, ch->master, ch->slave, sc->bufsz, ch->buffer)) + ? NULL : ch; return r; } @@ -767,7 +848,8 @@ CHANNEL_DECLARE(emupchan); /* channel interface */ static void * -emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_rchinfo *ch; @@ -783,7 +865,7 @@ emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->num = sc->rnum; switch(sc->rnum) { case 0: - ch->idxreg = ADCIDX; + ch->idxreg = sc->audigy ? A_ADCIDX : ADCIDX; ch->basereg = ADCBA; ch->sizereg = ADCBS; ch->setupreg = ADCCR; @@ -832,8 +914,12 @@ emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_rchinfo *ch = data; - if (ch->num == 0) - speed = adcspeed[emu_recval(speed)]; + if (ch->num == 0) { + if (ch->parent->audigy) + speed = audigy_adcspeed[audigy_recval(speed)]; + else + speed = adcspeed[emu_recval(speed)]; + } if (ch->num == 1) speed = 48000; if (ch->num == 2) @@ -897,10 +983,18 @@ emurchan_trigger(kobj_t obj, void *data, int go) ch->run = 1; emu_wrptr(sc, 0, ch->sizereg, sz); if (ch->num == 0) { - val = ADCCR_LCHANENABLE; - if (ch->fmt & AFMT_STEREO) - val |= ADCCR_RCHANENABLE; - val |= emu_recval(ch->spd); + if (sc->audigy) { + val = A_ADCCR_LCHANENABLE; + if (ch->fmt & AFMT_STEREO) + val |= A_ADCCR_RCHANENABLE; + val |= audigy_recval(ch->spd); + } else { + val = ADCCR_LCHANENABLE; + if (ch->fmt & AFMT_STEREO) + val |= ADCCR_RCHANENABLE; + val |= emu_recval(ch->spd); + } + emu_wrptr(sc, 0, ch->setupreg, 0); emu_wrptr(sc, 0, ch->setupreg, val); } @@ -982,7 +1076,7 @@ emu_intr(void *p) if (stat & IPR_INTERVALTIMER) { ack |= IPR_INTERVALTIMER; x = 0; - for (i = 0; i < EMU_CHANS; i++) { + for (i = 0; i < sc->nchans; i++) { if (sc->pch[i].run) { x = 1; chn_intr(sc->pch[i].channel); @@ -1015,7 +1109,10 @@ emu_intr(void *p) } if (stat & IPR_SAMPLERATETRACKER) { ack |= IPR_SAMPLERATETRACKER; - /* device_printf(sc->dev, "sample rate tracker lock status change\n"); */ +#ifdef EMUDEBUG + device_printf(sc->dev, + "sample rate tracker lock status change\n"); +#endif } if (stat & ~ack) @@ -1076,7 +1173,7 @@ emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr) /* find a free block in the bitmap */ found = 0; start = 1; - while (!found && start + blksz < MAXPAGES) { + while (!found && start + blksz < EMUMAXPAGES) { found = 1; for (idx = start; idx < start + blksz; idx++) if (mem->bmap[idx >> 3] & (1 << (idx & 7))) @@ -1098,12 +1195,18 @@ emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr) blk->buf = buf; blk->pte_start = start; blk->pte_size = blksz; - /* printf("buf %p, pte_start %d, pte_size %d\n", blk->buf, blk->pte_start, blk->pte_size); */ +#ifdef EMUDEBUG + printf("buf %p, pte_start %d, pte_size %d\n", blk->buf, + blk->pte_start, blk->pte_size); +#endif ofs = 0; for (idx = start; idx < start + blksz; idx++) { mem->bmap[idx >> 3] |= 1 << (idx & 7); tmp = (u_int32_t)(u_long)((u_int8_t *)blk->buf_addr + ofs); - /* printf("pte[%d] -> %x phys, %x virt\n", idx, tmp, ((u_int32_t)buf) + ofs); */ +#ifdef EMUDEBUG + printf("pte[%d] -> %x phys, %x virt\n", idx, tmp, + ((u_int32_t)buf) + ofs); +#endif mem->ptb_pages[idx] = (tmp << 1) | idx; ofs += EMUPAGESIZE; } @@ -1153,7 +1256,8 @@ emu_memstart(struct sc_info *sc, void *buf) } static void -emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, u_int32_t *pc) +emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, + u_int32_t *pc) { emu_wrefx(sc, (*pc) * 2, (x << 10) | y); emu_wrefx(sc, (*pc) * 2 + 1, (op << 20) | (z << 10) | w); @@ -1161,11 +1265,175 @@ emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, u_int32_t * } static void +audigy_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, + u_int32_t *pc) +{ + emu_wrefx(sc, (*pc) * 2, (x << 12) | y); + emu_wrefx(sc, (*pc) * 2 + 1, (op << 24) | (z << 12) | w); + (*pc)++; +} + +static void +audigy_initefx(struct sc_info *sc) +{ + int i; + u_int32_t pc = 0; + + /* skip 0, 0, -1, 0 - NOPs */ + for (i = 0; i < 512; i++) + audigy_addefxop(sc, 0x0f, 0x0c0, 0x0c0, 0x0cf, 0x0c0, &pc); + + for (i = 0; i < 512; i++) + emu_wrptr(sc, 0, A_FXGPREGBASE + i, 0x0); + + pc = 16; + + /* stop fx processor */ + emu_wrptr(sc, 0, A_DBG, A_DBG_SINGLE_STEP); + + /* Audigy 2 (EMU10K2) DSP Registers: + FX Bus + 0x000-0x00f : 16 registers (???) + Input + 0x040/0x041 : AC97 Codec (l/r) + 0x042/0x043 : ADC, S/PDIF (l/r) + 0x044/0x045 : Optical S/PDIF in (l/r) + 0x046/0x047 : ??? + 0x048/0x049 : Line/Mic 2 (l/r) + 0x04a/0x04b : RCA S/PDIF (l/r) + 0x04c/0x04d : Aux 2 (l/r) + Output + 0x060/0x061 : Digital Front (l/r) + 0x062/0x063 : Digital Center/LFE + 0x064/0x065 : AudigyDrive Heaphone (l/r) + 0x066/0x067 : Digital Rear (l/r) + 0x068/0x069 : Analog Front (l/r) + 0x06a/0x06b : Analog Center/LFE + 0x06c/0x06d : ??? + 0x06e/0x06f : Analog Rear (l/r) + 0x070/0x071 : AC97 Output (l/r) + 0x072/0x073 : ??? + 0x074/0x075 : ??? + 0x076/0x077 : ADC Recording Buffer (l/r) + Constants + 0x0c0 - 0x0c4 = 0 - 4 + 0x0c5 = 0x8, 0x0c6 = 0x10, 0x0c7 = 0x20 + 0x0c8 = 0x100, 0x0c9 = 0x10000, 0x0ca = 0x80000 + 0x0cb = 0x10000000, 0x0cc = 0x20000000, 0x0cd = 0x40000000 + 0x0ce = 0x80000000, 0x0cf = 0x7fffffff, 0x0d0 = 0xffffffff + 0x0d1 = 0xfffffffe, 0x0d2 = 0xc0000000, 0x0d3 = 0x41fbbcdc + 0x0d4 = 0x5a7ef9db, 0x0d5 = 0x00100000, 0x0dc = 0x00000001 (???) + Temporary Values + 0x0d6 : Accumulator (???) + 0x0d7 : Condition Register + 0x0d8 : Noise source + 0x0d9 : Noise source + Tank Memory Data Registers + 0x200 - 0x2ff + Tank Memory Address Registers + 0x300 - 0x3ff + General Purpose Registers + 0x400 - 0x5ff + */ + + /* AC97Output[l/r] = FXBus PCM[l/r] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_L), A_C_00000000, + A_C_00000000, A_FXBUS(FXBUS_PCM_LEFT), &pc); + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_R), A_C_00000000, + A_C_00000000, A_FXBUS(FXBUS_PCM_RIGHT), &pc); + + /* GPR[0/1] = RCA S/PDIF[l/r] -- Master volume */ + audigy_addefxop(sc, iACC3, A_GPR(0), A_C_00000000, + A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_L), &pc); + audigy_addefxop(sc, iACC3, A_GPR(1), A_C_00000000, + A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_R), &pc); + + /* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */ + audigy_addefxop(sc, iINTERP, A_GPR(2), A_GPR(1), + A_C_40000000, A_GPR(0), &pc); + + /* Headphones[l/r] = GPR[0/1] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_HEADPHONE_L), + A_C_00000000, A_C_00000000, A_GPR(0), &pc); + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_HEADPHONE_R), + A_C_00000000, A_C_00000000, A_GPR(1), &pc); + + /* Analog Front[l/r] = GPR[0/1] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AFRONT_L), A_C_00000000, + A_C_00000000, A_GPR(0), &pc); + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AFRONT_R), A_C_00000000, + A_C_00000000, A_GPR(1), &pc); + + /* Digital Front[l/r] = GPR[0/1] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L), A_C_00000000, + A_C_00000000, A_GPR(0), &pc); + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_FRONT_R), A_C_00000000, + A_C_00000000, A_GPR(1), &pc); + + /* Center and Subwoofer configuration */ + /* Analog Center = GPR[0] + GPR[2] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ACENTER), A_C_00000000, + A_GPR(0), A_GPR(2), &pc); + /* Analog Sub = GPR[1] + GPR[2] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ALFE), A_C_00000000, + A_GPR(1), A_GPR(2), &pc); + + /* Digital Center = GPR[0] + GPR[2] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_CENTER), A_C_00000000, + A_GPR(0), A_GPR(2), &pc); + /* Digital Sub = GPR[1] + GPR[2] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_LFE), A_C_00000000, + A_GPR(1), A_GPR(2), &pc); + +#if 0 + /* Analog Rear[l/r] = (GPR[0/1] * RearVolume[l/r]) >> 31 */ + /* RearVolume = GPR[0x10/0x11] (Will this ever be implemented?) */ + audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_AREAR_L), A_C_00000000, + A_GPR(16), A_GPR(0), &pc); + audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_AREAR_R), A_C_00000000, + A_GPR(17), A_GPR(1), &pc); + + /* Digital Rear[l/r] = (GPR[0/1] * RearVolume[l/r]) >> 31 */ + /* RearVolume = GPR[0x10/0x11] (Will this ever be implemented?) */ + audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_REAR_L), A_C_00000000, + A_GPR(16), A_GPR(0), &pc); + audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_REAR_R), A_C_00000000, + A_GPR(17), A_GPR(1), &pc); +#else + /* XXX This is just a copy to the channel, since we do not have + * a patch manager, it is useful for have another output enabled. + */ + + /* Analog Rear[l/r] = GPR[0/1] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AREAR_L), A_C_00000000, + A_C_00000000, A_GPR(0), &pc); + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AREAR_R), A_C_00000000, + A_C_00000000, A_GPR(1), &pc); + + /* Digital Rear[l/r] = GPR[0/1] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_REAR_L), A_C_00000000, + A_C_00000000, A_GPR(0), &pc); + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_REAR_R), A_C_00000000, + A_C_00000000, A_GPR(1), &pc); +#endif + + /* ADC Recording buffer[l/r] = AC97Input[l/r] */ + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ADC_CAP_L), A_C_00000000, + A_C_00000000, A_EXTIN(A_EXTIN_AC97_L), &pc); + audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ADC_CAP_R), A_C_00000000, + A_C_00000000, A_EXTIN(A_EXTIN_AC97_R), &pc); + + /* resume normal operations */ + emu_wrptr(sc, 0, A_DBG, 0); +} + +static void emu_initefx(struct sc_info *sc) { int i; u_int32_t pc = 16; + /* acc3 0,0,0,0 - NOPs */ for (i = 0; i < 512; i++) { emu_wrefx(sc, i * 2, 0x10040); emu_wrefx(sc, i * 2 + 1, 0x610040); @@ -1181,24 +1449,34 @@ emu_initefx(struct sc_info *sc) 0x010/0x011 : AC97 Codec (l/r) 0x012/0x013 : ADC, S/PDIF (l/r) 0x014/0x015 : Mic(left), Zoom (l/r) - 0x016/0x017 : APS S/PDIF?? (l/r) + 0x016/0x017 : TOS link in (l/r) + 0x018/0x019 : Line/Mic 1 (l/r) + 0x01a/0x01b : COAX S/PDIF (l/r) + 0x01c/0x01d : Line/Mic 2 (l/r) Output 0x020/0x021 : AC97 Output (l/r) 0x022/0x023 : TOS link out (l/r) - 0x024/0x025 : ??? (l/r) + 0x024/0x025 : Center/LFE 0x026/0x027 : LiveDrive Headphone (l/r) 0x028/0x029 : Rear Channel (l/r) 0x02a/0x02b : ADC Recording Buffer (l/r) + 0x02c : Mic Recording Buffer + 0x031/0x032 : Analog Center/LFE Constants 0x040 - 0x044 = 0 - 4 0x045 = 0x8, 0x046 = 0x10, 0x047 = 0x20 0x048 = 0x100, 0x049 = 0x10000, 0x04a = 0x80000 0x04b = 0x10000000, 0x04c = 0x20000000, 0x04d = 0x40000000 - 0x04e = 0x80000000, 0x04f = 0x7fffffff + 0x04e = 0x80000000, 0x04f = 0x7fffffff, 0x050 = 0xffffffff + 0x051 = 0xfffffffe, 0x052 = 0xc0000000, 0x053 = 0x41fbbcdc + 0x054 = 0x5a7ef9db, 0x055 = 0x00100000 Temporary Values 0x056 : Accumulator - 0x058 : Noise source? - 0x059 : Noise source? + 0x057 : Condition Register + 0x058 : Noise source + 0x059 : Noise source + 0x05a : IRQ Register + 0x05b : TRAM Delay Base Address Count General Purpose Registers 0x100 - 0x1ff Tank Memory Data Registers @@ -1207,40 +1485,81 @@ emu_initefx(struct sc_info *sc) 0x300 - 0x3ff */ - /* Operators: - 0 : z := w + (x * y >> 31) - 4 : z := w + x * y - 6 : z := w + x + y - */ - /* Routing - this will be configurable in later version */ /* GPR[0/1] = FX * 4 + SPDIF-in */ - emu_addefxop(sc, 4, 0x100, 0x12, 0, 0x44, &pc); - emu_addefxop(sc, 4, 0x101, 0x13, 1, 0x44, &pc); + emu_addefxop(sc, iMACINT0, GPR(0), EXTIN(EXTIN_SPDIF_CD_L), + FXBUS(FXBUS_PCM_LEFT), C_00000004, &pc); + emu_addefxop(sc, iMACINT0, GPR(1), EXTIN(EXTIN_SPDIF_CD_R), + FXBUS(FXBUS_PCM_RIGHT), C_00000004, &pc); + /* GPR[0/1] += APS-input */ - emu_addefxop(sc, 6, 0x100, 0x100, 0x40, sc->APS ? 0x16 : 0x40, &pc); - emu_addefxop(sc, 6, 0x101, 0x101, 0x40, sc->APS ? 0x17 : 0x40, &pc); + emu_addefxop(sc, iACC3, GPR(0), GPR(0), C_00000000, + sc->APS ? EXTIN(EXTIN_TOSLINK_L) : C_00000000, &pc); + emu_addefxop(sc, iACC3, GPR(1), GPR(1), C_00000000, + sc->APS ? EXTIN(EXTIN_TOSLINK_R) : C_00000000, &pc); + /* FrontOut (AC97) = GPR[0/1] */ - emu_addefxop(sc, 6, 0x20, 0x40, 0x40, 0x100, &pc); - emu_addefxop(sc, 6, 0x21, 0x40, 0x41, 0x101, &pc); + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_L), C_00000000, + C_00000000, GPR(0), &pc); + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_R), C_00000000, + C_00000001, GPR(1), &pc); + + /* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */ + emu_addefxop(sc, iINTERP, GPR(2), GPR(1), C_40000000, GPR(0), &pc); + +#if 0 /* RearOut = (GPR[0/1] * RearVolume) >> 31 */ - /* RearVolume = GRP[0x10/0x11] */ - emu_addefxop(sc, 0, 0x28, 0x40, 0x110, 0x100, &pc); - emu_addefxop(sc, 0, 0x29, 0x40, 0x111, 0x101, &pc); - /* TOS out = GPR[0/1] */ - emu_addefxop(sc, 6, 0x22, 0x40, 0x40, 0x100, &pc); - emu_addefxop(sc, 6, 0x23, 0x40, 0x40, 0x101, &pc); - /* Mute Out2 */ - emu_addefxop(sc, 6, 0x24, 0x40, 0x40, 0x40, &pc); - emu_addefxop(sc, 6, 0x25, 0x40, 0x40, 0x40, &pc); - /* Mute Out3 */ - emu_addefxop(sc, 6, 0x26, 0x40, 0x40, 0x40, &pc); - emu_addefxop(sc, 6, 0x27, 0x40, 0x40, 0x40, &pc); - /* Input0 (AC97) -> Record */ - emu_addefxop(sc, 6, 0x2a, 0x40, 0x40, 0x10, &pc); - emu_addefxop(sc, 6, 0x2b, 0x40, 0x40, 0x11, &pc); + /* RearVolume = GPR[0x10/0x11] */ + emu_addefxop(sc, iMAC0, EXTOUT(EXTOUT_REAR_L), C_00000000, + GPR(16), GPR(0), &pc); + emu_addefxop(sc, iMAC0, EXTOUT(EXTOUT_REAR_R), C_00000000, + GPR(17), GPR(1), &pc); +#else + /* XXX This is just a copy to the channel, since we do not have + * a patch manager, it is useful for have another output enabled. + */ + /* Rear[l/r] = GPR[0/1] */ + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_REAR_L), C_00000000, + C_00000000, GPR(0), &pc); + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_REAR_R), C_00000000, + C_00000000, GPR(1), &pc); +#endif + + /* TOS out[l/r] = GPR[0/1] */ + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_TOSLINK_L), C_00000000, + C_00000000, GPR(0), &pc); + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_TOSLINK_R), C_00000000, + C_00000000, GPR(1), &pc); + + /* Center and Subwoofer configuration */ + /* Analog Center = GPR[0] + GPR[2] */ + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ACENTER), C_00000000, + GPR(0), GPR(2), &pc); + /* Analog Sub = GPR[1] + GPR[2] */ + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ALFE), C_00000000, + GPR(1), GPR(2), &pc); + /* Digital Center = GPR[0] + GPR[2] */ + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_CENTER), C_00000000, + GPR(0), GPR(2), &pc); + /* Digital Sub = GPR[1] + GPR[2] */ + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_LFE), C_00000000, + GPR(1), GPR(2), &pc); + + /* Headphones[l/r] = GPR[0/1] */ + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_HEADPHONE_L), C_00000000, + C_00000000, GPR(0), &pc); + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_HEADPHONE_R), C_00000000, + C_00000000, GPR(1), &pc); + + /* ADC Recording buffer[l/r] = AC97Input[l/r] */ + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ADC_CAP_L), C_00000000, + C_00000000, EXTIN(EXTIN_AC97_L), &pc); + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ADC_CAP_R), C_00000000, + C_00000000, EXTIN(EXTIN_AC97_R), &pc); + + /* resume normal operations */ emu_wrptr(sc, 0, DBG, 0); } @@ -1250,8 +1569,15 @@ emu_init(struct sc_info *sc) { u_int32_t spcs, ch, tmp, i; + if (sc->audigy) { + /* enable additional AC97 slots */ + emu_wrptr(sc, 0, AC97SLOT, AC97SLOT_CNTR | AC97SLOT_LFE); + } + /* disable audio and lock cache */ - emu_wr(sc, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, 4); + emu_wr(sc, HCFG, + HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, + 4); /* reset recording buffers */ emu_wrptr(sc, 0, MICBS, ADCBS_BUFSIZE_NONE); @@ -1262,12 +1588,20 @@ emu_init(struct sc_info *sc) emu_wrptr(sc, 0, ADCBA, 0); /* disable channel interrupt */ - emu_wr(sc, INTE, INTE_INTERVALTIMERENB | INTE_SAMPLERATETRACKER | INTE_PCIERRORENABLE, 4); + emu_wr(sc, INTE, + INTE_INTERVALTIMERENB | INTE_SAMPLERATETRACKER | INTE_PCIERRORENABLE, + 4); emu_wrptr(sc, 0, CLIEL, 0); emu_wrptr(sc, 0, CLIEH, 0); emu_wrptr(sc, 0, SOLEL, 0); emu_wrptr(sc, 0, SOLEH, 0); + /* wonder what these do... */ + if (sc->audigy) { + emu_wrptr(sc, 0, SPBYPASS, 0xf00); + emu_wrptr(sc, 0, AC97SLOT, 0x3); + } + /* init envelope engine */ for (ch = 0; ch < NUM_G; ch++) { emu_wrptr(sc, ch, DCYSUSV, ENV_OFF); @@ -1301,6 +1635,18 @@ emu_init(struct sc_info *sc) emu_wrptr(sc, ch, ENVVOL, 0); emu_wrptr(sc, ch, ENVVAL, 0); + if (sc->audigy) { + /* audigy cards need this to initialize correctly */ + emu_wrptr(sc, ch, 0x4c, 0); + emu_wrptr(sc, ch, 0x4d, 0); + emu_wrptr(sc, ch, 0x4e, 0); + emu_wrptr(sc, ch, 0x4f, 0); + /* set default routing */ + emu_wrptr(sc, ch, A_FXRT1, 0x03020100); + emu_wrptr(sc, ch, A_FXRT2, 0x3f3f3f3f); + emu_wrptr(sc, ch, A_SENDAMOUNTS, 0); + } + sc->voice[ch].vnum = ch; sc->voice[ch].slave = NULL; sc->voice[ch].busy = 0; @@ -1337,14 +1683,35 @@ emu_init(struct sc_info *sc) emu_wrptr(sc, 0, SPCS1, spcs); emu_wrptr(sc, 0, SPCS2, spcs); - emu_initefx(sc); + if (!sc->audigy) + emu_initefx(sc); + else if (sc->audigy2) { /* Audigy 2 */ + /* from ALSA initialization code: */ + + /* Hack for Alice3 to work independent of haP16V driver */ + u_int32_t tmp; + + /* Setup SRCMulti_I2S SamplingRate */ + tmp = emu_rdptr(sc, 0, A_SPDIF_SAMPLERATE) & 0xfffff1ff; + emu_wrptr(sc, 0, A_SPDIF_SAMPLERATE, tmp | 0x400); + + /* Setup SRCSel (Enable SPDIF, I2S SRCMulti) */ + emu_wr(sc, 0x20, 0x00600000, 4); + emu_wr(sc, 0x24, 0x00000014, 4); + + /* Setup SRCMulti Input Audio Enable */ + emu_wr(sc, 0x20, 0x006e0000, 4); + emu_wr(sc, 0x24, 0xff00ff00, 4); + } SLIST_INIT(&sc->mem.blocks); - sc->mem.ptb_pages = emu_malloc(sc, MAXPAGES * sizeof(u_int32_t), &sc->mem.ptb_pages_addr); + sc->mem.ptb_pages = emu_malloc(sc, EMUMAXPAGES * sizeof(u_int32_t), + &sc->mem.ptb_pages_addr); if (sc->mem.ptb_pages == NULL) return -1; - sc->mem.silent_page = emu_malloc(sc, EMUPAGESIZE, &sc->mem.silent_page_addr); + sc->mem.silent_page = emu_malloc(sc, EMUPAGESIZE, + &sc->mem.silent_page_addr); if (sc->mem.silent_page == NULL) { emu_free(sc, sc->mem.ptb_pages); return -1; @@ -1352,7 +1719,7 @@ emu_init(struct sc_info *sc) /* Clear page with silence & setup all pointers to this page */ bzero(sc->mem.silent_page, EMUPAGESIZE); tmp = (u_int32_t)(sc->mem.silent_page_addr) << 1; - for (i = 0; i < MAXPAGES; i++) + for (i = 0; i < EMUMAXPAGES; i++) sc->mem.ptb_pages[i] = tmp | i; emu_wrptr(sc, 0, PTB, (sc->mem.ptb_pages_addr)); @@ -1367,26 +1734,70 @@ emu_init(struct sc_info *sc) /* emu_memalloc(sc, EMUPAGESIZE); */ /* * Hokay, now enable the AUD bit + * + * Audigy + * Enable Audio = 0 (enabled after fx processor initialization) + * Mute Disable Audio = 0 + * Joystick = 1 + * + * Audigy 2 + * Enable Audio = 1 + * Mute Disable Audio = 0 + * Joystick = 1 + * GP S/PDIF AC3 Enable = 1 + * CD S/PDIF AC3 Enable = 1 + * + * EMU10K1 * Enable Audio = 1 * Mute Disable Audio = 0 * Lock Tank Memory = 1 * Lock Sound Memory = 0 * Auto Mute = 1 */ - tmp = HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE; - if (sc->rev >= 6) - tmp |= HCFG_JOYENABLE; - emu_wr(sc, HCFG, tmp, 4); - - /* TOSLink detection */ - sc->tos_link = 0; - tmp = emu_rd(sc, HCFG, 4); - if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { - emu_wr(sc, HCFG, tmp | 0x800, 4); - DELAY(50); - if (tmp != (emu_rd(sc, HCFG, 4) & ~0x800)) { - sc->tos_link = 1; - emu_wr(sc, HCFG, tmp, 4); + + if (sc->audigy) { + tmp = HCFG_AUTOMUTE | HCFG_JOYENABLE; + if (sc->audigy2) /* Audigy 2 */ + tmp = HCFG_AUDIOENABLE | HCFG_AC3ENABLE_CDSPDIF | + HCFG_AC3ENABLE_GPSPDIF; + emu_wr(sc, HCFG, tmp, 4); + + audigy_initefx(sc); + + /* from ALSA initialization code: */ + + /* enable audio and disable both audio/digital outputs */ + emu_wr(sc, HCFG, emu_rd(sc, HCFG, 4) | HCFG_AUDIOENABLE, 4); + emu_wr(sc, A_IOCFG, emu_rd(sc, A_IOCFG, 4) & ~A_IOCFG_GPOUT_AD, + 4); + if (sc->audigy2) { /* Audigy 2 */ + /* Unmute Analog. + * Set GPO6 to 1 for Apollo. This has to be done after + * init Alice3 I2SOut beyond 48kHz. + * So, sequence is important. + */ + emu_wr(sc, A_IOCFG, + emu_rd(sc, A_IOCFG, 4) | A_IOCFG_GPOUT_A, 4); + } + } else { + /* EMU10K1 initialization code */ + tmp = HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK + | HCFG_AUTOMUTE; + if (sc->rev >= 6) + tmp |= HCFG_JOYENABLE; + + emu_wr(sc, HCFG, tmp, 4); + + /* TOSLink detection */ + sc->tos_link = 0; + tmp = emu_rd(sc, HCFG, 4); + if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { + emu_wr(sc, HCFG, tmp | HCFG_GPOUT1, 4); + DELAY(50); + if (tmp != (emu_rd(sc, HCFG, 4) & ~HCFG_GPOUT1)) { + sc->tos_link = 1; + emu_wr(sc, HCFG, tmp, 4); + } } } @@ -1408,8 +1819,14 @@ emu_uninit(struct sc_info *sc) emu_wrptr(sc, ch, CPF, 0); } + if (sc->audigy) { /* stop fx processor */ + emu_wrptr(sc, 0, A_DBG, A_DBG_SINGLE_STEP); + } + /* disable audio and lock cache */ - emu_wr(sc, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, 4); + emu_wr(sc, HCFG, + HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, + 4); emu_wrptr(sc, 0, PTB, 0); /* reset recording buffers */ @@ -1447,11 +1864,14 @@ emu_pci_probe(device_t dev) case EMU10K1_PCI_ID: s = "Creative EMU10K1"; break; -/* + case EMU10K2_PCI_ID: - s = "Creative EMU10K2"; + if (pci_get_revid(dev) == 0x04) + s = "Creative Audigy 2 (EMU10K2)"; + else + s = "Creative Audigy (EMU10K2)"; break; -*/ + default: return ENXIO; } @@ -1478,6 +1898,10 @@ emu_pci_attach(device_t dev) sc->dev = dev; sc->type = pci_get_devid(dev); sc->rev = pci_get_revid(dev); + sc->audigy = (sc->type == EMU10K2_PCI_ID); + sc->audigy2 = (sc->audigy && sc->rev == 0x04); + sc->nchans = sc->audigy ? 8 : 4; + sc->addrmask = sc->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); @@ -1485,7 +1909,8 @@ emu_pci_attach(device_t dev) data = pci_read_config(dev, PCIR_COMMAND, 2); i = PCIR_BAR(0); - sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &i, 0, ~0, 1, RF_ACTIVE); + sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &i, 0, ~0, 1, + RF_ACTIVE); if (sc->reg == NULL) { device_printf(dev, "unable to map register space\n"); goto bad; @@ -1517,16 +1942,19 @@ emu_pci_attach(device_t dev) if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; i = 0; - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &i, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih)) { + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &i, 0, ~0, 1, + RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || + snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", rman_get_start(sc->reg), rman_get_start(sc->irq)); + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", + rman_get_start(sc->reg), rman_get_start(sc->irq)); - if (pcm_register(dev, sc, EMU_CHANS, gotmic ? 3 : 2)) goto bad; - for (i = 0; i < EMU_CHANS; i++) + if (pcm_register(dev, sc, sc->nchans, gotmic ? 3 : 2)) goto bad; + for (i = 0; i < sc->nchans; i++) pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc); for (i = 0; i < (gotmic ? 3 : 2); i++) pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc); |