1098ca2bdSWarner Losh /*- 23f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 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 2990da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3090da2b28SAriff Abdullah #include "opt_snd.h" 3190da2b28SAriff Abdullah #endif 3290da2b28SAriff Abdullah 33d5fa8408SCameron Grant #include <dev/sound/pcm/sound.h> 34d5fa8408SCameron Grant #include <dev/sound/pcm/ac97.h> 35d5fa8408SCameron Grant #include <dev/sound/pci/neomagic.h> 36d5fa8408SCameron Grant #include <dev/sound/pci/neomagic-coeff.h> 37d5fa8408SCameron Grant 3890cf0136SWarner Losh #include <dev/pci/pcireg.h> 3990cf0136SWarner Losh #include <dev/pci/pcivar.h> 40d5fa8408SCameron Grant 4167b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 4267b1dce3SCameron Grant 43d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 44d5fa8408SCameron Grant 45d5fa8408SCameron Grant #define NM_BUFFSIZE 16384 46d5fa8408SCameron Grant 47d5fa8408SCameron Grant #define NM256AV_PCI_ID 0x800510c8 48d5fa8408SCameron Grant #define NM256ZX_PCI_ID 0x800610c8 49d5fa8408SCameron Grant 50d5fa8408SCameron Grant struct sc_info; 51d5fa8408SCameron Grant 52d5fa8408SCameron Grant /* channel registers */ 53d5fa8408SCameron Grant struct sc_chinfo { 5438c81beeSOrion Hodson int active, spd, dir, fmt; 5538c81beeSOrion Hodson u_int32_t blksize, wmark; 5666ef8af5SCameron Grant struct snd_dbuf *buffer; 5766ef8af5SCameron Grant struct pcm_channel *channel; 58d5fa8408SCameron Grant struct sc_info *parent; 59d5fa8408SCameron Grant }; 60d5fa8408SCameron Grant 61d5fa8408SCameron Grant /* device private data */ 62d5fa8408SCameron Grant struct sc_info { 63d5fa8408SCameron Grant device_t dev; 64d5fa8408SCameron Grant u_int32_t type; 65d5fa8408SCameron Grant 66d5fa8408SCameron Grant struct resource *reg, *irq, *buf; 67d5fa8408SCameron Grant int regid, irqid, bufid; 68d5fa8408SCameron Grant void *ih; 69d5fa8408SCameron Grant 70d5fa8408SCameron Grant u_int32_t ac97_base, ac97_status, ac97_busy; 71d5fa8408SCameron Grant u_int32_t buftop, pbuf, rbuf, cbuf, acbuf; 72d5fa8408SCameron Grant u_int32_t playint, recint, misc1int, misc2int; 73d5fa8408SCameron Grant u_int32_t irsz, badintr; 74d5fa8408SCameron Grant 75d5fa8408SCameron Grant struct sc_chinfo pch, rch; 76d5fa8408SCameron Grant }; 77d5fa8408SCameron Grant 78d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 79d5fa8408SCameron Grant 80d5fa8408SCameron Grant /* 81d5fa8408SCameron Grant * prototypes 82d5fa8408SCameron Grant */ 83d5fa8408SCameron Grant 84d5fa8408SCameron Grant /* stuff */ 85d5fa8408SCameron Grant static int nm_loadcoeff(struct sc_info *sc, int dir, int num); 86d5fa8408SCameron Grant static int nm_setch(struct sc_chinfo *ch); 87d5fa8408SCameron Grant static int nm_init(struct sc_info *); 88d5fa8408SCameron Grant static void nm_intr(void *); 89d5fa8408SCameron Grant 90d5fa8408SCameron Grant /* talk to the card */ 91d5fa8408SCameron Grant static u_int32_t nm_rd(struct sc_info *, int, int); 92d5fa8408SCameron Grant static void nm_wr(struct sc_info *, int, u_int32_t, int); 93d5fa8408SCameron Grant static u_int32_t nm_rdbuf(struct sc_info *, int, int); 94d5fa8408SCameron Grant static void nm_wrbuf(struct sc_info *, int, u_int32_t, int); 95d5fa8408SCameron Grant 96e7fb3296SCameron Grant static u_int32_t badcards[] = { 97e7fb3296SCameron Grant 0x0007103c, 98e7fb3296SCameron Grant 0x008f1028, 994873b46dSCameron Grant 0x00dd1014, 100e572fcd4SGeorge C A Reid 0x8005110a, 101e7fb3296SCameron Grant }; 102e7fb3296SCameron Grant #define NUM_BADCARDS (sizeof(badcards) / sizeof(u_int32_t)) 103e7fb3296SCameron Grant 104d5fa8408SCameron Grant /* The actual rates supported by the card. */ 105d5fa8408SCameron Grant static int samplerates[9] = { 106e7fb3296SCameron Grant 8000, 107e7fb3296SCameron Grant 11025, 108e7fb3296SCameron Grant 16000, 109e7fb3296SCameron Grant 22050, 110e7fb3296SCameron Grant 24000, 111e7fb3296SCameron Grant 32000, 112e7fb3296SCameron Grant 44100, 113e7fb3296SCameron Grant 48000, 114e7fb3296SCameron Grant 99999999 115d5fa8408SCameron Grant }; 116d5fa8408SCameron Grant 117d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 118d5fa8408SCameron Grant 119513693beSCameron Grant static u_int32_t nm_fmt[] = { 12090da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 1, 0), 12190da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 2, 0), 12290da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 1, 0), 12390da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 2, 0), 124513693beSCameron Grant 0 125d5fa8408SCameron Grant }; 12666ef8af5SCameron Grant static struct pcmchan_caps nm_caps = {4000, 48000, nm_fmt, 0}; 127d5fa8408SCameron Grant 128d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 129d5fa8408SCameron Grant 130d5fa8408SCameron Grant /* Hardware */ 131d5fa8408SCameron Grant static u_int32_t 132d5fa8408SCameron Grant nm_rd(struct sc_info *sc, int regno, int size) 133d5fa8408SCameron Grant { 134d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->reg); 135d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->reg); 136d5fa8408SCameron Grant 137d5fa8408SCameron Grant switch (size) { 138d5fa8408SCameron Grant case 1: 139d5fa8408SCameron Grant return bus_space_read_1(st, sh, regno); 140d5fa8408SCameron Grant case 2: 141d5fa8408SCameron Grant return bus_space_read_2(st, sh, regno); 142d5fa8408SCameron Grant case 4: 143d5fa8408SCameron Grant return bus_space_read_4(st, sh, regno); 144d5fa8408SCameron Grant default: 145d5fa8408SCameron Grant return 0xffffffff; 146d5fa8408SCameron Grant } 147d5fa8408SCameron Grant } 148d5fa8408SCameron Grant 149d5fa8408SCameron Grant static void 150d5fa8408SCameron Grant nm_wr(struct sc_info *sc, int regno, u_int32_t data, int size) 151d5fa8408SCameron Grant { 152d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->reg); 153d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->reg); 154d5fa8408SCameron Grant 155d5fa8408SCameron Grant switch (size) { 156d5fa8408SCameron Grant case 1: 157d5fa8408SCameron Grant bus_space_write_1(st, sh, regno, data); 158d5fa8408SCameron Grant break; 159d5fa8408SCameron Grant case 2: 160d5fa8408SCameron Grant bus_space_write_2(st, sh, regno, data); 161d5fa8408SCameron Grant break; 162d5fa8408SCameron Grant case 4: 163d5fa8408SCameron Grant bus_space_write_4(st, sh, regno, data); 164d5fa8408SCameron Grant break; 165d5fa8408SCameron Grant } 166d5fa8408SCameron Grant } 167d5fa8408SCameron Grant 168d5fa8408SCameron Grant static u_int32_t 169d5fa8408SCameron Grant nm_rdbuf(struct sc_info *sc, int regno, int size) 170d5fa8408SCameron Grant { 171d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->buf); 172d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->buf); 173d5fa8408SCameron Grant 174d5fa8408SCameron Grant switch (size) { 175d5fa8408SCameron Grant case 1: 176d5fa8408SCameron Grant return bus_space_read_1(st, sh, regno); 177d5fa8408SCameron Grant case 2: 178d5fa8408SCameron Grant return bus_space_read_2(st, sh, regno); 179d5fa8408SCameron Grant case 4: 180d5fa8408SCameron Grant return bus_space_read_4(st, sh, regno); 181d5fa8408SCameron Grant default: 182d5fa8408SCameron Grant return 0xffffffff; 183d5fa8408SCameron Grant } 184d5fa8408SCameron Grant } 185d5fa8408SCameron Grant 186d5fa8408SCameron Grant static void 187d5fa8408SCameron Grant nm_wrbuf(struct sc_info *sc, int regno, u_int32_t data, int size) 188d5fa8408SCameron Grant { 189d5fa8408SCameron Grant bus_space_tag_t st = rman_get_bustag(sc->buf); 190d5fa8408SCameron Grant bus_space_handle_t sh = rman_get_bushandle(sc->buf); 191d5fa8408SCameron Grant 192d5fa8408SCameron Grant switch (size) { 193d5fa8408SCameron Grant case 1: 194d5fa8408SCameron Grant bus_space_write_1(st, sh, regno, data); 195d5fa8408SCameron Grant break; 196d5fa8408SCameron Grant case 2: 197d5fa8408SCameron Grant bus_space_write_2(st, sh, regno, data); 198d5fa8408SCameron Grant break; 199d5fa8408SCameron Grant case 4: 200d5fa8408SCameron Grant bus_space_write_4(st, sh, regno, data); 201d5fa8408SCameron Grant break; 202d5fa8408SCameron Grant } 203d5fa8408SCameron Grant } 204d5fa8408SCameron Grant 2050f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 206d5fa8408SCameron Grant /* ac97 codec */ 207d5fa8408SCameron Grant static int 208d5fa8408SCameron Grant nm_waitcd(struct sc_info *sc) 209d5fa8408SCameron Grant { 210d5fa8408SCameron Grant int cnt = 10; 21138c81beeSOrion Hodson int fail = 1; 212d5fa8408SCameron Grant 213d5fa8408SCameron Grant while (cnt-- > 0) { 21438c81beeSOrion Hodson if (nm_rd(sc, sc->ac97_status, 2) & sc->ac97_busy) { 215d5fa8408SCameron Grant DELAY(100); 21638c81beeSOrion Hodson } else { 21738c81beeSOrion Hodson fail = 0; 218d5fa8408SCameron Grant break; 219d5fa8408SCameron Grant } 22038c81beeSOrion Hodson } 22138c81beeSOrion Hodson return (fail); 222d5fa8408SCameron Grant } 223d5fa8408SCameron Grant 224d5fa8408SCameron Grant static u_int32_t 2250f55ac6cSCameron Grant nm_initcd(kobj_t obj, void *devinfo) 22639004e69SCameron Grant { 22739004e69SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 22839004e69SCameron Grant 22939004e69SCameron Grant nm_wr(sc, 0x6c0, 0x01, 1); 230df54be70SAlexander Leidinger #if 0 231df54be70SAlexander Leidinger /* 232df54be70SAlexander Leidinger * The following code-line may cause a hang for some chipsets, see 233df54be70SAlexander Leidinger * PR 56617. 234df54be70SAlexander Leidinger * In case of a bugreport without this line have a look at the PR and 235df54be70SAlexander Leidinger * conditionize the code-line based upon the specific version of 236df54be70SAlexander Leidinger * the chip. 237df54be70SAlexander Leidinger */ 23839004e69SCameron Grant nm_wr(sc, 0x6cc, 0x87, 1); 239df54be70SAlexander Leidinger #endif 24039004e69SCameron Grant nm_wr(sc, 0x6cc, 0x80, 1); 24139004e69SCameron Grant nm_wr(sc, 0x6cc, 0x00, 1); 242cd2c103aSCameron Grant return 1; 24339004e69SCameron Grant } 24439004e69SCameron Grant 2450f55ac6cSCameron Grant static int 2460f55ac6cSCameron Grant nm_rdcd(kobj_t obj, void *devinfo, int regno) 247d5fa8408SCameron Grant { 248d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 249d5fa8408SCameron Grant u_int32_t x; 250d5fa8408SCameron Grant 251d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 252d5fa8408SCameron Grant x = nm_rd(sc, sc->ac97_base + regno, 2); 253d5fa8408SCameron Grant DELAY(1000); 254d5fa8408SCameron Grant return x; 255d5fa8408SCameron Grant } else { 256d5fa8408SCameron Grant device_printf(sc->dev, "ac97 codec not ready\n"); 2570f55ac6cSCameron Grant return -1; 258d5fa8408SCameron Grant } 259d5fa8408SCameron Grant } 260d5fa8408SCameron Grant 2610f55ac6cSCameron Grant static int 2620f55ac6cSCameron Grant nm_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 263d5fa8408SCameron Grant { 264d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 265d5fa8408SCameron Grant int cnt = 3; 266d5fa8408SCameron Grant 267d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 268d5fa8408SCameron Grant while (cnt-- > 0) { 269d5fa8408SCameron Grant nm_wr(sc, sc->ac97_base + regno, data, 2); 270d5fa8408SCameron Grant if (!nm_waitcd(sc)) { 271d5fa8408SCameron Grant DELAY(1000); 2720f55ac6cSCameron Grant return 0; 273d5fa8408SCameron Grant } 274d5fa8408SCameron Grant } 275d5fa8408SCameron Grant } 276d5fa8408SCameron Grant device_printf(sc->dev, "ac97 codec not ready\n"); 2770f55ac6cSCameron Grant return -1; 278d5fa8408SCameron Grant } 279d5fa8408SCameron Grant 2800f55ac6cSCameron Grant static kobj_method_t nm_ac97_methods[] = { 2810f55ac6cSCameron Grant KOBJMETHOD(ac97_init, nm_initcd), 2820f55ac6cSCameron Grant KOBJMETHOD(ac97_read, nm_rdcd), 2830f55ac6cSCameron Grant KOBJMETHOD(ac97_write, nm_wrcd), 28490da2b28SAriff Abdullah KOBJMETHOD_END 2850f55ac6cSCameron Grant }; 2860f55ac6cSCameron Grant AC97_DECLARE(nm_ac97); 2870f55ac6cSCameron Grant 2880f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 2890f55ac6cSCameron Grant 290d5fa8408SCameron Grant static void 291d5fa8408SCameron Grant nm_ackint(struct sc_info *sc, u_int32_t num) 292d5fa8408SCameron Grant { 293d5fa8408SCameron Grant if (sc->type == NM256AV_PCI_ID) { 294d5fa8408SCameron Grant nm_wr(sc, NM_INT_REG, num << 1, 2); 295d5fa8408SCameron Grant } else if (sc->type == NM256ZX_PCI_ID) { 296d5fa8408SCameron Grant nm_wr(sc, NM_INT_REG, num, 4); 297d5fa8408SCameron Grant } 298d5fa8408SCameron Grant } 299d5fa8408SCameron Grant 300d5fa8408SCameron Grant static int 301d5fa8408SCameron Grant nm_loadcoeff(struct sc_info *sc, int dir, int num) 302d5fa8408SCameron Grant { 303d5fa8408SCameron Grant int ofs, sz, i; 304d5fa8408SCameron Grant u_int32_t addr; 305d5fa8408SCameron Grant 306d5fa8408SCameron Grant addr = (dir == PCMDIR_PLAY)? 0x01c : 0x21c; 307d5fa8408SCameron Grant if (dir == PCMDIR_REC) 308d5fa8408SCameron Grant num += 8; 309d5fa8408SCameron Grant sz = coefficientSizes[num]; 310d5fa8408SCameron Grant ofs = 0; 311d5fa8408SCameron Grant while (num-- > 0) 312d5fa8408SCameron Grant ofs+= coefficientSizes[num]; 313d5fa8408SCameron Grant for (i = 0; i < sz; i++) 314d5fa8408SCameron Grant nm_wrbuf(sc, sc->cbuf + i, coefficients[ofs + i], 1); 315d5fa8408SCameron Grant nm_wr(sc, addr, sc->cbuf, 4); 316d5fa8408SCameron Grant if (dir == PCMDIR_PLAY) 317d5fa8408SCameron Grant sz--; 318d5fa8408SCameron Grant nm_wr(sc, addr + 4, sc->cbuf + sz, 4); 319d5fa8408SCameron Grant return 0; 320d5fa8408SCameron Grant } 321d5fa8408SCameron Grant 322d5fa8408SCameron Grant static int 323d5fa8408SCameron Grant nm_setch(struct sc_chinfo *ch) 324d5fa8408SCameron Grant { 325d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 326d5fa8408SCameron Grant u_int32_t base; 327d5fa8408SCameron Grant u_int8_t x; 328d5fa8408SCameron Grant 329d5fa8408SCameron Grant for (x = 0; x < 8; x++) 330d5fa8408SCameron Grant if (ch->spd < (samplerates[x] + samplerates[x + 1]) / 2) 331d5fa8408SCameron Grant break; 332d5fa8408SCameron Grant 333d5fa8408SCameron Grant if (x == 8) return 1; 334d5fa8408SCameron Grant 335d5fa8408SCameron Grant ch->spd = samplerates[x]; 336d5fa8408SCameron Grant nm_loadcoeff(sc, ch->dir, x); 337d5fa8408SCameron Grant 338d5fa8408SCameron Grant x <<= 4; 339d5fa8408SCameron Grant x &= NM_RATE_MASK; 340d5fa8408SCameron Grant if (ch->fmt & AFMT_16BIT) x |= NM_RATE_BITS_16; 34190da2b28SAriff Abdullah if (AFMT_CHANNEL(ch->fmt) > 1) x |= NM_RATE_STEREO; 342d5fa8408SCameron Grant 343d5fa8408SCameron Grant base = (ch->dir == PCMDIR_PLAY)? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET; 344d5fa8408SCameron Grant nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1); 345d5fa8408SCameron Grant return 0; 346d5fa8408SCameron Grant } 347d5fa8408SCameron Grant 348d5fa8408SCameron Grant /* channel interface */ 349d5fa8408SCameron Grant static void * 35066ef8af5SCameron Grant nmchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 351d5fa8408SCameron Grant { 352d5fa8408SCameron Grant struct sc_info *sc = devinfo; 353d5fa8408SCameron Grant struct sc_chinfo *ch; 354d5fa8408SCameron Grant u_int32_t chnbuf; 355d5fa8408SCameron Grant 356d5fa8408SCameron Grant chnbuf = (dir == PCMDIR_PLAY)? sc->pbuf : sc->rbuf; 357d5fa8408SCameron Grant ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; 35838c81beeSOrion Hodson ch->active = 0; 35938c81beeSOrion Hodson ch->blksize = 0; 36038c81beeSOrion Hodson ch->wmark = 0; 361d5fa8408SCameron Grant ch->buffer = b; 362350a5fafSCameron Grant sndbuf_setup(ch->buffer, (u_int8_t *)rman_get_virtual(sc->buf) + chnbuf, NM_BUFFSIZE); 3635b4c3f3cSCameron Grant if (bootverbose) 3645b4c3f3cSCameron Grant device_printf(sc->dev, "%s buf %p\n", (dir == PCMDIR_PLAY)? 36566ef8af5SCameron Grant "play" : "rec", sndbuf_getbuf(ch->buffer)); 366d5fa8408SCameron Grant ch->parent = sc; 367d5fa8408SCameron Grant ch->channel = c; 368d5fa8408SCameron Grant ch->dir = dir; 369d5fa8408SCameron Grant return ch; 370d5fa8408SCameron Grant } 371d5fa8408SCameron Grant 372d5fa8408SCameron Grant static int 3730f55ac6cSCameron Grant nmchan_free(kobj_t obj, void *data) 37433dbf14aSCameron Grant { 37533dbf14aSCameron Grant return 0; 37633dbf14aSCameron Grant } 37733dbf14aSCameron Grant 37833dbf14aSCameron Grant static int 3790f55ac6cSCameron Grant nmchan_setformat(kobj_t obj, void *data, u_int32_t format) 380d5fa8408SCameron Grant { 381d5fa8408SCameron Grant struct sc_chinfo *ch = data; 382d5fa8408SCameron Grant 383d5fa8408SCameron Grant ch->fmt = format; 384d5fa8408SCameron Grant return nm_setch(ch); 385d5fa8408SCameron Grant } 386d5fa8408SCameron Grant 38790da2b28SAriff Abdullah static u_int32_t 3880f55ac6cSCameron Grant nmchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 389d5fa8408SCameron Grant { 390d5fa8408SCameron Grant struct sc_chinfo *ch = data; 391d5fa8408SCameron Grant 392d5fa8408SCameron Grant ch->spd = speed; 3934ee07471SCameron Grant return nm_setch(ch)? 0 : ch->spd; 394d5fa8408SCameron Grant } 395d5fa8408SCameron Grant 39690da2b28SAriff Abdullah static u_int32_t 3970f55ac6cSCameron Grant nmchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 398d5fa8408SCameron Grant { 39938c81beeSOrion Hodson struct sc_chinfo *ch = data; 40038c81beeSOrion Hodson 40138c81beeSOrion Hodson ch->blksize = blocksize; 40238c81beeSOrion Hodson 403d5fa8408SCameron Grant return blocksize; 404d5fa8408SCameron Grant } 405d5fa8408SCameron Grant 406d5fa8408SCameron Grant static int 4070f55ac6cSCameron Grant nmchan_trigger(kobj_t obj, void *data, int go) 408d5fa8408SCameron Grant { 409d5fa8408SCameron Grant struct sc_chinfo *ch = data; 410d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 411d5fa8408SCameron Grant int ssz; 412d5fa8408SCameron Grant 413bdfbdcecSAriff Abdullah if (!PCMTRIG_COMMON(go)) 41435f9e4a1SCameron Grant return 0; 415d5fa8408SCameron Grant 416d5fa8408SCameron Grant ssz = (ch->fmt & AFMT_16BIT)? 2 : 1; 41790da2b28SAriff Abdullah if (AFMT_CHANNEL(ch->fmt) > 1) 418d5fa8408SCameron Grant ssz <<= 1; 419d5fa8408SCameron Grant 420d5fa8408SCameron Grant if (ch->dir == PCMDIR_PLAY) { 421d5fa8408SCameron Grant if (go == PCMTRIG_START) { 42238c81beeSOrion Hodson ch->active = 1; 42338c81beeSOrion Hodson ch->wmark = ch->blksize; 424d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_START, sc->pbuf, 4); 425d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_END, sc->pbuf + NM_BUFFSIZE - ssz, 4); 426d5fa8408SCameron Grant nm_wr(sc, NM_PBUFFER_CURRP, sc->pbuf, 4); 42738c81beeSOrion Hodson nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + ch->wmark, 4); 428d5fa8408SCameron Grant nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | 429d5fa8408SCameron Grant NM_PLAYBACK_ENABLE_FLAG, 1); 430d5fa8408SCameron Grant nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); 431d5fa8408SCameron Grant } else { 43238c81beeSOrion Hodson ch->active = 0; 433d5fa8408SCameron Grant nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); 434d5fa8408SCameron Grant nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); 435d5fa8408SCameron Grant } 436d5fa8408SCameron Grant } else { 437d5fa8408SCameron Grant if (go == PCMTRIG_START) { 43838c81beeSOrion Hodson ch->active = 1; 43938c81beeSOrion Hodson ch->wmark = ch->blksize; 440d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN | 441d5fa8408SCameron Grant NM_RECORD_ENABLE_FLAG, 1); 442d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_START, sc->rbuf, 4); 443d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_END, sc->rbuf + NM_BUFFSIZE, 4); 444d5fa8408SCameron Grant nm_wr(sc, NM_RBUFFER_CURRP, sc->rbuf, 4); 44538c81beeSOrion Hodson nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + ch->wmark, 4); 446d5fa8408SCameron Grant } else { 44738c81beeSOrion Hodson ch->active = 0; 448d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); 449d5fa8408SCameron Grant } 450d5fa8408SCameron Grant } 451d5fa8408SCameron Grant return 0; 452d5fa8408SCameron Grant } 453d5fa8408SCameron Grant 45490da2b28SAriff Abdullah static u_int32_t 4550f55ac6cSCameron Grant nmchan_getptr(kobj_t obj, void *data) 456d5fa8408SCameron Grant { 457d5fa8408SCameron Grant struct sc_chinfo *ch = data; 458d5fa8408SCameron Grant struct sc_info *sc = ch->parent; 459d5fa8408SCameron Grant 460d5fa8408SCameron Grant if (ch->dir == PCMDIR_PLAY) 461d5fa8408SCameron Grant return nm_rd(sc, NM_PBUFFER_CURRP, 4) - sc->pbuf; 462d5fa8408SCameron Grant else 463d5fa8408SCameron Grant return nm_rd(sc, NM_RBUFFER_CURRP, 4) - sc->rbuf; 464d5fa8408SCameron Grant } 465d5fa8408SCameron Grant 46666ef8af5SCameron Grant static struct pcmchan_caps * 4670f55ac6cSCameron Grant nmchan_getcaps(kobj_t obj, void *data) 468d5fa8408SCameron Grant { 469d5fa8408SCameron Grant return &nm_caps; 470d5fa8408SCameron Grant } 471d5fa8408SCameron Grant 4720f55ac6cSCameron Grant static kobj_method_t nmchan_methods[] = { 4730f55ac6cSCameron Grant KOBJMETHOD(channel_init, nmchan_init), 4740f55ac6cSCameron Grant KOBJMETHOD(channel_free, nmchan_free), 4750f55ac6cSCameron Grant KOBJMETHOD(channel_setformat, nmchan_setformat), 4760f55ac6cSCameron Grant KOBJMETHOD(channel_setspeed, nmchan_setspeed), 4770f55ac6cSCameron Grant KOBJMETHOD(channel_setblocksize, nmchan_setblocksize), 4780f55ac6cSCameron Grant KOBJMETHOD(channel_trigger, nmchan_trigger), 4790f55ac6cSCameron Grant KOBJMETHOD(channel_getptr, nmchan_getptr), 4800f55ac6cSCameron Grant KOBJMETHOD(channel_getcaps, nmchan_getcaps), 48190da2b28SAriff Abdullah KOBJMETHOD_END 4820f55ac6cSCameron Grant }; 4830f55ac6cSCameron Grant CHANNEL_DECLARE(nmchan); 4840f55ac6cSCameron Grant 485d5fa8408SCameron Grant /* The interrupt handler */ 486d5fa8408SCameron Grant static void 487d5fa8408SCameron Grant nm_intr(void *p) 488d5fa8408SCameron Grant { 489d5fa8408SCameron Grant struct sc_info *sc = (struct sc_info *)p; 4904873b46dSCameron Grant int status, x; 491d5fa8408SCameron Grant 492d5fa8408SCameron Grant status = nm_rd(sc, NM_INT_REG, sc->irsz); 4934873b46dSCameron Grant if (status == 0) 494d5fa8408SCameron Grant return; 495d5fa8408SCameron Grant 496d5fa8408SCameron Grant if (status & sc->playint) { 497d5fa8408SCameron Grant status &= ~sc->playint; 49838c81beeSOrion Hodson sc->pch.wmark += sc->pch.blksize; 49938c81beeSOrion Hodson sc->pch.wmark %= NM_BUFFSIZE; 50038c81beeSOrion Hodson nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + sc->pch.wmark, 4); 50138c81beeSOrion Hodson 502d5fa8408SCameron Grant nm_ackint(sc, sc->playint); 503d5fa8408SCameron Grant chn_intr(sc->pch.channel); 504d5fa8408SCameron Grant } 505d5fa8408SCameron Grant if (status & sc->recint) { 506d5fa8408SCameron Grant status &= ~sc->recint; 50738c81beeSOrion Hodson sc->rch.wmark += sc->rch.blksize; 50838c81beeSOrion Hodson sc->rch.wmark %= NM_BUFFSIZE; 50938c81beeSOrion Hodson nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + sc->rch.wmark, 4); 51038c81beeSOrion Hodson 511d5fa8408SCameron Grant nm_ackint(sc, sc->recint); 512d5fa8408SCameron Grant chn_intr(sc->rch.channel); 513d5fa8408SCameron Grant } 514d5fa8408SCameron Grant if (status & sc->misc1int) { 515d5fa8408SCameron Grant status &= ~sc->misc1int; 516d5fa8408SCameron Grant nm_ackint(sc, sc->misc1int); 517d5fa8408SCameron Grant x = nm_rd(sc, 0x400, 1); 518d5fa8408SCameron Grant nm_wr(sc, 0x400, x | 2, 1); 519d5fa8408SCameron Grant device_printf(sc->dev, "misc int 1\n"); 520d5fa8408SCameron Grant } 521d5fa8408SCameron Grant if (status & sc->misc2int) { 522d5fa8408SCameron Grant status &= ~sc->misc2int; 523d5fa8408SCameron Grant nm_ackint(sc, sc->misc2int); 524d5fa8408SCameron Grant x = nm_rd(sc, 0x400, 1); 525d5fa8408SCameron Grant nm_wr(sc, 0x400, x & ~2, 1); 526d5fa8408SCameron Grant device_printf(sc->dev, "misc int 2\n"); 527d5fa8408SCameron Grant } 528d5fa8408SCameron Grant if (status) { 5294873b46dSCameron Grant nm_ackint(sc, status); 530d5fa8408SCameron Grant device_printf(sc->dev, "unknown int\n"); 531d5fa8408SCameron Grant } 532d5fa8408SCameron Grant } 533d5fa8408SCameron Grant 534d5fa8408SCameron Grant /* -------------------------------------------------------------------- */ 535d5fa8408SCameron Grant 536d5fa8408SCameron Grant /* 537d5fa8408SCameron Grant * Probe and attach the card 538d5fa8408SCameron Grant */ 539d5fa8408SCameron Grant 540d5fa8408SCameron Grant static int 541d5fa8408SCameron Grant nm_init(struct sc_info *sc) 542d5fa8408SCameron Grant { 543d5fa8408SCameron Grant u_int32_t ofs, i; 544d5fa8408SCameron Grant 545d5fa8408SCameron Grant if (sc->type == NM256AV_PCI_ID) { 546d5fa8408SCameron Grant sc->ac97_base = NM_MIXER_OFFSET; 547d5fa8408SCameron Grant sc->ac97_status = NM_MIXER_STATUS_OFFSET; 548d5fa8408SCameron Grant sc->ac97_busy = NM_MIXER_READY_MASK; 549d5fa8408SCameron Grant 550d5fa8408SCameron Grant sc->buftop = 2560 * 1024; 551d5fa8408SCameron Grant 552d5fa8408SCameron Grant sc->irsz = 2; 553d5fa8408SCameron Grant sc->playint = NM_PLAYBACK_INT; 554d5fa8408SCameron Grant sc->recint = NM_RECORD_INT; 555d5fa8408SCameron Grant sc->misc1int = NM_MISC_INT_1; 556d5fa8408SCameron Grant sc->misc2int = NM_MISC_INT_2; 557d5fa8408SCameron Grant } else if (sc->type == NM256ZX_PCI_ID) { 558d5fa8408SCameron Grant sc->ac97_base = NM_MIXER_OFFSET; 559d5fa8408SCameron Grant sc->ac97_status = NM2_MIXER_STATUS_OFFSET; 560d5fa8408SCameron Grant sc->ac97_busy = NM2_MIXER_READY_MASK; 561d5fa8408SCameron Grant 562d5fa8408SCameron Grant sc->buftop = (nm_rd(sc, 0xa0b, 2)? 6144 : 4096) * 1024; 563d5fa8408SCameron Grant 564d5fa8408SCameron Grant sc->irsz = 4; 565d5fa8408SCameron Grant sc->playint = NM2_PLAYBACK_INT; 566d5fa8408SCameron Grant sc->recint = NM2_RECORD_INT; 567d5fa8408SCameron Grant sc->misc1int = NM2_MISC_INT_1; 568d5fa8408SCameron Grant sc->misc2int = NM2_MISC_INT_2; 56910b23f4cSCameron Grant } else return -1; 570d5fa8408SCameron Grant sc->badintr = 0; 571d5fa8408SCameron Grant ofs = sc->buftop - 0x0400; 572d5fa8408SCameron Grant sc->buftop -= 0x1400; 573d5fa8408SCameron Grant 57438c81beeSOrion Hodson if (bootverbose) 57538c81beeSOrion Hodson device_printf(sc->dev, "buftop is 0x%08x\n", sc->buftop); 576d5fa8408SCameron Grant if ((nm_rdbuf(sc, ofs, 4) & NM_SIG_MASK) == NM_SIGNATURE) { 577d5fa8408SCameron Grant i = nm_rdbuf(sc, ofs + 4, 4); 57838c81beeSOrion Hodson if (i != 0 && i != 0xffffffff) { 57938c81beeSOrion Hodson if (bootverbose) 58038c81beeSOrion Hodson device_printf(sc->dev, "buftop is changed to 0x%08x\n", i); 581d5fa8408SCameron Grant sc->buftop = i; 582d5fa8408SCameron Grant } 58338c81beeSOrion Hodson } 584d5fa8408SCameron Grant 585d5fa8408SCameron Grant sc->cbuf = sc->buftop - NM_MAX_COEFFICIENT; 586d5fa8408SCameron Grant sc->rbuf = sc->cbuf - NM_BUFFSIZE; 587d5fa8408SCameron Grant sc->pbuf = sc->rbuf - NM_BUFFSIZE; 588d5fa8408SCameron Grant sc->acbuf = sc->pbuf - (NM_TOTAL_COEFF_COUNT * 4); 589d5fa8408SCameron Grant 590d5fa8408SCameron Grant nm_wr(sc, 0, 0x11, 1); 591d5fa8408SCameron Grant nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); 592d5fa8408SCameron Grant nm_wr(sc, 0x214, 0, 2); 593d5fa8408SCameron Grant 594d5fa8408SCameron Grant return 0; 595d5fa8408SCameron Grant } 596d5fa8408SCameron Grant 597d5fa8408SCameron Grant static int 598d5fa8408SCameron Grant nm_pci_probe(device_t dev) 599d5fa8408SCameron Grant { 600e572fcd4SGeorge C A Reid struct sc_info *sc = NULL; 601d5fa8408SCameron Grant char *s = NULL; 602e572fcd4SGeorge C A Reid u_int32_t subdev, i, data; 603d5fa8408SCameron Grant 604e7fb3296SCameron Grant subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); 605d5fa8408SCameron Grant switch (pci_get_devid(dev)) { 606d5fa8408SCameron Grant case NM256AV_PCI_ID: 607e7fb3296SCameron Grant i = 0; 608e7fb3296SCameron Grant while ((i < NUM_BADCARDS) && (badcards[i] != subdev)) 609e7fb3296SCameron Grant i++; 610e572fcd4SGeorge C A Reid 611e572fcd4SGeorge C A Reid /* Try to catch other non-ac97 cards */ 612e572fcd4SGeorge C A Reid 613e572fcd4SGeorge C A Reid if (i == NUM_BADCARDS) { 614733a4ea7SGeorge C A Reid if (!(sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO))) { 615e572fcd4SGeorge C A Reid device_printf(dev, "cannot allocate softc\n"); 616e572fcd4SGeorge C A Reid return ENXIO; 617e572fcd4SGeorge C A Reid } 618e572fcd4SGeorge C A Reid 619e572fcd4SGeorge C A Reid data = pci_read_config(dev, PCIR_COMMAND, 2); 620e572fcd4SGeorge C A Reid pci_write_config(dev, PCIR_COMMAND, data | 621e572fcd4SGeorge C A Reid PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | 622e572fcd4SGeorge C A Reid PCIM_CMD_BUSMASTEREN, 2); 623e572fcd4SGeorge C A Reid 624e27951b2SJohn Baldwin sc->regid = PCIR_BAR(1); 6255f96beb9SNate Lawson sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 6265f96beb9SNate Lawson &sc->regid, 627e572fcd4SGeorge C A Reid RF_ACTIVE); 628e572fcd4SGeorge C A Reid 629e572fcd4SGeorge C A Reid if (!sc->reg) { 630e572fcd4SGeorge C A Reid device_printf(dev, "unable to map register space\n"); 631e572fcd4SGeorge C A Reid pci_write_config(dev, PCIR_COMMAND, data, 2); 632e572fcd4SGeorge C A Reid free(sc, M_DEVBUF); 633e572fcd4SGeorge C A Reid return ENXIO; 634e572fcd4SGeorge C A Reid } 635e572fcd4SGeorge C A Reid 63638c81beeSOrion Hodson /* 63738c81beeSOrion Hodson * My Panasonic CF-M2EV needs resetting device 63838c81beeSOrion Hodson * before checking mixer is present or not. 63938c81beeSOrion Hodson * t.ichinoseki@nifty.com. 64038c81beeSOrion Hodson */ 64138c81beeSOrion Hodson nm_wr(sc, 0, 0x11, 1); /* reset device */ 642e572fcd4SGeorge C A Reid if ((nm_rd(sc, NM_MIXER_PRESENCE, 2) & 643e572fcd4SGeorge C A Reid NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { 644e572fcd4SGeorge C A Reid i = 0; /* non-ac97 card, but not listed */ 645e572fcd4SGeorge C A Reid DEB(device_printf(dev, "subdev = 0x%x - badcard?\n", 646e572fcd4SGeorge C A Reid subdev)); 647e572fcd4SGeorge C A Reid } 648e572fcd4SGeorge C A Reid pci_write_config(dev, PCIR_COMMAND, data, 2); 649e572fcd4SGeorge C A Reid bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, 650e572fcd4SGeorge C A Reid sc->reg); 651e572fcd4SGeorge C A Reid free(sc, M_DEVBUF); 652e572fcd4SGeorge C A Reid } 653e572fcd4SGeorge C A Reid 654e7fb3296SCameron Grant if (i == NUM_BADCARDS) 655d5fa8408SCameron Grant s = "NeoMagic 256AV"; 6565b4c3f3cSCameron Grant DEB(else) 6575b4c3f3cSCameron Grant DEB(device_printf(dev, "this is a non-ac97 NM256AV, not attaching\n")); 658e572fcd4SGeorge C A Reid 659d5fa8408SCameron Grant break; 660d5fa8408SCameron Grant 661d5fa8408SCameron Grant case NM256ZX_PCI_ID: 662d5fa8408SCameron Grant s = "NeoMagic 256ZX"; 663d5fa8408SCameron Grant break; 664d5fa8408SCameron Grant } 665d5fa8408SCameron Grant 666d5fa8408SCameron Grant if (s) device_set_desc(dev, s); 667d5fa8408SCameron Grant return s? 0 : ENXIO; 668d5fa8408SCameron Grant } 669d5fa8408SCameron Grant 670d5fa8408SCameron Grant static int 671d5fa8408SCameron Grant nm_pci_attach(device_t dev) 672d5fa8408SCameron Grant { 673d5fa8408SCameron Grant u_int32_t data; 674d5fa8408SCameron Grant struct sc_info *sc; 675306f91b6SCameron Grant struct ac97_info *codec = 0; 676d5fa8408SCameron Grant char status[SND_STATUSLEN]; 677d5fa8408SCameron Grant 678082f6383SAriff Abdullah sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 679d5fa8408SCameron Grant sc->dev = dev; 680d5fa8408SCameron Grant sc->type = pci_get_devid(dev); 681d5fa8408SCameron Grant 682d5fa8408SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 683d5fa8408SCameron Grant data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 684d5fa8408SCameron Grant pci_write_config(dev, PCIR_COMMAND, data, 2); 685d5fa8408SCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 686d5fa8408SCameron Grant 687e27951b2SJohn Baldwin sc->bufid = PCIR_BAR(0); 6885f96beb9SNate Lawson sc->buf = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->bufid, 6895f96beb9SNate Lawson RF_ACTIVE); 690e27951b2SJohn Baldwin sc->regid = PCIR_BAR(1); 6915f96beb9SNate Lawson sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->regid, 6925f96beb9SNate Lawson RF_ACTIVE); 693d5fa8408SCameron Grant 694d5fa8408SCameron Grant if (!sc->buf || !sc->reg) { 695d5fa8408SCameron Grant device_printf(dev, "unable to map register space\n"); 696d5fa8408SCameron Grant goto bad; 697d5fa8408SCameron Grant } 698d5fa8408SCameron Grant 699d5fa8408SCameron Grant if (nm_init(sc) == -1) { 700d5fa8408SCameron Grant device_printf(dev, "unable to initialize the card\n"); 701d5fa8408SCameron Grant goto bad; 702d5fa8408SCameron Grant } 703d5fa8408SCameron Grant 7040f55ac6cSCameron Grant codec = AC97_CREATE(dev, sc, nm_ac97); 705d5fa8408SCameron Grant if (codec == NULL) goto bad; 7060f55ac6cSCameron Grant if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; 707d5fa8408SCameron Grant 708d5fa8408SCameron Grant sc->irqid = 0; 7095f96beb9SNate Lawson sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, 7105f96beb9SNate Lawson RF_ACTIVE | RF_SHAREABLE); 71166ef8af5SCameron Grant if (!sc->irq || snd_setup_intr(dev, sc->irq, 0, nm_intr, sc, &sc->ih)) { 712d5fa8408SCameron Grant device_printf(dev, "unable to map interrupt\n"); 713d5fa8408SCameron Grant goto bad; 714d5fa8408SCameron Grant } 715d5fa8408SCameron Grant 7160d8ed52eSMathew Kanner snprintf(status, SND_STATUSLEN, "at memory 0x%lx, 0x%lx irq %ld %s", 717d5fa8408SCameron Grant rman_get_start(sc->buf), rman_get_start(sc->reg), 7180d8ed52eSMathew Kanner rman_get_start(sc->irq),PCM_KLDSTRING(snd_neomagic)); 719d5fa8408SCameron Grant 720d5fa8408SCameron Grant if (pcm_register(dev, sc, 1, 1)) goto bad; 7210f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_REC, &nmchan_class, sc); 7220f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_PLAY, &nmchan_class, sc); 723d5fa8408SCameron Grant pcm_setstatus(dev, status); 724d5fa8408SCameron Grant 725d5fa8408SCameron Grant return 0; 726d5fa8408SCameron Grant 727d5fa8408SCameron Grant bad: 728306f91b6SCameron Grant if (codec) ac97_destroy(codec); 729d5fa8408SCameron Grant if (sc->buf) bus_release_resource(dev, SYS_RES_MEMORY, sc->bufid, sc->buf); 730d5fa8408SCameron Grant if (sc->reg) bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); 731d5fa8408SCameron Grant if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); 732d5fa8408SCameron Grant if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 733d5fa8408SCameron Grant free(sc, M_DEVBUF); 734d5fa8408SCameron Grant return ENXIO; 735d5fa8408SCameron Grant } 736d5fa8408SCameron Grant 73739004e69SCameron Grant static int 738306f91b6SCameron Grant nm_pci_detach(device_t dev) 739306f91b6SCameron Grant { 740306f91b6SCameron Grant int r; 741306f91b6SCameron Grant struct sc_info *sc; 742306f91b6SCameron Grant 743306f91b6SCameron Grant r = pcm_unregister(dev); 744306f91b6SCameron Grant if (r) 745306f91b6SCameron Grant return r; 746306f91b6SCameron Grant 747306f91b6SCameron Grant sc = pcm_getdevinfo(dev); 748306f91b6SCameron Grant bus_release_resource(dev, SYS_RES_MEMORY, sc->bufid, sc->buf); 749306f91b6SCameron Grant bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); 750306f91b6SCameron Grant bus_teardown_intr(dev, sc->irq, sc->ih); 751306f91b6SCameron Grant bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 752306f91b6SCameron Grant free(sc, M_DEVBUF); 753306f91b6SCameron Grant 754306f91b6SCameron Grant return 0; 755306f91b6SCameron Grant } 756306f91b6SCameron Grant 757306f91b6SCameron Grant static int 75838c81beeSOrion Hodson nm_pci_suspend(device_t dev) 75938c81beeSOrion Hodson { 76038c81beeSOrion Hodson struct sc_info *sc; 76138c81beeSOrion Hodson 76238c81beeSOrion Hodson sc = pcm_getdevinfo(dev); 76338c81beeSOrion Hodson 76438c81beeSOrion Hodson /* stop playing */ 76538c81beeSOrion Hodson if (sc->pch.active) { 76638c81beeSOrion Hodson nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); 76738c81beeSOrion Hodson nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); 76838c81beeSOrion Hodson } 76938c81beeSOrion Hodson /* stop recording */ 77038c81beeSOrion Hodson if (sc->rch.active) { 77138c81beeSOrion Hodson nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); 77238c81beeSOrion Hodson } 77338c81beeSOrion Hodson return 0; 77438c81beeSOrion Hodson } 77538c81beeSOrion Hodson 77638c81beeSOrion Hodson static int 77739004e69SCameron Grant nm_pci_resume(device_t dev) 77839004e69SCameron Grant { 77939004e69SCameron Grant struct sc_info *sc; 78039004e69SCameron Grant 78139004e69SCameron Grant sc = pcm_getdevinfo(dev); 78239004e69SCameron Grant 78338c81beeSOrion Hodson /* 78438c81beeSOrion Hodson * Reinit audio device. 78538c81beeSOrion Hodson * Don't call nm_init(). It would change buftop if X ran or 78638c81beeSOrion Hodson * is running. This makes playing and recording buffer address 78738c81beeSOrion Hodson * shift but these buffers of channel layer are not changed. 78838c81beeSOrion Hodson * As a result of this inconsistency, periodic noise will be 78938c81beeSOrion Hodson * generated while playing. 79038c81beeSOrion Hodson */ 79138c81beeSOrion Hodson nm_wr(sc, 0, 0x11, 1); 79238c81beeSOrion Hodson nm_wr(sc, 0x214, 0, 2); 79338c81beeSOrion Hodson 79439004e69SCameron Grant /* Reinit mixer */ 79533dbf14aSCameron Grant if (mixer_reinit(dev) == -1) { 79639004e69SCameron Grant device_printf(dev, "unable to reinitialize the mixer\n"); 79739004e69SCameron Grant return ENXIO; 79839004e69SCameron Grant } 79938c81beeSOrion Hodson /* restart playing */ 80038c81beeSOrion Hodson if (sc->pch.active) { 80138c81beeSOrion Hodson nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | 80238c81beeSOrion Hodson NM_PLAYBACK_ENABLE_FLAG, 1); 80338c81beeSOrion Hodson nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); 80438c81beeSOrion Hodson } 80538c81beeSOrion Hodson /* restart recording */ 80638c81beeSOrion Hodson if (sc->rch.active) { 80738c81beeSOrion Hodson nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN | 80838c81beeSOrion Hodson NM_RECORD_ENABLE_FLAG, 1); 80938c81beeSOrion Hodson } 81039004e69SCameron Grant return 0; 81139004e69SCameron Grant } 81239004e69SCameron Grant 813d5fa8408SCameron Grant static device_method_t nm_methods[] = { 814d5fa8408SCameron Grant /* Device interface */ 815d5fa8408SCameron Grant DEVMETHOD(device_probe, nm_pci_probe), 816d5fa8408SCameron Grant DEVMETHOD(device_attach, nm_pci_attach), 8177dfc9325SCameron Grant DEVMETHOD(device_detach, nm_pci_detach), 81838c81beeSOrion Hodson DEVMETHOD(device_suspend, nm_pci_suspend), 81939004e69SCameron Grant DEVMETHOD(device_resume, nm_pci_resume), 820d5fa8408SCameron Grant { 0, 0 } 821d5fa8408SCameron Grant }; 822d5fa8408SCameron Grant 823d5fa8408SCameron Grant static driver_t nm_driver = { 824d5fa8408SCameron Grant "pcm", 825d5fa8408SCameron Grant nm_methods, 82667b1dce3SCameron Grant PCM_SOFTC_SIZE, 827d5fa8408SCameron Grant }; 828d5fa8408SCameron Grant 829f314f3daSCameron Grant DRIVER_MODULE(snd_neomagic, pci, nm_driver, pcm_devclass, 0, 0); 8300739ea1dSSeigo Tanimura MODULE_DEPEND(snd_neomagic, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 831f314f3daSCameron Grant MODULE_VERSION(snd_neomagic, 1); 832