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 29d5fa8408SCameron Grant #include <dev/sound/pcm/sound.h> 30d5fa8408SCameron Grant #include <dev/sound/pcm/ac97.h> 31d5fa8408SCameron Grant #include <dev/sound/pci/neomagic.h> 32d5fa8408SCameron Grant #include <dev/sound/pci/neomagic-coeff.h> 33d5fa8408SCameron Grant 34d5fa8408SCameron Grant #include <pci/pcireg.h> 35d5fa8408SCameron Grant #include <pci/pcivar.h> 36d5fa8408SCameron Grant 3767b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3867b1dce3SCameron Grant 39d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 40d5fa8408SCameron Grant 41d5fa8408SCameron Grant #define NM_BUFFSIZE 16384 42d5fa8408SCameron Grant 43d5fa8408SCameron Grant #define NM256AV_PCI_ID 0x800510c8 44d5fa8408SCameron Grant #define NM256ZX_PCI_ID 0x800610c8 45d5fa8408SCameron Grant 46d5fa8408SCameron Grant struct sc_info; 47d5fa8408SCameron Grant 48d5fa8408SCameron Grant /* channel registers */ 49d5fa8408SCameron Grant struct sc_chinfo { 50d5fa8408SCameron Grant int spd, dir, fmt; 5166ef8af5SCameron Grant struct snd_dbuf *buffer; 5266ef8af5SCameron Grant struct pcm_channel *channel; 53d5fa8408SCameron Grant struct sc_info *parent; 54d5fa8408SCameron Grant }; 55d5fa8408SCameron Grant 56d5fa8408SCameron Grant /* device private data */ 57d5fa8408SCameron Grant struct sc_info { 58d5fa8408SCameron Grant device_t dev; 59d5fa8408SCameron Grant u_int32_t type; 60d5fa8408SCameron Grant 61d5fa8408SCameron Grant struct resource *reg, *irq, *buf; 62d5fa8408SCameron Grant int regid, irqid, bufid; 63d5fa8408SCameron Grant void *ih; 64d5fa8408SCameron Grant 65d5fa8408SCameron Grant u_int32_t ac97_base, ac97_status, ac97_busy; 66d5fa8408SCameron Grant u_int32_t buftop, pbuf, rbuf, cbuf, acbuf; 67d5fa8408SCameron Grant u_int32_t playint, recint, misc1int, misc2int; 68d5fa8408SCameron Grant u_int32_t irsz, badintr; 69d5fa8408SCameron Grant 70d5fa8408SCameron Grant struct sc_chinfo pch, rch; 71d5fa8408SCameron Grant }; 72d5fa8408SCameron Grant 73d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 74d5fa8408SCameron Grant 75d5fa8408SCameron Grant /* 76d5fa8408SCameron Grant * prototypes 77d5fa8408SCameron Grant */ 78d5fa8408SCameron Grant 79d5fa8408SCameron Grant /* stuff */ 80d5fa8408SCameron Grant static int nm_loadcoeff(struct sc_info *sc, int dir, int num); 81d5fa8408SCameron Grant static int nm_setch(struct sc_chinfo *ch); 82d5fa8408SCameron Grant static int nm_init(struct sc_info *); 83d5fa8408SCameron Grant static void nm_intr(void *); 84d5fa8408SCameron Grant 85d5fa8408SCameron Grant /* talk to the card */ 86d5fa8408SCameron Grant static u_int32_t nm_rd(struct sc_info *, int, int); 87d5fa8408SCameron Grant static void nm_wr(struct sc_info *, int, u_int32_t, int); 88d5fa8408SCameron Grant static u_int32_t nm_rdbuf(struct sc_info *, int, int); 89d5fa8408SCameron Grant static void nm_wrbuf(struct sc_info *, int, u_int32_t, int); 90d5fa8408SCameron Grant 91e7fb3296SCameron Grant static u_int32_t badcards[] = { 92e7fb3296SCameron Grant 0x0007103c, 93e7fb3296SCameron Grant 0x008f1028, 944873b46dSCameron Grant 0x00dd1014, 95e572fcd4SGeorge C A Reid 0x8005110a, 96e7fb3296SCameron Grant }; 97e7fb3296SCameron Grant #define NUM_BADCARDS (sizeof(badcards) / sizeof(u_int32_t)) 98e7fb3296SCameron Grant 99d5fa8408SCameron Grant /* The actual rates supported by the card. */ 100d5fa8408SCameron Grant static int samplerates[9] = { 101e7fb3296SCameron Grant 8000, 102e7fb3296SCameron Grant 11025, 103e7fb3296SCameron Grant 16000, 104e7fb3296SCameron Grant 22050, 105e7fb3296SCameron Grant 24000, 106e7fb3296SCameron Grant 32000, 107e7fb3296SCameron Grant 44100, 108e7fb3296SCameron Grant 48000, 109e7fb3296SCameron Grant 99999999 110d5fa8408SCameron Grant }; 111d5fa8408SCameron Grant 112d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 113d5fa8408SCameron Grant 114513693beSCameron Grant static u_int32_t nm_fmt[] = { 115513693beSCameron Grant AFMT_U8, 116513693beSCameron Grant AFMT_STEREO | AFMT_U8, 117513693beSCameron Grant AFMT_S16_LE, 118513693beSCameron Grant AFMT_STEREO | AFMT_S16_LE, 119513693beSCameron Grant 0 120d5fa8408SCameron Grant }; 12166ef8af5SCameron Grant static struct pcmchan_caps nm_caps = {4000, 48000, nm_fmt, 0}; 122d5fa8408SCameron Grant 123d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 124d5fa8408SCameron Grant 125d5fa8408SCameron Grant /* Hardware */ 126d5fa8408SCameron Grant static u_int32_t 127d5fa8408SCameron Grant nm_rd(struct sc_info *sc, int regno, int size) 128d5fa8408SCameron Grant { 129d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->reg); 130d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->reg); 131d5fa8408SCameron Grant 132d5fa8408SCameron Grant switch (size) { 133d5fa8408SCameron Grant case 1: 134d5fa8408SCameron Grant return bus_space_read_1(st, sh, regno); 135d5fa8408SCameron Grant case 2: 136d5fa8408SCameron Grant return bus_space_read_2(st, sh, regno); 137d5fa8408SCameron Grant case 4: 138d5fa8408SCameron Grant return bus_space_read_4(st, sh, regno); 139d5fa8408SCameron Grant default: 140d5fa8408SCameron Grant return 0xffffffff; 141d5fa8408SCameron Grant } 142d5fa8408SCameron Grant } 143d5fa8408SCameron Grant 144d5fa8408SCameron Grant static void 145d5fa8408SCameron Grant nm_wr(struct sc_info *sc, int regno, u_int32_t data, int size) 146d5fa8408SCameron Grant { 147d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->reg); 148d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->reg); 149d5fa8408SCameron Grant 150d5fa8408SCameron Grant switch (size) { 151d5fa8408SCameron Grant case 1: 152d5fa8408SCameron Grant bus_space_write_1(st, sh, regno, data); 153d5fa8408SCameron Grant break; 154d5fa8408SCameron Grant case 2: 155d5fa8408SCameron Grant bus_space_write_2(st, sh, regno, data); 156d5fa8408SCameron Grant break; 157d5fa8408SCameron Grant case 4: 158d5fa8408SCameron Grant bus_space_write_4(st, sh, regno, data); 159d5fa8408SCameron Grant break; 160d5fa8408SCameron Grant } 161d5fa8408SCameron Grant } 162d5fa8408SCameron Grant 163d5fa8408SCameron Grant static u_int32_t 164d5fa8408SCameron Grant nm_rdbuf(struct sc_info *sc, int regno, int size) 165d5fa8408SCameron Grant { 166d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->buf); 167d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->buf); 168d5fa8408SCameron Grant 169d5fa8408SCameron Grant switch (size) { 170d5fa8408SCameron Grant case 1: 171d5fa8408SCameron Grant return bus_space_read_1(st, sh, regno); 172d5fa8408SCameron Grant case 2: 173d5fa8408SCameron Grant return bus_space_read_2(st, sh, regno); 174d5fa8408SCameron Grant case 4: 175d5fa8408SCameron Grant return bus_space_read_4(st, sh, regno); 176d5fa8408SCameron Grant default: 177d5fa8408SCameron Grant return 0xffffffff; 178d5fa8408SCameron Grant } 179d5fa8408SCameron Grant } 180d5fa8408SCameron Grant 181d5fa8408SCameron Grant static void 182d5fa8408SCameron Grant nm_wrbuf(struct sc_info *sc, int regno, u_int32_t data, int size) 183d5fa8408SCameron Grant { 184d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->buf); 185d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->buf); 186d5fa8408SCameron Grant 187d5fa8408SCameron Grant switch (size) { 188d5fa8408SCameron Grant case 1: 189d5fa8408SCameron Grant bus_space_write_1(st, sh, regno, data); 190d5fa8408SCameron Grant break; 191d5fa8408SCameron Grant case 2: 192d5fa8408SCameron Grant bus_space_write_2(st, sh, regno, data); 193d5fa8408SCameron Grant break; 194d5fa8408SCameron Grant case 4: 195d5fa8408SCameron Grant bus_space_write_4(st, sh, regno, data); 196d5fa8408SCameron Grant break; 197d5fa8408SCameron Grant } 198d5fa8408SCameron Grant } 199d5fa8408SCameron Grant 2000f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 201d5fa8408SCameron Grant /* ac97 codec */ 202d5fa8408SCameron Grant static int 203d5fa8408SCameron Grant nm_waitcd(struct sc_info *sc) 204d5fa8408SCameron Grant { 205d5fa8408SCameron Grant int cnt = 10; 206d5fa8408SCameron Grant 207d5fa8408SCameron Grant while (cnt-- > 0) { 208d5fa8408SCameron Grant if (nm_rd(sc, sc->ac97_status, 2) & sc->ac97_busy) 209d5fa8408SCameron Grant DELAY(100); 210d5fa8408SCameron Grant else 211d5fa8408SCameron Grant break; 212d5fa8408SCameron Grant } 213d5fa8408SCameron Grant return (nm_rd(sc, sc->ac97_status, 2) & sc->ac97_busy); 214d5fa8408SCameron Grant } 215d5fa8408SCameron Grant 216d5fa8408SCameron Grant static u_int32_t 2170f55ac6cSCameron Grant nm_initcd(kobj_t obj, void *devinfo) 21839004e69SCameron Grant { 21939004e69SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 22039004e69SCameron Grant 22139004e69SCameron Grant nm_wr(sc, 0x6c0, 0x01, 1); 22239004e69SCameron Grant nm_wr(sc, 0x6cc, 0x87, 1); 22339004e69SCameron Grant nm_wr(sc, 0x6cc, 0x80, 1); 22439004e69SCameron Grant nm_wr(sc, 0x6cc, 0x00, 1); 225cd2c103aSCameron Grant return 1; 22639004e69SCameron Grant } 22739004e69SCameron Grant 2280f55ac6cSCameron Grant static int 2290f55ac6cSCameron Grant nm_rdcd(kobj_t obj, void *devinfo, int regno) 230d5fa8408SCameron Grant { 231d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 232d5fa8408SCameron Grant u_int32_t x; 233d5fa8408SCameron Grant 234d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 235d5fa8408SCameron Grant x = nm_rd(sc, sc->ac97_base + regno, 2); 236d5fa8408SCameron Grant DELAY(1000); 237d5fa8408SCameron Grant return x; 238d5fa8408SCameron Grant } else { 239d5fa8408SCameron Grant device_printf(sc->dev, "ac97 codec not ready\n"); 2400f55ac6cSCameron Grant return -1; 241d5fa8408SCameron Grant } 242d5fa8408SCameron Grant } 243d5fa8408SCameron Grant 2440f55ac6cSCameron Grant static int 2450f55ac6cSCameron Grant nm_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 246d5fa8408SCameron Grant { 247d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 248d5fa8408SCameron Grant int cnt = 3; 249d5fa8408SCameron Grant 250d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 251d5fa8408SCameron Grant while (cnt-- > 0) { 252d5fa8408SCameron Grant nm_wr(sc, sc->ac97_base + regno, data, 2); 253d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 254d5fa8408SCameron Grant DELAY(1000); 2550f55ac6cSCameron Grant return 0; 256d5fa8408SCameron Grant } 257d5fa8408SCameron Grant } 258d5fa8408SCameron Grant } 259d5fa8408SCameron Grant device_printf(sc->dev, "ac97 codec not ready\n"); 2600f55ac6cSCameron Grant return -1; 261d5fa8408SCameron Grant } 262d5fa8408SCameron Grant 2630f55ac6cSCameron Grant static kobj_method_t nm_ac97_methods[] = { 2640f55ac6cSCameron Grant KOBJMETHOD(ac97_init, nm_initcd), 2650f55ac6cSCameron Grant KOBJMETHOD(ac97_read, nm_rdcd), 2660f55ac6cSCameron Grant KOBJMETHOD(ac97_write, nm_wrcd), 2670f55ac6cSCameron Grant { 0, 0 } 2680f55ac6cSCameron Grant }; 2690f55ac6cSCameron Grant AC97_DECLARE(nm_ac97); 2700f55ac6cSCameron Grant 2710f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 2720f55ac6cSCameron Grant 273d5fa8408SCameron Grant static void 274d5fa8408SCameron Grant nm_ackint(struct sc_info *sc, u_int32_t num) 275d5fa8408SCameron Grant { 276d5fa8408SCameron Grant if (sc->type == NM256AV_PCI_ID) { 277d5fa8408SCameron Grant nm_wr(sc, NM_INT_REG, num << 1, 2); 278d5fa8408SCameron Grant } else if (sc->type == NM256ZX_PCI_ID) { 279d5fa8408SCameron Grant nm_wr(sc, NM_INT_REG, num, 4); 280d5fa8408SCameron Grant } 281d5fa8408SCameron Grant } 282d5fa8408SCameron Grant 283d5fa8408SCameron Grant static int 284d5fa8408SCameron Grant nm_loadcoeff(struct sc_info *sc, int dir, int num) 285d5fa8408SCameron Grant { 286d5fa8408SCameron Grant int ofs, sz, i; 287d5fa8408SCameron Grant u_int32_t addr; 288d5fa8408SCameron Grant 289d5fa8408SCameron Grant addr = (dir == PCMDIR_PLAY)? 0x01c : 0x21c; 290d5fa8408SCameron Grant if (dir == PCMDIR_REC) 291d5fa8408SCameron Grant num += 8; 292d5fa8408SCameron Grant sz = coefficientSizes[num]; 293d5fa8408SCameron Grant ofs = 0; 294d5fa8408SCameron Grant while (num-- > 0) 295d5fa8408SCameron Grant ofs+= coefficientSizes[num]; 296d5fa8408SCameron Grant for (i = 0; i < sz; i++) 297d5fa8408SCameron Grant nm_wrbuf(sc, sc->cbuf + i, coefficients[ofs + i], 1); 298d5fa8408SCameron Grant nm_wr(sc, addr, sc->cbuf, 4); 299d5fa8408SCameron Grant if (dir == PCMDIR_PLAY) 300d5fa8408SCameron Grant sz--; 301d5fa8408SCameron Grant nm_wr(sc, addr + 4, sc->cbuf + sz, 4); 302d5fa8408SCameron Grant return 0; 303d5fa8408SCameron Grant } 304d5fa8408SCameron Grant 305d5fa8408SCameron Grant static int 306d5fa8408SCameron Grant nm_setch(struct sc_chinfo *ch) 307d5fa8408SCameron Grant { 308d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 309d5fa8408SCameron Grant u_int32_t base; 310d5fa8408SCameron Grant u_int8_t x; 311d5fa8408SCameron Grant 312d5fa8408SCameron Grant for (x = 0; x < 8; x++) 313d5fa8408SCameron Grant if (ch->spd < (samplerates[x] + samplerates[x + 1]) / 2) 314d5fa8408SCameron Grant break; 315d5fa8408SCameron Grant 316d5fa8408SCameron Grant if (x == 8) return 1; 317d5fa8408SCameron Grant 318d5fa8408SCameron Grant ch->spd = samplerates[x]; 319d5fa8408SCameron Grant nm_loadcoeff(sc, ch->dir, x); 320d5fa8408SCameron Grant 321d5fa8408SCameron Grant x <<= 4; 322d5fa8408SCameron Grant x &= NM_RATE_MASK; 323d5fa8408SCameron Grant if (ch->fmt & AFMT_16BIT) x |= NM_RATE_BITS_16; 324d5fa8408SCameron Grant if (ch->fmt & AFMT_STEREO) x |= NM_RATE_STEREO; 325d5fa8408SCameron Grant 326d5fa8408SCameron Grant base = (ch->dir == PCMDIR_PLAY)? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET; 327d5fa8408SCameron Grant nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1); 328d5fa8408SCameron Grant return 0; 329d5fa8408SCameron Grant } 330d5fa8408SCameron Grant 331d5fa8408SCameron Grant /* channel interface */ 332d5fa8408SCameron Grant static void * 33366ef8af5SCameron Grant nmchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 334d5fa8408SCameron Grant { 335d5fa8408SCameron Grant struct sc_info *sc = devinfo; 336d5fa8408SCameron Grant struct sc_chinfo *ch; 337d5fa8408SCameron Grant u_int32_t chnbuf; 338d5fa8408SCameron Grant 339d5fa8408SCameron Grant chnbuf = (dir == PCMDIR_PLAY)? sc->pbuf : sc->rbuf; 340d5fa8408SCameron Grant ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; 341d5fa8408SCameron Grant ch->buffer = b; 342350a5fafSCameron Grant sndbuf_setup(ch->buffer, (u_int8_t *)rman_get_virtual(sc->buf) + chnbuf, NM_BUFFSIZE); 3435b4c3f3cSCameron Grant if (bootverbose) 3445b4c3f3cSCameron Grant device_printf(sc->dev, "%s buf %p\n", (dir == PCMDIR_PLAY)? 34566ef8af5SCameron Grant "play" : "rec", sndbuf_getbuf(ch->buffer)); 346d5fa8408SCameron Grant ch->parent = sc; 347d5fa8408SCameron Grant ch->channel = c; 348d5fa8408SCameron Grant ch->dir = dir; 349d5fa8408SCameron Grant return ch; 350d5fa8408SCameron Grant } 351d5fa8408SCameron Grant 352d5fa8408SCameron Grant static int 3530f55ac6cSCameron Grant nmchan_free(kobj_t obj, void *data) 35433dbf14aSCameron Grant { 35533dbf14aSCameron Grant return 0; 35633dbf14aSCameron Grant } 35733dbf14aSCameron Grant 35833dbf14aSCameron Grant static int 3590f55ac6cSCameron Grant nmchan_setformat(kobj_t obj, void *data, u_int32_t format) 360d5fa8408SCameron Grant { 361d5fa8408SCameron Grant struct sc_chinfo *ch = data; 362d5fa8408SCameron Grant 363d5fa8408SCameron Grant ch->fmt = format; 364d5fa8408SCameron Grant return nm_setch(ch); 365d5fa8408SCameron Grant } 366d5fa8408SCameron Grant 367d5fa8408SCameron Grant static int 3680f55ac6cSCameron Grant nmchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 369d5fa8408SCameron Grant { 370d5fa8408SCameron Grant struct sc_chinfo *ch = data; 371d5fa8408SCameron Grant 372d5fa8408SCameron Grant ch->spd = speed; 3734ee07471SCameron Grant return nm_setch(ch)? 0 : ch->spd; 374d5fa8408SCameron Grant } 375d5fa8408SCameron Grant 376d5fa8408SCameron Grant static int 3770f55ac6cSCameron Grant nmchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 378d5fa8408SCameron Grant { 379d5fa8408SCameron Grant return blocksize; 380d5fa8408SCameron Grant } 381d5fa8408SCameron Grant 382d5fa8408SCameron Grant static int 3830f55ac6cSCameron Grant nmchan_trigger(kobj_t obj, void *data, int go) 384d5fa8408SCameron Grant { 385d5fa8408SCameron Grant struct sc_chinfo *ch = data; 386d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 387d5fa8408SCameron Grant int ssz; 388d5fa8408SCameron Grant 38935f9e4a1SCameron Grant if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 39035f9e4a1SCameron Grant return 0; 391d5fa8408SCameron Grant 392d5fa8408SCameron Grant ssz = (ch->fmt & AFMT_16BIT)? 2 : 1; 393d5fa8408SCameron Grant if (ch->fmt & AFMT_STEREO) 394d5fa8408SCameron Grant ssz <<= 1; 395d5fa8408SCameron Grant 396d5fa8408SCameron Grant if (ch->dir == PCMDIR_PLAY) { 397d5fa8408SCameron Grant if (go == PCMTRIG_START) { 398d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_START, sc->pbuf, 4); 399d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_END, sc->pbuf + NM_BUFFSIZE - ssz, 4); 400d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_CURRP, sc->pbuf, 4); 401d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + NM_BUFFSIZE / 2, 4); 402d5fa8408SCameron Grant nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | 403d5fa8408SCameron Grant NM_PLAYBACK_ENABLE_FLAG, 1); 404d5fa8408SCameron Grant nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); 405d5fa8408SCameron Grant } else { 406d5fa8408SCameron Grant nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); 407d5fa8408SCameron Grant nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); 408d5fa8408SCameron Grant } 409d5fa8408SCameron Grant } else { 410d5fa8408SCameron Grant if (go == PCMTRIG_START) { 411d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN | 412d5fa8408SCameron Grant NM_RECORD_ENABLE_FLAG, 1); 413d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_START, sc->rbuf, 4); 414d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_END, sc->rbuf + NM_BUFFSIZE, 4); 415d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_CURRP, sc->rbuf, 4); 416d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + NM_BUFFSIZE / 2, 4); 417d5fa8408SCameron Grant } else { 418d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); 419d5fa8408SCameron Grant } 420d5fa8408SCameron Grant } 421d5fa8408SCameron Grant return 0; 422d5fa8408SCameron Grant } 423d5fa8408SCameron Grant 424d5fa8408SCameron Grant static int 4250f55ac6cSCameron Grant nmchan_getptr(kobj_t obj, void *data) 426d5fa8408SCameron Grant { 427d5fa8408SCameron Grant struct sc_chinfo *ch = data; 428d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 429d5fa8408SCameron Grant 430d5fa8408SCameron Grant if (ch->dir == PCMDIR_PLAY) 431d5fa8408SCameron Grant return nm_rd(sc, NM_PBUFFER_CURRP, 4) - sc->pbuf; 432d5fa8408SCameron Grant else 433d5fa8408SCameron Grant return nm_rd(sc, NM_RBUFFER_CURRP, 4) - sc->rbuf; 434d5fa8408SCameron Grant } 435d5fa8408SCameron Grant 43666ef8af5SCameron Grant static struct pcmchan_caps * 4370f55ac6cSCameron Grant nmchan_getcaps(kobj_t obj, void *data) 438d5fa8408SCameron Grant { 439d5fa8408SCameron Grant return &nm_caps; 440d5fa8408SCameron Grant } 441d5fa8408SCameron Grant 4420f55ac6cSCameron Grant static kobj_method_t nmchan_methods[] = { 4430f55ac6cSCameron Grant KOBJMETHOD(channel_init, nmchan_init), 4440f55ac6cSCameron Grant KOBJMETHOD(channel_free, nmchan_free), 4450f55ac6cSCameron Grant KOBJMETHOD(channel_setformat, nmchan_setformat), 4460f55ac6cSCameron Grant KOBJMETHOD(channel_setspeed, nmchan_setspeed), 4470f55ac6cSCameron Grant KOBJMETHOD(channel_setblocksize, nmchan_setblocksize), 4480f55ac6cSCameron Grant KOBJMETHOD(channel_trigger, nmchan_trigger), 4490f55ac6cSCameron Grant KOBJMETHOD(channel_getptr, nmchan_getptr), 4500f55ac6cSCameron Grant KOBJMETHOD(channel_getcaps, nmchan_getcaps), 4510f55ac6cSCameron Grant { 0, 0 } 4520f55ac6cSCameron Grant }; 4530f55ac6cSCameron Grant CHANNEL_DECLARE(nmchan); 4540f55ac6cSCameron Grant 455d5fa8408SCameron Grant /* The interrupt handler */ 456d5fa8408SCameron Grant static void 457d5fa8408SCameron Grant nm_intr(void *p) 458d5fa8408SCameron Grant { 459d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)p; 4604873b46dSCameron Grant int status, x; 461d5fa8408SCameron Grant 462d5fa8408SCameron Grant status = nm_rd(sc, NM_INT_REG, sc->irsz); 4634873b46dSCameron Grant if (status == 0) 464d5fa8408SCameron Grant return; 465d5fa8408SCameron Grant 466d5fa8408SCameron Grant if (status & sc->playint) { 467d5fa8408SCameron Grant status &= ~sc->playint; 468d5fa8408SCameron Grant nm_ackint(sc, sc->playint); 469d5fa8408SCameron Grant chn_intr(sc->pch.channel); 470d5fa8408SCameron Grant } 471d5fa8408SCameron Grant if (status & sc->recint) { 472d5fa8408SCameron Grant status &= ~sc->recint; 473d5fa8408SCameron Grant nm_ackint(sc, sc->recint); 474d5fa8408SCameron Grant chn_intr(sc->rch.channel); 475d5fa8408SCameron Grant } 476d5fa8408SCameron Grant if (status & sc->misc1int) { 477d5fa8408SCameron Grant status &= ~sc->misc1int; 478d5fa8408SCameron Grant nm_ackint(sc, sc->misc1int); 479d5fa8408SCameron Grant x = nm_rd(sc, 0x400, 1); 480d5fa8408SCameron Grant nm_wr(sc, 0x400, x | 2, 1); 481d5fa8408SCameron Grant device_printf(sc->dev, "misc int 1\n"); 482d5fa8408SCameron Grant } 483d5fa8408SCameron Grant if (status & sc->misc2int) { 484d5fa8408SCameron Grant status &= ~sc->misc2int; 485d5fa8408SCameron Grant nm_ackint(sc, sc->misc2int); 486d5fa8408SCameron Grant x = nm_rd(sc, 0x400, 1); 487d5fa8408SCameron Grant nm_wr(sc, 0x400, x & ~2, 1); 488d5fa8408SCameron Grant device_printf(sc->dev, "misc int 2\n"); 489d5fa8408SCameron Grant } 490d5fa8408SCameron Grant if (status) { 4914873b46dSCameron Grant nm_ackint(sc, status); 492d5fa8408SCameron Grant device_printf(sc->dev, "unknown int\n"); 493d5fa8408SCameron Grant } 494d5fa8408SCameron Grant } 495d5fa8408SCameron Grant 496d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 497d5fa8408SCameron Grant 498d5fa8408SCameron Grant /* 499d5fa8408SCameron Grant * Probe and attach the card 500d5fa8408SCameron Grant */ 501d5fa8408SCameron Grant 502d5fa8408SCameron Grant static int 503d5fa8408SCameron Grant nm_init(struct sc_info *sc) 504d5fa8408SCameron Grant { 505d5fa8408SCameron Grant u_int32_t ofs, i; 506d5fa8408SCameron Grant 507d5fa8408SCameron Grant if (sc->type == NM256AV_PCI_ID) { 508d5fa8408SCameron Grant sc->ac97_base = NM_MIXER_OFFSET; 509d5fa8408SCameron Grant sc->ac97_status = NM_MIXER_STATUS_OFFSET; 510d5fa8408SCameron Grant sc->ac97_busy = NM_MIXER_READY_MASK; 511d5fa8408SCameron Grant 512d5fa8408SCameron Grant sc->buftop = 2560 * 1024; 513d5fa8408SCameron Grant 514d5fa8408SCameron Grant sc->irsz = 2; 515d5fa8408SCameron Grant sc->playint = NM_PLAYBACK_INT; 516d5fa8408SCameron Grant sc->recint = NM_RECORD_INT; 517d5fa8408SCameron Grant sc->misc1int = NM_MISC_INT_1; 518d5fa8408SCameron Grant sc->misc2int = NM_MISC_INT_2; 519d5fa8408SCameron Grant } else if (sc->type == NM256ZX_PCI_ID) { 520d5fa8408SCameron Grant sc->ac97_base = NM_MIXER_OFFSET; 521d5fa8408SCameron Grant sc->ac97_status = NM2_MIXER_STATUS_OFFSET; 522d5fa8408SCameron Grant sc->ac97_busy = NM2_MIXER_READY_MASK; 523d5fa8408SCameron Grant 524d5fa8408SCameron Grant sc->buftop = (nm_rd(sc, 0xa0b, 2)? 6144 : 4096) * 1024; 525d5fa8408SCameron Grant 526d5fa8408SCameron Grant sc->irsz = 4; 527d5fa8408SCameron Grant sc->playint = NM2_PLAYBACK_INT; 528d5fa8408SCameron Grant sc->recint = NM2_RECORD_INT; 529d5fa8408SCameron Grant sc->misc1int = NM2_MISC_INT_1; 530d5fa8408SCameron Grant sc->misc2int = NM2_MISC_INT_2; 53110b23f4cSCameron Grant } else return -1; 532d5fa8408SCameron Grant sc->badintr = 0; 533d5fa8408SCameron Grant ofs = sc->buftop - 0x0400; 534d5fa8408SCameron Grant sc->buftop -= 0x1400; 535d5fa8408SCameron Grant 536d5fa8408SCameron Grant if ((nm_rdbuf(sc, ofs, 4) & NM_SIG_MASK) == NM_SIGNATURE) { 537d5fa8408SCameron Grant i = nm_rdbuf(sc, ofs + 4, 4); 538d5fa8408SCameron Grant if (i != 0 && i != 0xffffffff) 539d5fa8408SCameron Grant sc->buftop = i; 540d5fa8408SCameron Grant } 541d5fa8408SCameron Grant 542d5fa8408SCameron Grant sc->cbuf = sc->buftop - NM_MAX_COEFFICIENT; 543d5fa8408SCameron Grant sc->rbuf = sc->cbuf - NM_BUFFSIZE; 544d5fa8408SCameron Grant sc->pbuf = sc->rbuf - NM_BUFFSIZE; 545d5fa8408SCameron Grant sc->acbuf = sc->pbuf - (NM_TOTAL_COEFF_COUNT * 4); 546d5fa8408SCameron Grant 547d5fa8408SCameron Grant nm_wr(sc, 0, 0x11, 1); 548d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); 549d5fa8408SCameron Grant nm_wr(sc, 0x214, 0, 2); 550d5fa8408SCameron Grant 551d5fa8408SCameron Grant return 0; 552d5fa8408SCameron Grant } 553d5fa8408SCameron Grant 554d5fa8408SCameron Grant static int 555d5fa8408SCameron Grant nm_pci_probe(device_t dev) 556d5fa8408SCameron Grant { 557e572fcd4SGeorge C A Reid struct sc_info *sc = NULL; 558d5fa8408SCameron Grant char *s = NULL; 559e572fcd4SGeorge C A Reid u_int32_t subdev, i, data; 560d5fa8408SCameron Grant 561e7fb3296SCameron Grant subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); 562d5fa8408SCameron Grant switch (pci_get_devid(dev)) { 563d5fa8408SCameron Grant case NM256AV_PCI_ID: 564e7fb3296SCameron Grant i = 0; 565e7fb3296SCameron Grant while ((i < NUM_BADCARDS) && (badcards[i] != subdev)) 566e7fb3296SCameron Grant i++; 567e572fcd4SGeorge C A Reid 568e572fcd4SGeorge C A Reid /* Try to catch other non-ac97 cards */ 569e572fcd4SGeorge C A Reid 570e572fcd4SGeorge C A Reid if (i == NUM_BADCARDS) { 571733a4ea7SGeorge C A Reid if (!(sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO))) { 572e572fcd4SGeorge C A Reid device_printf(dev, "cannot allocate softc\n"); 573e572fcd4SGeorge C A Reid return ENXIO; 574e572fcd4SGeorge C A Reid } 575e572fcd4SGeorge C A Reid 576e572fcd4SGeorge C A Reid data = pci_read_config(dev, PCIR_COMMAND, 2); 577e572fcd4SGeorge C A Reid pci_write_config(dev, PCIR_COMMAND, data | 578e572fcd4SGeorge C A Reid PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | 579e572fcd4SGeorge C A Reid PCIM_CMD_BUSMASTEREN, 2); 580e572fcd4SGeorge C A Reid 581e572fcd4SGeorge C A Reid sc->regid = PCIR_MAPS + 4; 582e572fcd4SGeorge C A Reid sc->reg = bus_alloc_resource(dev, SYS_RES_MEMORY, 583e572fcd4SGeorge C A Reid &sc->regid, 0, ~0, 1, 584e572fcd4SGeorge C A Reid RF_ACTIVE); 585e572fcd4SGeorge C A Reid 586e572fcd4SGeorge C A Reid if (!sc->reg) { 587e572fcd4SGeorge C A Reid device_printf(dev, "unable to map register space\n"); 588e572fcd4SGeorge C A Reid pci_write_config(dev, PCIR_COMMAND, data, 2); 589e572fcd4SGeorge C A Reid free(sc, M_DEVBUF); 590e572fcd4SGeorge C A Reid return ENXIO; 591e572fcd4SGeorge C A Reid } 592e572fcd4SGeorge C A Reid 593e572fcd4SGeorge C A Reid if ((nm_rd(sc, NM_MIXER_PRESENCE, 2) & 594e572fcd4SGeorge C A Reid NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { 595e572fcd4SGeorge C A Reid i = 0; /* non-ac97 card, but not listed */ 596e572fcd4SGeorge C A Reid DEB(device_printf(dev, "subdev = 0x%x - badcard?\n", 597e572fcd4SGeorge C A Reid subdev)); 598e572fcd4SGeorge C A Reid } 599e572fcd4SGeorge C A Reid pci_write_config(dev, PCIR_COMMAND, data, 2); 600e572fcd4SGeorge C A Reid bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, 601e572fcd4SGeorge C A Reid sc->reg); 602e572fcd4SGeorge C A Reid free(sc, M_DEVBUF); 603e572fcd4SGeorge C A Reid } 604e572fcd4SGeorge C A Reid 605e7fb3296SCameron Grant if (i == NUM_BADCARDS) 606d5fa8408SCameron Grant s = "NeoMagic 256AV"; 6075b4c3f3cSCameron Grant DEB(else) 6085b4c3f3cSCameron Grant DEB(device_printf(dev, "this is a non-ac97 NM256AV, not attaching\n")); 609e572fcd4SGeorge C A Reid 610d5fa8408SCameron Grant break; 611d5fa8408SCameron Grant 612d5fa8408SCameron Grant case NM256ZX_PCI_ID: 613d5fa8408SCameron Grant s = "NeoMagic 256ZX"; 614d5fa8408SCameron Grant break; 615d5fa8408SCameron Grant } 616d5fa8408SCameron Grant 617d5fa8408SCameron Grant if (s) device_set_desc(dev, s); 618d5fa8408SCameron Grant return s? 0 : ENXIO; 619d5fa8408SCameron Grant } 620d5fa8408SCameron Grant 621d5fa8408SCameron Grant static int 622d5fa8408SCameron Grant nm_pci_attach(device_t dev) 623d5fa8408SCameron Grant { 624d5fa8408SCameron Grant u_int32_t data; 625d5fa8408SCameron Grant struct sc_info *sc; 626306f91b6SCameron Grant struct ac97_info *codec = 0; 627d5fa8408SCameron Grant char status[SND_STATUSLEN]; 628d5fa8408SCameron Grant 629733a4ea7SGeorge C A Reid if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { 630d5fa8408SCameron Grant device_printf(dev, "cannot allocate softc\n"); 631d5fa8408SCameron Grant return ENXIO; 632d5fa8408SCameron Grant } 633d5fa8408SCameron Grant 634d5fa8408SCameron Grant sc->dev = dev; 635d5fa8408SCameron Grant sc->type = pci_get_devid(dev); 636d5fa8408SCameron Grant 637d5fa8408SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 638d5fa8408SCameron Grant data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 639d5fa8408SCameron Grant pci_write_config(dev, PCIR_COMMAND, data, 2); 640d5fa8408SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 641d5fa8408SCameron Grant 642d5fa8408SCameron Grant sc->bufid = PCIR_MAPS; 643d5fa8408SCameron Grant sc->buf = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bufid, 644d5fa8408SCameron Grant 0, ~0, 1, RF_ACTIVE); 645d5fa8408SCameron Grant sc->regid = PCIR_MAPS + 4; 646d5fa8408SCameron Grant sc->reg = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->regid, 647d5fa8408SCameron Grant 0, ~0, 1, RF_ACTIVE); 648d5fa8408SCameron Grant 649d5fa8408SCameron Grant if (!sc->buf || !sc->reg) { 650d5fa8408SCameron Grant device_printf(dev, "unable to map register space\n"); 651d5fa8408SCameron Grant goto bad; 652d5fa8408SCameron Grant } 653d5fa8408SCameron Grant 654d5fa8408SCameron Grant if (nm_init(sc) == -1) { 655d5fa8408SCameron Grant device_printf(dev, "unable to initialize the card\n"); 656d5fa8408SCameron Grant goto bad; 657d5fa8408SCameron Grant } 658d5fa8408SCameron Grant 6590f55ac6cSCameron Grant codec = AC97_CREATE(dev, sc, nm_ac97); 660d5fa8408SCameron Grant if (codec == NULL) goto bad; 6610f55ac6cSCameron Grant if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; 662d5fa8408SCameron Grant 663d5fa8408SCameron Grant sc->irqid = 0; 664d5fa8408SCameron Grant sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 665d5fa8408SCameron Grant 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 66666ef8af5SCameron Grant if (!sc->irq || snd_setup_intr(dev, sc->irq, 0, nm_intr, sc, &sc->ih)) { 667d5fa8408SCameron Grant device_printf(dev, "unable to map interrupt\n"); 668d5fa8408SCameron Grant goto bad; 669d5fa8408SCameron Grant } 670d5fa8408SCameron Grant 671d5fa8408SCameron Grant snprintf(status, SND_STATUSLEN, "at memory 0x%lx, 0x%lx irq %ld", 672d5fa8408SCameron Grant rman_get_start(sc->buf), rman_get_start(sc->reg), 673d5fa8408SCameron Grant rman_get_start(sc->irq)); 674d5fa8408SCameron Grant 675d5fa8408SCameron Grant if (pcm_register(dev, sc, 1, 1)) goto bad; 6760f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_REC, &nmchan_class, sc); 6770f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_PLAY, &nmchan_class, sc); 678d5fa8408SCameron Grant pcm_setstatus(dev, status); 679d5fa8408SCameron Grant 680d5fa8408SCameron Grant return 0; 681d5fa8408SCameron Grant 682d5fa8408SCameron Grant bad: 683306f91b6SCameron Grant if (codec) ac97_destroy(codec); 684d5fa8408SCameron Grant if (sc->buf) bus_release_resource(dev, SYS_RES_MEMORY, sc->bufid, sc->buf); 685d5fa8408SCameron Grant if (sc->reg) bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); 686d5fa8408SCameron Grant if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); 687d5fa8408SCameron Grant if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 688d5fa8408SCameron Grant free(sc, M_DEVBUF); 689d5fa8408SCameron Grant return ENXIO; 690d5fa8408SCameron Grant } 691d5fa8408SCameron Grant 69239004e69SCameron Grant static int 693306f91b6SCameron Grant nm_pci_detach(device_t dev) 694306f91b6SCameron Grant { 695306f91b6SCameron Grant int r; 696306f91b6SCameron Grant struct sc_info *sc; 697306f91b6SCameron Grant 698306f91b6SCameron Grant r = pcm_unregister(dev); 699306f91b6SCameron Grant if (r) 700306f91b6SCameron Grant return r; 701306f91b6SCameron Grant 702306f91b6SCameron Grant sc = pcm_getdevinfo(dev); 703306f91b6SCameron Grant bus_release_resource(dev, SYS_RES_MEMORY, sc->bufid, sc->buf); 704306f91b6SCameron Grant bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); 705306f91b6SCameron Grant bus_teardown_intr(dev, sc->irq, sc->ih); 706306f91b6SCameron Grant bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 707306f91b6SCameron Grant free(sc, M_DEVBUF); 708306f91b6SCameron Grant 709306f91b6SCameron Grant return 0; 710306f91b6SCameron Grant } 711306f91b6SCameron Grant 712306f91b6SCameron Grant static int 71339004e69SCameron Grant nm_pci_resume(device_t dev) 71439004e69SCameron Grant { 71539004e69SCameron Grant struct sc_info *sc; 71639004e69SCameron Grant 71739004e69SCameron Grant sc = pcm_getdevinfo(dev); 71839004e69SCameron Grant 71939004e69SCameron Grant /* Reinit audio device */ 72039004e69SCameron Grant if (nm_init(sc) == -1) { 72139004e69SCameron Grant device_printf(dev, "unable to reinitialize the card\n"); 72239004e69SCameron Grant return ENXIO; 72339004e69SCameron Grant } 72439004e69SCameron Grant /* Reinit mixer */ 72533dbf14aSCameron Grant if (mixer_reinit(dev) == -1) { 72639004e69SCameron Grant device_printf(dev, "unable to reinitialize the mixer\n"); 72739004e69SCameron Grant return ENXIO; 72839004e69SCameron Grant } 72939004e69SCameron Grant return 0; 73039004e69SCameron Grant } 73139004e69SCameron Grant 732d5fa8408SCameron Grant static device_method_t nm_methods[] = { 733d5fa8408SCameron Grant /* Device interface */ 734d5fa8408SCameron Grant DEVMETHOD(device_probe, nm_pci_probe), 735d5fa8408SCameron Grant DEVMETHOD(device_attach, nm_pci_attach), 7367dfc9325SCameron Grant DEVMETHOD(device_detach, nm_pci_detach), 73739004e69SCameron Grant DEVMETHOD(device_resume, nm_pci_resume), 738d5fa8408SCameron Grant { 0, 0 } 739d5fa8408SCameron Grant }; 740d5fa8408SCameron Grant 741d5fa8408SCameron Grant static driver_t nm_driver = { 742d5fa8408SCameron Grant "pcm", 743d5fa8408SCameron Grant nm_methods, 74467b1dce3SCameron Grant PCM_SOFTC_SIZE, 745d5fa8408SCameron Grant }; 746d5fa8408SCameron Grant 747f314f3daSCameron Grant DRIVER_MODULE(snd_neomagic, pci, nm_driver, pcm_devclass, 0, 0); 748f314f3daSCameron Grant MODULE_DEPEND(snd_neomagic, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 749f314f3daSCameron Grant MODULE_VERSION(snd_neomagic, 1); 750