/*- * Copyright (c) 2000 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/ds1.c,v 1.43 2005/03/01 08:58:05 imp Exp $"); /* -------------------------------------------------------------------- */ #define DS1_CHANS 4 #define DS1_RECPRIMARY 0 #define DS1_IRQHZ ((48000 << 8) / 256) #define DS1_BUFFSIZE 4096 struct pbank { volatile u_int32_t Format; volatile u_int32_t LoopDefault; volatile u_int32_t PgBase; volatile u_int32_t PgLoop; volatile u_int32_t PgLoopEnd; volatile u_int32_t PgLoopFrac; volatile u_int32_t PgDeltaEnd; volatile u_int32_t LpfKEnd; volatile u_int32_t EgGainEnd; volatile u_int32_t LchGainEnd; volatile u_int32_t RchGainEnd; volatile u_int32_t Effect1GainEnd; volatile u_int32_t Effect2GainEnd; volatile u_int32_t Effect3GainEnd; volatile u_int32_t LpfQ; volatile u_int32_t Status; volatile u_int32_t NumOfFrames; volatile u_int32_t LoopCount; volatile u_int32_t PgStart; volatile u_int32_t PgStartFrac; volatile u_int32_t PgDelta; volatile u_int32_t LpfK; volatile u_int32_t EgGain; volatile u_int32_t LchGain; volatile u_int32_t RchGain; volatile u_int32_t Effect1Gain; volatile u_int32_t Effect2Gain; volatile u_int32_t Effect3Gain; volatile u_int32_t LpfD1; volatile u_int32_t LpfD2; }; struct rbank { volatile u_int32_t PgBase; volatile u_int32_t PgLoopEnd; volatile u_int32_t PgStart; volatile u_int32_t NumOfLoops; }; struct sc_info; /* channel registers */ struct sc_pchinfo { int run, spd, dir, fmt; struct snd_dbuf *buffer; struct pcm_channel *channel; volatile struct pbank *lslot, *rslot; int lsnum, rsnum; struct sc_info *parent; }; struct sc_rchinfo { int run, spd, dir, fmt, num; struct snd_dbuf *buffer; struct pcm_channel *channel; volatile struct rbank *slot; struct sc_info *parent; }; /* device private data */ struct sc_info { device_t dev; u_int32_t type, rev; u_int32_t cd2id, ctrlbase; bus_space_tag_t st; bus_space_handle_t sh; bus_dma_tag_t buffer_dmat, control_dmat; bus_dmamap_t map; struct resource *reg, *irq; int regid, irqid; void *ih; struct mtx *lock; void *regbase; u_int32_t *pbase, pbankbase, pbanksize; volatile struct pbank *pbank[2 * 64]; volatile struct rbank *rbank; int pslotfree, currbank, pchn, rchn; unsigned int bufsz; struct sc_pchinfo pch[DS1_CHANS]; struct sc_rchinfo rch[2]; }; struct { u_int32_t dev, subdev; char *name; u_int32_t *mcode; } ds_devs[] = { {0x00041073, 0, "Yamaha DS-1 (YMF724)", CntrlInst}, {0x000d1073, 0, "Yamaha DS-1E (YMF724F)", CntrlInst1E}, {0x00051073, 0, "Yamaha DS-1? (YMF734)", CntrlInst}, {0x00081073, 0, "Yamaha DS-1? (YMF737)", CntrlInst}, {0x00201073, 0, "Yamaha DS-1? (YMF738)", CntrlInst}, {0x00061073, 0, "Yamaha DS-1? (YMF738_TEG)", CntrlInst}, {0x000a1073, 0x00041073, "Yamaha DS-1 (YMF740)", CntrlInst}, {0x000a1073, 0x000a1073, "Yamaha DS-1 (YMF740B)", CntrlInst}, {0x000a1073, 0x53328086, "Yamaha DS-1 (YMF740I)", CntrlInst}, {0x000a1073, 0, "Yamaha DS-1 (YMF740?)", CntrlInst}, {0x000c1073, 0, "Yamaha DS-1E (YMF740C)", CntrlInst1E}, {0x00101073, 0, "Yamaha DS-1E (YMF744)", CntrlInst1E}, {0x00121073, 0, "Yamaha DS-1E (YMF754)", CntrlInst1E}, {0, 0, NULL, NULL} }; /* -------------------------------------------------------------------- */ /* * prototypes */ /* stuff */ static int ds_init(struct sc_info *); static void ds_intr(void *); /* talk to the card */ static u_int32_t ds_rd(struct sc_info *, int, int); static void ds_wr(struct sc_info *, int, u_int32_t, int); /* -------------------------------------------------------------------- */ static u_int32_t ds_recfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, 0 }; static struct pcmchan_caps ds_reccaps = {4000, 48000, ds_recfmt, 0}; static u_int32_t ds_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, /* AFMT_S16_LE, */ AFMT_STEREO | AFMT_S16_LE, 0 }; static struct pcmchan_caps ds_playcaps = {4000, 96000, ds_playfmt, 0}; /* -------------------------------------------------------------------- */ /* Hardware */ static u_int32_t ds_rd(struct sc_info *sc, int regno, int size) { switch (size) { case 1: return bus_space_read_1(sc->st, sc->sh, regno); case 2: return bus_space_read_2(sc->st, sc->sh, regno); case 4: return bus_space_read_4(sc->st, sc->sh, regno); default: return 0xffffffff; } } static void ds_wr(struct sc_info *sc, int regno, u_int32_t data, int size) { switch (size) { case 1: bus_space_write_1(sc->st, sc->sh, regno, data); break; case 2: bus_space_write_2(sc->st, sc->sh, regno, data); break; case 4: bus_space_write_4(sc->st, sc->sh, regno, data); break; } } static void wrl(struct sc_info *sc, u_int32_t *ptr, u_int32_t val) { *(volatile u_int32_t *)ptr = val; bus_space_barrier(sc->st, sc->sh, 0, 0, BUS_SPACE_BARRIER_WRITE); } /* -------------------------------------------------------------------- */ /* ac97 codec */ static int ds_cdbusy(struct sc_info *sc, int sec) { int i, reg; reg = sec? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; i = YDSXG_AC97TIMEOUT; while (i > 0) { if (!(ds_rd(sc, reg, 2) & 0x8000)) return 0; i--; } return ETIMEDOUT; } static u_int32_t ds_initcd(kobj_t obj, void *devinfo) { struct sc_info *sc = (struct sc_info *)devinfo; u_int32_t x; x = pci_read_config(sc->dev, PCIR_DSXGCTRL, 1); if (x & 0x03) { pci_write_config(sc->dev, PCIR_DSXGCTRL, x & ~0x03, 1); pci_write_config(sc->dev, PCIR_DSXGCTRL, x | 0x03, 1); pci_write_config(sc->dev, PCIR_DSXGCTRL, x & ~0x03, 1); /* * The YMF740 on some Intel motherboards requires a pretty * hefty delay after this reset for some reason... Otherwise: * "pcm0: ac97 codec init failed" * Maybe this is needed for all YMF740's? * 400ms and 500ms here seem to work, 300ms does not. * * do it for all chips -cg */ DELAY(500000); } return ds_cdbusy(sc, 0)? 0 : 1; } static int ds_rdcd(kobj_t obj, void *devinfo, int regno) { struct sc_info *sc = (struct sc_info *)devinfo; int sec, cid, i; u_int32_t cmd, reg; sec = regno & 0x100; regno &= 0xff; cid = sec? (sc->cd2id << 8) : 0; reg = sec? YDSXGR_SECSTATUSDATA : YDSXGR_PRISTATUSDATA; if (sec && cid == 0) return 0xffffffff; cmd = YDSXG_AC97READCMD | cid | regno; ds_wr(sc, YDSXGR_AC97CMDADR, cmd, 2); if (ds_cdbusy(sc, sec)) return 0xffffffff; if (sc->type == 11 && sc->rev < 2) for (i = 0; i < 600; i++) ds_rd(sc, reg, 2); return ds_rd(sc, reg, 2); } static int ds_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) { struct sc_info *sc = (struct sc_info *)devinfo; int sec, cid; u_int32_t cmd; sec = regno & 0x100; regno &= 0xff; cid = sec? (sc->cd2id << 8) : 0; if (sec && cid == 0) return ENXIO; cmd = YDSXG_AC97WRITECMD | cid | regno; cmd <<= 16; cmd |= data; ds_wr(sc, YDSXGR_AC97CMDDATA, cmd, 4); return ds_cdbusy(sc, sec); } static kobj_method_t ds_ac97_methods[] = { KOBJMETHOD(ac97_init, ds_initcd), KOBJMETHOD(ac97_read, ds_rdcd), KOBJMETHOD(ac97_write, ds_wrcd), { 0, 0 } }; AC97_DECLARE(ds_ac97); /* -------------------------------------------------------------------- */ static void ds_enadsp(struct sc_info *sc, int on) { u_int32_t v, i; v = on? 1 : 0; if (on) { ds_wr(sc, YDSXGR_CONFIG, 0x00000001, 4); } else { if (ds_rd(sc, YDSXGR_CONFIG, 4)) ds_wr(sc, YDSXGR_CONFIG, 0x00000000, 4); i = YDSXG_WORKBITTIMEOUT; while (i > 0) { if (!(ds_rd(sc, YDSXGR_CONFIG, 4) & 0x00000002)) break; i--; } } } static volatile struct pbank * ds_allocpslot(struct sc_info *sc) { int slot; if (sc->pslotfree > 63) return NULL; slot = sc->pslotfree++; return sc->pbank[slot * 2]; } static int ds_initpbank(volatile struct pbank *pb, int ch, int b16, int stereo, u_int32_t rate, bus_addr_t base, u_int32_t len) { u_int32_t lv[] = {1, 1, 0, 0, 0}; u_int32_t rv[] = {1, 0, 1, 0, 0}; u_int32_t e1[] = {0, 0, 0, 0, 0}; u_int32_t e2[] = {1, 0, 0, 1, 0}; u_int32_t e3[] = {1, 0, 0, 0, 1}; int ss, i; u_int32_t delta; struct { int rate, fK, fQ; } speedinfo[] = { { 100, 0x00570000, 0x35280000}, { 2000, 0x06aa0000, 0x34a70000}, { 8000, 0x18b20000, 0x32020000}, {11025, 0x20930000, 0x31770000}, {16000, 0x2b9a0000, 0x31390000}, {22050, 0x35a10000, 0x31c90000}, {32000, 0x3eaa0000, 0x33d00000}, /* {44100, 0x04646000, 0x370a0000}, */ {48000, 0x40000000, 0x40000000}, }; ss = b16? 1 : 0; ss += stereo? 1 : 0; delta = (65536 * rate) / 48000; i = 0; while (i < 7 && speedinfo[i].rate < rate) i++; pb->Format = stereo? 0x00010000 : 0; pb->Format |= b16? 0 : 0x80000000; pb->Format |= (stereo && (ch == 2 || ch == 4))? 0x00000001 : 0; pb->LoopDefault = 0; pb->PgBase = base? base : 0; pb->PgLoop = 0; pb->PgLoopEnd = len >> ss; pb->PgLoopFrac = 0; pb->Status = 0; pb->NumOfFrames = 0; pb->LoopCount = 0; pb->PgStart = 0; pb->PgStartFrac = 0; pb->PgDelta = pb->PgDeltaEnd = delta << 12; pb->LpfQ = speedinfo[i].fQ; pb->LpfK = pb->LpfKEnd = speedinfo[i].fK; pb->LpfD1 = pb->LpfD2 = 0; pb->EgGain = pb->EgGainEnd = 0x40000000; pb->LchGain = pb->LchGainEnd = lv[ch] * 0x40000000; pb->RchGain = pb->RchGainEnd = rv[ch] * 0x40000000; pb->Effect1Gain = pb->Effect1GainEnd = e1[ch] * 0x40000000; pb->Effect2Gain = pb->Effect2GainEnd = e2[ch] * 0x40000000; pb->Effect3Gain = pb->Effect3GainEnd = e3[ch] * 0x40000000; return 0; } static void ds_enapslot(struct sc_info *sc, int slot, int go) { wrl(sc, &sc->pbase[slot + 1], go? (sc->pbankbase + 2 * slot * sc->pbanksize) : 0); /* printf("pbase[%d] = 0x%x\n", slot + 1, go? (sc->pbankbase + 2 * slot * sc->pbanksize) : 0); */ } static void ds_setuppch(struct sc_pchinfo *ch) { int stereo, b16, c, sz; bus_addr_t addr; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; c = stereo? 1 : 0; addr = sndbuf_getbufaddr(ch->buffer); sz = sndbuf_getsize(ch->buffer); ds_initpbank(ch->lslot, c, stereo, b16, ch->spd, addr, sz); ds_initpbank(ch->lslot + 1, c, stereo, b16, ch->spd, addr, sz); ds_initpbank(ch->rslot, 2, stereo, b16, ch->spd, addr, sz); ds_initpbank(ch->rslot + 1, 2, stereo, b16, ch->spd, addr, sz); } static void ds_setuprch(struct sc_rchinfo *ch) { struct sc_info *sc = ch->parent; int stereo, b16, i, sz, pri; u_int32_t x, y; bus_addr_t addr; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; addr = sndbuf_getbufaddr(ch->buffer); sz = sndbuf_getsize(ch->buffer); pri = (ch->num == DS1_RECPRIMARY)? 1 : 0; for (i = 0; i < 2; i++) { ch->slot[i].PgBase = addr; ch->slot[i].PgLoopEnd = sz; ch->slot[i].PgStart = 0; ch->slot[i].NumOfLoops = 0; } x = (b16? 0x00 : 0x01) | (stereo? 0x02 : 0x00); y = (48000 * 4096) / ch->spd; y--; /* printf("pri = %d, x = %d, y = %d\n", pri, x, y); */ ds_wr(sc, pri? YDSXGR_ADCFORMAT : YDSXGR_RECFORMAT, x, 4); ds_wr(sc, pri? YDSXGR_ADCSLOTSR : YDSXGR_RECSLOTSR, y, 4); } /* -------------------------------------------------------------------- */ /* play channel interface */ static void * ds1pchan_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; KASSERT(dir == PCMDIR_PLAY, ("ds1pchan_init: bad direction")); ch = &sc->pch[sc->pchn++]; ch->buffer = b; ch->parent = sc; ch->channel = c; ch->dir = dir; ch->fmt = AFMT_U8; ch->spd = 8000; ch->run = 0; if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, sc->bufsz) != 0) return NULL; else { ch->lsnum = sc->pslotfree; ch->lslot = ds_allocpslot(sc); ch->rsnum = sc->pslotfree; ch->rslot = ds_allocpslot(sc); ds_setuppch(ch); return ch; } } static int ds1pchan_setformat(kobj_t obj, void *data, u_int32_t format) { struct sc_pchinfo *ch = data; ch->fmt = format; return 0; } static int ds1pchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_pchinfo *ch = data; ch->spd = speed; return speed; } static int ds1pchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; int drate; /* irq rate is fixed at 187.5hz */ drate = ch->spd * sndbuf_getbps(ch->buffer); blocksize = roundup2((drate << 8) / DS1_IRQHZ, 4); sndbuf_resize(ch->buffer, sc->bufsz / blocksize, blocksize); return blocksize; } /* semantic note: must start at beginning of buffer */ static int ds1pchan_trigger(kobj_t obj, void *data, int go) { struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; int stereo; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; if (go == PCMTRIG_START) { ch->run = 1; ds_setuppch(ch); ds_enapslot(sc, ch->lsnum, 1); ds_enapslot(sc, ch->rsnum, stereo); snd_mtxlock(sc->lock); ds_wr(sc, YDSXGR_MODE, 0x00000003, 4); snd_mtxunlock(sc->lock); } else { ch->run = 0; /* ds_setuppch(ch); */ ds_enapslot(sc, ch->lsnum, 0); ds_enapslot(sc, ch->rsnum, 0); } return 0; } static int ds1pchan_getptr(kobj_t obj, void *data) { struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; volatile struct pbank *bank; int ss; u_int32_t ptr; ss = (ch->fmt & AFMT_STEREO)? 1 : 0; ss += (ch->fmt & AFMT_16BIT)? 1 : 0; bank = ch->lslot + sc->currbank; /* printf("getptr: %d\n", bank->PgStart << ss); */ ptr = bank->PgStart; ptr <<= ss; return ptr; } static struct pcmchan_caps * ds1pchan_getcaps(kobj_t obj, void *data) { return &ds_playcaps; } static kobj_method_t ds1pchan_methods[] = { KOBJMETHOD(channel_init, ds1pchan_init), KOBJMETHOD(channel_setformat, ds1pchan_setformat), KOBJMETHOD(channel_setspeed, ds1pchan_setspeed), KOBJMETHOD(channel_setblocksize, ds1pchan_setblocksize), KOBJMETHOD(channel_trigger, ds1pchan_trigger), KOBJMETHOD(channel_getptr, ds1pchan_getptr), KOBJMETHOD(channel_getcaps, ds1pchan_getcaps), { 0, 0 } }; CHANNEL_DECLARE(ds1pchan); /* -------------------------------------------------------------------- */ /* record channel interface */ static void * ds1rchan_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; KASSERT(dir == PCMDIR_REC, ("ds1rchan_init: bad direction")); ch = &sc->rch[sc->rchn]; ch->num = sc->rchn++; ch->buffer = b; ch->parent = sc; ch->channel = c; ch->dir = dir; ch->fmt = AFMT_U8; ch->spd = 8000; if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, sc->bufsz) != 0) return NULL; else { ch->slot = (ch->num == DS1_RECPRIMARY)? sc->rbank + 2: sc->rbank; ds_setuprch(ch); return ch; } } static int ds1rchan_setformat(kobj_t obj, void *data, u_int32_t format) { struct sc_rchinfo *ch = data; ch->fmt = format; return 0; } static int ds1rchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_rchinfo *ch = data; ch->spd = speed; return speed; } static int ds1rchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_rchinfo *ch = data; struct sc_info *sc = ch->parent; int drate; /* irq rate is fixed at 187.5hz */ drate = ch->spd * sndbuf_getbps(ch->buffer); blocksize = roundup2((drate << 8) / DS1_IRQHZ, 4); sndbuf_resize(ch->buffer, sc->bufsz / blocksize, blocksize); return blocksize; } /* semantic note: must start at beginning of buffer */ static int ds1rchan_trigger(kobj_t obj, void *data, int go) { struct sc_rchinfo *ch = data; struct sc_info *sc = ch->parent; u_int32_t x; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; if (go == PCMTRIG_START) { ch->run = 1; ds_setuprch(ch); snd_mtxlock(sc->lock); x = ds_rd(sc, YDSXGR_MAPOFREC, 4); x |= (ch->num == DS1_RECPRIMARY)? 0x02 : 0x01; ds_wr(sc, YDSXGR_MAPOFREC, x, 4); ds_wr(sc, YDSXGR_MODE, 0x00000003, 4); snd_mtxunlock(sc->lock); } else { ch->run = 0; snd_mtxlock(sc->lock); x = ds_rd(sc, YDSXGR_MAPOFREC, 4); x &= ~((ch->num == DS1_RECPRIMARY)? 0x02 : 0x01); ds_wr(sc, YDSXGR_MAPOFREC, x, 4); snd_mtxunlock(sc->lock); } return 0; } static int ds1rchan_getptr(kobj_t obj, void *data) { struct sc_rchinfo *ch = data; struct sc_info *sc = ch->parent; return ch->slot[sc->currbank].PgStart; } static struct pcmchan_caps * ds1rchan_getcaps(kobj_t obj, void *data) { return &ds_reccaps; } static kobj_method_t ds1rchan_methods[] = { KOBJMETHOD(channel_init, ds1rchan_init), KOBJMETHOD(channel_setformat, ds1rchan_setformat), KOBJMETHOD(channel_setspeed, ds1rchan_setspeed), KOBJMETHOD(channel_setblocksize, ds1rchan_setblocksize), KOBJMETHOD(channel_trigger, ds1rchan_trigger), KOBJMETHOD(channel_getptr, ds1rchan_getptr), KOBJMETHOD(channel_getcaps, ds1rchan_getcaps), { 0, 0 } }; CHANNEL_DECLARE(ds1rchan); /* -------------------------------------------------------------------- */ /* The interrupt handler */ static void ds_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; u_int32_t i, x; snd_mtxlock(sc->lock); i = ds_rd(sc, YDSXGR_STATUS, 4); if (i & 0x00008000) device_printf(sc->dev, "timeout irq\n"); if (i & 0x80008000) { ds_wr(sc, YDSXGR_STATUS, i & 0x80008000, 4); sc->currbank = ds_rd(sc, YDSXGR_CTRLSELECT, 4) & 0x00000001; x = 0; for (i = 0; i < DS1_CHANS; i++) { if (sc->pch[i].run) { x = 1; snd_mtxunlock(sc->lock); chn_intr(sc->pch[i].channel); snd_mtxlock(sc->lock); } } for (i = 0; i < 2; i++) { if (sc->rch[i].run) { x = 1; snd_mtxunlock(sc->lock); chn_intr(sc->rch[i].channel); snd_mtxlock(sc->lock); } } i = ds_rd(sc, YDSXGR_MODE, 4); if (x) ds_wr(sc, YDSXGR_MODE, i | 0x00000002, 4); } snd_mtxunlock(sc->lock); } /* -------------------------------------------------------------------- */ /* * Probe and attach the card */ static void ds_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct sc_info *sc = arg; sc->ctrlbase = error? 0 : (u_int32_t)segs->ds_addr; if (bootverbose) { printf("ds1: setmap (%lx, %lx), nseg=%d, error=%d\n", (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, nseg, error); } } static int ds_init(struct sc_info *sc) { int i; u_int32_t *ci, r, pcs, rcs, ecs, ws, memsz, cb; u_int8_t *t; void *buf; ci = ds_devs[sc->type].mcode; ds_wr(sc, YDSXGR_NATIVEDACOUTVOL, 0x00000000, 4); ds_enadsp(sc, 0); ds_wr(sc, YDSXGR_MODE, 0x00010000, 4); ds_wr(sc, YDSXGR_MODE, 0x00000000, 4); ds_wr(sc, YDSXGR_MAPOFREC, 0x00000000, 4); ds_wr(sc, YDSXGR_MAPOFEFFECT, 0x00000000, 4); ds_wr(sc, YDSXGR_PLAYCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_RECCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_EFFCTRLBASE, 0x00000000, 4); r = ds_rd(sc, YDSXGR_GLOBALCTRL, 2); ds_wr(sc, YDSXGR_GLOBALCTRL, r & ~0x0007, 2); for (i = 0; i < YDSXG_DSPLENGTH; i += 4) ds_wr(sc, YDSXGR_DSPINSTRAM + i, DspInst[i >> 2], 4); for (i = 0; i < YDSXG_CTRLLENGTH; i += 4) ds_wr(sc, YDSXGR_CTRLINSTRAM + i, ci[i >> 2], 4); ds_enadsp(sc, 1); pcs = 0; for (i = 100; i > 0; i--) { pcs = ds_rd(sc, YDSXGR_PLAYCTRLSIZE, 4) << 2; if (pcs == sizeof(struct pbank)) break; DELAY(1000); } if (pcs != sizeof(struct pbank)) { device_printf(sc->dev, "preposterous playctrlsize (%d)\n", pcs); return -1; } rcs = ds_rd(sc, YDSXGR_RECCTRLSIZE, 4) << 2; ecs = ds_rd(sc, YDSXGR_EFFCTRLSIZE, 4) << 2; ws = ds_rd(sc, YDSXGR_WORKSIZE, 4) << 2; memsz = 64 * 2 * pcs + 2 * 2 * rcs + 5 * 2 * ecs + ws; memsz += (64 + 1) * 4; if (sc->regbase == NULL) { if (bus_dma_tag_create(NULL, 2, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, memsz, 1, memsz, 0, busdma_lock_mutex, &Giant, &sc->control_dmat)) return -1; if (bus_dmamem_alloc(sc->control_dmat, &buf, BUS_DMA_NOWAIT, &sc->map)) return -1; if (bus_dmamap_load(sc->control_dmat, sc->map, buf, memsz, ds_setmap, sc, 0) || !sc->ctrlbase) { device_printf(sc->dev, "pcs=%d, rcs=%d, ecs=%d, ws=%d, memsz=%d\n", pcs, rcs, ecs, ws, memsz); return -1; } sc->regbase = buf; } else buf = sc->regbase; cb = 0; t = buf; ds_wr(sc, YDSXGR_WORKBASE, sc->ctrlbase + cb, 4); cb += ws; sc->pbase = (u_int32_t *)(t + cb); /* printf("pbase = %p -> 0x%x\n", sc->pbase, sc->ctrlbase + cb); */ ds_wr(sc, YDSXGR_PLAYCTRLBASE, sc->ctrlbase + cb, 4); cb += (64 + 1) * 4; sc->rbank = (struct rbank *)(t + cb); ds_wr(sc, YDSXGR_RECCTRLBASE, sc->ctrlbase + cb, 4); cb += 2 * 2 * rcs; ds_wr(sc, YDSXGR_EFFCTRLBASE, sc->ctrlbase + cb, 4); cb += 5 * 2 * ecs; sc->pbankbase = sc->ctrlbase + cb; sc->pbanksize = pcs; for (i = 0; i < 64; i++) { wrl(sc, &sc->pbase[i + 1], 0); sc->pbank[i * 2] = (struct pbank *)(t + cb); /* printf("pbank[%d] = %p -> 0x%x; ", i * 2, (struct pbank *)(t + cb), sc->ctrlbase + cb - vtophys(t + cb)); */ cb += pcs; sc->pbank[i * 2 + 1] = (struct pbank *)(t + cb); /* printf("pbank[%d] = %p -> 0x%x\n", i * 2 + 1, (struct pbank *)(t + cb), sc->ctrlbase + cb - vtophys(t + cb)); */ cb += pcs; } wrl(sc, &sc->pbase[0], DS1_CHANS * 2); sc->pchn = sc->rchn = 0; ds_wr(sc, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff, 4); ds_wr(sc, YDSXGR_NATIVEADCINVOL, 0x3fff3fff, 4); ds_wr(sc, YDSXGR_NATIVEDACINVOL, 0x3fff3fff, 4); return 0; } static int ds_uninit(struct sc_info *sc) { ds_wr(sc, YDSXGR_NATIVEDACOUTVOL, 0x00000000, 4); ds_wr(sc, YDSXGR_NATIVEADCINVOL, 0, 4); ds_wr(sc, YDSXGR_NATIVEDACINVOL, 0, 4); ds_enadsp(sc, 0); ds_wr(sc, YDSXGR_MODE, 0x00010000, 4); ds_wr(sc, YDSXGR_MAPOFREC, 0x00000000, 4); ds_wr(sc, YDSXGR_MAPOFEFFECT, 0x00000000, 4); ds_wr(sc, YDSXGR_PLAYCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_RECCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_EFFCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_GLOBALCTRL, 0, 2); bus_dmamap_unload(sc->control_dmat, sc->map); bus_dmamem_free(sc->control_dmat, sc->regbase, sc->map); return 0; } static int ds_finddev(u_int32_t dev, u_int32_t subdev) { int i; for (i = 0; ds_devs[i].dev; i++) { if (ds_devs[i].dev == dev && (ds_devs[i].subdev == subdev || ds_devs[i].subdev == 0)) return i; } return -1; } static int ds_pci_probe(device_t dev) { int i; u_int32_t subdev; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); i = ds_finddev(pci_get_devid(dev), subdev); if (i >= 0) { device_set_desc(dev, ds_devs[i].name); return BUS_PROBE_DEFAULT; } else return ENXIO; } static int ds_pci_attach(device_t dev) { u_int32_t data; u_int32_t subdev, i; struct sc_info *sc; struct ac97_info *codec = NULL; char status[SND_STATUSLEN]; if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); sc->dev = dev; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); sc->type = ds_finddev(pci_get_devid(dev), subdev); sc->rev = pci_get_revid(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); sc->regid = PCIR_BAR(0); sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->regid, RF_ACTIVE); if (!sc->reg) { device_printf(dev, "unable to map register space\n"); goto bad; } sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); sc->bufsz = pcm_getbuffersize(dev, 4096, DS1_BUFFSIZE, 65536); if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, /*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant, &sc->buffer_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } sc->regbase = NULL; if (ds_init(sc) == -1) { device_printf(dev, "unable to initialize the card\n"); goto bad; } codec = AC97_CREATE(dev, sc, ds_ac97); if (codec == NULL) goto bad; /* * Turn on inverted external amplifier sense flags for few * 'special' boards. */ switch (subdev) { case 0x81171033: /* NEC - ? */ ac97_setflags(codec, ac97_getflags(codec) | AC97_F_EAPD_INV); break; default: break; } mixer_init(dev, ac97_getmixerclass(), codec); sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE | RF_SHAREABLE); if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ds_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_ds1)); if (pcm_register(dev, sc, DS1_CHANS, 2)) goto bad; for (i = 0; i < DS1_CHANS; i++) pcm_addchan(dev, PCMDIR_PLAY, &ds1pchan_class, sc); for (i = 0; i < 2; i++) pcm_addchan(dev, PCMDIR_REC, &ds1rchan_class, sc); pcm_setstatus(dev, status); return 0; bad: if (codec) ac97_destroy(codec); if (sc->reg) bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); if (sc->buffer_dmat) bus_dma_tag_destroy(sc->buffer_dmat); if (sc->control_dmat) bus_dma_tag_destroy(sc->control_dmat); if (sc->lock) snd_mtxfree(sc->lock); free(sc, M_DEVBUF); return ENXIO; } static int ds_pci_resume(device_t dev) { struct sc_info *sc; sc = pcm_getdevinfo(dev); if (ds_init(sc) == -1) { device_printf(dev, "unable to reinitialize the card\n"); return ENXIO; } if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); return ENXIO; } return 0; } static int ds_pci_detach(device_t dev) { int r; struct sc_info *sc; r = pcm_unregister(dev); if (r) return r; sc = pcm_getdevinfo(dev); ds_uninit(sc); bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); bus_dma_tag_destroy(sc->buffer_dmat); bus_dma_tag_destroy(sc->control_dmat); snd_mtxfree(sc->lock); free(sc, M_DEVBUF); return 0; } static device_method_t ds1_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ds_pci_probe), DEVMETHOD(device_attach, ds_pci_attach), DEVMETHOD(device_detach, ds_pci_detach), DEVMETHOD(device_resume, ds_pci_resume), { 0, 0 } }; static driver_t ds1_driver = { "pcm", ds1_methods, PCM_SOFTC_SIZE, }; DRIVER_MODULE(snd_ds1, pci, ds1_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_ds1, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_ds1, 1);