static void nm_ackint(struct neo_softc *sc, u_int32_t num) { if (sc->type == NM256AV_PCI_ID) nm_wr(sc, NM_INT_REG, num << 1, 2); else if (sc->type == NM256ZX_PCI_ID) nm_wr(sc, NM_INT_REG, num, 4); }
static void nm_ackint(struct sc_info *sc, u_int32_t num) { if (sc->type == NM256AV_PCI_ID) { nm_wr(sc, NM_INT_REG, num << 1, 2); } else if (sc->type == NM256ZX_PCI_ID) { nm_wr(sc, NM_INT_REG, num, 4); } }
static int nm_init(struct sc_info *sc) { u_int32_t ofs, i; if (sc->type == NM256AV_PCI_ID) { sc->ac97_base = NM_MIXER_OFFSET; sc->ac97_status = NM_MIXER_STATUS_OFFSET; sc->ac97_busy = NM_MIXER_READY_MASK; sc->buftop = 2560 * 1024; sc->irsz = 2; sc->playint = NM_PLAYBACK_INT; sc->recint = NM_RECORD_INT; sc->misc1int = NM_MISC_INT_1; sc->misc2int = NM_MISC_INT_2; } else if (sc->type == NM256ZX_PCI_ID) { sc->ac97_base = NM_MIXER_OFFSET; sc->ac97_status = NM2_MIXER_STATUS_OFFSET; sc->ac97_busy = NM2_MIXER_READY_MASK; sc->buftop = (nm_rd(sc, 0xa0b, 2)? 6144 : 4096) * 1024; sc->irsz = 4; sc->playint = NM2_PLAYBACK_INT; sc->recint = NM2_RECORD_INT; sc->misc1int = NM2_MISC_INT_1; sc->misc2int = NM2_MISC_INT_2; } else return -1; sc->badintr = 0; ofs = sc->buftop - 0x0400; sc->buftop -= 0x1400; if (bootverbose) device_printf(sc->dev, "buftop is 0x%08x\n", sc->buftop); if ((nm_rdbuf(sc, ofs, 4) & NM_SIG_MASK) == NM_SIGNATURE) { i = nm_rdbuf(sc, ofs + 4, 4); if (i != 0 && i != 0xffffffff) { if (bootverbose) device_printf(sc->dev, "buftop is changed to 0x%08x\n", i); sc->buftop = i; } } sc->cbuf = sc->buftop - NM_MAX_COEFFICIENT; sc->rbuf = sc->cbuf - NM_BUFFSIZE; sc->pbuf = sc->rbuf - NM_BUFFSIZE; sc->acbuf = sc->pbuf - (NM_TOTAL_COEFF_COUNT * 4); nm_wr(sc, 0, 0x11, 1); nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); nm_wr(sc, 0x214, 0, 2); return 0; }
void neo_reset_codec(void *sc) { nm_wr(sc, 0x6c0, 0x01, 1); nm_wr(sc, 0x6cc, 0x87, 1); nm_wr(sc, 0x6cc, 0x80, 1); nm_wr(sc, 0x6cc, 0x00, 1); return; }
int neo_halt_output(void *addr) { struct neo_softc *sc = (struct neo_softc *)addr; mtx_enter(&audio_lock); nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); sc->pintr = 0; mtx_leave(&audio_lock); return (0); }
static int nm_setch(struct sc_chinfo *ch) { struct sc_info *sc = ch->parent; u_int32_t base; u_int8_t x; for (x = 0; x < 8; x++) if (ch->spd < (samplerates[x] + samplerates[x + 1]) / 2) break; if (x == 8) return 1; ch->spd = samplerates[x]; nm_loadcoeff(sc, ch->dir, x); x <<= 4; x &= NM_RATE_MASK; if (ch->fmt & AFMT_16BIT) x |= NM_RATE_BITS_16; if (AFMT_CHANNEL(ch->fmt) > 1) x |= NM_RATE_STEREO; base = (ch->dir == PCMDIR_PLAY)? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET; nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1); return 0; }
/* The interrupt handler */ static void nm_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; int status, x, active; active = (sc->pch.channel->buffer.dl || sc->rch.channel->buffer.dl); status = nm_rd(sc, NM_INT_REG, sc->irsz); if (status == 0 && active) { if (sc->badintr++ > 1000) { device_printf(sc->dev, "1000 bad intrs\n"); sc->badintr = 0; } return; } sc->badintr = 0; if (status & sc->playint) { status &= ~sc->playint; nm_ackint(sc, sc->playint); chn_intr(sc->pch.channel); } if (status & sc->recint) { status &= ~sc->recint; nm_ackint(sc, sc->recint); chn_intr(sc->rch.channel); } if (status & sc->misc1int) { status &= ~sc->misc1int; nm_ackint(sc, sc->misc1int); x = nm_rd(sc, 0x400, 1); nm_wr(sc, 0x400, x | 2, 1); device_printf(sc->dev, "misc int 1\n"); } if (status & sc->misc2int) { status &= ~sc->misc2int; nm_ackint(sc, sc->misc2int); x = nm_rd(sc, 0x400, 1); nm_wr(sc, 0x400, x & ~2, 1); device_printf(sc->dev, "misc int 2\n"); } if (status) { status &= ~sc->misc2int; nm_ackint(sc, sc->misc2int); device_printf(sc->dev, "unknown int\n"); } }
/* The interrupt handler */ static void nm_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; int status, x; status = nm_rd(sc, NM_INT_REG, sc->irsz); if (status == 0) return; if (status & sc->playint) { status &= ~sc->playint; sc->pch.wmark += sc->pch.blksize; sc->pch.wmark %= NM_BUFFSIZE; nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + sc->pch.wmark, 4); nm_ackint(sc, sc->playint); chn_intr(sc->pch.channel); } if (status & sc->recint) { status &= ~sc->recint; sc->rch.wmark += sc->rch.blksize; sc->rch.wmark %= NM_BUFFSIZE; nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + sc->rch.wmark, 4); nm_ackint(sc, sc->recint); chn_intr(sc->rch.channel); } if (status & sc->misc1int) { status &= ~sc->misc1int; nm_ackint(sc, sc->misc1int); x = nm_rd(sc, 0x400, 1); nm_wr(sc, 0x400, x | 2, 1); device_printf(sc->dev, "misc int 1\n"); } if (status & sc->misc2int) { status &= ~sc->misc2int; nm_ackint(sc, sc->misc2int); x = nm_rd(sc, 0x400, 1); nm_wr(sc, 0x400, x & ~2, 1); device_printf(sc->dev, "misc int 2\n"); } if (status) { nm_ackint(sc, status); device_printf(sc->dev, "unknown int\n"); } }
static int nm_pci_suspend(device_t dev) { struct sc_info *sc; sc = pcm_getdevinfo(dev); /* stop playing */ if (sc->pch.active) { nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); } /* stop recording */ if (sc->rch.active) { nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); } return 0; }
/* Todo: don't commit settings to card until we've verified all parameters */ int neo_set_params(void *addr, int setmode, int usemode, struct audio_params *play, struct audio_params *rec) { struct neo_softc *sc = addr; u_int32_t base; u_int8_t x; int mode; struct audio_params *p; for (mode = AUMODE_RECORD; mode != -1; mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { if ((setmode & mode) == 0) continue; p = mode == AUMODE_PLAY ? play : rec; if (p == NULL) continue; for (x = 0; x < 8; x++) if (p->sample_rate < (samplerates[x] + samplerates[x + 1]) / 2) break; p->sample_rate = samplerates[x]; nm_loadcoeff(sc, mode, x); x <<= 4; x &= NM_RATE_MASK; if (p->precision == 16) x |= NM_RATE_BITS_16; if (p->channels == 2) x |= NM_RATE_STEREO; base = (mode == AUMODE_PLAY) ? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET; nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1); switch (p->encoding) { case AUDIO_ENCODING_SLINEAR_LE: if (p->precision != 16) return EINVAL; break; case AUDIO_ENCODING_ULINEAR_LE: case AUDIO_ENCODING_ULINEAR_BE: if (p->precision != 8) return EINVAL; break; default: return (EINVAL); } p->bps = AUDIO_BPS(p->precision); p->msb = 1; } return (0); }
static u_int32_t nm_initcd(kobj_t obj, void *devinfo) { struct sc_info *sc = (struct sc_info *)devinfo; nm_wr(sc, 0x6c0, 0x01, 1); #if 0 /* * The following code-line may cause a hang for some chipsets, see * PR 56617. * In case of a bugreport without this line have a look at the PR and * conditionize the code-line based upon the specific version of * the chip. */ nm_wr(sc, 0x6cc, 0x87, 1); #endif nm_wr(sc, 0x6cc, 0x80, 1); nm_wr(sc, 0x6cc, 0x00, 1); return 1; }
static int nm_loadcoeff(struct sc_info *sc, int dir, int num) { int ofs, sz, i; u_int32_t addr; addr = (dir == PCMDIR_PLAY)? 0x01c : 0x21c; if (dir == PCMDIR_REC) num += 8; sz = coefficientSizes[num]; ofs = 0; while (num-- > 0) ofs+= coefficientSizes[num]; for (i = 0; i < sz; i++) nm_wrbuf(sc, sc->cbuf + i, coefficients[ofs + i], 1); nm_wr(sc, addr, sc->cbuf, 4); if (dir == PCMDIR_PLAY) sz--; nm_wr(sc, addr + 4, sc->cbuf + sz, 4); return 0; }
int neo_halt_input(void *addr) { struct neo_softc *sc = (struct neo_softc *)addr; mtx_enter(&audio_lock); nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); sc->rintr = 0; mtx_leave(&audio_lock); return (0); }
int neo_trigger_output(void *addr, void *start, void *end, int blksize, void (*intr)(void *), void *arg, struct audio_params *param) { struct neo_softc *sc = addr; int ssz; mtx_enter(&audio_lock); sc->pintr = intr; sc->parg = arg; ssz = (param->precision == 16) ? 2 : 1; if (param->channels == 2) ssz <<= 1; sc->pbufsize = ((char *)end - (char *)start); sc->pblksize = blksize; sc->pwmark = blksize; nm_wr(sc, NM_PBUFFER_START, sc->pbuf, 4); nm_wr(sc, NM_PBUFFER_END, sc->pbuf + sc->pbufsize - ssz, 4); nm_wr(sc, NM_PBUFFER_CURRP, sc->pbuf, 4); nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + sc->pwmark, 4); nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | NM_PLAYBACK_ENABLE_FLAG, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); mtx_leave(&audio_lock); return (0); }
static int nm_pci_resume(device_t dev) { struct sc_info *sc; sc = pcm_getdevinfo(dev); /* * Reinit audio device. * Don't call nm_init(). It would change buftop if X ran or * is running. This makes playing and recording buffer address * shift but these buffers of channel layer are not changed. * As a result of this inconsistency, periodic noise will be * generated while playing. */ nm_wr(sc, 0, 0x11, 1); nm_wr(sc, 0x214, 0, 2); /* Reinit mixer */ if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); return ENXIO; } /* restart playing */ if (sc->pch.active) { nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | NM_PLAYBACK_ENABLE_FLAG, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); } /* restart recording */ if (sc->rch.active) { nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN | NM_RECORD_ENABLE_FLAG, 1); } return 0; }
static void nm_wrcd(void *devinfo, int regno, u_int32_t data) { struct sc_info *sc = (struct sc_info *)devinfo; int cnt = 3; if (!nm_waitcd(sc)) { while (cnt-- > 0) { nm_wr(sc, sc->ac97_base + regno, data, 2); if (!nm_waitcd(sc)) { DELAY(1000); return; } } } device_printf(sc->dev, "ac97 codec not ready\n"); }
int neo_write_codec(void *sc_, u_int8_t a, u_int16_t d) { struct neo_softc *sc = sc_; int cnt = 3; if (!nm_waitcd(sc)) { while (cnt-- > 0) { nm_wr(sc, sc->ac97_base + a, d, 2); if (!nm_waitcd(sc)) { DELAY(1000); return (0); } } } return (ENXIO); }
static int nm_pci_probe(device_t dev) { struct sc_info *sc = NULL; char *s = NULL; u_int32_t subdev, i; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); switch (pci_get_devid(dev)) { case NM256AV_PCI_ID: i = 0; while ((i < NUM_BADCARDS) && (badcards[i] != subdev)) i++; /* Try to catch other non-ac97 cards */ if (i == NUM_BADCARDS) { if (!(sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO))) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } sc->regid = PCIR_BAR(1); 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"); free(sc, M_DEVBUF); return ENXIO; } /* * My Panasonic CF-M2EV needs resetting device * before checking mixer is present or not. * [email protected]. */ nm_wr(sc, 0, 0x11, 1); /* reset device */ if ((nm_rd(sc, NM_MIXER_PRESENCE, 2) & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { i = 0; /* non-ac97 card, but not listed */ DEB(device_printf(dev, "subdev = 0x%x - badcard?\n", subdev)); } bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); free(sc, M_DEVBUF); } if (i == NUM_BADCARDS) s = "NeoMagic 256AV"; DEB(else) DEB(device_printf(dev, "this is a non-ac97 NM256AV, not attaching\n")); break; case NM256ZX_PCI_ID: s = "NeoMagic 256ZX"; break; } if (s) device_set_desc(dev, s); return s? 0 : ENXIO; }
static int nmchan_trigger(kobj_t obj, void *data, int go) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; int ssz; if (!PCMTRIG_COMMON(go)) return 0; ssz = (ch->fmt & AFMT_16BIT)? 2 : 1; if (AFMT_CHANNEL(ch->fmt) > 1) ssz <<= 1; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { ch->active = 1; ch->wmark = ch->blksize; nm_wr(sc, NM_PBUFFER_START, sc->pbuf, 4); nm_wr(sc, NM_PBUFFER_END, sc->pbuf + NM_BUFFSIZE - ssz, 4); nm_wr(sc, NM_PBUFFER_CURRP, sc->pbuf, 4); nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + ch->wmark, 4); nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | NM_PLAYBACK_ENABLE_FLAG, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); } else { ch->active = 0; nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); } } else { if (go == PCMTRIG_START) { ch->active = 1; ch->wmark = ch->blksize; nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN | NM_RECORD_ENABLE_FLAG, 1); nm_wr(sc, NM_RBUFFER_START, sc->rbuf, 4); nm_wr(sc, NM_RBUFFER_END, sc->rbuf + NM_BUFFSIZE, 4); nm_wr(sc, NM_RBUFFER_CURRP, sc->rbuf, 4); nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + ch->wmark, 4); } else { ch->active = 0; nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); } } return 0; }
/* The interrupt handler */ int neo_intr(void *p) { struct neo_softc *sc = (struct neo_softc *)p; int status, x; int rv = 0; mtx_enter(&audio_lock); status = nm_rd(sc, NM_INT_REG, sc->irsz); if (status & sc->playint) { status &= ~sc->playint; sc->pwmark += sc->pblksize; sc->pwmark %= sc->pbufsize; nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + sc->pwmark, 4); nm_ackint(sc, sc->playint); if (sc->pintr) (*sc->pintr)(sc->parg); rv = 1; } if (status & sc->recint) { status &= ~sc->recint; sc->rwmark += sc->rblksize; sc->rwmark %= sc->rbufsize; nm_ackint(sc, sc->recint); if (sc->rintr) (*sc->rintr)(sc->rarg); rv = 1; } if (status & sc->misc1int) { status &= ~sc->misc1int; nm_ackint(sc, sc->misc1int); x = nm_rd(sc, 0x400, 1); nm_wr(sc, 0x400, x | 2, 1); printf("%s: misc int 1\n", sc->dev.dv_xname); rv = 1; } if (status & sc->misc2int) { status &= ~sc->misc2int; nm_ackint(sc, sc->misc2int); x = nm_rd(sc, 0x400, 1); nm_wr(sc, 0x400, x & ~2, 1); printf("%s: misc int 2\n", sc->dev.dv_xname); rv = 1; } if (status) { status &= ~sc->misc2int; nm_ackint(sc, sc->misc2int); printf("%s: unknown int\n", sc->dev.dv_xname); rv = 1; } mtx_leave(&audio_lock); return (rv); }
static int nmchan_trigger(void *data, int go) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; int ssz; if (go == PCMTRIG_EMLDMAWR) return 0; ssz = (ch->fmt & AFMT_16BIT)? 2 : 1; if (ch->fmt & AFMT_STEREO) ssz <<= 1; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { nm_wr(sc, NM_PBUFFER_START, sc->pbuf, 4); nm_wr(sc, NM_PBUFFER_END, sc->pbuf + NM_BUFFSIZE - ssz, 4); nm_wr(sc, NM_PBUFFER_CURRP, sc->pbuf, 4); nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + NM_BUFFSIZE / 2, 4); nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | NM_PLAYBACK_ENABLE_FLAG, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); } else { nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); } } else { if (go == PCMTRIG_START) { nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN | NM_RECORD_ENABLE_FLAG, 1); nm_wr(sc, NM_RBUFFER_START, sc->rbuf, 4); nm_wr(sc, NM_RBUFFER_END, sc->rbuf + NM_BUFFSIZE, 4); nm_wr(sc, NM_RBUFFER_CURRP, sc->rbuf, 4); nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + NM_BUFFSIZE / 2, 4); } else { nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); } } return 0; }