1d5fa8408SCameron Grant /* 2d5fa8408SCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3d5fa8408SCameron Grant * All rights reserved. 4d5fa8408SCameron Grant * 5d5fa8408SCameron Grant * Derived from the public domain Linux driver 6d5fa8408SCameron Grant * 7d5fa8408SCameron Grant * Redistribution and use in source and binary forms, with or without 8d5fa8408SCameron Grant * modification, are permitted provided that the following conditions 9d5fa8408SCameron Grant * are met: 10d5fa8408SCameron Grant * 1. Redistributions of source code must retain the above copyright 11d5fa8408SCameron Grant * notice, this list of conditions and the following disclaimer. 12d5fa8408SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 13d5fa8408SCameron Grant * notice, this list of conditions and the following disclaimer in the 14d5fa8408SCameron Grant * documentation and/or other materials provided with the distribution. 15d5fa8408SCameron Grant * 16d5fa8408SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17d5fa8408SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18d5fa8408SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19d5fa8408SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20d5fa8408SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21d5fa8408SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22d5fa8408SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23d5fa8408SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 24d5fa8408SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25d5fa8408SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF 26d5fa8408SCameron Grant * SUCH DAMAGE. 27d5fa8408SCameron Grant * 28d5fa8408SCameron Grant * $FreeBSD$ 29d5fa8408SCameron Grant */ 30d5fa8408SCameron Grant 31d5fa8408SCameron Grant #include "pci.h" 32d5fa8408SCameron Grant #include "pcm.h" 33d5fa8408SCameron Grant 34d5fa8408SCameron Grant #include <dev/sound/pcm/sound.h> 35d5fa8408SCameron Grant #include <dev/sound/pcm/ac97.h> 36d5fa8408SCameron Grant #include <dev/sound/pci/neomagic.h> 37d5fa8408SCameron Grant #include <dev/sound/pci/neomagic-coeff.h> 38d5fa8408SCameron Grant 39d5fa8408SCameron Grant #include <pci/pcireg.h> 40d5fa8408SCameron Grant #include <pci/pcivar.h> 41d5fa8408SCameron Grant 42d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 43d5fa8408SCameron Grant 44d5fa8408SCameron Grant #define NM_BUFFSIZE 16384 45d5fa8408SCameron Grant 46d5fa8408SCameron Grant #define NM256AV_PCI_ID 0x800510c8 47d5fa8408SCameron Grant #define NM256ZX_PCI_ID 0x800610c8 48d5fa8408SCameron Grant 49d5fa8408SCameron Grant struct sc_info; 50d5fa8408SCameron Grant 51d5fa8408SCameron Grant /* channel registers */ 52d5fa8408SCameron Grant struct sc_chinfo { 53d5fa8408SCameron Grant int spd, dir, fmt; 54d5fa8408SCameron Grant snd_dbuf *buffer; 55d5fa8408SCameron Grant pcm_channel *channel; 56d5fa8408SCameron Grant struct sc_info *parent; 57d5fa8408SCameron Grant }; 58d5fa8408SCameron Grant 59d5fa8408SCameron Grant /* device private data */ 60d5fa8408SCameron Grant struct sc_info { 61d5fa8408SCameron Grant device_t dev; 62d5fa8408SCameron Grant u_int32_t type; 63d5fa8408SCameron Grant 64d5fa8408SCameron Grant struct resource *reg, *irq, *buf; 65d5fa8408SCameron Grant int regid, irqid, bufid; 66d5fa8408SCameron Grant void *ih; 67d5fa8408SCameron Grant 68d5fa8408SCameron Grant u_int32_t ac97_base, ac97_status, ac97_busy; 69d5fa8408SCameron Grant u_int32_t buftop, pbuf, rbuf, cbuf, acbuf; 70d5fa8408SCameron Grant u_int32_t playint, recint, misc1int, misc2int; 71d5fa8408SCameron Grant u_int32_t irsz, badintr; 72d5fa8408SCameron Grant 73d5fa8408SCameron Grant struct sc_chinfo pch, rch; 74d5fa8408SCameron Grant }; 75d5fa8408SCameron Grant 76d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 77d5fa8408SCameron Grant 78d5fa8408SCameron Grant /* 79d5fa8408SCameron Grant * prototypes 80d5fa8408SCameron Grant */ 81d5fa8408SCameron Grant 82d5fa8408SCameron Grant /* channel interface */ 83d5fa8408SCameron Grant static void *nmchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); 84d5fa8408SCameron Grant static int nmchan_setdir(void *data, int dir); 85d5fa8408SCameron Grant static int nmchan_setformat(void *data, u_int32_t format); 86d5fa8408SCameron Grant static int nmchan_setspeed(void *data, u_int32_t speed); 87d5fa8408SCameron Grant static int nmchan_setblocksize(void *data, u_int32_t blocksize); 88d5fa8408SCameron Grant static int nmchan_trigger(void *data, int go); 89d5fa8408SCameron Grant static int nmchan_getptr(void *data); 90d5fa8408SCameron Grant static pcmchan_caps *nmchan_getcaps(void *data); 91d5fa8408SCameron Grant 92d5fa8408SCameron Grant static int nm_waitcd(struct sc_info *sc); 93d5fa8408SCameron Grant /* talk to the codec - called from ac97.c */ 94d5fa8408SCameron Grant static u_int32_t nm_rdcd(void *, int); 95d5fa8408SCameron Grant static void nm_wrcd(void *, int, u_int32_t); 96d5fa8408SCameron Grant 97d5fa8408SCameron Grant /* stuff */ 98d5fa8408SCameron Grant static int nm_loadcoeff(struct sc_info *sc, int dir, int num); 99d5fa8408SCameron Grant static int nm_setch(struct sc_chinfo *ch); 100d5fa8408SCameron Grant static int nm_init(struct sc_info *); 101d5fa8408SCameron Grant static void nm_intr(void *); 102d5fa8408SCameron Grant 103d5fa8408SCameron Grant /* talk to the card */ 104d5fa8408SCameron Grant static u_int32_t nm_rd(struct sc_info *, int, int); 105d5fa8408SCameron Grant static void nm_wr(struct sc_info *, int, u_int32_t, int); 106d5fa8408SCameron Grant static u_int32_t nm_rdbuf(struct sc_info *, int, int); 107d5fa8408SCameron Grant static void nm_wrbuf(struct sc_info *, int, u_int32_t, int); 108d5fa8408SCameron Grant 109d5fa8408SCameron Grant /* The actual rates supported by the card. */ 110d5fa8408SCameron Grant static int samplerates[9] = { 111d5fa8408SCameron Grant 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 112d5fa8408SCameron Grant }; 113d5fa8408SCameron Grant 114d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 115d5fa8408SCameron Grant 116d5fa8408SCameron Grant static pcmchan_caps nm_caps = { 117d5fa8408SCameron Grant 4000, 48000, 118d5fa8408SCameron Grant AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, 119d5fa8408SCameron Grant AFMT_STEREO | AFMT_S16_LE 120d5fa8408SCameron Grant }; 121d5fa8408SCameron Grant 122d5fa8408SCameron Grant static pcm_channel nm_chantemplate = { 123d5fa8408SCameron Grant nmchan_init, 124d5fa8408SCameron Grant nmchan_setdir, 125d5fa8408SCameron Grant nmchan_setformat, 126d5fa8408SCameron Grant nmchan_setspeed, 127d5fa8408SCameron Grant nmchan_setblocksize, 128d5fa8408SCameron Grant nmchan_trigger, 129d5fa8408SCameron Grant nmchan_getptr, 130d5fa8408SCameron Grant nmchan_getcaps, 131d5fa8408SCameron Grant }; 132d5fa8408SCameron Grant 133d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 134d5fa8408SCameron Grant 135d5fa8408SCameron Grant /* Hardware */ 136d5fa8408SCameron Grant static u_int32_t 137d5fa8408SCameron Grant nm_rd(struct sc_info *sc, int regno, int size) 138d5fa8408SCameron Grant { 139d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->reg); 140d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->reg); 141d5fa8408SCameron Grant 142d5fa8408SCameron Grant switch (size) { 143d5fa8408SCameron Grant case 1: 144d5fa8408SCameron Grant return bus_space_read_1(st, sh, regno); 145d5fa8408SCameron Grant case 2: 146d5fa8408SCameron Grant return bus_space_read_2(st, sh, regno); 147d5fa8408SCameron Grant case 4: 148d5fa8408SCameron Grant return bus_space_read_4(st, sh, regno); 149d5fa8408SCameron Grant default: 150d5fa8408SCameron Grant return 0xffffffff; 151d5fa8408SCameron Grant } 152d5fa8408SCameron Grant } 153d5fa8408SCameron Grant 154d5fa8408SCameron Grant static void 155d5fa8408SCameron Grant nm_wr(struct sc_info *sc, int regno, u_int32_t data, int size) 156d5fa8408SCameron Grant { 157d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->reg); 158d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->reg); 159d5fa8408SCameron Grant 160d5fa8408SCameron Grant switch (size) { 161d5fa8408SCameron Grant case 1: 162d5fa8408SCameron Grant bus_space_write_1(st, sh, regno, data); 163d5fa8408SCameron Grant break; 164d5fa8408SCameron Grant case 2: 165d5fa8408SCameron Grant bus_space_write_2(st, sh, regno, data); 166d5fa8408SCameron Grant break; 167d5fa8408SCameron Grant case 4: 168d5fa8408SCameron Grant bus_space_write_4(st, sh, regno, data); 169d5fa8408SCameron Grant break; 170d5fa8408SCameron Grant } 171d5fa8408SCameron Grant } 172d5fa8408SCameron Grant 173d5fa8408SCameron Grant static u_int32_t 174d5fa8408SCameron Grant nm_rdbuf(struct sc_info *sc, int regno, int size) 175d5fa8408SCameron Grant { 176d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->buf); 177d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->buf); 178d5fa8408SCameron Grant 179d5fa8408SCameron Grant switch (size) { 180d5fa8408SCameron Grant case 1: 181d5fa8408SCameron Grant return bus_space_read_1(st, sh, regno); 182d5fa8408SCameron Grant case 2: 183d5fa8408SCameron Grant return bus_space_read_2(st, sh, regno); 184d5fa8408SCameron Grant case 4: 185d5fa8408SCameron Grant return bus_space_read_4(st, sh, regno); 186d5fa8408SCameron Grant default: 187d5fa8408SCameron Grant return 0xffffffff; 188d5fa8408SCameron Grant } 189d5fa8408SCameron Grant } 190d5fa8408SCameron Grant 191d5fa8408SCameron Grant static void 192d5fa8408SCameron Grant nm_wrbuf(struct sc_info *sc, int regno, u_int32_t data, int size) 193d5fa8408SCameron Grant { 194d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->buf); 195d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->buf); 196d5fa8408SCameron Grant 197d5fa8408SCameron Grant switch (size) { 198d5fa8408SCameron Grant case 1: 199d5fa8408SCameron Grant bus_space_write_1(st, sh, regno, data); 200d5fa8408SCameron Grant break; 201d5fa8408SCameron Grant case 2: 202d5fa8408SCameron Grant bus_space_write_2(st, sh, regno, data); 203d5fa8408SCameron Grant break; 204d5fa8408SCameron Grant case 4: 205d5fa8408SCameron Grant bus_space_write_4(st, sh, regno, data); 206d5fa8408SCameron Grant break; 207d5fa8408SCameron Grant } 208d5fa8408SCameron Grant } 209d5fa8408SCameron Grant 210d5fa8408SCameron Grant /* ac97 codec */ 211d5fa8408SCameron Grant static int 212d5fa8408SCameron Grant nm_waitcd(struct sc_info *sc) 213d5fa8408SCameron Grant { 214d5fa8408SCameron Grant int cnt = 10; 215d5fa8408SCameron Grant 216d5fa8408SCameron Grant while (cnt-- > 0) { 217d5fa8408SCameron Grant if (nm_rd(sc, sc->ac97_status, 2) & sc->ac97_busy) 218d5fa8408SCameron Grant DELAY(100); 219d5fa8408SCameron Grant else 220d5fa8408SCameron Grant break; 221d5fa8408SCameron Grant } 222d5fa8408SCameron Grant return (nm_rd(sc, sc->ac97_status, 2) & sc->ac97_busy); 223d5fa8408SCameron Grant } 224d5fa8408SCameron Grant 225d5fa8408SCameron Grant static u_int32_t 226d5fa8408SCameron Grant nm_rdcd(void *devinfo, int regno) 227d5fa8408SCameron Grant { 228d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 229d5fa8408SCameron Grant u_int32_t x; 230d5fa8408SCameron Grant 231d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 232d5fa8408SCameron Grant x = nm_rd(sc, sc->ac97_base + regno, 2); 233d5fa8408SCameron Grant DELAY(1000); 234d5fa8408SCameron Grant return x; 235d5fa8408SCameron Grant } else { 236d5fa8408SCameron Grant device_printf(sc->dev, "ac97 codec not ready\n"); 237d5fa8408SCameron Grant return 0xffffffff; 238d5fa8408SCameron Grant } 239d5fa8408SCameron Grant } 240d5fa8408SCameron Grant 241d5fa8408SCameron Grant static void 242d5fa8408SCameron Grant nm_wrcd(void *devinfo, int regno, u_int32_t data) 243d5fa8408SCameron Grant { 244d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 245d5fa8408SCameron Grant int cnt = 3; 246d5fa8408SCameron Grant 247d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 248d5fa8408SCameron Grant while (cnt-- > 0) { 249d5fa8408SCameron Grant nm_wr(sc, sc->ac97_base + regno, data, 2); 250d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 251d5fa8408SCameron Grant DELAY(1000); 252d5fa8408SCameron Grant return; 253d5fa8408SCameron Grant } 254d5fa8408SCameron Grant } 255d5fa8408SCameron Grant } 256d5fa8408SCameron Grant device_printf(sc->dev, "ac97 codec not ready\n"); 257d5fa8408SCameron Grant } 258d5fa8408SCameron Grant 259d5fa8408SCameron Grant static void 260d5fa8408SCameron Grant nm_ackint(struct sc_info *sc, u_int32_t num) 261d5fa8408SCameron Grant { 262d5fa8408SCameron Grant if (sc->type == NM256AV_PCI_ID) { 263d5fa8408SCameron Grant nm_wr(sc, NM_INT_REG, num << 1, 2); 264d5fa8408SCameron Grant } else if (sc->type == NM256ZX_PCI_ID) { 265d5fa8408SCameron Grant nm_wr(sc, NM_INT_REG, num, 4); 266d5fa8408SCameron Grant } 267d5fa8408SCameron Grant } 268d5fa8408SCameron Grant 269d5fa8408SCameron Grant static int 270d5fa8408SCameron Grant nm_loadcoeff(struct sc_info *sc, int dir, int num) 271d5fa8408SCameron Grant { 272d5fa8408SCameron Grant int ofs, sz, i; 273d5fa8408SCameron Grant u_int32_t addr; 274d5fa8408SCameron Grant 275d5fa8408SCameron Grant addr = (dir == PCMDIR_PLAY)? 0x01c : 0x21c; 276d5fa8408SCameron Grant if (dir == PCMDIR_REC) 277d5fa8408SCameron Grant num += 8; 278d5fa8408SCameron Grant sz = coefficientSizes[num]; 279d5fa8408SCameron Grant ofs = 0; 280d5fa8408SCameron Grant while (num-- > 0) 281d5fa8408SCameron Grant ofs+= coefficientSizes[num]; 282d5fa8408SCameron Grant for (i = 0; i < sz; i++) 283d5fa8408SCameron Grant nm_wrbuf(sc, sc->cbuf + i, coefficients[ofs + i], 1); 284d5fa8408SCameron Grant nm_wr(sc, addr, sc->cbuf, 4); 285d5fa8408SCameron Grant if (dir == PCMDIR_PLAY) 286d5fa8408SCameron Grant sz--; 287d5fa8408SCameron Grant nm_wr(sc, addr + 4, sc->cbuf + sz, 4); 288d5fa8408SCameron Grant return 0; 289d5fa8408SCameron Grant } 290d5fa8408SCameron Grant 291d5fa8408SCameron Grant static int 292d5fa8408SCameron Grant nm_setch(struct sc_chinfo *ch) 293d5fa8408SCameron Grant { 294d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 295d5fa8408SCameron Grant u_int32_t base; 296d5fa8408SCameron Grant u_int8_t x; 297d5fa8408SCameron Grant 298d5fa8408SCameron Grant for (x = 0; x < 8; x++) 299d5fa8408SCameron Grant if (ch->spd < (samplerates[x] + samplerates[x + 1]) / 2) 300d5fa8408SCameron Grant break; 301d5fa8408SCameron Grant 302d5fa8408SCameron Grant if (x == 8) return 1; 303d5fa8408SCameron Grant 304d5fa8408SCameron Grant ch->spd = samplerates[x]; 305d5fa8408SCameron Grant nm_loadcoeff(sc, ch->dir, x); 306d5fa8408SCameron Grant 307d5fa8408SCameron Grant x <<= 4; 308d5fa8408SCameron Grant x &= NM_RATE_MASK; 309d5fa8408SCameron Grant if (ch->fmt & AFMT_16BIT) x |= NM_RATE_BITS_16; 310d5fa8408SCameron Grant if (ch->fmt & AFMT_STEREO) x |= NM_RATE_STEREO; 311d5fa8408SCameron Grant 312d5fa8408SCameron Grant base = (ch->dir == PCMDIR_PLAY)? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET; 313d5fa8408SCameron Grant nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1); 314d5fa8408SCameron Grant return 0; 315d5fa8408SCameron Grant } 316d5fa8408SCameron Grant 317d5fa8408SCameron Grant /* channel interface */ 318d5fa8408SCameron Grant static void * 319d5fa8408SCameron Grant nmchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 320d5fa8408SCameron Grant { 321d5fa8408SCameron Grant struct sc_info *sc = devinfo; 322d5fa8408SCameron Grant struct sc_chinfo *ch; 323d5fa8408SCameron Grant u_int32_t chnbuf; 324d5fa8408SCameron Grant 325d5fa8408SCameron Grant chnbuf = (dir == PCMDIR_PLAY)? sc->pbuf : sc->rbuf; 326d5fa8408SCameron Grant ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; 327d5fa8408SCameron Grant ch->buffer = b; 328d5fa8408SCameron Grant ch->buffer->bufsize = NM_BUFFSIZE; 329d5fa8408SCameron Grant ch->buffer->buf = (u_int8_t *)(rman_get_bushandle(sc->buf) + chnbuf); 330d5fa8408SCameron Grant device_printf(sc->dev, "%s buf %p\n", (dir == PCMDIR_PLAY)? "play" : "rec", ch->buffer->buf); 331d5fa8408SCameron Grant ch->parent = sc; 332d5fa8408SCameron Grant ch->channel = c; 333d5fa8408SCameron Grant ch->dir = dir; 334d5fa8408SCameron Grant return ch; 335d5fa8408SCameron Grant } 336d5fa8408SCameron Grant 337d5fa8408SCameron Grant static int 338d5fa8408SCameron Grant nmchan_setdir(void *data, int dir) 339d5fa8408SCameron Grant { 340d5fa8408SCameron Grant return 0; 341d5fa8408SCameron Grant } 342d5fa8408SCameron Grant 343d5fa8408SCameron Grant static int 344d5fa8408SCameron Grant nmchan_setformat(void *data, u_int32_t format) 345d5fa8408SCameron Grant { 346d5fa8408SCameron Grant struct sc_chinfo *ch = data; 347d5fa8408SCameron Grant 348d5fa8408SCameron Grant ch->fmt = format; 349d5fa8408SCameron Grant return nm_setch(ch); 350d5fa8408SCameron Grant } 351d5fa8408SCameron Grant 352d5fa8408SCameron Grant static int 353d5fa8408SCameron Grant nmchan_setspeed(void *data, u_int32_t speed) 354d5fa8408SCameron Grant { 355d5fa8408SCameron Grant struct sc_chinfo *ch = data; 356d5fa8408SCameron Grant 357d5fa8408SCameron Grant ch->spd = speed; 358d5fa8408SCameron Grant return nm_setch(ch); 359d5fa8408SCameron Grant } 360d5fa8408SCameron Grant 361d5fa8408SCameron Grant static int 362d5fa8408SCameron Grant nmchan_setblocksize(void *data, u_int32_t blocksize) 363d5fa8408SCameron Grant { 364d5fa8408SCameron Grant return blocksize; 365d5fa8408SCameron Grant } 366d5fa8408SCameron Grant 367d5fa8408SCameron Grant static int 368d5fa8408SCameron Grant nmchan_trigger(void *data, int go) 369d5fa8408SCameron Grant { 370d5fa8408SCameron Grant struct sc_chinfo *ch = data; 371d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 372d5fa8408SCameron Grant int ssz; 373d5fa8408SCameron Grant 374d5fa8408SCameron Grant if (go == PCMTRIG_EMLDMAWR) return 0; 375d5fa8408SCameron Grant 376d5fa8408SCameron Grant ssz = (ch->fmt & AFMT_16BIT)? 2 : 1; 377d5fa8408SCameron Grant if (ch->fmt & AFMT_STEREO) 378d5fa8408SCameron Grant ssz <<= 1; 379d5fa8408SCameron Grant 380d5fa8408SCameron Grant if (ch->dir == PCMDIR_PLAY) { 381d5fa8408SCameron Grant if (go == PCMTRIG_START) { 382d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_START, sc->pbuf, 4); 383d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_END, sc->pbuf + NM_BUFFSIZE - ssz, 4); 384d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_CURRP, sc->pbuf, 4); 385d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + NM_BUFFSIZE / 2, 4); 386d5fa8408SCameron Grant nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | 387d5fa8408SCameron Grant NM_PLAYBACK_ENABLE_FLAG, 1); 388d5fa8408SCameron Grant nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); 389d5fa8408SCameron Grant } else { 390d5fa8408SCameron Grant nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); 391d5fa8408SCameron Grant nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); 392d5fa8408SCameron Grant } 393d5fa8408SCameron Grant } else { 394d5fa8408SCameron Grant if (go == PCMTRIG_START) { 395d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN | 396d5fa8408SCameron Grant NM_RECORD_ENABLE_FLAG, 1); 397d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_START, sc->rbuf, 4); 398d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_END, sc->rbuf + NM_BUFFSIZE, 4); 399d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_CURRP, sc->rbuf, 4); 400d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + NM_BUFFSIZE / 2, 4); 401d5fa8408SCameron Grant } else { 402d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); 403d5fa8408SCameron Grant } 404d5fa8408SCameron Grant } 405d5fa8408SCameron Grant return 0; 406d5fa8408SCameron Grant } 407d5fa8408SCameron Grant 408d5fa8408SCameron Grant static int 409d5fa8408SCameron Grant nmchan_getptr(void *data) 410d5fa8408SCameron Grant { 411d5fa8408SCameron Grant struct sc_chinfo *ch = data; 412d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 413d5fa8408SCameron Grant 414d5fa8408SCameron Grant if (ch->dir == PCMDIR_PLAY) 415d5fa8408SCameron Grant return nm_rd(sc, NM_PBUFFER_CURRP, 4) - sc->pbuf; 416d5fa8408SCameron Grant else 417d5fa8408SCameron Grant return nm_rd(sc, NM_RBUFFER_CURRP, 4) - sc->rbuf; 418d5fa8408SCameron Grant } 419d5fa8408SCameron Grant 420d5fa8408SCameron Grant static pcmchan_caps * 421d5fa8408SCameron Grant nmchan_getcaps(void *data) 422d5fa8408SCameron Grant { 423d5fa8408SCameron Grant return &nm_caps; 424d5fa8408SCameron Grant } 425d5fa8408SCameron Grant 426d5fa8408SCameron Grant /* The interrupt handler */ 427d5fa8408SCameron Grant static void 428d5fa8408SCameron Grant nm_intr(void *p) 429d5fa8408SCameron Grant { 430d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)p; 431d5fa8408SCameron Grant int status, x; 432d5fa8408SCameron Grant 433d5fa8408SCameron Grant status = nm_rd(sc, NM_INT_REG, sc->irsz); 434d5fa8408SCameron Grant if (status == 0) { 435d5fa8408SCameron Grant if (sc->badintr++ > 1000) { 436d5fa8408SCameron Grant device_printf(sc->dev, "1000 bad intrs\n"); 437d5fa8408SCameron Grant sc->badintr = 0; 438d5fa8408SCameron Grant } 439d5fa8408SCameron Grant return; 440d5fa8408SCameron Grant } 441d5fa8408SCameron Grant sc->badintr = 0; 442d5fa8408SCameron Grant 443d5fa8408SCameron Grant if (status & sc->playint) { 444d5fa8408SCameron Grant status &= ~sc->playint; 445d5fa8408SCameron Grant nm_ackint(sc, sc->playint); 446d5fa8408SCameron Grant chn_intr(sc->pch.channel); 447d5fa8408SCameron Grant } 448d5fa8408SCameron Grant if (status & sc->recint) { 449d5fa8408SCameron Grant status &= ~sc->recint; 450d5fa8408SCameron Grant nm_ackint(sc, sc->recint); 451d5fa8408SCameron Grant chn_intr(sc->rch.channel); 452d5fa8408SCameron Grant } 453d5fa8408SCameron Grant if (status & sc->misc1int) { 454d5fa8408SCameron Grant status &= ~sc->misc1int; 455d5fa8408SCameron Grant nm_ackint(sc, sc->misc1int); 456d5fa8408SCameron Grant x = nm_rd(sc, 0x400, 1); 457d5fa8408SCameron Grant nm_wr(sc, 0x400, x | 2, 1); 458d5fa8408SCameron Grant device_printf(sc->dev, "misc int 1\n"); 459d5fa8408SCameron Grant } 460d5fa8408SCameron Grant if (status & sc->misc2int) { 461d5fa8408SCameron Grant status &= ~sc->misc2int; 462d5fa8408SCameron Grant nm_ackint(sc, sc->misc2int); 463d5fa8408SCameron Grant x = nm_rd(sc, 0x400, 1); 464d5fa8408SCameron Grant nm_wr(sc, 0x400, x & ~2, 1); 465d5fa8408SCameron Grant device_printf(sc->dev, "misc int 2\n"); 466d5fa8408SCameron Grant } 467d5fa8408SCameron Grant if (status) { 468d5fa8408SCameron Grant status &= ~sc->misc2int; 469d5fa8408SCameron Grant nm_ackint(sc, sc->misc2int); 470d5fa8408SCameron Grant device_printf(sc->dev, "unknown int\n"); 471d5fa8408SCameron Grant } 472d5fa8408SCameron Grant } 473d5fa8408SCameron Grant 474d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 475d5fa8408SCameron Grant 476d5fa8408SCameron Grant /* 477d5fa8408SCameron Grant * Probe and attach the card 478d5fa8408SCameron Grant */ 479d5fa8408SCameron Grant 480d5fa8408SCameron Grant static int 481d5fa8408SCameron Grant nm_init(struct sc_info *sc) 482d5fa8408SCameron Grant { 483d5fa8408SCameron Grant u_int32_t ofs, i; 484d5fa8408SCameron Grant 485d5fa8408SCameron Grant if (sc->type == NM256AV_PCI_ID) { 486d5fa8408SCameron Grant sc->ac97_base = NM_MIXER_OFFSET; 487d5fa8408SCameron Grant sc->ac97_status = NM_MIXER_STATUS_OFFSET; 488d5fa8408SCameron Grant sc->ac97_busy = NM_MIXER_READY_MASK; 489d5fa8408SCameron Grant 490d5fa8408SCameron Grant sc->buftop = 2560 * 1024; 491d5fa8408SCameron Grant 492d5fa8408SCameron Grant sc->irsz = 2; 493d5fa8408SCameron Grant sc->playint = NM_PLAYBACK_INT; 494d5fa8408SCameron Grant sc->recint = NM_RECORD_INT; 495d5fa8408SCameron Grant sc->misc1int = NM_MISC_INT_1; 496d5fa8408SCameron Grant sc->misc2int = NM_MISC_INT_2; 497d5fa8408SCameron Grant } else if (sc->type == NM256ZX_PCI_ID) { 498d5fa8408SCameron Grant sc->ac97_base = NM_MIXER_OFFSET; 499d5fa8408SCameron Grant sc->ac97_status = NM2_MIXER_STATUS_OFFSET; 500d5fa8408SCameron Grant sc->ac97_busy = NM2_MIXER_READY_MASK; 501d5fa8408SCameron Grant 502d5fa8408SCameron Grant sc->buftop = (nm_rd(sc, 0xa0b, 2)? 6144 : 4096) * 1024; 503d5fa8408SCameron Grant 504d5fa8408SCameron Grant sc->irsz = 4; 505d5fa8408SCameron Grant sc->playint = NM2_PLAYBACK_INT; 506d5fa8408SCameron Grant sc->recint = NM2_RECORD_INT; 507d5fa8408SCameron Grant sc->misc1int = NM2_MISC_INT_1; 508d5fa8408SCameron Grant sc->misc2int = NM2_MISC_INT_2; 509d5fa8408SCameron Grant } 510d5fa8408SCameron Grant sc->badintr = 0; 511d5fa8408SCameron Grant ofs = sc->buftop - 0x0400; 512d5fa8408SCameron Grant sc->buftop -= 0x1400; 513d5fa8408SCameron Grant 514d5fa8408SCameron Grant if ((nm_rdbuf(sc, ofs, 4) & NM_SIG_MASK) == NM_SIGNATURE) { 515d5fa8408SCameron Grant i = nm_rdbuf(sc, ofs + 4, 4); 516d5fa8408SCameron Grant if (i != 0 && i != 0xffffffff) 517d5fa8408SCameron Grant sc->buftop = i; 518d5fa8408SCameron Grant } 519d5fa8408SCameron Grant 520d5fa8408SCameron Grant sc->cbuf = sc->buftop - NM_MAX_COEFFICIENT; 521d5fa8408SCameron Grant sc->rbuf = sc->cbuf - NM_BUFFSIZE; 522d5fa8408SCameron Grant sc->pbuf = sc->rbuf - NM_BUFFSIZE; 523d5fa8408SCameron Grant sc->acbuf = sc->pbuf - (NM_TOTAL_COEFF_COUNT * 4); 524d5fa8408SCameron Grant 525d5fa8408SCameron Grant nm_wr(sc, 0, 0x11, 1); 526d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); 527d5fa8408SCameron Grant nm_wr(sc, 0x214, 0, 2); 528d5fa8408SCameron Grant 529d5fa8408SCameron Grant return 0; 530d5fa8408SCameron Grant } 531d5fa8408SCameron Grant 532d5fa8408SCameron Grant static int 533d5fa8408SCameron Grant nm_pci_probe(device_t dev) 534d5fa8408SCameron Grant { 535d5fa8408SCameron Grant char *s = NULL; 536d5fa8408SCameron Grant 537d5fa8408SCameron Grant switch (pci_get_devid(dev)) { 538d5fa8408SCameron Grant case NM256AV_PCI_ID: 539d5fa8408SCameron Grant s = "NeoMagic 256AV"; 540d5fa8408SCameron Grant break; 541d5fa8408SCameron Grant 542d5fa8408SCameron Grant case NM256ZX_PCI_ID: 543d5fa8408SCameron Grant s = "NeoMagic 256ZX"; 544d5fa8408SCameron Grant break; 545d5fa8408SCameron Grant } 546d5fa8408SCameron Grant 547d5fa8408SCameron Grant if (s) device_set_desc(dev, s); 548d5fa8408SCameron Grant return s? 0 : ENXIO; 549d5fa8408SCameron Grant } 550d5fa8408SCameron Grant 551d5fa8408SCameron Grant static int 552d5fa8408SCameron Grant nm_pci_attach(device_t dev) 553d5fa8408SCameron Grant { 554d5fa8408SCameron Grant snddev_info *d; 555d5fa8408SCameron Grant u_int32_t data; 556d5fa8408SCameron Grant struct sc_info *sc; 557d5fa8408SCameron Grant struct ac97_info *codec; 558d5fa8408SCameron Grant char status[SND_STATUSLEN]; 559d5fa8408SCameron Grant 560d5fa8408SCameron Grant d = device_get_softc(dev); 561d5fa8408SCameron Grant if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) { 562d5fa8408SCameron Grant device_printf(dev, "cannot allocate softc\n"); 563d5fa8408SCameron Grant return ENXIO; 564d5fa8408SCameron Grant } 565d5fa8408SCameron Grant 566d5fa8408SCameron Grant bzero(sc, sizeof(*sc)); 567d5fa8408SCameron Grant sc->dev = dev; 568d5fa8408SCameron Grant sc->type = pci_get_devid(dev); 569d5fa8408SCameron Grant 570d5fa8408SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 571d5fa8408SCameron Grant data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 572d5fa8408SCameron Grant pci_write_config(dev, PCIR_COMMAND, data, 2); 573d5fa8408SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 574d5fa8408SCameron Grant 575d5fa8408SCameron Grant sc->bufid = PCIR_MAPS; 576d5fa8408SCameron Grant sc->buf = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bufid, 577d5fa8408SCameron Grant 0, ~0, 1, RF_ACTIVE); 578d5fa8408SCameron Grant sc->regid = PCIR_MAPS + 4; 579d5fa8408SCameron Grant sc->reg = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->regid, 580d5fa8408SCameron Grant 0, ~0, 1, RF_ACTIVE); 581d5fa8408SCameron Grant 582d5fa8408SCameron Grant if (!sc->buf || !sc->reg) { 583d5fa8408SCameron Grant device_printf(dev, "unable to map register space\n"); 584d5fa8408SCameron Grant goto bad; 585d5fa8408SCameron Grant } 586d5fa8408SCameron Grant 587d5fa8408SCameron Grant if (nm_init(sc) == -1) { 588d5fa8408SCameron Grant device_printf(dev, "unable to initialize the card\n"); 589d5fa8408SCameron Grant goto bad; 590d5fa8408SCameron Grant } 591d5fa8408SCameron Grant 592d5fa8408SCameron Grant codec = ac97_create(sc, nm_rdcd, nm_wrcd); 593d5fa8408SCameron Grant if (codec == NULL) goto bad; 594d5fa8408SCameron Grant mixer_init(d, &ac97_mixer, codec); 595d5fa8408SCameron Grant 596d5fa8408SCameron Grant sc->irqid = 0; 597d5fa8408SCameron Grant sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 598d5fa8408SCameron Grant 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 599d5fa8408SCameron Grant if (!sc->irq || 600d5fa8408SCameron Grant bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, nm_intr, sc, &sc->ih)) { 601d5fa8408SCameron Grant device_printf(dev, "unable to map interrupt\n"); 602d5fa8408SCameron Grant goto bad; 603d5fa8408SCameron Grant } 604d5fa8408SCameron Grant 605d5fa8408SCameron Grant snprintf(status, SND_STATUSLEN, "at memory 0x%lx, 0x%lx irq %ld", 606d5fa8408SCameron Grant rman_get_start(sc->buf), rman_get_start(sc->reg), 607d5fa8408SCameron Grant rman_get_start(sc->irq)); 608d5fa8408SCameron Grant 609d5fa8408SCameron Grant if (pcm_register(dev, sc, 1, 1)) goto bad; 610d5fa8408SCameron Grant pcm_addchan(dev, PCMDIR_REC, &nm_chantemplate, sc); 611d5fa8408SCameron Grant pcm_addchan(dev, PCMDIR_PLAY, &nm_chantemplate, sc); 612d5fa8408SCameron Grant pcm_setstatus(dev, status); 613d5fa8408SCameron Grant 614d5fa8408SCameron Grant return 0; 615d5fa8408SCameron Grant 616d5fa8408SCameron Grant bad: 617d5fa8408SCameron Grant if (sc->buf) bus_release_resource(dev, SYS_RES_MEMORY, sc->bufid, sc->buf); 618d5fa8408SCameron Grant if (sc->reg) bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); 619d5fa8408SCameron Grant if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); 620d5fa8408SCameron Grant if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 621d5fa8408SCameron Grant free(sc, M_DEVBUF); 622d5fa8408SCameron Grant return ENXIO; 623d5fa8408SCameron Grant } 624d5fa8408SCameron Grant 625d5fa8408SCameron Grant static device_method_t nm_methods[] = { 626d5fa8408SCameron Grant /* Device interface */ 627d5fa8408SCameron Grant DEVMETHOD(device_probe, nm_pci_probe), 628d5fa8408SCameron Grant DEVMETHOD(device_attach, nm_pci_attach), 629d5fa8408SCameron Grant 630d5fa8408SCameron Grant { 0, 0 } 631d5fa8408SCameron Grant }; 632d5fa8408SCameron Grant 633d5fa8408SCameron Grant static driver_t nm_driver = { 634d5fa8408SCameron Grant "pcm", 635d5fa8408SCameron Grant nm_methods, 636d5fa8408SCameron Grant sizeof(snddev_info), 637d5fa8408SCameron Grant }; 638d5fa8408SCameron Grant 639d5fa8408SCameron Grant static devclass_t pcm_devclass; 640d5fa8408SCameron Grant 641d5fa8408SCameron Grant DRIVER_MODULE(nm, pci, nm_driver, pcm_devclass, 0, 0); 642