1098ca2bdSWarner Losh /*- 22511c244SDavid E. O'Brien * Copyright (c) 2004 David O'Brien <obrien@FreeBSD.org> 321f1e37cSDavid E. O'Brien * Copyright (c) 2003 Orlando Bassotto <orlando.bassotto@ieo-research.it> 43f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 5c067afaaSCameron Grant * All rights reserved. 6c067afaaSCameron Grant * 7c067afaaSCameron Grant * Redistribution and use in source and binary forms, with or without 8c067afaaSCameron Grant * modification, are permitted provided that the following conditions 9c067afaaSCameron Grant * are met: 10c067afaaSCameron Grant * 1. Redistributions of source code must retain the above copyright 11c067afaaSCameron Grant * notice, this list of conditions and the following disclaimer. 12c067afaaSCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 13c067afaaSCameron Grant * notice, this list of conditions and the following disclaimer in the 14c067afaaSCameron Grant * documentation and/or other materials provided with the distribution. 15c067afaaSCameron Grant * 16c067afaaSCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17c067afaaSCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18c067afaaSCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19c067afaaSCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20c067afaaSCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21c067afaaSCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22c067afaaSCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23c067afaaSCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 24c067afaaSCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25c067afaaSCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26c067afaaSCameron Grant * SUCH DAMAGE. 27c067afaaSCameron Grant */ 28c067afaaSCameron Grant 2990da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3090da2b28SAriff Abdullah #include "opt_snd.h" 3190da2b28SAriff Abdullah #endif 3290da2b28SAriff Abdullah 33c067afaaSCameron Grant #include <dev/sound/pcm/sound.h> 34c067afaaSCameron Grant #include <dev/sound/pcm/ac97.h> 35c7e0c9dbSPedro F. Giffuni #include <dev/sound/pci/emuxkireg.h> 36c067afaaSCameron Grant 3790cf0136SWarner Losh #include <dev/pci/pcireg.h> 3890cf0136SWarner Losh #include <dev/pci/pcivar.h> 39c067afaaSCameron Grant #include <sys/queue.h> 40c067afaaSCameron Grant 41f510d240SAlexander Leidinger #include <dev/sound/midi/mpu401.h> 42f510d240SAlexander Leidinger #include "mpufoi_if.h" 43f510d240SAlexander Leidinger 4467b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 4567b1dce3SCameron Grant 46c067afaaSCameron Grant /* -------------------------------------------------------------------- */ 47c067afaaSCameron Grant 482511c244SDavid E. O'Brien #define NUM_G 64 /* use all channels */ 492511c244SDavid E. O'Brien #define WAVEOUT_MAXBUFSIZE 32768 502511c244SDavid E. O'Brien #define EMUPAGESIZE 4096 /* don't change */ 5121f1e37cSDavid E. O'Brien #define EMUMAXPAGES (WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE) 5221f1e37cSDavid E. O'Brien #define EMU10K1_PCI_ID 0x00021102 /* 1102 => Creative Labs Vendor ID */ 53a791cfeeSCameron Grant #define EMU10K2_PCI_ID 0x00041102 54d2b677bbSWarner Losh #define EMU10K3_PCI_ID 0x00081102 55a791cfeeSCameron Grant #define EMU_DEFAULT_BUFSZ 4096 5621f1e37cSDavid E. O'Brien #define EMU_MAX_CHANS 8 5770776a9cSCameron Grant #define EMU_CHANS 4 5821f1e37cSDavid E. O'Brien 5921f1e37cSDavid E. O'Brien #define MAXREQVOICES 8 6021f1e37cSDavid E. O'Brien #define RESERVED 0 6121f1e37cSDavid E. O'Brien #define NUM_MIDI 16 6221f1e37cSDavid E. O'Brien #define NUM_FXSENDS 4 6321f1e37cSDavid E. O'Brien 6421f1e37cSDavid E. O'Brien #define TMEMSIZE 256*1024 6521f1e37cSDavid E. O'Brien #define TMEMSIZEREG 4 6621f1e37cSDavid E. O'Brien 6721f1e37cSDavid E. O'Brien #define ENABLE 0xffffffff 6821f1e37cSDavid E. O'Brien #define DISABLE 0x00000000 69c7e0c9dbSPedro F. Giffuni #define ENV_ON EMU_CHAN_DCYSUSV_CHANNELENABLE_MASK 702511c244SDavid E. O'Brien #define ENV_OFF 0x00 /* XXX: should this be 1? */ 71c067afaaSCameron Grant 72c7e0c9dbSPedro F. Giffuni #define EMU_A_IOCFG_GPOUT_A 0x40 73c7e0c9dbSPedro F. Giffuni #define EMU_A_IOCFG_GPOUT_D 0x04 74c7e0c9dbSPedro F. Giffuni #define EMU_A_IOCFG_GPOUT_AD (EMU_A_IOCFG_GPOUT_A|EMU_A_IOCFG_GPOUT_D) /* EMU_A_IOCFG_GPOUT0 */ 75c7e0c9dbSPedro F. Giffuni 76c7e0c9dbSPedro F. Giffuni #define EMU_HCFG_GPOUT1 0x00000800 77c7e0c9dbSPedro F. Giffuni 78c7e0c9dbSPedro F. Giffuni /* instruction set */ 79c7e0c9dbSPedro F. Giffuni #define iACC3 0x06 80c7e0c9dbSPedro F. Giffuni #define iMACINT0 0x04 81c7e0c9dbSPedro F. Giffuni #define iINTERP 0x0e 82c7e0c9dbSPedro F. Giffuni 83c7e0c9dbSPedro F. Giffuni #define C_00000000 0x40 84c7e0c9dbSPedro F. Giffuni #define C_00000001 0x41 85c7e0c9dbSPedro F. Giffuni #define C_00000004 0x44 86c7e0c9dbSPedro F. Giffuni #define C_40000000 0x4d 87c7e0c9dbSPedro F. Giffuni /* Audigy constants */ 88c7e0c9dbSPedro F. Giffuni #define A_C_00000000 0xc0 89c7e0c9dbSPedro F. Giffuni #define A_C_40000000 0xcd 90c7e0c9dbSPedro F. Giffuni 91c7e0c9dbSPedro F. Giffuni /* GPRs */ 92c7e0c9dbSPedro F. Giffuni #define FXBUS(x) (0x00 + (x)) 93c7e0c9dbSPedro F. Giffuni #define EXTIN(x) (0x10 + (x)) 94c7e0c9dbSPedro F. Giffuni #define EXTOUT(x) (0x20 + (x)) 95c7e0c9dbSPedro F. Giffuni 96c7e0c9dbSPedro F. Giffuni #define GPR(x) (EMU_FXGPREGBASE + (x)) 97c7e0c9dbSPedro F. Giffuni #define A_EXTIN(x) (0x40 + (x)) 98c7e0c9dbSPedro F. Giffuni #define A_FXBUS(x) (0x00 + (x)) 99c7e0c9dbSPedro F. Giffuni #define A_EXTOUT(x) (0x60 + (x)) 100c7e0c9dbSPedro F. Giffuni #define A_GPR(x) (EMU_A_FXGPREGBASE + (x)) 101c7e0c9dbSPedro F. Giffuni 102c7e0c9dbSPedro F. Giffuni /* FX buses */ 103c7e0c9dbSPedro F. Giffuni #define FXBUS_PCM_LEFT 0x00 104c7e0c9dbSPedro F. Giffuni #define FXBUS_PCM_RIGHT 0x01 105c7e0c9dbSPedro F. Giffuni #define FXBUS_MIDI_LEFT 0x04 106c7e0c9dbSPedro F. Giffuni #define FXBUS_MIDI_RIGHT 0x05 107c7e0c9dbSPedro F. Giffuni #define FXBUS_MIDI_REVERB 0x0c 108c7e0c9dbSPedro F. Giffuni #define FXBUS_MIDI_CHORUS 0x0d 109c7e0c9dbSPedro F. Giffuni 110c7e0c9dbSPedro F. Giffuni /* Inputs */ 111c7e0c9dbSPedro F. Giffuni #define EXTIN_AC97_L 0x00 112c7e0c9dbSPedro F. Giffuni #define EXTIN_AC97_R 0x01 113c7e0c9dbSPedro F. Giffuni #define EXTIN_SPDIF_CD_L 0x02 114c7e0c9dbSPedro F. Giffuni #define EXTIN_SPDIF_CD_R 0x03 115c7e0c9dbSPedro F. Giffuni #define EXTIN_TOSLINK_L 0x06 116c7e0c9dbSPedro F. Giffuni #define EXTIN_TOSLINK_R 0x07 117c7e0c9dbSPedro F. Giffuni #define EXTIN_COAX_SPDIF_L 0x0a 118c7e0c9dbSPedro F. Giffuni #define EXTIN_COAX_SPDIF_R 0x0b 119c7e0c9dbSPedro F. Giffuni /* Audigy Inputs */ 120c7e0c9dbSPedro F. Giffuni #define A_EXTIN_AC97_L 0x00 121c7e0c9dbSPedro F. Giffuni #define A_EXTIN_AC97_R 0x01 122c7e0c9dbSPedro F. Giffuni 123c7e0c9dbSPedro F. Giffuni /* Outputs */ 124c7e0c9dbSPedro F. Giffuni #define EXTOUT_AC97_L 0x00 125c7e0c9dbSPedro F. Giffuni #define EXTOUT_AC97_R 0x01 126c7e0c9dbSPedro F. Giffuni #define EXTOUT_TOSLINK_L 0x02 127c7e0c9dbSPedro F. Giffuni #define EXTOUT_TOSLINK_R 0x03 128c7e0c9dbSPedro F. Giffuni #define EXTOUT_AC97_CENTER 0x04 129c7e0c9dbSPedro F. Giffuni #define EXTOUT_AC97_LFE 0x05 130c7e0c9dbSPedro F. Giffuni #define EXTOUT_HEADPHONE_L 0x06 131c7e0c9dbSPedro F. Giffuni #define EXTOUT_HEADPHONE_R 0x07 132c7e0c9dbSPedro F. Giffuni #define EXTOUT_REAR_L 0x08 133c7e0c9dbSPedro F. Giffuni #define EXTOUT_REAR_R 0x09 134c7e0c9dbSPedro F. Giffuni #define EXTOUT_ADC_CAP_L 0x0a 135c7e0c9dbSPedro F. Giffuni #define EXTOUT_ADC_CAP_R 0x0b 136c7e0c9dbSPedro F. Giffuni #define EXTOUT_ACENTER 0x11 137c7e0c9dbSPedro F. Giffuni #define EXTOUT_ALFE 0x12 138c7e0c9dbSPedro F. Giffuni /* Audigy Outputs */ 139c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_FRONT_L 0x00 140c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_FRONT_R 0x01 141c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_CENTER 0x02 142c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_LFE 0x03 143c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_HEADPHONE_L 0x04 144c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_HEADPHONE_R 0x05 145c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_REAR_L 0x06 146c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_REAR_R 0x07 147c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AFRONT_L 0x08 148c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AFRONT_R 0x09 149c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_ACENTER 0x0a 150c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_ALFE 0x0b 151c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AREAR_L 0x0e 152c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AREAR_R 0x0f 153c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AC97_L 0x10 154c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_AC97_R 0x11 155c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_ADC_CAP_L 0x16 156c7e0c9dbSPedro F. Giffuni #define A_EXTOUT_ADC_CAP_R 0x17 15721f1e37cSDavid E. O'Brien 158c067afaaSCameron Grant struct emu_memblk { 159e3975643SJake Burkholder SLIST_ENTRY(emu_memblk) link; 160c067afaaSCameron Grant void *buf; 16138cc9942SOlivier Houchard bus_addr_t buf_addr; 162c067afaaSCameron Grant u_int32_t pte_start, pte_size; 163c067afaaSCameron Grant }; 164c067afaaSCameron Grant 165c067afaaSCameron Grant struct emu_mem { 16621f1e37cSDavid E. O'Brien u_int8_t bmap[EMUMAXPAGES / 8]; 16797b3c9d8SCameron Grant u_int32_t *ptb_pages; 168c067afaaSCameron Grant void *silent_page; 16938cc9942SOlivier Houchard bus_addr_t silent_page_addr; 17038cc9942SOlivier Houchard bus_addr_t ptb_pages_addr; 171e3975643SJake Burkholder SLIST_HEAD(, emu_memblk) blocks; 172c067afaaSCameron Grant }; 173c067afaaSCameron Grant 174c067afaaSCameron Grant struct emu_voice { 175c067afaaSCameron Grant int vnum; 17679462204SAriff Abdullah unsigned int b16:1, stereo:1, busy:1, running:1, ismaster:1; 177c067afaaSCameron Grant int speed; 1782d3ce9d5SCameron Grant int start, end, vol; 17921f1e37cSDavid E. O'Brien int fxrt1; /* FX routing */ 18021f1e37cSDavid E. O'Brien int fxrt2; /* FX routing (only for audigy) */ 18197b3c9d8SCameron Grant u_int32_t buf; 1826c1146c0SCameron Grant struct emu_voice *slave; 18366ef8af5SCameron Grant struct pcm_channel *channel; 184c067afaaSCameron Grant }; 185c067afaaSCameron Grant 186c067afaaSCameron Grant struct sc_info; 187c067afaaSCameron Grant 188c067afaaSCameron Grant /* channel registers */ 18970776a9cSCameron Grant struct sc_pchinfo { 190350a5fafSCameron Grant int spd, fmt, blksz, run; 1916c1146c0SCameron Grant struct emu_voice *master, *slave; 19266ef8af5SCameron Grant struct snd_dbuf *buffer; 19366ef8af5SCameron Grant struct pcm_channel *channel; 194c067afaaSCameron Grant struct sc_info *parent; 195c067afaaSCameron Grant }; 196c067afaaSCameron Grant 19770776a9cSCameron Grant struct sc_rchinfo { 198350a5fafSCameron Grant int spd, fmt, run, blksz, num; 19970776a9cSCameron Grant u_int32_t idxreg, basereg, sizereg, setupreg, irqmask; 20066ef8af5SCameron Grant struct snd_dbuf *buffer; 20166ef8af5SCameron Grant struct pcm_channel *channel; 20270776a9cSCameron Grant struct sc_info *parent; 20370776a9cSCameron Grant }; 20470776a9cSCameron Grant 205c067afaaSCameron Grant /* device private data */ 206c067afaaSCameron Grant struct sc_info { 207c067afaaSCameron Grant device_t dev; 208c067afaaSCameron Grant u_int32_t type, rev; 20921f1e37cSDavid E. O'Brien u_int32_t tos_link:1, APS:1, audigy:1, audigy2:1; 21021f1e37cSDavid E. O'Brien u_int32_t addrmask; /* wider if audigy */ 211c067afaaSCameron Grant 212c067afaaSCameron Grant bus_space_tag_t st; 213c067afaaSCameron Grant bus_space_handle_t sh; 214c067afaaSCameron Grant bus_dma_tag_t parent_dmat; 215c067afaaSCameron Grant 216c067afaaSCameron Grant struct resource *reg, *irq; 217c067afaaSCameron Grant void *ih; 21800acb133SCameron Grant struct mtx *lock; 219c067afaaSCameron Grant 220a791cfeeSCameron Grant unsigned int bufsz; 221350a5fafSCameron Grant int timer, timerinterval; 22270776a9cSCameron Grant int pnum, rnum; 22321f1e37cSDavid E. O'Brien int nchans; 224c067afaaSCameron Grant struct emu_mem mem; 225c067afaaSCameron Grant struct emu_voice voice[64]; 22621f1e37cSDavid E. O'Brien struct sc_pchinfo pch[EMU_MAX_CHANS]; 2276c1146c0SCameron Grant struct sc_rchinfo rch[3]; 228f510d240SAlexander Leidinger struct mpu401 *mpu; 229f510d240SAlexander Leidinger mpu401_intr_t *mpu_intr; 230f510d240SAlexander Leidinger int mputx; 231c067afaaSCameron Grant }; 232c067afaaSCameron Grant 233c067afaaSCameron Grant /* -------------------------------------------------------------------- */ 234c067afaaSCameron Grant 235c067afaaSCameron Grant /* 236c067afaaSCameron Grant * prototypes 237c067afaaSCameron Grant */ 238c067afaaSCameron Grant 239c067afaaSCameron Grant /* stuff */ 240c067afaaSCameron Grant static int emu_init(struct sc_info *); 241c067afaaSCameron Grant static void emu_intr(void *); 24238cc9942SOlivier Houchard static void *emu_malloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr); 24338cc9942SOlivier Houchard static void *emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr); 244c067afaaSCameron Grant static int emu_memfree(struct sc_info *sc, void *buf); 245c067afaaSCameron Grant static int emu_memstart(struct sc_info *sc, void *buf); 2468046c763SCameron Grant #ifdef EMUDEBUG 2478046c763SCameron Grant static void emu_vdump(struct sc_info *sc, struct emu_voice *v); 2488046c763SCameron Grant #endif 249c067afaaSCameron Grant 250c067afaaSCameron Grant /* talk to the card */ 251c067afaaSCameron Grant static u_int32_t emu_rd(struct sc_info *, int, int); 252c067afaaSCameron Grant static void emu_wr(struct sc_info *, int, u_int32_t, int); 253c067afaaSCameron Grant 254c067afaaSCameron Grant /* -------------------------------------------------------------------- */ 255c067afaaSCameron Grant 256513693beSCameron Grant static u_int32_t emu_rfmt_ac97[] = { 25790da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 1, 0), 25890da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 2, 0), 259513693beSCameron Grant 0 260c067afaaSCameron Grant }; 261c067afaaSCameron Grant 262513693beSCameron Grant static u_int32_t emu_rfmt_mic[] = { 26390da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 1, 0), 264513693beSCameron Grant 0 265c067afaaSCameron Grant }; 266c067afaaSCameron Grant 267513693beSCameron Grant static u_int32_t emu_rfmt_efx[] = { 26890da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 2, 0), 269513693beSCameron Grant 0 270513693beSCameron Grant }; 271513693beSCameron Grant 27266ef8af5SCameron Grant static struct pcmchan_caps emu_reccaps[3] = { 273513693beSCameron Grant {8000, 48000, emu_rfmt_ac97, 0}, 274513693beSCameron Grant {8000, 8000, emu_rfmt_mic, 0}, 275513693beSCameron Grant {48000, 48000, emu_rfmt_efx, 0}, 276513693beSCameron Grant }; 277513693beSCameron Grant 278513693beSCameron Grant static u_int32_t emu_pfmt[] = { 27990da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 1, 0), 28090da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 2, 0), 28190da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 1, 0), 28290da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 2, 0), 283513693beSCameron Grant 0 284513693beSCameron Grant }; 285513693beSCameron Grant 28666ef8af5SCameron Grant static struct pcmchan_caps emu_playcaps = {4000, 48000, emu_pfmt, 0}; 28770776a9cSCameron Grant 28870776a9cSCameron Grant static int adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}; 28921f1e37cSDavid E. O'Brien /* audigy supports 12kHz. */ 29021f1e37cSDavid E. O'Brien static int audigy_adcspeed[9] = { 29121f1e37cSDavid E. O'Brien 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000 29221f1e37cSDavid E. O'Brien }; 29370776a9cSCameron Grant 294c067afaaSCameron Grant /* -------------------------------------------------------------------- */ 295c067afaaSCameron Grant /* Hardware */ 296c067afaaSCameron Grant static u_int32_t 297c067afaaSCameron Grant emu_rd(struct sc_info *sc, int regno, int size) 298c067afaaSCameron Grant { 299c067afaaSCameron Grant switch (size) { 300c067afaaSCameron Grant case 1: 301c067afaaSCameron Grant return bus_space_read_1(sc->st, sc->sh, regno); 302c067afaaSCameron Grant case 2: 303c067afaaSCameron Grant return bus_space_read_2(sc->st, sc->sh, regno); 304c067afaaSCameron Grant case 4: 305c067afaaSCameron Grant return bus_space_read_4(sc->st, sc->sh, regno); 306c067afaaSCameron Grant default: 307c067afaaSCameron Grant return 0xffffffff; 308c067afaaSCameron Grant } 309c067afaaSCameron Grant } 310c067afaaSCameron Grant 311c067afaaSCameron Grant static void 312c067afaaSCameron Grant emu_wr(struct sc_info *sc, int regno, u_int32_t data, int size) 313c067afaaSCameron Grant { 314c067afaaSCameron Grant switch (size) { 315c067afaaSCameron Grant case 1: 316c067afaaSCameron Grant bus_space_write_1(sc->st, sc->sh, regno, data); 317c067afaaSCameron Grant break; 318c067afaaSCameron Grant case 2: 319c067afaaSCameron Grant bus_space_write_2(sc->st, sc->sh, regno, data); 320c067afaaSCameron Grant break; 321c067afaaSCameron Grant case 4: 322c067afaaSCameron Grant bus_space_write_4(sc->st, sc->sh, regno, data); 323c067afaaSCameron Grant break; 324c067afaaSCameron Grant } 325c067afaaSCameron Grant } 326c067afaaSCameron Grant 327c067afaaSCameron Grant static u_int32_t 328c067afaaSCameron Grant emu_rdptr(struct sc_info *sc, int chn, int reg) 329c067afaaSCameron Grant { 330c067afaaSCameron Grant u_int32_t ptr, val, mask, size, offset; 331c067afaaSCameron Grant 332c7e0c9dbSPedro F. Giffuni ptr = ((reg << 16) & sc->addrmask) | (chn & EMU_PTR_CHNO_MASK); 333c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_PTR, ptr, 4); 334c7e0c9dbSPedro F. Giffuni val = emu_rd(sc, EMU_DATA, 4); 335c067afaaSCameron Grant if (reg & 0xff000000) { 336c067afaaSCameron Grant size = (reg >> 24) & 0x3f; 337c067afaaSCameron Grant offset = (reg >> 16) & 0x1f; 338c067afaaSCameron Grant mask = ((1 << size) - 1) << offset; 339c067afaaSCameron Grant val &= mask; 340c067afaaSCameron Grant val >>= offset; 341c067afaaSCameron Grant } 342c067afaaSCameron Grant return val; 343c067afaaSCameron Grant } 344c067afaaSCameron Grant 345c067afaaSCameron Grant static void 346c067afaaSCameron Grant emu_wrptr(struct sc_info *sc, int chn, int reg, u_int32_t data) 347c067afaaSCameron Grant { 348c067afaaSCameron Grant u_int32_t ptr, mask, size, offset; 349c067afaaSCameron Grant 350c7e0c9dbSPedro F. Giffuni ptr = ((reg << 16) & sc->addrmask) | (chn & EMU_PTR_CHNO_MASK); 351c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_PTR, ptr, 4); 352c067afaaSCameron Grant if (reg & 0xff000000) { 353c067afaaSCameron Grant size = (reg >> 24) & 0x3f; 354c067afaaSCameron Grant offset = (reg >> 16) & 0x1f; 355c067afaaSCameron Grant mask = ((1 << size) - 1) << offset; 356c067afaaSCameron Grant data <<= offset; 357c067afaaSCameron Grant data &= mask; 358c7e0c9dbSPedro F. Giffuni data |= emu_rd(sc, EMU_DATA, 4) & ~mask; 359c067afaaSCameron Grant } 360c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_DATA, data, 4); 361c067afaaSCameron Grant } 362c067afaaSCameron Grant 363c067afaaSCameron Grant static void 364c067afaaSCameron Grant emu_wrefx(struct sc_info *sc, unsigned int pc, unsigned int data) 365c067afaaSCameron Grant { 366c7e0c9dbSPedro F. Giffuni pc += sc->audigy ? EMU_A_MICROCODEBASE : EMU_MICROCODEBASE; 36721f1e37cSDavid E. O'Brien emu_wrptr(sc, 0, pc, data); 368c067afaaSCameron Grant } 369c067afaaSCameron Grant 3700f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 3716c1146c0SCameron Grant /* ac97 codec */ 37266ef8af5SCameron Grant /* no locking needed */ 3730f55ac6cSCameron Grant 3740f55ac6cSCameron Grant static int 3750f55ac6cSCameron Grant emu_rdcd(kobj_t obj, void *devinfo, int regno) 3766c1146c0SCameron Grant { 3776c1146c0SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 3786c1146c0SCameron Grant 379c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_AC97ADDR, regno, 1); 380c7e0c9dbSPedro F. Giffuni return emu_rd(sc, EMU_AC97DATA, 2); 3816c1146c0SCameron Grant } 3826c1146c0SCameron Grant 3830f55ac6cSCameron Grant static int 3840f55ac6cSCameron Grant emu_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 3856c1146c0SCameron Grant { 3866c1146c0SCameron Grant struct sc_info *sc = (struct sc_info *)devinfo; 3876c1146c0SCameron Grant 388c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_AC97ADDR, regno, 1); 389c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_AC97DATA, data, 2); 3900f55ac6cSCameron Grant return 0; 3916c1146c0SCameron Grant } 3926c1146c0SCameron Grant 3930f55ac6cSCameron Grant static kobj_method_t emu_ac97_methods[] = { 3940f55ac6cSCameron Grant KOBJMETHOD(ac97_read, emu_rdcd), 3950f55ac6cSCameron Grant KOBJMETHOD(ac97_write, emu_wrcd), 39690da2b28SAriff Abdullah KOBJMETHOD_END 3970f55ac6cSCameron Grant }; 3980f55ac6cSCameron Grant AC97_DECLARE(emu_ac97); 3990f55ac6cSCameron Grant 4000f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 4016c1146c0SCameron Grant /* stuff */ 4026c1146c0SCameron Grant static int 403350a5fafSCameron Grant emu_settimer(struct sc_info *sc) 404350a5fafSCameron Grant { 405350a5fafSCameron Grant struct sc_pchinfo *pch; 406350a5fafSCameron Grant struct sc_rchinfo *rch; 407350a5fafSCameron Grant int i, tmp, rate; 408350a5fafSCameron Grant 409350a5fafSCameron Grant rate = 0; 41021f1e37cSDavid E. O'Brien for (i = 0; i < sc->nchans; i++) { 411350a5fafSCameron Grant pch = &sc->pch[i]; 412a791cfeeSCameron Grant if (pch->buffer) { 41390da2b28SAriff Abdullah tmp = (pch->spd * sndbuf_getalign(pch->buffer)) 41421f1e37cSDavid E. O'Brien / pch->blksz; 415350a5fafSCameron Grant if (tmp > rate) 416350a5fafSCameron Grant rate = tmp; 417350a5fafSCameron Grant } 418a791cfeeSCameron Grant } 419350a5fafSCameron Grant 420350a5fafSCameron Grant for (i = 0; i < 3; i++) { 421350a5fafSCameron Grant rch = &sc->rch[i]; 422a791cfeeSCameron Grant if (rch->buffer) { 42390da2b28SAriff Abdullah tmp = (rch->spd * sndbuf_getalign(rch->buffer)) 42421f1e37cSDavid E. O'Brien / rch->blksz; 425350a5fafSCameron Grant if (tmp > rate) 426350a5fafSCameron Grant rate = tmp; 427350a5fafSCameron Grant } 428a791cfeeSCameron Grant } 429350a5fafSCameron Grant RANGE(rate, 48, 9600); 430350a5fafSCameron Grant sc->timerinterval = 48000 / rate; 431c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_TIMER, sc->timerinterval & 0x03ff, 2); 432350a5fafSCameron Grant 433350a5fafSCameron Grant return sc->timerinterval; 434350a5fafSCameron Grant } 435350a5fafSCameron Grant 436350a5fafSCameron Grant static int 4376c1146c0SCameron Grant emu_enatimer(struct sc_info *sc, int go) 4386c1146c0SCameron Grant { 4396c1146c0SCameron Grant u_int32_t x; 4406c1146c0SCameron Grant if (go) { 4416c1146c0SCameron Grant if (sc->timer++ == 0) { 442c7e0c9dbSPedro F. Giffuni x = emu_rd(sc, EMU_INTE, 4); 443c7e0c9dbSPedro F. Giffuni x |= EMU_INTE_INTERTIMERENB; 444c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_INTE, x, 4); 4456c1146c0SCameron Grant } 4466c1146c0SCameron Grant } else { 4476c1146c0SCameron Grant sc->timer = 0; 448c7e0c9dbSPedro F. Giffuni x = emu_rd(sc, EMU_INTE, 4); 449c7e0c9dbSPedro F. Giffuni x &= ~EMU_INTE_INTERTIMERENB; 450c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_INTE, x, 4); 4516c1146c0SCameron Grant } 4526c1146c0SCameron Grant return 0; 4536c1146c0SCameron Grant } 454c067afaaSCameron Grant 455c067afaaSCameron Grant static void 456c067afaaSCameron Grant emu_enastop(struct sc_info *sc, char channel, int enable) 457c067afaaSCameron Grant { 458c7e0c9dbSPedro F. Giffuni int reg = (channel & 0x20) ? EMU_SOLEH : EMU_SOLEL; 459c067afaaSCameron Grant channel &= 0x1f; 460c067afaaSCameron Grant reg |= 1 << 24; 461c067afaaSCameron Grant reg |= channel << 16; 462c067afaaSCameron Grant emu_wrptr(sc, 0, reg, enable); 463c067afaaSCameron Grant } 464c067afaaSCameron Grant 46570776a9cSCameron Grant static int 46670776a9cSCameron Grant emu_recval(int speed) { 46770776a9cSCameron Grant int val; 46870776a9cSCameron Grant 46970776a9cSCameron Grant val = 0; 47070776a9cSCameron Grant while (val < 7 && speed < adcspeed[val]) 47170776a9cSCameron Grant val++; 47270776a9cSCameron Grant return val; 47370776a9cSCameron Grant } 47470776a9cSCameron Grant 47521f1e37cSDavid E. O'Brien static int 47621f1e37cSDavid E. O'Brien audigy_recval(int speed) { 47721f1e37cSDavid E. O'Brien int val; 47821f1e37cSDavid E. O'Brien 47921f1e37cSDavid E. O'Brien val = 0; 48021f1e37cSDavid E. O'Brien while (val < 8 && speed < audigy_adcspeed[val]) 48121f1e37cSDavid E. O'Brien val++; 48221f1e37cSDavid E. O'Brien return val; 48321f1e37cSDavid E. O'Brien } 48421f1e37cSDavid E. O'Brien 485c067afaaSCameron Grant static u_int32_t 486c067afaaSCameron Grant emu_rate_to_pitch(u_int32_t rate) 487c067afaaSCameron Grant { 488c067afaaSCameron Grant static u_int32_t logMagTable[128] = { 489c067afaaSCameron Grant 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, 490c067afaaSCameron Grant 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, 491c067afaaSCameron Grant 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, 492c067afaaSCameron Grant 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, 493c067afaaSCameron Grant 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, 494c067afaaSCameron Grant 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, 495c067afaaSCameron Grant 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, 496c067afaaSCameron Grant 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, 497c067afaaSCameron Grant 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, 498c067afaaSCameron Grant 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, 499c067afaaSCameron Grant 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, 500c067afaaSCameron Grant 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, 501c067afaaSCameron Grant 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, 502c067afaaSCameron Grant 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, 503c067afaaSCameron Grant 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, 504c067afaaSCameron Grant 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df 505c067afaaSCameron Grant }; 506c067afaaSCameron Grant static char logSlopeTable[128] = { 507c067afaaSCameron Grant 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, 508c067afaaSCameron Grant 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, 509c067afaaSCameron Grant 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, 510c067afaaSCameron Grant 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, 511c067afaaSCameron Grant 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, 512c067afaaSCameron Grant 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 513c067afaaSCameron Grant 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, 514c067afaaSCameron Grant 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 515c067afaaSCameron Grant 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 516c067afaaSCameron Grant 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 517c067afaaSCameron Grant 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, 518c067afaaSCameron Grant 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, 519c067afaaSCameron Grant 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, 520c067afaaSCameron Grant 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, 521c067afaaSCameron Grant 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 522c067afaaSCameron Grant 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f 523c067afaaSCameron Grant }; 524c067afaaSCameron Grant int i; 525c067afaaSCameron Grant 526c067afaaSCameron Grant if (rate == 0) 527c067afaaSCameron Grant return 0; /* Bail out if no leading "1" */ 528c067afaaSCameron Grant rate *= 11185; /* Scale 48000 to 0x20002380 */ 529c067afaaSCameron Grant for (i = 31; i > 0; i--) { 530c067afaaSCameron Grant if (rate & 0x80000000) { /* Detect leading "1" */ 531c067afaaSCameron Grant return (((u_int32_t) (i - 15) << 20) + 532c067afaaSCameron Grant logMagTable[0x7f & (rate >> 24)] + 533c067afaaSCameron Grant (0x7f & (rate >> 17)) * 534c067afaaSCameron Grant logSlopeTable[0x7f & (rate >> 24)]); 535c067afaaSCameron Grant } 536c067afaaSCameron Grant rate <<= 1; 537c067afaaSCameron Grant } 538c067afaaSCameron Grant 539c067afaaSCameron Grant return 0; /* Should never reach this point */ 540c067afaaSCameron Grant } 541c067afaaSCameron Grant 5426c1146c0SCameron Grant static u_int32_t 5436c1146c0SCameron Grant emu_rate_to_linearpitch(u_int32_t rate) 5446c1146c0SCameron Grant { 5456c1146c0SCameron Grant rate = (rate << 8) / 375; 5466c1146c0SCameron Grant return (rate >> 1) + (rate & 1); 5476c1146c0SCameron Grant } 5486c1146c0SCameron Grant 549c067afaaSCameron Grant static struct emu_voice * 550c067afaaSCameron Grant emu_valloc(struct sc_info *sc) 551c067afaaSCameron Grant { 552c067afaaSCameron Grant struct emu_voice *v; 553c067afaaSCameron Grant int i; 554c067afaaSCameron Grant 555c067afaaSCameron Grant v = NULL; 556c067afaaSCameron Grant for (i = 0; i < 64 && sc->voice[i].busy; i++); 557c067afaaSCameron Grant if (i < 64) { 558c067afaaSCameron Grant v = &sc->voice[i]; 559c067afaaSCameron Grant v->busy = 1; 560c067afaaSCameron Grant } 561c067afaaSCameron Grant return v; 562c067afaaSCameron Grant } 563c067afaaSCameron Grant 564c067afaaSCameron Grant static int 5656c1146c0SCameron Grant emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, 56666ef8af5SCameron Grant u_int32_t sz, struct snd_dbuf *b) 567c067afaaSCameron Grant { 568c067afaaSCameron Grant void *buf; 56938cc9942SOlivier Houchard bus_addr_t tmp_addr; 570c067afaaSCameron Grant 57138cc9942SOlivier Houchard buf = emu_memalloc(sc, sz, &tmp_addr); 572c067afaaSCameron Grant if (buf == NULL) 573c067afaaSCameron Grant return -1; 57466ef8af5SCameron Grant if (b != NULL) 57566ef8af5SCameron Grant sndbuf_setup(b, buf, sz); 576c067afaaSCameron Grant m->start = emu_memstart(sc, buf) * EMUPAGESIZE; 57797b3c9d8SCameron Grant m->end = m->start + sz; 5782d3ce9d5SCameron Grant m->channel = NULL; 579c067afaaSCameron Grant m->speed = 0; 580c067afaaSCameron Grant m->b16 = 0; 581c067afaaSCameron Grant m->stereo = 0; 5826c1146c0SCameron Grant m->running = 0; 5832d3ce9d5SCameron Grant m->ismaster = 1; 5842d3ce9d5SCameron Grant m->vol = 0xff; 58538cc9942SOlivier Houchard m->buf = tmp_addr; 586c067afaaSCameron Grant m->slave = s; 58721f1e37cSDavid E. O'Brien if (sc->audigy) { 5889190333cSAlexander Leidinger m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 8 | 5899190333cSAlexander Leidinger FXBUS_PCM_LEFT << 16 | FXBUS_MIDI_REVERB << 24; 59021f1e37cSDavid E. O'Brien m->fxrt2 = 0x3f3f3f3f; /* No effects on second route */ 59121f1e37cSDavid E. O'Brien } else { 5929190333cSAlexander Leidinger m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 4 | 5939190333cSAlexander Leidinger FXBUS_PCM_LEFT << 8 | FXBUS_MIDI_REVERB << 12; 59421f1e37cSDavid E. O'Brien m->fxrt2 = 0; 59521f1e37cSDavid E. O'Brien } 59621f1e37cSDavid E. O'Brien 597c067afaaSCameron Grant if (s != NULL) { 598c067afaaSCameron Grant s->start = m->start; 599c067afaaSCameron Grant s->end = m->end; 600c067afaaSCameron Grant s->channel = NULL; 601c067afaaSCameron Grant s->speed = 0; 602c067afaaSCameron Grant s->b16 = 0; 603c067afaaSCameron Grant s->stereo = 0; 6046c1146c0SCameron Grant s->running = 0; 6052d3ce9d5SCameron Grant s->ismaster = 0; 6062d3ce9d5SCameron Grant s->vol = m->vol; 60797b3c9d8SCameron Grant s->buf = m->buf; 60821f1e37cSDavid E. O'Brien s->fxrt1 = m->fxrt1; 60921f1e37cSDavid E. O'Brien s->fxrt2 = m->fxrt2; 610c067afaaSCameron Grant s->slave = NULL; 611c067afaaSCameron Grant } 612c067afaaSCameron Grant return 0; 613c067afaaSCameron Grant } 614c067afaaSCameron Grant 615c067afaaSCameron Grant static void 61670776a9cSCameron Grant emu_vsetup(struct sc_pchinfo *ch) 617c067afaaSCameron Grant { 618c067afaaSCameron Grant struct emu_voice *v = ch->master; 619c067afaaSCameron Grant 620c067afaaSCameron Grant if (ch->fmt) { 621c067afaaSCameron Grant v->b16 = (ch->fmt & AFMT_16BIT) ? 1 : 0; 62290da2b28SAriff Abdullah v->stereo = (AFMT_CHANNEL(ch->fmt) > 1) ? 1 : 0; 623c067afaaSCameron Grant if (v->slave != NULL) { 624c067afaaSCameron Grant v->slave->b16 = v->b16; 625c067afaaSCameron Grant v->slave->stereo = v->stereo; 626c067afaaSCameron Grant } 627c067afaaSCameron Grant } 628c067afaaSCameron Grant if (ch->spd) { 629c067afaaSCameron Grant v->speed = ch->spd; 630c067afaaSCameron Grant if (v->slave != NULL) 631c067afaaSCameron Grant v->slave->speed = v->speed; 632c067afaaSCameron Grant } 633c067afaaSCameron Grant } 634c067afaaSCameron Grant 635c067afaaSCameron Grant static void 636c067afaaSCameron Grant emu_vwrite(struct sc_info *sc, struct emu_voice *v) 637c067afaaSCameron Grant { 6386c1146c0SCameron Grant int s; 6396c1146c0SCameron Grant int l, r, x, y; 6406c1146c0SCameron Grant u_int32_t sa, ea, start, val, silent_page; 641c067afaaSCameron Grant 642c067afaaSCameron Grant s = (v->stereo ? 1 : 0) + (v->b16 ? 1 : 0); 6436c1146c0SCameron Grant 644c067afaaSCameron Grant sa = v->start >> s; 645c067afaaSCameron Grant ea = v->end >> s; 6466c1146c0SCameron Grant 6476c1146c0SCameron Grant l = r = x = y = v->vol; 648c067afaaSCameron Grant if (v->stereo) { 6492d3ce9d5SCameron Grant l = v->ismaster ? l : 0; 6502d3ce9d5SCameron Grant r = v->ismaster ? 0 : r; 651c067afaaSCameron Grant } 652c067afaaSCameron Grant 653c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CPF, v->stereo ? EMU_CHAN_CPF_STEREO_MASK : 0); 6546c1146c0SCameron Grant val = v->stereo ? 28 : 30; 6556c1146c0SCameron Grant val *= v->b16 ? 1 : 2; 6566c1146c0SCameron Grant start = sa + val; 6576c1146c0SCameron Grant 65821f1e37cSDavid E. O'Brien if (sc->audigy) { 659c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_A_CHAN_FXRT1, v->fxrt1); 660c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_A_CHAN_FXRT2, v->fxrt2); 661c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_A_CHAN_SENDAMOUNTS, 0); 66221f1e37cSDavid E. O'Brien } 66321f1e37cSDavid E. O'Brien else 664c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_FXRT, v->fxrt1 << 16); 665c067afaaSCameron Grant 666c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX, (x << 8) | r); 667c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_DSL, ea | (y << 24)); 668c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_PSST, sa | (l << 24)); 669c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CCCA, start | (v->b16 ? 0 : EMU_CHAN_CCCA_8BITSELECT)); 670c067afaaSCameron Grant 671c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_Z1, 0); 672c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_Z2, 0); 673c067afaaSCameron Grant 67421f1e37cSDavid E. O'Brien silent_page = ((u_int32_t)(sc->mem.silent_page_addr) << 1) 675c7e0c9dbSPedro F. Giffuni | EMU_CHAN_MAP_PTI_MASK; 676c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_MAPA, silent_page); 677c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_MAPB, silent_page); 678c067afaaSCameron Grant 679c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, EMU_CHAN_CVCF_CURRFILTER_MASK); 680c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, EMU_CHAN_VTFT_FILTERTARGET_MASK); 681c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_ATKHLDM, 0); 682c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_DCYSUSM, EMU_CHAN_DCYSUSM_DECAYTIME_MASK); 683c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_LFOVAL1, 0x8000); 684c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_LFOVAL2, 0x8000); 685c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_FMMOD, 0); 686c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_TREMFRQ, 0); 687c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_FM2FRQ2, 0); 688c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_ENVVAL, 0x8000); 6896c1146c0SCameron Grant 690c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_ATKHLDV, 691c7e0c9dbSPedro F. Giffuni EMU_CHAN_ATKHLDV_HOLDTIME_MASK | EMU_CHAN_ATKHLDV_ATTACKTIME_MASK); 692c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_ENVVOL, 0x8000); 6936c1146c0SCameron Grant 694c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_PEFE_FILTERAMOUNT, 0x7f); 695c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_PEFE_PITCHAMOUNT, 0); 6962d3ce9d5SCameron Grant 6972d3ce9d5SCameron Grant if (v->slave != NULL) 6982d3ce9d5SCameron Grant emu_vwrite(sc, v->slave); 699c067afaaSCameron Grant } 700c067afaaSCameron Grant 701c067afaaSCameron Grant static void 702c067afaaSCameron Grant emu_vtrigger(struct sc_info *sc, struct emu_voice *v, int go) 703c067afaaSCameron Grant { 7046c1146c0SCameron Grant u_int32_t pitch_target, initial_pitch; 7056c1146c0SCameron Grant u_int32_t cra, cs, ccis; 7066c1146c0SCameron Grant u_int32_t sample, i; 7076c1146c0SCameron Grant 708c067afaaSCameron Grant if (go) { 7096c1146c0SCameron Grant cra = 64; 7106c1146c0SCameron Grant cs = v->stereo ? 4 : 2; 7116c1146c0SCameron Grant ccis = v->stereo ? 28 : 30; 7126c1146c0SCameron Grant ccis *= v->b16 ? 1 : 2; 7136c1146c0SCameron Grant sample = v->b16 ? 0x00000000 : 0x80808080; 7146c1146c0SCameron Grant 7156c1146c0SCameron Grant for (i = 0; i < cs; i++) 716c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CD0 + i, sample); 717c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_CACHEINVALIDSIZE, 0); 718c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_READADDRESS, cra); 719c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_CACHEINVALIDSIZE, ccis); 7206c1146c0SCameron Grant 721c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_IFATN, 0xff00); 722c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, 0xffffffff); 723c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, 0xffffffff); 724c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_DCYSUSV, 0x00007f7f); 7256c1146c0SCameron Grant emu_enastop(sc, v->vnum, 0); 7266c1146c0SCameron Grant 7276c1146c0SCameron Grant pitch_target = emu_rate_to_linearpitch(v->speed); 7286c1146c0SCameron Grant initial_pitch = emu_rate_to_pitch(v->speed) >> 8; 729c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX_PITCHTARGET, pitch_target); 730c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CPF_PITCH, pitch_target); 731c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_IP, initial_pitch); 732c067afaaSCameron Grant } else { 733c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX_PITCHTARGET, 0); 734c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CPF_PITCH, 0); 735c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_IFATN, 0xffff); 736c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, 0x0000ffff); 737c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, 0x0000ffff); 738c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, v->vnum, EMU_CHAN_IP, 0); 7396c1146c0SCameron Grant emu_enastop(sc, v->vnum, 1); 740c067afaaSCameron Grant } 7412d3ce9d5SCameron Grant if (v->slave != NULL) 7422d3ce9d5SCameron Grant emu_vtrigger(sc, v->slave, go); 743c067afaaSCameron Grant } 744c067afaaSCameron Grant 745c067afaaSCameron Grant static int 746c067afaaSCameron Grant emu_vpos(struct sc_info *sc, struct emu_voice *v) 747c067afaaSCameron Grant { 74870776a9cSCameron Grant int s, ptr; 74970776a9cSCameron Grant 750c067afaaSCameron Grant s = (v->b16 ? 1 : 0) + (v->stereo ? 1 : 0); 751c7e0c9dbSPedro F. Giffuni ptr = (emu_rdptr(sc, v->vnum, EMU_CHAN_CCCA_CURRADDR) - (v->start >> s)) << s; 7526c1146c0SCameron Grant return ptr & ~0x0000001f; 753c067afaaSCameron Grant } 754c067afaaSCameron Grant 755c067afaaSCameron Grant #ifdef EMUDEBUG 756c067afaaSCameron Grant static void 757c067afaaSCameron Grant emu_vdump(struct sc_info *sc, struct emu_voice *v) 758c067afaaSCameron Grant { 75982d4d32dSDavid E. O'Brien char *regname[] = { 76082d4d32dSDavid E. O'Brien "cpf", "ptrx", "cvcf", "vtft", "z2", "z1", "psst", "dsl", 761c067afaaSCameron Grant "ccca", "ccr", "clp", "fxrt", "mapa", "mapb", NULL, NULL, 762c067afaaSCameron Grant "envvol", "atkhldv", "dcysusv", "lfoval1", 763c067afaaSCameron Grant "envval", "atkhldm", "dcysusm", "lfoval2", 764c067afaaSCameron Grant "ip", "ifatn", "pefe", "fmmod", "tremfrq", "fmfrq2", 76582d4d32dSDavid E. O'Brien "tempenv" 76682d4d32dSDavid E. O'Brien }; 76721f1e37cSDavid E. O'Brien char *regname2[] = { 76821f1e37cSDavid E. O'Brien "mudata1", "mustat1", "mudata2", "mustat2", 76921f1e37cSDavid E. O'Brien "fxwc1", "fxwc2", "spdrate", NULL, NULL, 77021f1e37cSDavid E. O'Brien NULL, NULL, NULL, "fxrt2", "sndamnt", "fxrt1", 77121f1e37cSDavid E. O'Brien NULL, NULL 77221f1e37cSDavid E. O'Brien }; 773c067afaaSCameron Grant int i, x; 774c067afaaSCameron Grant 775c067afaaSCameron Grant printf("voice number %d\n", v->vnum); 776c067afaaSCameron Grant for (i = 0, x = 0; i <= 0x1e; i++) { 777c067afaaSCameron Grant if (regname[i] == NULL) 778c067afaaSCameron Grant continue; 779c067afaaSCameron Grant printf("%s\t[%08x]", regname[i], emu_rdptr(sc, v->vnum, i)); 780c067afaaSCameron Grant printf("%s", (x == 2) ? "\n" : "\t"); 781c067afaaSCameron Grant x++; 782c067afaaSCameron Grant if (x > 2) 783c067afaaSCameron Grant x = 0; 784c067afaaSCameron Grant } 78521f1e37cSDavid E. O'Brien 78621f1e37cSDavid E. O'Brien /* Print out audigy extra registers */ 78721f1e37cSDavid E. O'Brien if (sc->audigy) { 78821f1e37cSDavid E. O'Brien for (i = 0; i <= 0xe; i++) { 78921f1e37cSDavid E. O'Brien if (regname2[i] == NULL) 79021f1e37cSDavid E. O'Brien continue; 79121f1e37cSDavid E. O'Brien printf("%s\t[%08x]", regname2[i], 79221f1e37cSDavid E. O'Brien emu_rdptr(sc, v->vnum, i + 0x70)); 79321f1e37cSDavid E. O'Brien printf("%s", (x == 2)? "\n" : "\t"); 79421f1e37cSDavid E. O'Brien x++; 79521f1e37cSDavid E. O'Brien if (x > 2) 79621f1e37cSDavid E. O'Brien x = 0; 79721f1e37cSDavid E. O'Brien } 79821f1e37cSDavid E. O'Brien } 799c067afaaSCameron Grant printf("\n\n"); 800c067afaaSCameron Grant } 801c067afaaSCameron Grant #endif 802c067afaaSCameron Grant 803c067afaaSCameron Grant /* channel interface */ 8040f55ac6cSCameron Grant static void * 80521f1e37cSDavid E. O'Brien emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 80621f1e37cSDavid E. O'Brien struct pcm_channel *c, int dir) 807c067afaaSCameron Grant { 808c067afaaSCameron Grant struct sc_info *sc = devinfo; 80970776a9cSCameron Grant struct sc_pchinfo *ch; 81066ef8af5SCameron Grant void *r; 811c067afaaSCameron Grant 81270776a9cSCameron Grant KASSERT(dir == PCMDIR_PLAY, ("emupchan_init: bad direction")); 81370776a9cSCameron Grant ch = &sc->pch[sc->pnum++]; 814c067afaaSCameron Grant ch->buffer = b; 815c067afaaSCameron Grant ch->parent = sc; 816c067afaaSCameron Grant ch->channel = c; 817a791cfeeSCameron Grant ch->blksz = sc->bufsz / 2; 81890da2b28SAriff Abdullah ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); 819350a5fafSCameron Grant ch->spd = 8000; 82066ef8af5SCameron Grant snd_mtxlock(sc->lock); 821c067afaaSCameron Grant ch->master = emu_valloc(sc); 822c067afaaSCameron Grant ch->slave = emu_valloc(sc); 82366ef8af5SCameron Grant snd_mtxunlock(sc->lock); 82421f1e37cSDavid E. O'Brien r = (emu_vinit(sc, ch->master, ch->slave, sc->bufsz, ch->buffer)) 82521f1e37cSDavid E. O'Brien ? NULL : ch; 82666ef8af5SCameron Grant 82766ef8af5SCameron Grant return r; 828c067afaaSCameron Grant } 829c067afaaSCameron Grant 830c067afaaSCameron Grant static int 8310f55ac6cSCameron Grant emupchan_free(kobj_t obj, void *data) 83233dbf14aSCameron Grant { 83333dbf14aSCameron Grant struct sc_pchinfo *ch = data; 83433dbf14aSCameron Grant struct sc_info *sc = ch->parent; 83566ef8af5SCameron Grant int r; 83633dbf14aSCameron Grant 83766ef8af5SCameron Grant snd_mtxlock(sc->lock); 83866ef8af5SCameron Grant r = emu_memfree(sc, sndbuf_getbuf(ch->buffer)); 83966ef8af5SCameron Grant snd_mtxunlock(sc->lock); 84066ef8af5SCameron Grant 84166ef8af5SCameron Grant return r; 84233dbf14aSCameron Grant } 84333dbf14aSCameron Grant 84433dbf14aSCameron Grant static int 8450f55ac6cSCameron Grant emupchan_setformat(kobj_t obj, void *data, u_int32_t format) 846c067afaaSCameron Grant { 84770776a9cSCameron Grant struct sc_pchinfo *ch = data; 848c067afaaSCameron Grant 849c067afaaSCameron Grant ch->fmt = format; 850c067afaaSCameron Grant return 0; 851c067afaaSCameron Grant } 852c067afaaSCameron Grant 85390da2b28SAriff Abdullah static u_int32_t 8540f55ac6cSCameron Grant emupchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 855c067afaaSCameron Grant { 85670776a9cSCameron Grant struct sc_pchinfo *ch = data; 857c067afaaSCameron Grant 858c067afaaSCameron Grant ch->spd = speed; 859c067afaaSCameron Grant return ch->spd; 860c067afaaSCameron Grant } 861c067afaaSCameron Grant 86290da2b28SAriff Abdullah static u_int32_t 8630f55ac6cSCameron Grant emupchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 864c067afaaSCameron Grant { 865350a5fafSCameron Grant struct sc_pchinfo *ch = data; 866350a5fafSCameron Grant struct sc_info *sc = ch->parent; 867350a5fafSCameron Grant int irqrate, blksz; 868350a5fafSCameron Grant 869350a5fafSCameron Grant ch->blksz = blocksize; 87066ef8af5SCameron Grant snd_mtxlock(sc->lock); 871350a5fafSCameron Grant emu_settimer(sc); 872350a5fafSCameron Grant irqrate = 48000 / sc->timerinterval; 87366ef8af5SCameron Grant snd_mtxunlock(sc->lock); 87490da2b28SAriff Abdullah blksz = (ch->spd * sndbuf_getalign(ch->buffer)) / irqrate; 875c067afaaSCameron Grant return blocksize; 876c067afaaSCameron Grant } 877c067afaaSCameron Grant 878c067afaaSCameron Grant static int 8790f55ac6cSCameron Grant emupchan_trigger(kobj_t obj, void *data, int go) 880c067afaaSCameron Grant { 88170776a9cSCameron Grant struct sc_pchinfo *ch = data; 882c067afaaSCameron Grant struct sc_info *sc = ch->parent; 883c067afaaSCameron Grant 884bdfbdcecSAriff Abdullah if (!PCMTRIG_COMMON(go)) 88535f9e4a1SCameron Grant return 0; 88635f9e4a1SCameron Grant 88766ef8af5SCameron Grant snd_mtxlock(sc->lock); 888c067afaaSCameron Grant if (go == PCMTRIG_START) { 889c067afaaSCameron Grant emu_vsetup(ch); 890c067afaaSCameron Grant emu_vwrite(sc, ch->master); 891350a5fafSCameron Grant emu_settimer(sc); 8926c1146c0SCameron Grant emu_enatimer(sc, 1); 893c067afaaSCameron Grant #ifdef EMUDEBUG 894c067afaaSCameron Grant printf("start [%d bit, %s, %d hz]\n", 895c067afaaSCameron Grant ch->master->b16 ? 16 : 8, 896c067afaaSCameron Grant ch->master->stereo ? "stereo" : "mono", 897c067afaaSCameron Grant ch->master->speed); 898c067afaaSCameron Grant emu_vdump(sc, ch->master); 899c067afaaSCameron Grant emu_vdump(sc, ch->slave); 900c067afaaSCameron Grant #endif 901c067afaaSCameron Grant } 90270776a9cSCameron Grant ch->run = (go == PCMTRIG_START) ? 1 : 0; 90370776a9cSCameron Grant emu_vtrigger(sc, ch->master, ch->run); 90466ef8af5SCameron Grant snd_mtxunlock(sc->lock); 905c067afaaSCameron Grant return 0; 906c067afaaSCameron Grant } 907c067afaaSCameron Grant 90890da2b28SAriff Abdullah static u_int32_t 9090f55ac6cSCameron Grant emupchan_getptr(kobj_t obj, void *data) 910c067afaaSCameron Grant { 91170776a9cSCameron Grant struct sc_pchinfo *ch = data; 912c067afaaSCameron Grant struct sc_info *sc = ch->parent; 91366ef8af5SCameron Grant int r; 914c067afaaSCameron Grant 91566ef8af5SCameron Grant snd_mtxlock(sc->lock); 91666ef8af5SCameron Grant r = emu_vpos(sc, ch->master); 91766ef8af5SCameron Grant snd_mtxunlock(sc->lock); 91866ef8af5SCameron Grant 91966ef8af5SCameron Grant return r; 920c067afaaSCameron Grant } 921c067afaaSCameron Grant 92266ef8af5SCameron Grant static struct pcmchan_caps * 9230f55ac6cSCameron Grant emupchan_getcaps(kobj_t obj, void *data) 924c067afaaSCameron Grant { 92570776a9cSCameron Grant return &emu_playcaps; 92670776a9cSCameron Grant } 927c067afaaSCameron Grant 9280f55ac6cSCameron Grant static kobj_method_t emupchan_methods[] = { 9290f55ac6cSCameron Grant KOBJMETHOD(channel_init, emupchan_init), 9300f55ac6cSCameron Grant KOBJMETHOD(channel_free, emupchan_free), 9310f55ac6cSCameron Grant KOBJMETHOD(channel_setformat, emupchan_setformat), 9320f55ac6cSCameron Grant KOBJMETHOD(channel_setspeed, emupchan_setspeed), 9330f55ac6cSCameron Grant KOBJMETHOD(channel_setblocksize, emupchan_setblocksize), 9340f55ac6cSCameron Grant KOBJMETHOD(channel_trigger, emupchan_trigger), 9350f55ac6cSCameron Grant KOBJMETHOD(channel_getptr, emupchan_getptr), 9360f55ac6cSCameron Grant KOBJMETHOD(channel_getcaps, emupchan_getcaps), 93790da2b28SAriff Abdullah KOBJMETHOD_END 9380f55ac6cSCameron Grant }; 9390f55ac6cSCameron Grant CHANNEL_DECLARE(emupchan); 9400f55ac6cSCameron Grant 94170776a9cSCameron Grant /* channel interface */ 94270776a9cSCameron Grant static void * 94321f1e37cSDavid E. O'Brien emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 94421f1e37cSDavid E. O'Brien struct pcm_channel *c, int dir) 94570776a9cSCameron Grant { 94670776a9cSCameron Grant struct sc_info *sc = devinfo; 94770776a9cSCameron Grant struct sc_rchinfo *ch; 94870776a9cSCameron Grant 9496c1146c0SCameron Grant KASSERT(dir == PCMDIR_REC, ("emurchan_init: bad direction")); 95070776a9cSCameron Grant ch = &sc->rch[sc->rnum]; 95170776a9cSCameron Grant ch->buffer = b; 95270776a9cSCameron Grant ch->parent = sc; 95370776a9cSCameron Grant ch->channel = c; 954a791cfeeSCameron Grant ch->blksz = sc->bufsz / 2; 95590da2b28SAriff Abdullah ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); 95670776a9cSCameron Grant ch->spd = 8000; 95770776a9cSCameron Grant ch->num = sc->rnum; 95870776a9cSCameron Grant switch(sc->rnum) { 95970776a9cSCameron Grant case 0: 960c7e0c9dbSPedro F. Giffuni ch->idxreg = sc->audigy ? EMU_A_ADCIDX : EMU_ADCIDX; 961c7e0c9dbSPedro F. Giffuni ch->basereg = EMU_ADCBA; 962c7e0c9dbSPedro F. Giffuni ch->sizereg = EMU_ADCBS; 963c7e0c9dbSPedro F. Giffuni ch->setupreg = EMU_ADCCR; 964c7e0c9dbSPedro F. Giffuni ch->irqmask = EMU_INTE_ADCBUFENABLE; 96570776a9cSCameron Grant break; 96670776a9cSCameron Grant 96770776a9cSCameron Grant case 1: 968c7e0c9dbSPedro F. Giffuni ch->idxreg = EMU_FXIDX; 969c7e0c9dbSPedro F. Giffuni ch->basereg = EMU_FXBA; 970c7e0c9dbSPedro F. Giffuni ch->sizereg = EMU_FXBS; 971c7e0c9dbSPedro F. Giffuni ch->setupreg = EMU_FXWC; 972c7e0c9dbSPedro F. Giffuni ch->irqmask = EMU_INTE_EFXBUFENABLE; 97370776a9cSCameron Grant break; 974a791cfeeSCameron Grant 975a791cfeeSCameron Grant case 2: 976c7e0c9dbSPedro F. Giffuni ch->idxreg = EMU_MICIDX; 977c7e0c9dbSPedro F. Giffuni ch->basereg = EMU_MICBA; 978c7e0c9dbSPedro F. Giffuni ch->sizereg = EMU_MICBS; 979a791cfeeSCameron Grant ch->setupreg = 0; 980c7e0c9dbSPedro F. Giffuni ch->irqmask = EMU_INTE_MICBUFENABLE; 981a791cfeeSCameron Grant break; 98270776a9cSCameron Grant } 98370776a9cSCameron Grant sc->rnum++; 9842e334adfSAriff Abdullah if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) 98570776a9cSCameron Grant return NULL; 98670776a9cSCameron Grant else { 98766ef8af5SCameron Grant snd_mtxlock(sc->lock); 98838cc9942SOlivier Houchard emu_wrptr(sc, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer)); 98970776a9cSCameron Grant emu_wrptr(sc, 0, ch->sizereg, 0); /* off */ 99066ef8af5SCameron Grant snd_mtxunlock(sc->lock); 99170776a9cSCameron Grant return ch; 99270776a9cSCameron Grant } 99370776a9cSCameron Grant } 99470776a9cSCameron Grant 99570776a9cSCameron Grant static int 9960f55ac6cSCameron Grant emurchan_setformat(kobj_t obj, void *data, u_int32_t format) 99770776a9cSCameron Grant { 99870776a9cSCameron Grant struct sc_rchinfo *ch = data; 99970776a9cSCameron Grant 100070776a9cSCameron Grant ch->fmt = format; 100170776a9cSCameron Grant return 0; 100270776a9cSCameron Grant } 100370776a9cSCameron Grant 100490da2b28SAriff Abdullah static u_int32_t 10050f55ac6cSCameron Grant emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 100670776a9cSCameron Grant { 100770776a9cSCameron Grant struct sc_rchinfo *ch = data; 100870776a9cSCameron Grant 100921f1e37cSDavid E. O'Brien if (ch->num == 0) { 101021f1e37cSDavid E. O'Brien if (ch->parent->audigy) 101121f1e37cSDavid E. O'Brien speed = audigy_adcspeed[audigy_recval(speed)]; 101221f1e37cSDavid E. O'Brien else 101370776a9cSCameron Grant speed = adcspeed[emu_recval(speed)]; 101421f1e37cSDavid E. O'Brien } 101570776a9cSCameron Grant if (ch->num == 1) 101670776a9cSCameron Grant speed = 48000; 1017a791cfeeSCameron Grant if (ch->num == 2) 1018a791cfeeSCameron Grant speed = 8000; 101970776a9cSCameron Grant ch->spd = speed; 102070776a9cSCameron Grant return ch->spd; 102170776a9cSCameron Grant } 102270776a9cSCameron Grant 102390da2b28SAriff Abdullah static u_int32_t 10240f55ac6cSCameron Grant emurchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 102570776a9cSCameron Grant { 1026350a5fafSCameron Grant struct sc_rchinfo *ch = data; 1027350a5fafSCameron Grant struct sc_info *sc = ch->parent; 1028350a5fafSCameron Grant int irqrate, blksz; 1029350a5fafSCameron Grant 1030350a5fafSCameron Grant ch->blksz = blocksize; 103166ef8af5SCameron Grant snd_mtxlock(sc->lock); 1032350a5fafSCameron Grant emu_settimer(sc); 1033350a5fafSCameron Grant irqrate = 48000 / sc->timerinterval; 103466ef8af5SCameron Grant snd_mtxunlock(sc->lock); 103590da2b28SAriff Abdullah blksz = (ch->spd * sndbuf_getalign(ch->buffer)) / irqrate; 103670776a9cSCameron Grant return blocksize; 103770776a9cSCameron Grant } 103870776a9cSCameron Grant 103970776a9cSCameron Grant /* semantic note: must start at beginning of buffer */ 104070776a9cSCameron Grant static int 10410f55ac6cSCameron Grant emurchan_trigger(kobj_t obj, void *data, int go) 104270776a9cSCameron Grant { 104370776a9cSCameron Grant struct sc_rchinfo *ch = data; 104470776a9cSCameron Grant struct sc_info *sc = ch->parent; 1045a791cfeeSCameron Grant u_int32_t val, sz; 1046a791cfeeSCameron Grant 1047bdfbdcecSAriff Abdullah if (!PCMTRIG_COMMON(go)) 1048bdfbdcecSAriff Abdullah return 0; 1049bdfbdcecSAriff Abdullah 1050a791cfeeSCameron Grant switch(sc->bufsz) { 1051a791cfeeSCameron Grant case 4096: 1052c7e0c9dbSPedro F. Giffuni sz = EMU_RECBS_BUFSIZE_4096; 1053a791cfeeSCameron Grant break; 1054a791cfeeSCameron Grant 1055a791cfeeSCameron Grant case 8192: 1056c7e0c9dbSPedro F. Giffuni sz = EMU_RECBS_BUFSIZE_8192; 1057a791cfeeSCameron Grant break; 1058a791cfeeSCameron Grant 1059a791cfeeSCameron Grant case 16384: 1060c7e0c9dbSPedro F. Giffuni sz = EMU_RECBS_BUFSIZE_16384; 1061a791cfeeSCameron Grant break; 1062a791cfeeSCameron Grant 1063a791cfeeSCameron Grant case 32768: 1064c7e0c9dbSPedro F. Giffuni sz = EMU_RECBS_BUFSIZE_32768; 1065a791cfeeSCameron Grant break; 1066a791cfeeSCameron Grant 1067a791cfeeSCameron Grant case 65536: 1068c7e0c9dbSPedro F. Giffuni sz = EMU_RECBS_BUFSIZE_65536; 1069a791cfeeSCameron Grant break; 1070a791cfeeSCameron Grant 1071a791cfeeSCameron Grant default: 1072c7e0c9dbSPedro F. Giffuni sz = EMU_RECBS_BUFSIZE_4096; 1073a791cfeeSCameron Grant } 107470776a9cSCameron Grant 107566ef8af5SCameron Grant snd_mtxlock(sc->lock); 107670776a9cSCameron Grant switch(go) { 107770776a9cSCameron Grant case PCMTRIG_START: 107870776a9cSCameron Grant ch->run = 1; 1079a791cfeeSCameron Grant emu_wrptr(sc, 0, ch->sizereg, sz); 108070776a9cSCameron Grant if (ch->num == 0) { 108121f1e37cSDavid E. O'Brien if (sc->audigy) { 10827523592fSPedro F. Giffuni val = EMU_A_ADCCR_LCHANENABLE; 108390da2b28SAriff Abdullah if (AFMT_CHANNEL(ch->fmt) > 1) 10847523592fSPedro F. Giffuni val |= EMU_A_ADCCR_RCHANENABLE; 108521f1e37cSDavid E. O'Brien val |= audigy_recval(ch->spd); 108621f1e37cSDavid E. O'Brien } else { 1087c7e0c9dbSPedro F. Giffuni val = EMU_ADCCR_LCHANENABLE; 108890da2b28SAriff Abdullah if (AFMT_CHANNEL(ch->fmt) > 1) 1089c7e0c9dbSPedro F. Giffuni val |= EMU_ADCCR_RCHANENABLE; 109070776a9cSCameron Grant val |= emu_recval(ch->spd); 109121f1e37cSDavid E. O'Brien } 109221f1e37cSDavid E. O'Brien 1093a791cfeeSCameron Grant emu_wrptr(sc, 0, ch->setupreg, 0); 109470776a9cSCameron Grant emu_wrptr(sc, 0, ch->setupreg, val); 109570776a9cSCameron Grant } 1096c7e0c9dbSPedro F. Giffuni val = emu_rd(sc, EMU_INTE, 4); 109770776a9cSCameron Grant val |= ch->irqmask; 1098c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_INTE, val, 4); 109970776a9cSCameron Grant break; 110070776a9cSCameron Grant 110170776a9cSCameron Grant case PCMTRIG_STOP: 110270776a9cSCameron Grant case PCMTRIG_ABORT: 110370776a9cSCameron Grant ch->run = 0; 110470776a9cSCameron Grant emu_wrptr(sc, 0, ch->sizereg, 0); 110570776a9cSCameron Grant if (ch->setupreg) 110670776a9cSCameron Grant emu_wrptr(sc, 0, ch->setupreg, 0); 1107c7e0c9dbSPedro F. Giffuni val = emu_rd(sc, EMU_INTE, 4); 110870776a9cSCameron Grant val &= ~ch->irqmask; 1109c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_INTE, val, 4); 111070776a9cSCameron Grant break; 111170776a9cSCameron Grant 111270776a9cSCameron Grant case PCMTRIG_EMLDMAWR: 111370776a9cSCameron Grant case PCMTRIG_EMLDMARD: 111470776a9cSCameron Grant default: 111570776a9cSCameron Grant break; 111670776a9cSCameron Grant } 111766ef8af5SCameron Grant snd_mtxunlock(sc->lock); 111870776a9cSCameron Grant 111970776a9cSCameron Grant return 0; 112070776a9cSCameron Grant } 112170776a9cSCameron Grant 112290da2b28SAriff Abdullah static u_int32_t 11230f55ac6cSCameron Grant emurchan_getptr(kobj_t obj, void *data) 112470776a9cSCameron Grant { 112570776a9cSCameron Grant struct sc_rchinfo *ch = data; 112670776a9cSCameron Grant struct sc_info *sc = ch->parent; 112766ef8af5SCameron Grant int r; 112870776a9cSCameron Grant 112966ef8af5SCameron Grant snd_mtxlock(sc->lock); 113066ef8af5SCameron Grant r = emu_rdptr(sc, 0, ch->idxreg) & 0x0000ffff; 113166ef8af5SCameron Grant snd_mtxunlock(sc->lock); 113266ef8af5SCameron Grant 113366ef8af5SCameron Grant return r; 113470776a9cSCameron Grant } 113570776a9cSCameron Grant 113666ef8af5SCameron Grant static struct pcmchan_caps * 11370f55ac6cSCameron Grant emurchan_getcaps(kobj_t obj, void *data) 113870776a9cSCameron Grant { 113970776a9cSCameron Grant struct sc_rchinfo *ch = data; 114070776a9cSCameron Grant 114170776a9cSCameron Grant return &emu_reccaps[ch->num]; 1142c067afaaSCameron Grant } 1143c067afaaSCameron Grant 1144a791cfeeSCameron Grant static kobj_method_t emurchan_methods[] = { 1145a791cfeeSCameron Grant KOBJMETHOD(channel_init, emurchan_init), 1146a791cfeeSCameron Grant KOBJMETHOD(channel_setformat, emurchan_setformat), 1147a791cfeeSCameron Grant KOBJMETHOD(channel_setspeed, emurchan_setspeed), 1148a791cfeeSCameron Grant KOBJMETHOD(channel_setblocksize, emurchan_setblocksize), 1149a791cfeeSCameron Grant KOBJMETHOD(channel_trigger, emurchan_trigger), 1150a791cfeeSCameron Grant KOBJMETHOD(channel_getptr, emurchan_getptr), 1151a791cfeeSCameron Grant KOBJMETHOD(channel_getcaps, emurchan_getcaps), 115290da2b28SAriff Abdullah KOBJMETHOD_END 1153a791cfeeSCameron Grant }; 1154a791cfeeSCameron Grant CHANNEL_DECLARE(emurchan); 1155a791cfeeSCameron Grant 1156f510d240SAlexander Leidinger static unsigned char 115790da2b28SAriff Abdullah emu_mread(struct mpu401 *arg, void *sc, int reg) 1158f510d240SAlexander Leidinger { 1159f510d240SAlexander Leidinger unsigned int d; 1160f510d240SAlexander Leidinger 116190da2b28SAriff Abdullah d = emu_rd((struct sc_info *)sc, 0x18 + reg, 1); 1162f510d240SAlexander Leidinger return d; 1163f510d240SAlexander Leidinger } 1164f510d240SAlexander Leidinger 1165f510d240SAlexander Leidinger static void 116690da2b28SAriff Abdullah emu_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b) 1167f510d240SAlexander Leidinger { 1168f510d240SAlexander Leidinger 116990da2b28SAriff Abdullah emu_wr((struct sc_info *)sc, 0x18 + reg, b, 1); 1170f510d240SAlexander Leidinger } 1171f510d240SAlexander Leidinger 1172f510d240SAlexander Leidinger static int 117390da2b28SAriff Abdullah emu_muninit(struct mpu401 *arg, void *cookie) 1174f510d240SAlexander Leidinger { 117590da2b28SAriff Abdullah struct sc_info *sc = cookie; 1176f510d240SAlexander Leidinger 1177f510d240SAlexander Leidinger snd_mtxlock(sc->lock); 1178f510d240SAlexander Leidinger sc->mpu_intr = 0; 1179f510d240SAlexander Leidinger snd_mtxunlock(sc->lock); 1180f510d240SAlexander Leidinger 1181f510d240SAlexander Leidinger return 0; 1182f510d240SAlexander Leidinger } 1183f510d240SAlexander Leidinger 1184f510d240SAlexander Leidinger static kobj_method_t emu_mpu_methods[] = { 1185f510d240SAlexander Leidinger KOBJMETHOD(mpufoi_read, emu_mread), 1186f510d240SAlexander Leidinger KOBJMETHOD(mpufoi_write, emu_mwrite), 1187f510d240SAlexander Leidinger KOBJMETHOD(mpufoi_uninit, emu_muninit), 118890da2b28SAriff Abdullah KOBJMETHOD_END 1189f510d240SAlexander Leidinger }; 1190f510d240SAlexander Leidinger 119175d7240eSAlexander Leidinger static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0); 1192f510d240SAlexander Leidinger 1193f510d240SAlexander Leidinger static void 1194f510d240SAlexander Leidinger emu_intr2(void *p) 1195f510d240SAlexander Leidinger { 1196f510d240SAlexander Leidinger struct sc_info *sc = (struct sc_info *)p; 1197f510d240SAlexander Leidinger 1198f510d240SAlexander Leidinger if (sc->mpu_intr) 1199f510d240SAlexander Leidinger (sc->mpu_intr)(sc->mpu); 1200f510d240SAlexander Leidinger } 1201f510d240SAlexander Leidinger 1202f510d240SAlexander Leidinger static void 1203f510d240SAlexander Leidinger emu_midiattach(struct sc_info *sc) 1204f510d240SAlexander Leidinger { 1205f510d240SAlexander Leidinger int i; 1206f510d240SAlexander Leidinger 1207c7e0c9dbSPedro F. Giffuni i = emu_rd(sc, EMU_INTE, 4); 1208c7e0c9dbSPedro F. Giffuni i |= EMU_INTE_MIDIRXENABLE; 1209c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_INTE, i, 4); 1210f510d240SAlexander Leidinger 1211f510d240SAlexander Leidinger sc->mpu = mpu401_init(&emu_mpu_class, sc, emu_intr2, &sc->mpu_intr); 1212f510d240SAlexander Leidinger } 1213a791cfeeSCameron Grant /* -------------------------------------------------------------------- */ 1214c067afaaSCameron Grant /* The interrupt handler */ 1215f510d240SAlexander Leidinger 1216c067afaaSCameron Grant static void 121705478063SMaxime Henrion emu_intr(void *data) 1218c067afaaSCameron Grant { 121905478063SMaxime Henrion struct sc_info *sc = data; 12206c1146c0SCameron Grant u_int32_t stat, ack, i, x; 1221c067afaaSCameron Grant 122205478063SMaxime Henrion snd_mtxlock(sc->lock); 122370776a9cSCameron Grant while (1) { 1224c7e0c9dbSPedro F. Giffuni stat = emu_rd(sc, EMU_IPR, 4); 122570776a9cSCameron Grant if (stat == 0) 122670776a9cSCameron Grant break; 122770776a9cSCameron Grant ack = 0; 1228c067afaaSCameron Grant 1229c067afaaSCameron Grant /* process irq */ 1230c7e0c9dbSPedro F. Giffuni if (stat & EMU_IPR_INTERVALTIMER) 1231c7e0c9dbSPedro F. Giffuni ack |= EMU_IPR_INTERVALTIMER; 123270776a9cSCameron Grant 1233c7e0c9dbSPedro F. Giffuni if (stat & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL)) 1234c7e0c9dbSPedro F. Giffuni ack |= stat & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL); 123505478063SMaxime Henrion 1236c7e0c9dbSPedro F. Giffuni if (stat & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL)) 1237c7e0c9dbSPedro F. Giffuni ack |= stat & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL); 123805478063SMaxime Henrion 1239c7e0c9dbSPedro F. Giffuni if (stat & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL)) 1240c7e0c9dbSPedro F. Giffuni ack |= stat & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL); 124105478063SMaxime Henrion 1242c7e0c9dbSPedro F. Giffuni if (stat & EMU_PCIERROR) { 1243c7e0c9dbSPedro F. Giffuni ack |= EMU_PCIERROR; 124470776a9cSCameron Grant device_printf(sc->dev, "pci error\n"); 12456c1146c0SCameron Grant /* we still get an nmi with ecc ram even if we ack this */ 12466c1146c0SCameron Grant } 1247c7e0c9dbSPedro F. Giffuni if (stat & EMU_IPR_RATETRCHANGE) { 1248c7e0c9dbSPedro F. Giffuni ack |= EMU_IPR_RATETRCHANGE; 124921f1e37cSDavid E. O'Brien #ifdef EMUDEBUG 125021f1e37cSDavid E. O'Brien device_printf(sc->dev, 125121f1e37cSDavid E. O'Brien "sample rate tracker lock status change\n"); 125221f1e37cSDavid E. O'Brien #endif 125370776a9cSCameron Grant } 125470776a9cSCameron Grant 1255c7e0c9dbSPedro F. Giffuni if (stat & EMU_IPR_MIDIRECVBUFE) 1256f510d240SAlexander Leidinger if (sc->mpu_intr) { 1257f510d240SAlexander Leidinger (sc->mpu_intr)(sc->mpu); 1258c7e0c9dbSPedro F. Giffuni ack |= EMU_IPR_MIDIRECVBUFE | EMU_IPR_MIDITRANSBUFE; 1259f510d240SAlexander Leidinger } 126070776a9cSCameron Grant if (stat & ~ack) 126105478063SMaxime Henrion device_printf(sc->dev, "dodgy irq: %x (harmless)\n", 126205478063SMaxime Henrion stat & ~ack); 1263c067afaaSCameron Grant 1264c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_IPR, stat, 4); 126505478063SMaxime Henrion 126605478063SMaxime Henrion if (ack) { 126705478063SMaxime Henrion snd_mtxunlock(sc->lock); 126805478063SMaxime Henrion 1269c7e0c9dbSPedro F. Giffuni if (ack & EMU_IPR_INTERVALTIMER) { 127005478063SMaxime Henrion x = 0; 127105478063SMaxime Henrion for (i = 0; i < sc->nchans; i++) { 127205478063SMaxime Henrion if (sc->pch[i].run) { 127305478063SMaxime Henrion x = 1; 127405478063SMaxime Henrion chn_intr(sc->pch[i].channel); 127570776a9cSCameron Grant } 1276c067afaaSCameron Grant } 127705478063SMaxime Henrion if (x == 0) 127805478063SMaxime Henrion emu_enatimer(sc, 0); 127905478063SMaxime Henrion } 128005478063SMaxime Henrion 128105478063SMaxime Henrion 1282c7e0c9dbSPedro F. Giffuni if (ack & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL)) { 128305478063SMaxime Henrion if (sc->rch[0].channel) 128405478063SMaxime Henrion chn_intr(sc->rch[0].channel); 128505478063SMaxime Henrion } 1286c7e0c9dbSPedro F. Giffuni if (ack & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL)) { 128705478063SMaxime Henrion if (sc->rch[1].channel) 128805478063SMaxime Henrion chn_intr(sc->rch[1].channel); 128905478063SMaxime Henrion } 1290c7e0c9dbSPedro F. Giffuni if (ack & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL)) { 129105478063SMaxime Henrion if (sc->rch[2].channel) 129205478063SMaxime Henrion chn_intr(sc->rch[2].channel); 129305478063SMaxime Henrion } 129405478063SMaxime Henrion 129505478063SMaxime Henrion snd_mtxlock(sc->lock); 129605478063SMaxime Henrion } 129705478063SMaxime Henrion } 129805478063SMaxime Henrion snd_mtxunlock(sc->lock); 129905478063SMaxime Henrion } 1300c067afaaSCameron Grant 1301c067afaaSCameron Grant /* -------------------------------------------------------------------- */ 1302c067afaaSCameron Grant 130370776a9cSCameron Grant static void 130470776a9cSCameron Grant emu_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 130570776a9cSCameron Grant { 130633673595SOlivier Houchard bus_addr_t *phys = arg; 130770776a9cSCameron Grant 130833673595SOlivier Houchard *phys = error ? 0 : (bus_addr_t)segs->ds_addr; 130970776a9cSCameron Grant 131070776a9cSCameron Grant if (bootverbose) { 131170776a9cSCameron Grant printf("emu: setmap (%lx, %lx), nseg=%d, error=%d\n", 131270776a9cSCameron Grant (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, 131370776a9cSCameron Grant nseg, error); 131470776a9cSCameron Grant } 131570776a9cSCameron Grant } 131670776a9cSCameron Grant 1317c067afaaSCameron Grant static void * 131838cc9942SOlivier Houchard emu_malloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr) 1319c067afaaSCameron Grant { 132038cc9942SOlivier Houchard void *buf; 1321c067afaaSCameron Grant bus_dmamap_t map; 1322c067afaaSCameron Grant 132338cc9942SOlivier Houchard *addr = 0; 1324c067afaaSCameron Grant if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map)) 1325c067afaaSCameron Grant return NULL; 132638cc9942SOlivier Houchard if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, emu_setmap, addr, 0) 132733673595SOlivier Houchard || !*addr) 132870776a9cSCameron Grant return NULL; 1329c067afaaSCameron Grant return buf; 1330c067afaaSCameron Grant } 1331c067afaaSCameron Grant 1332c067afaaSCameron Grant static void 1333c067afaaSCameron Grant emu_free(struct sc_info *sc, void *buf) 1334c067afaaSCameron Grant { 1335c067afaaSCameron Grant bus_dmamem_free(sc->parent_dmat, buf, NULL); 1336c067afaaSCameron Grant } 1337c067afaaSCameron Grant 1338c067afaaSCameron Grant static void * 133938cc9942SOlivier Houchard emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr) 1340c067afaaSCameron Grant { 1341c067afaaSCameron Grant u_int32_t blksz, start, idx, ofs, tmp, found; 1342c067afaaSCameron Grant struct emu_mem *mem = &sc->mem; 1343c067afaaSCameron Grant struct emu_memblk *blk; 1344c067afaaSCameron Grant void *buf; 1345c067afaaSCameron Grant 1346c067afaaSCameron Grant blksz = sz / EMUPAGESIZE; 1347c067afaaSCameron Grant if (sz > (blksz * EMUPAGESIZE)) 1348c067afaaSCameron Grant blksz++; 1349c067afaaSCameron Grant /* find a free block in the bitmap */ 1350c067afaaSCameron Grant found = 0; 13516c1146c0SCameron Grant start = 1; 135221f1e37cSDavid E. O'Brien while (!found && start + blksz < EMUMAXPAGES) { 1353c067afaaSCameron Grant found = 1; 1354c067afaaSCameron Grant for (idx = start; idx < start + blksz; idx++) 1355c067afaaSCameron Grant if (mem->bmap[idx >> 3] & (1 << (idx & 7))) 1356c067afaaSCameron Grant found = 0; 1357c067afaaSCameron Grant if (!found) 1358c067afaaSCameron Grant start++; 1359c067afaaSCameron Grant } 1360c067afaaSCameron Grant if (!found) 1361c067afaaSCameron Grant return NULL; 1362c067afaaSCameron Grant blk = malloc(sizeof(*blk), M_DEVBUF, M_NOWAIT); 1363c067afaaSCameron Grant if (blk == NULL) 1364c067afaaSCameron Grant return NULL; 136538cc9942SOlivier Houchard buf = emu_malloc(sc, sz, &blk->buf_addr); 136638cc9942SOlivier Houchard *addr = blk->buf_addr; 1367c067afaaSCameron Grant if (buf == NULL) { 1368c067afaaSCameron Grant free(blk, M_DEVBUF); 1369c067afaaSCameron Grant return NULL; 1370c067afaaSCameron Grant } 1371c067afaaSCameron Grant blk->buf = buf; 1372c067afaaSCameron Grant blk->pte_start = start; 1373c067afaaSCameron Grant blk->pte_size = blksz; 137421f1e37cSDavid E. O'Brien #ifdef EMUDEBUG 137521f1e37cSDavid E. O'Brien printf("buf %p, pte_start %d, pte_size %d\n", blk->buf, 137621f1e37cSDavid E. O'Brien blk->pte_start, blk->pte_size); 137721f1e37cSDavid E. O'Brien #endif 1378c067afaaSCameron Grant ofs = 0; 1379c067afaaSCameron Grant for (idx = start; idx < start + blksz; idx++) { 1380c067afaaSCameron Grant mem->bmap[idx >> 3] |= 1 << (idx & 7); 1381*3ac12483SMarius Strobl tmp = (bus_addr_t)((u_int8_t *)(uintptr_t)blk->buf_addr + ofs); 138221f1e37cSDavid E. O'Brien #ifdef EMUDEBUG 138321f1e37cSDavid E. O'Brien printf("pte[%d] -> %x phys, %x virt\n", idx, tmp, 138421f1e37cSDavid E. O'Brien ((u_int32_t)buf) + ofs); 138521f1e37cSDavid E. O'Brien #endif 138619b6ac09SCameron Grant mem->ptb_pages[idx] = (tmp << 1) | idx; 1387c067afaaSCameron Grant ofs += EMUPAGESIZE; 1388c067afaaSCameron Grant } 1389c067afaaSCameron Grant SLIST_INSERT_HEAD(&mem->blocks, blk, link); 1390c067afaaSCameron Grant return buf; 1391c067afaaSCameron Grant } 1392c067afaaSCameron Grant 1393c067afaaSCameron Grant static int 1394c067afaaSCameron Grant emu_memfree(struct sc_info *sc, void *buf) 1395c067afaaSCameron Grant { 1396c067afaaSCameron Grant u_int32_t idx, tmp; 1397c067afaaSCameron Grant struct emu_mem *mem = &sc->mem; 1398c067afaaSCameron Grant struct emu_memblk *blk, *i; 1399c067afaaSCameron Grant 1400c067afaaSCameron Grant blk = NULL; 1401c067afaaSCameron Grant SLIST_FOREACH(i, &mem->blocks, link) { 1402c067afaaSCameron Grant if (i->buf == buf) 1403c067afaaSCameron Grant blk = i; 1404c067afaaSCameron Grant } 1405c067afaaSCameron Grant if (blk == NULL) 1406c067afaaSCameron Grant return EINVAL; 1407c067afaaSCameron Grant SLIST_REMOVE(&mem->blocks, blk, emu_memblk, link); 1408c067afaaSCameron Grant emu_free(sc, buf); 140938cc9942SOlivier Houchard tmp = (u_int32_t)(sc->mem.silent_page_addr) << 1; 1410c067afaaSCameron Grant for (idx = blk->pte_start; idx < blk->pte_start + blk->pte_size; idx++) { 1411c067afaaSCameron Grant mem->bmap[idx >> 3] &= ~(1 << (idx & 7)); 1412c067afaaSCameron Grant mem->ptb_pages[idx] = tmp | idx; 1413c067afaaSCameron Grant } 1414c067afaaSCameron Grant free(blk, M_DEVBUF); 1415c067afaaSCameron Grant return 0; 1416c067afaaSCameron Grant } 1417c067afaaSCameron Grant 1418c067afaaSCameron Grant static int 1419c067afaaSCameron Grant emu_memstart(struct sc_info *sc, void *buf) 1420c067afaaSCameron Grant { 1421c067afaaSCameron Grant struct emu_mem *mem = &sc->mem; 1422c067afaaSCameron Grant struct emu_memblk *blk, *i; 1423c067afaaSCameron Grant 1424c067afaaSCameron Grant blk = NULL; 1425c067afaaSCameron Grant SLIST_FOREACH(i, &mem->blocks, link) { 1426c067afaaSCameron Grant if (i->buf == buf) 1427c067afaaSCameron Grant blk = i; 1428c067afaaSCameron Grant } 1429c067afaaSCameron Grant if (blk == NULL) 1430c067afaaSCameron Grant return -EINVAL; 1431c067afaaSCameron Grant return blk->pte_start; 1432c067afaaSCameron Grant } 1433c067afaaSCameron Grant 1434c067afaaSCameron Grant static void 143521f1e37cSDavid E. O'Brien emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, 143621f1e37cSDavid E. O'Brien u_int32_t *pc) 1437c067afaaSCameron Grant { 1438c067afaaSCameron Grant emu_wrefx(sc, (*pc) * 2, (x << 10) | y); 1439c067afaaSCameron Grant emu_wrefx(sc, (*pc) * 2 + 1, (op << 20) | (z << 10) | w); 1440c067afaaSCameron Grant (*pc)++; 1441c067afaaSCameron Grant } 1442c067afaaSCameron Grant 1443c067afaaSCameron Grant static void 144421f1e37cSDavid E. O'Brien audigy_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, 144521f1e37cSDavid E. O'Brien u_int32_t *pc) 144621f1e37cSDavid E. O'Brien { 144721f1e37cSDavid E. O'Brien emu_wrefx(sc, (*pc) * 2, (x << 12) | y); 144821f1e37cSDavid E. O'Brien emu_wrefx(sc, (*pc) * 2 + 1, (op << 24) | (z << 12) | w); 144921f1e37cSDavid E. O'Brien (*pc)++; 145021f1e37cSDavid E. O'Brien } 145121f1e37cSDavid E. O'Brien 145221f1e37cSDavid E. O'Brien static void 145321f1e37cSDavid E. O'Brien audigy_initefx(struct sc_info *sc) 145421f1e37cSDavid E. O'Brien { 145521f1e37cSDavid E. O'Brien int i; 145621f1e37cSDavid E. O'Brien u_int32_t pc = 0; 145721f1e37cSDavid E. O'Brien 145821f1e37cSDavid E. O'Brien /* skip 0, 0, -1, 0 - NOPs */ 145921f1e37cSDavid E. O'Brien for (i = 0; i < 512; i++) 146021f1e37cSDavid E. O'Brien audigy_addefxop(sc, 0x0f, 0x0c0, 0x0c0, 0x0cf, 0x0c0, &pc); 146121f1e37cSDavid E. O'Brien 146221f1e37cSDavid E. O'Brien for (i = 0; i < 512; i++) 1463c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_A_FXGPREGBASE + i, 0x0); 146421f1e37cSDavid E. O'Brien 146521f1e37cSDavid E. O'Brien pc = 16; 146621f1e37cSDavid E. O'Brien 146721f1e37cSDavid E. O'Brien /* stop fx processor */ 1468c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_A_DBG, EMU_A_DBG_SINGLE_STEP); 146921f1e37cSDavid E. O'Brien 147021f1e37cSDavid E. O'Brien /* Audigy 2 (EMU10K2) DSP Registers: 147121f1e37cSDavid E. O'Brien FX Bus 147230baa78dSAlexander Leidinger 0x000-0x00f : 16 registers (?) 147321f1e37cSDavid E. O'Brien Input 147421f1e37cSDavid E. O'Brien 0x040/0x041 : AC97 Codec (l/r) 147521f1e37cSDavid E. O'Brien 0x042/0x043 : ADC, S/PDIF (l/r) 147621f1e37cSDavid E. O'Brien 0x044/0x045 : Optical S/PDIF in (l/r) 147730baa78dSAlexander Leidinger 0x046/0x047 : ? 147821f1e37cSDavid E. O'Brien 0x048/0x049 : Line/Mic 2 (l/r) 147921f1e37cSDavid E. O'Brien 0x04a/0x04b : RCA S/PDIF (l/r) 148021f1e37cSDavid E. O'Brien 0x04c/0x04d : Aux 2 (l/r) 148121f1e37cSDavid E. O'Brien Output 148221f1e37cSDavid E. O'Brien 0x060/0x061 : Digital Front (l/r) 148321f1e37cSDavid E. O'Brien 0x062/0x063 : Digital Center/LFE 148421f1e37cSDavid E. O'Brien 0x064/0x065 : AudigyDrive Heaphone (l/r) 148521f1e37cSDavid E. O'Brien 0x066/0x067 : Digital Rear (l/r) 148621f1e37cSDavid E. O'Brien 0x068/0x069 : Analog Front (l/r) 148721f1e37cSDavid E. O'Brien 0x06a/0x06b : Analog Center/LFE 148830baa78dSAlexander Leidinger 0x06c/0x06d : ? 148921f1e37cSDavid E. O'Brien 0x06e/0x06f : Analog Rear (l/r) 149021f1e37cSDavid E. O'Brien 0x070/0x071 : AC97 Output (l/r) 149130baa78dSAlexander Leidinger 0x072/0x073 : ? 149230baa78dSAlexander Leidinger 0x074/0x075 : ? 149321f1e37cSDavid E. O'Brien 0x076/0x077 : ADC Recording Buffer (l/r) 149421f1e37cSDavid E. O'Brien Constants 149521f1e37cSDavid E. O'Brien 0x0c0 - 0x0c4 = 0 - 4 149621f1e37cSDavid E. O'Brien 0x0c5 = 0x8, 0x0c6 = 0x10, 0x0c7 = 0x20 149721f1e37cSDavid E. O'Brien 0x0c8 = 0x100, 0x0c9 = 0x10000, 0x0ca = 0x80000 149821f1e37cSDavid E. O'Brien 0x0cb = 0x10000000, 0x0cc = 0x20000000, 0x0cd = 0x40000000 149921f1e37cSDavid E. O'Brien 0x0ce = 0x80000000, 0x0cf = 0x7fffffff, 0x0d0 = 0xffffffff 150021f1e37cSDavid E. O'Brien 0x0d1 = 0xfffffffe, 0x0d2 = 0xc0000000, 0x0d3 = 0x41fbbcdc 150130baa78dSAlexander Leidinger 0x0d4 = 0x5a7ef9db, 0x0d5 = 0x00100000, 0x0dc = 0x00000001 (?) 150221f1e37cSDavid E. O'Brien Temporary Values 150330baa78dSAlexander Leidinger 0x0d6 : Accumulator (?) 150421f1e37cSDavid E. O'Brien 0x0d7 : Condition Register 150521f1e37cSDavid E. O'Brien 0x0d8 : Noise source 150621f1e37cSDavid E. O'Brien 0x0d9 : Noise source 150721f1e37cSDavid E. O'Brien Tank Memory Data Registers 150821f1e37cSDavid E. O'Brien 0x200 - 0x2ff 150921f1e37cSDavid E. O'Brien Tank Memory Address Registers 151021f1e37cSDavid E. O'Brien 0x300 - 0x3ff 151121f1e37cSDavid E. O'Brien General Purpose Registers 151221f1e37cSDavid E. O'Brien 0x400 - 0x5ff 151321f1e37cSDavid E. O'Brien */ 151421f1e37cSDavid E. O'Brien 151521f1e37cSDavid E. O'Brien /* AC97Output[l/r] = FXBus PCM[l/r] */ 151621f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_L), A_C_00000000, 151721f1e37cSDavid E. O'Brien A_C_00000000, A_FXBUS(FXBUS_PCM_LEFT), &pc); 151821f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_R), A_C_00000000, 151921f1e37cSDavid E. O'Brien A_C_00000000, A_FXBUS(FXBUS_PCM_RIGHT), &pc); 152021f1e37cSDavid E. O'Brien 152121f1e37cSDavid E. O'Brien /* GPR[0/1] = RCA S/PDIF[l/r] -- Master volume */ 152221f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_GPR(0), A_C_00000000, 152321f1e37cSDavid E. O'Brien A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_L), &pc); 152421f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_GPR(1), A_C_00000000, 152521f1e37cSDavid E. O'Brien A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_R), &pc); 152621f1e37cSDavid E. O'Brien 152721f1e37cSDavid E. O'Brien /* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */ 152821f1e37cSDavid E. O'Brien audigy_addefxop(sc, iINTERP, A_GPR(2), A_GPR(1), 152921f1e37cSDavid E. O'Brien A_C_40000000, A_GPR(0), &pc); 153021f1e37cSDavid E. O'Brien 153121f1e37cSDavid E. O'Brien /* Headphones[l/r] = GPR[0/1] */ 153221f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_HEADPHONE_L), 153321f1e37cSDavid E. O'Brien A_C_00000000, A_C_00000000, A_GPR(0), &pc); 153421f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_HEADPHONE_R), 153521f1e37cSDavid E. O'Brien A_C_00000000, A_C_00000000, A_GPR(1), &pc); 153621f1e37cSDavid E. O'Brien 153721f1e37cSDavid E. O'Brien /* Analog Front[l/r] = GPR[0/1] */ 153821f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AFRONT_L), A_C_00000000, 153921f1e37cSDavid E. O'Brien A_C_00000000, A_GPR(0), &pc); 154021f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AFRONT_R), A_C_00000000, 154121f1e37cSDavid E. O'Brien A_C_00000000, A_GPR(1), &pc); 154221f1e37cSDavid E. O'Brien 154321f1e37cSDavid E. O'Brien /* Digital Front[l/r] = GPR[0/1] */ 154421f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L), A_C_00000000, 154521f1e37cSDavid E. O'Brien A_C_00000000, A_GPR(0), &pc); 154621f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_FRONT_R), A_C_00000000, 154721f1e37cSDavid E. O'Brien A_C_00000000, A_GPR(1), &pc); 154821f1e37cSDavid E. O'Brien 154921f1e37cSDavid E. O'Brien /* Center and Subwoofer configuration */ 155021f1e37cSDavid E. O'Brien /* Analog Center = GPR[0] + GPR[2] */ 155121f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ACENTER), A_C_00000000, 155221f1e37cSDavid E. O'Brien A_GPR(0), A_GPR(2), &pc); 155321f1e37cSDavid E. O'Brien /* Analog Sub = GPR[1] + GPR[2] */ 155421f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ALFE), A_C_00000000, 155521f1e37cSDavid E. O'Brien A_GPR(1), A_GPR(2), &pc); 155621f1e37cSDavid E. O'Brien 155721f1e37cSDavid E. O'Brien /* Digital Center = GPR[0] + GPR[2] */ 155821f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_CENTER), A_C_00000000, 155921f1e37cSDavid E. O'Brien A_GPR(0), A_GPR(2), &pc); 156021f1e37cSDavid E. O'Brien /* Digital Sub = GPR[1] + GPR[2] */ 156121f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_LFE), A_C_00000000, 156221f1e37cSDavid E. O'Brien A_GPR(1), A_GPR(2), &pc); 156321f1e37cSDavid E. O'Brien 156421f1e37cSDavid E. O'Brien #if 0 156521f1e37cSDavid E. O'Brien /* Analog Rear[l/r] = (GPR[0/1] * RearVolume[l/r]) >> 31 */ 156621f1e37cSDavid E. O'Brien /* RearVolume = GPR[0x10/0x11] (Will this ever be implemented?) */ 156721f1e37cSDavid E. O'Brien audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_AREAR_L), A_C_00000000, 156821f1e37cSDavid E. O'Brien A_GPR(16), A_GPR(0), &pc); 156921f1e37cSDavid E. O'Brien audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_AREAR_R), A_C_00000000, 157021f1e37cSDavid E. O'Brien A_GPR(17), A_GPR(1), &pc); 157121f1e37cSDavid E. O'Brien 157221f1e37cSDavid E. O'Brien /* Digital Rear[l/r] = (GPR[0/1] * RearVolume[l/r]) >> 31 */ 157321f1e37cSDavid E. O'Brien /* RearVolume = GPR[0x10/0x11] (Will this ever be implemented?) */ 157421f1e37cSDavid E. O'Brien audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_REAR_L), A_C_00000000, 157521f1e37cSDavid E. O'Brien A_GPR(16), A_GPR(0), &pc); 157621f1e37cSDavid E. O'Brien audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_REAR_R), A_C_00000000, 157721f1e37cSDavid E. O'Brien A_GPR(17), A_GPR(1), &pc); 157821f1e37cSDavid E. O'Brien #else 157921f1e37cSDavid E. O'Brien /* XXX This is just a copy to the channel, since we do not have 158021f1e37cSDavid E. O'Brien * a patch manager, it is useful for have another output enabled. 158121f1e37cSDavid E. O'Brien */ 158221f1e37cSDavid E. O'Brien 158321f1e37cSDavid E. O'Brien /* Analog Rear[l/r] = GPR[0/1] */ 158421f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AREAR_L), A_C_00000000, 158521f1e37cSDavid E. O'Brien A_C_00000000, A_GPR(0), &pc); 158621f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AREAR_R), A_C_00000000, 158721f1e37cSDavid E. O'Brien A_C_00000000, A_GPR(1), &pc); 158821f1e37cSDavid E. O'Brien 158921f1e37cSDavid E. O'Brien /* Digital Rear[l/r] = GPR[0/1] */ 159021f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_REAR_L), A_C_00000000, 159121f1e37cSDavid E. O'Brien A_C_00000000, A_GPR(0), &pc); 159221f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_REAR_R), A_C_00000000, 159321f1e37cSDavid E. O'Brien A_C_00000000, A_GPR(1), &pc); 159421f1e37cSDavid E. O'Brien #endif 159521f1e37cSDavid E. O'Brien 159621f1e37cSDavid E. O'Brien /* ADC Recording buffer[l/r] = AC97Input[l/r] */ 159721f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ADC_CAP_L), A_C_00000000, 159821f1e37cSDavid E. O'Brien A_C_00000000, A_EXTIN(A_EXTIN_AC97_L), &pc); 159921f1e37cSDavid E. O'Brien audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ADC_CAP_R), A_C_00000000, 160021f1e37cSDavid E. O'Brien A_C_00000000, A_EXTIN(A_EXTIN_AC97_R), &pc); 160121f1e37cSDavid E. O'Brien 160221f1e37cSDavid E. O'Brien /* resume normal operations */ 1603c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_A_DBG, 0); 160421f1e37cSDavid E. O'Brien } 160521f1e37cSDavid E. O'Brien 160621f1e37cSDavid E. O'Brien static void 1607c067afaaSCameron Grant emu_initefx(struct sc_info *sc) 1608c067afaaSCameron Grant { 1609c067afaaSCameron Grant int i; 1610c067afaaSCameron Grant u_int32_t pc = 16; 1611c067afaaSCameron Grant 161221f1e37cSDavid E. O'Brien /* acc3 0,0,0,0 - NOPs */ 1613c067afaaSCameron Grant for (i = 0; i < 512; i++) { 1614c067afaaSCameron Grant emu_wrefx(sc, i * 2, 0x10040); 1615c067afaaSCameron Grant emu_wrefx(sc, i * 2 + 1, 0x610040); 1616c067afaaSCameron Grant } 1617c067afaaSCameron Grant 1618c067afaaSCameron Grant for (i = 0; i < 256; i++) 1619c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_FXGPREGBASE + i, 0); 1620c067afaaSCameron Grant 1621c067afaaSCameron Grant /* FX-8010 DSP Registers: 1622c067afaaSCameron Grant FX Bus 1623c067afaaSCameron Grant 0x000-0x00f : 16 registers 1624c067afaaSCameron Grant Input 1625c067afaaSCameron Grant 0x010/0x011 : AC97 Codec (l/r) 1626c067afaaSCameron Grant 0x012/0x013 : ADC, S/PDIF (l/r) 1627c067afaaSCameron Grant 0x014/0x015 : Mic(left), Zoom (l/r) 162821f1e37cSDavid E. O'Brien 0x016/0x017 : TOS link in (l/r) 162921f1e37cSDavid E. O'Brien 0x018/0x019 : Line/Mic 1 (l/r) 163021f1e37cSDavid E. O'Brien 0x01a/0x01b : COAX S/PDIF (l/r) 163121f1e37cSDavid E. O'Brien 0x01c/0x01d : Line/Mic 2 (l/r) 1632c067afaaSCameron Grant Output 1633c067afaaSCameron Grant 0x020/0x021 : AC97 Output (l/r) 1634c067afaaSCameron Grant 0x022/0x023 : TOS link out (l/r) 163521f1e37cSDavid E. O'Brien 0x024/0x025 : Center/LFE 1636c067afaaSCameron Grant 0x026/0x027 : LiveDrive Headphone (l/r) 1637c067afaaSCameron Grant 0x028/0x029 : Rear Channel (l/r) 1638c067afaaSCameron Grant 0x02a/0x02b : ADC Recording Buffer (l/r) 163921f1e37cSDavid E. O'Brien 0x02c : Mic Recording Buffer 164021f1e37cSDavid E. O'Brien 0x031/0x032 : Analog Center/LFE 1641c067afaaSCameron Grant Constants 1642c067afaaSCameron Grant 0x040 - 0x044 = 0 - 4 1643c067afaaSCameron Grant 0x045 = 0x8, 0x046 = 0x10, 0x047 = 0x20 1644c067afaaSCameron Grant 0x048 = 0x100, 0x049 = 0x10000, 0x04a = 0x80000 1645c067afaaSCameron Grant 0x04b = 0x10000000, 0x04c = 0x20000000, 0x04d = 0x40000000 164621f1e37cSDavid E. O'Brien 0x04e = 0x80000000, 0x04f = 0x7fffffff, 0x050 = 0xffffffff 164721f1e37cSDavid E. O'Brien 0x051 = 0xfffffffe, 0x052 = 0xc0000000, 0x053 = 0x41fbbcdc 164821f1e37cSDavid E. O'Brien 0x054 = 0x5a7ef9db, 0x055 = 0x00100000 1649c067afaaSCameron Grant Temporary Values 1650c067afaaSCameron Grant 0x056 : Accumulator 165121f1e37cSDavid E. O'Brien 0x057 : Condition Register 165221f1e37cSDavid E. O'Brien 0x058 : Noise source 165321f1e37cSDavid E. O'Brien 0x059 : Noise source 165421f1e37cSDavid E. O'Brien 0x05a : IRQ Register 165521f1e37cSDavid E. O'Brien 0x05b : TRAM Delay Base Address Count 1656c067afaaSCameron Grant General Purpose Registers 1657c067afaaSCameron Grant 0x100 - 0x1ff 1658c067afaaSCameron Grant Tank Memory Data Registers 1659c067afaaSCameron Grant 0x200 - 0x2ff 1660c067afaaSCameron Grant Tank Memory Address Registers 1661c067afaaSCameron Grant 0x300 - 0x3ff 1662c067afaaSCameron Grant */ 1663c067afaaSCameron Grant 1664c067afaaSCameron Grant /* Routing - this will be configurable in later version */ 1665c067afaaSCameron Grant 1666c067afaaSCameron Grant /* GPR[0/1] = FX * 4 + SPDIF-in */ 166721f1e37cSDavid E. O'Brien emu_addefxop(sc, iMACINT0, GPR(0), EXTIN(EXTIN_SPDIF_CD_L), 166821f1e37cSDavid E. O'Brien FXBUS(FXBUS_PCM_LEFT), C_00000004, &pc); 166921f1e37cSDavid E. O'Brien emu_addefxop(sc, iMACINT0, GPR(1), EXTIN(EXTIN_SPDIF_CD_R), 167021f1e37cSDavid E. O'Brien FXBUS(FXBUS_PCM_RIGHT), C_00000004, &pc); 1671c067afaaSCameron Grant 167221f1e37cSDavid E. O'Brien /* GPR[0/1] += APS-input */ 167321f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, GPR(0), GPR(0), C_00000000, 167421f1e37cSDavid E. O'Brien sc->APS ? EXTIN(EXTIN_TOSLINK_L) : C_00000000, &pc); 167521f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, GPR(1), GPR(1), C_00000000, 167621f1e37cSDavid E. O'Brien sc->APS ? EXTIN(EXTIN_TOSLINK_R) : C_00000000, &pc); 167721f1e37cSDavid E. O'Brien 167821f1e37cSDavid E. O'Brien /* FrontOut (AC97) = GPR[0/1] */ 167921f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_L), C_00000000, 168021f1e37cSDavid E. O'Brien C_00000000, GPR(0), &pc); 168121f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_R), C_00000000, 168221f1e37cSDavid E. O'Brien C_00000001, GPR(1), &pc); 168321f1e37cSDavid E. O'Brien 168421f1e37cSDavid E. O'Brien /* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */ 168521f1e37cSDavid E. O'Brien emu_addefxop(sc, iINTERP, GPR(2), GPR(1), C_40000000, GPR(0), &pc); 168621f1e37cSDavid E. O'Brien 168721f1e37cSDavid E. O'Brien #if 0 168821f1e37cSDavid E. O'Brien /* RearOut = (GPR[0/1] * RearVolume) >> 31 */ 168921f1e37cSDavid E. O'Brien /* RearVolume = GPR[0x10/0x11] */ 169021f1e37cSDavid E. O'Brien emu_addefxop(sc, iMAC0, EXTOUT(EXTOUT_REAR_L), C_00000000, 169121f1e37cSDavid E. O'Brien GPR(16), GPR(0), &pc); 169221f1e37cSDavid E. O'Brien emu_addefxop(sc, iMAC0, EXTOUT(EXTOUT_REAR_R), C_00000000, 169321f1e37cSDavid E. O'Brien GPR(17), GPR(1), &pc); 169421f1e37cSDavid E. O'Brien #else 169521f1e37cSDavid E. O'Brien /* XXX This is just a copy to the channel, since we do not have 169621f1e37cSDavid E. O'Brien * a patch manager, it is useful for have another output enabled. 169721f1e37cSDavid E. O'Brien */ 169821f1e37cSDavid E. O'Brien 169921f1e37cSDavid E. O'Brien /* Rear[l/r] = GPR[0/1] */ 170021f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_REAR_L), C_00000000, 170121f1e37cSDavid E. O'Brien C_00000000, GPR(0), &pc); 170221f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_REAR_R), C_00000000, 170321f1e37cSDavid E. O'Brien C_00000000, GPR(1), &pc); 170421f1e37cSDavid E. O'Brien #endif 170521f1e37cSDavid E. O'Brien 170621f1e37cSDavid E. O'Brien /* TOS out[l/r] = GPR[0/1] */ 170721f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_TOSLINK_L), C_00000000, 170821f1e37cSDavid E. O'Brien C_00000000, GPR(0), &pc); 170921f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_TOSLINK_R), C_00000000, 171021f1e37cSDavid E. O'Brien C_00000000, GPR(1), &pc); 171121f1e37cSDavid E. O'Brien 171221f1e37cSDavid E. O'Brien /* Center and Subwoofer configuration */ 171321f1e37cSDavid E. O'Brien /* Analog Center = GPR[0] + GPR[2] */ 171421f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ACENTER), C_00000000, 171521f1e37cSDavid E. O'Brien GPR(0), GPR(2), &pc); 171621f1e37cSDavid E. O'Brien /* Analog Sub = GPR[1] + GPR[2] */ 171721f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ALFE), C_00000000, 171821f1e37cSDavid E. O'Brien GPR(1), GPR(2), &pc); 171921f1e37cSDavid E. O'Brien /* Digital Center = GPR[0] + GPR[2] */ 172053f4fb11SAlexander Leidinger emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_CENTER), C_00000000, 172121f1e37cSDavid E. O'Brien GPR(0), GPR(2), &pc); 172221f1e37cSDavid E. O'Brien /* Digital Sub = GPR[1] + GPR[2] */ 172353f4fb11SAlexander Leidinger emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_LFE), C_00000000, 172421f1e37cSDavid E. O'Brien GPR(1), GPR(2), &pc); 172521f1e37cSDavid E. O'Brien 172621f1e37cSDavid E. O'Brien /* Headphones[l/r] = GPR[0/1] */ 172721f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_HEADPHONE_L), C_00000000, 172821f1e37cSDavid E. O'Brien C_00000000, GPR(0), &pc); 172921f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_HEADPHONE_R), C_00000000, 173021f1e37cSDavid E. O'Brien C_00000000, GPR(1), &pc); 173121f1e37cSDavid E. O'Brien 173221f1e37cSDavid E. O'Brien /* ADC Recording buffer[l/r] = AC97Input[l/r] */ 173321f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ADC_CAP_L), C_00000000, 173421f1e37cSDavid E. O'Brien C_00000000, EXTIN(EXTIN_AC97_L), &pc); 173521f1e37cSDavid E. O'Brien emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ADC_CAP_R), C_00000000, 173621f1e37cSDavid E. O'Brien C_00000000, EXTIN(EXTIN_AC97_R), &pc); 173721f1e37cSDavid E. O'Brien 173821f1e37cSDavid E. O'Brien /* resume normal operations */ 1739c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_DBG, 0); 1740c067afaaSCameron Grant } 1741c067afaaSCameron Grant 1742c067afaaSCameron Grant /* Probe and attach the card */ 1743c067afaaSCameron Grant static int 1744c067afaaSCameron Grant emu_init(struct sc_info *sc) 1745c067afaaSCameron Grant { 1746c067afaaSCameron Grant u_int32_t spcs, ch, tmp, i; 1747c067afaaSCameron Grant 174821f1e37cSDavid E. O'Brien if (sc->audigy) { 174921f1e37cSDavid E. O'Brien /* enable additional AC97 slots */ 1750c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_AC97SLOT, EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE); 175121f1e37cSDavid E. O'Brien } 175221f1e37cSDavid E. O'Brien 1753c067afaaSCameron Grant /* disable audio and lock cache */ 1754c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_HCFG, 1755c7e0c9dbSPedro F. Giffuni EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE, 175621f1e37cSDavid E. O'Brien 4); 1757c067afaaSCameron Grant 1758c067afaaSCameron Grant /* reset recording buffers */ 1759c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE); 1760c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_MICBA, 0); 1761c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE); 1762c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_FXBA, 0); 1763c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE); 1764c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_ADCBA, 0); 1765c067afaaSCameron Grant 1766c067afaaSCameron Grant /* disable channel interrupt */ 1767c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_INTE, 1768c7e0c9dbSPedro F. Giffuni EMU_INTE_INTERTIMERENB | EMU_INTE_SAMPLERATER | EMU_INTE_PCIERRENABLE, 176921f1e37cSDavid E. O'Brien 4); 1770c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_CLIEL, 0); 1771c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_CLIEH, 0); 1772c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_SOLEL, 0); 1773c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_SOLEH, 0); 1774c067afaaSCameron Grant 177521f1e37cSDavid E. O'Brien /* wonder what these do... */ 177621f1e37cSDavid E. O'Brien if (sc->audigy) { 1777c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_SPBYPASS, 0xf00); 1778c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_AC97SLOT, 0x3); 177921f1e37cSDavid E. O'Brien } 178021f1e37cSDavid E. O'Brien 1781c067afaaSCameron Grant /* init envelope engine */ 1782c067afaaSCameron Grant for (ch = 0; ch < NUM_G; ch++) { 1783c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_DCYSUSV, ENV_OFF); 1784c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_IP, 0); 1785c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_VTFT, 0xffff); 1786c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_CVCF, 0xffff); 1787c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_PTRX, 0); 1788c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_CPF, 0); 1789c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_CCR, 0); 1790c067afaaSCameron Grant 1791c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_PSST, 0); 1792c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_DSL, 0x10); 1793c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_CCCA, 0); 1794c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_Z1, 0); 1795c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_Z2, 0); 1796c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_FXRT, 0xd01c0000); 1797c067afaaSCameron Grant 1798c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_ATKHLDM, 0); 1799c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_DCYSUSM, 0); 1800c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_IFATN, 0xffff); 1801c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_PEFE, 0); 1802c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_FMMOD, 0); 1803c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_TREMFRQ, 24); /* 1 Hz */ 1804c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_FM2FRQ2, 24); /* 1 Hz */ 1805c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_TEMPENV, 0); 1806c067afaaSCameron Grant 1807c067afaaSCameron Grant /*** these are last so OFF prevents writing ***/ 1808c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_LFOVAL2, 0); 1809c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_LFOVAL1, 0); 1810c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_ATKHLDV, 0); 1811c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_ENVVOL, 0); 1812c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_ENVVAL, 0); 1813c067afaaSCameron Grant 181421f1e37cSDavid E. O'Brien if (sc->audigy) { 181521f1e37cSDavid E. O'Brien /* audigy cards need this to initialize correctly */ 181621f1e37cSDavid E. O'Brien emu_wrptr(sc, ch, 0x4c, 0); 181721f1e37cSDavid E. O'Brien emu_wrptr(sc, ch, 0x4d, 0); 181821f1e37cSDavid E. O'Brien emu_wrptr(sc, ch, 0x4e, 0); 181921f1e37cSDavid E. O'Brien emu_wrptr(sc, ch, 0x4f, 0); 182021f1e37cSDavid E. O'Brien /* set default routing */ 1821c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_A_CHAN_FXRT1, 0x03020100); 1822c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_A_CHAN_FXRT2, 0x3f3f3f3f); 1823c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_A_CHAN_SENDAMOUNTS, 0); 182421f1e37cSDavid E. O'Brien } 182521f1e37cSDavid E. O'Brien 1826c067afaaSCameron Grant sc->voice[ch].vnum = ch; 1827c067afaaSCameron Grant sc->voice[ch].slave = NULL; 1828c067afaaSCameron Grant sc->voice[ch].busy = 0; 182970776a9cSCameron Grant sc->voice[ch].ismaster = 0; 18306c1146c0SCameron Grant sc->voice[ch].running = 0; 1831c067afaaSCameron Grant sc->voice[ch].b16 = 0; 1832c067afaaSCameron Grant sc->voice[ch].stereo = 0; 1833c067afaaSCameron Grant sc->voice[ch].speed = 0; 1834c067afaaSCameron Grant sc->voice[ch].start = 0; 1835c067afaaSCameron Grant sc->voice[ch].end = 0; 1836c067afaaSCameron Grant sc->voice[ch].channel = NULL; 1837c067afaaSCameron Grant } 183870776a9cSCameron Grant sc->pnum = sc->rnum = 0; 1839c067afaaSCameron Grant 1840c067afaaSCameron Grant /* 1841c067afaaSCameron Grant * Init to 0x02109204 : 1842c067afaaSCameron Grant * Clock accuracy = 0 (1000ppm) 1843c067afaaSCameron Grant * Sample Rate = 2 (48kHz) 1844c067afaaSCameron Grant * Audio Channel = 1 (Left of 2) 1845c067afaaSCameron Grant * Source Number = 0 (Unspecified) 1846c067afaaSCameron Grant * Generation Status = 1 (Original for Cat Code 12) 1847c067afaaSCameron Grant * Cat Code = 12 (Digital Signal Mixer) 1848c067afaaSCameron Grant * Mode = 0 (Mode 0) 1849c067afaaSCameron Grant * Emphasis = 0 (None) 1850c067afaaSCameron Grant * CP = 1 (Copyright unasserted) 1851c067afaaSCameron Grant * AN = 0 (Audio data) 1852c067afaaSCameron Grant * P = 0 (Consumer) 1853c067afaaSCameron Grant */ 1854c7e0c9dbSPedro F. Giffuni spcs = EMU_SPCS_CLKACCY_1000PPM | EMU_SPCS_SAMPLERATE_48 | 1855c7e0c9dbSPedro F. Giffuni EMU_SPCS_CHANNELNUM_LEFT | EMU_SPCS_SOURCENUM_UNSPEC | 1856c7e0c9dbSPedro F. Giffuni EMU_SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 | 1857c7e0c9dbSPedro F. Giffuni EMU_SPCS_EMPHASIS_NONE | EMU_SPCS_COPYRIGHT; 1858c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_SPCS0, spcs); 1859c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_SPCS1, spcs); 1860c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_SPCS2, spcs); 1861c067afaaSCameron Grant 186221f1e37cSDavid E. O'Brien if (!sc->audigy) 1863c067afaaSCameron Grant emu_initefx(sc); 186421f1e37cSDavid E. O'Brien else if (sc->audigy2) { /* Audigy 2 */ 186521f1e37cSDavid E. O'Brien /* from ALSA initialization code: */ 186621f1e37cSDavid E. O'Brien 186721f1e37cSDavid E. O'Brien /* Hack for Alice3 to work independent of haP16V driver */ 186821f1e37cSDavid E. O'Brien u_int32_t tmp; 186921f1e37cSDavid E. O'Brien 187021f1e37cSDavid E. O'Brien /* Setup SRCMulti_I2S SamplingRate */ 1871c7e0c9dbSPedro F. Giffuni tmp = emu_rdptr(sc, 0, EMU_A_SPDIF_SAMPLERATE) & 0xfffff1ff; 1872c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_A_SPDIF_SAMPLERATE, tmp | 0x400); 187321f1e37cSDavid E. O'Brien 187421f1e37cSDavid E. O'Brien /* Setup SRCSel (Enable SPDIF, I2S SRCMulti) */ 187521f1e37cSDavid E. O'Brien emu_wr(sc, 0x20, 0x00600000, 4); 187621f1e37cSDavid E. O'Brien emu_wr(sc, 0x24, 0x00000014, 4); 187721f1e37cSDavid E. O'Brien 187821f1e37cSDavid E. O'Brien /* Setup SRCMulti Input Audio Enable */ 187921f1e37cSDavid E. O'Brien emu_wr(sc, 0x20, 0x006e0000, 4); 188021f1e37cSDavid E. O'Brien emu_wr(sc, 0x24, 0xff00ff00, 4); 188121f1e37cSDavid E. O'Brien } 1882c067afaaSCameron Grant 188397b3c9d8SCameron Grant SLIST_INIT(&sc->mem.blocks); 188421f1e37cSDavid E. O'Brien sc->mem.ptb_pages = emu_malloc(sc, EMUMAXPAGES * sizeof(u_int32_t), 188521f1e37cSDavid E. O'Brien &sc->mem.ptb_pages_addr); 1886c067afaaSCameron Grant if (sc->mem.ptb_pages == NULL) 1887c067afaaSCameron Grant return -1; 188897b3c9d8SCameron Grant 188921f1e37cSDavid E. O'Brien sc->mem.silent_page = emu_malloc(sc, EMUPAGESIZE, 189021f1e37cSDavid E. O'Brien &sc->mem.silent_page_addr); 189197b3c9d8SCameron Grant if (sc->mem.silent_page == NULL) { 189297b3c9d8SCameron Grant emu_free(sc, sc->mem.ptb_pages); 189397b3c9d8SCameron Grant return -1; 189497b3c9d8SCameron Grant } 189597b3c9d8SCameron Grant /* Clear page with silence & setup all pointers to this page */ 189697b3c9d8SCameron Grant bzero(sc->mem.silent_page, EMUPAGESIZE); 189738cc9942SOlivier Houchard tmp = (u_int32_t)(sc->mem.silent_page_addr) << 1; 189821f1e37cSDavid E. O'Brien for (i = 0; i < EMUMAXPAGES; i++) 189919b6ac09SCameron Grant sc->mem.ptb_pages[i] = tmp | i; 190097b3c9d8SCameron Grant 1901c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_PTB, (sc->mem.ptb_pages_addr)); 1902c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_TCB, 0); /* taken from original driver */ 1903c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_TCBS, 0); /* taken from original driver */ 1904c067afaaSCameron Grant 1905c067afaaSCameron Grant for (ch = 0; ch < NUM_G; ch++) { 1906c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_MAPA, tmp | EMU_CHAN_MAP_PTI_MASK); 1907c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_MAPB, tmp | EMU_CHAN_MAP_PTI_MASK); 1908c067afaaSCameron Grant } 1909c067afaaSCameron Grant 1910c067afaaSCameron Grant /* emu_memalloc(sc, EMUPAGESIZE); */ 1911c067afaaSCameron Grant /* 1912c067afaaSCameron Grant * Hokay, now enable the AUD bit 191321f1e37cSDavid E. O'Brien * 191421f1e37cSDavid E. O'Brien * Audigy 191521f1e37cSDavid E. O'Brien * Enable Audio = 0 (enabled after fx processor initialization) 191621f1e37cSDavid E. O'Brien * Mute Disable Audio = 0 191721f1e37cSDavid E. O'Brien * Joystick = 1 191821f1e37cSDavid E. O'Brien * 191921f1e37cSDavid E. O'Brien * Audigy 2 192021f1e37cSDavid E. O'Brien * Enable Audio = 1 192121f1e37cSDavid E. O'Brien * Mute Disable Audio = 0 192221f1e37cSDavid E. O'Brien * Joystick = 1 192321f1e37cSDavid E. O'Brien * GP S/PDIF AC3 Enable = 1 192421f1e37cSDavid E. O'Brien * CD S/PDIF AC3 Enable = 1 192521f1e37cSDavid E. O'Brien * 192621f1e37cSDavid E. O'Brien * EMU10K1 1927c067afaaSCameron Grant * Enable Audio = 1 1928c067afaaSCameron Grant * Mute Disable Audio = 0 1929c067afaaSCameron Grant * Lock Tank Memory = 1 1930c067afaaSCameron Grant * Lock Sound Memory = 0 1931c067afaaSCameron Grant * Auto Mute = 1 1932c067afaaSCameron Grant */ 193321f1e37cSDavid E. O'Brien 193421f1e37cSDavid E. O'Brien if (sc->audigy) { 1935c7e0c9dbSPedro F. Giffuni tmp = EMU_HCFG_AUTOMUTE | EMU_HCFG_JOYENABLE; 193621f1e37cSDavid E. O'Brien if (sc->audigy2) /* Audigy 2 */ 1937c7e0c9dbSPedro F. Giffuni tmp = EMU_HCFG_AUDIOENABLE | EMU_HCFG_AC3ENABLE_CDSPDIF | 1938c7e0c9dbSPedro F. Giffuni EMU_HCFG_AC3ENABLE_GPSPDIF; 1939c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_HCFG, tmp, 4); 194021f1e37cSDavid E. O'Brien 194121f1e37cSDavid E. O'Brien audigy_initefx(sc); 194221f1e37cSDavid E. O'Brien 194321f1e37cSDavid E. O'Brien /* from ALSA initialization code: */ 194421f1e37cSDavid E. O'Brien 194521f1e37cSDavid E. O'Brien /* enable audio and disable both audio/digital outputs */ 1946c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_HCFG, emu_rd(sc, EMU_HCFG, 4) | EMU_HCFG_AUDIOENABLE, 4); 1947c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_A_IOCFG, emu_rd(sc, EMU_A_IOCFG, 4) & ~EMU_A_IOCFG_GPOUT_AD, 194821f1e37cSDavid E. O'Brien 4); 194921f1e37cSDavid E. O'Brien if (sc->audigy2) { /* Audigy 2 */ 195021f1e37cSDavid E. O'Brien /* Unmute Analog. 195121f1e37cSDavid E. O'Brien * Set GPO6 to 1 for Apollo. This has to be done after 195221f1e37cSDavid E. O'Brien * init Alice3 I2SOut beyond 48kHz. 195321f1e37cSDavid E. O'Brien * So, sequence is important. 195421f1e37cSDavid E. O'Brien */ 1955c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_A_IOCFG, 1956c7e0c9dbSPedro F. Giffuni emu_rd(sc, EMU_A_IOCFG, 4) | EMU_A_IOCFG_GPOUT_A, 4); 195721f1e37cSDavid E. O'Brien } 195821f1e37cSDavid E. O'Brien } else { 195921f1e37cSDavid E. O'Brien /* EMU10K1 initialization code */ 1960c7e0c9dbSPedro F. Giffuni tmp = EMU_HCFG_AUDIOENABLE | EMU_HCFG_LOCKTANKCACHE_MASK 1961c7e0c9dbSPedro F. Giffuni | EMU_HCFG_AUTOMUTE; 1962c067afaaSCameron Grant if (sc->rev >= 6) 1963c7e0c9dbSPedro F. Giffuni tmp |= EMU_HCFG_JOYENABLE; 196421f1e37cSDavid E. O'Brien 1965c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_HCFG, tmp, 4); 1966c067afaaSCameron Grant 1967c067afaaSCameron Grant /* TOSLink detection */ 1968c067afaaSCameron Grant sc->tos_link = 0; 1969c7e0c9dbSPedro F. Giffuni tmp = emu_rd(sc, EMU_HCFG, 4); 1970c7e0c9dbSPedro F. Giffuni if (tmp & (EMU_HCFG_GPINPUT0 | EMU_HCFG_GPINPUT1)) { 1971c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_HCFG, tmp | EMU_HCFG_GPOUT1, 4); 1972c067afaaSCameron Grant DELAY(50); 1973c7e0c9dbSPedro F. Giffuni if (tmp != (emu_rd(sc, EMU_HCFG, 4) & ~EMU_HCFG_GPOUT1)) { 1974c067afaaSCameron Grant sc->tos_link = 1; 1975c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_HCFG, tmp, 4); 1976c067afaaSCameron Grant } 1977c067afaaSCameron Grant } 197821f1e37cSDavid E. O'Brien } 1979c067afaaSCameron Grant 1980c067afaaSCameron Grant return 0; 1981c067afaaSCameron Grant } 1982c067afaaSCameron Grant 1983c067afaaSCameron Grant static int 1984c02f3f7aSCameron Grant emu_uninit(struct sc_info *sc) 1985c02f3f7aSCameron Grant { 1986c02f3f7aSCameron Grant u_int32_t ch; 1987c02f3f7aSCameron Grant 1988c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_INTE, 0, 4); 1989c02f3f7aSCameron Grant for (ch = 0; ch < NUM_G; ch++) 1990c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_DCYSUSV, ENV_OFF); 1991c02f3f7aSCameron Grant for (ch = 0; ch < NUM_G; ch++) { 1992c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_VTFT, 0); 1993c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_CVCF, 0); 1994c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_PTRX, 0); 1995c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, ch, EMU_CHAN_CPF, 0); 1996c02f3f7aSCameron Grant } 1997c02f3f7aSCameron Grant 199821f1e37cSDavid E. O'Brien if (sc->audigy) { /* stop fx processor */ 1999c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_A_DBG, EMU_A_DBG_SINGLE_STEP); 200021f1e37cSDavid E. O'Brien } 200121f1e37cSDavid E. O'Brien 2002c02f3f7aSCameron Grant /* disable audio and lock cache */ 2003c7e0c9dbSPedro F. Giffuni emu_wr(sc, EMU_HCFG, 2004c7e0c9dbSPedro F. Giffuni EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE, 200521f1e37cSDavid E. O'Brien 4); 2006c02f3f7aSCameron Grant 2007c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_PTB, 0); 2008c02f3f7aSCameron Grant /* reset recording buffers */ 2009c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE); 2010c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_MICBA, 0); 2011c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE); 2012c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_FXBA, 0); 2013c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_FXWC, 0); 2014c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE); 2015c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_ADCBA, 0); 2016c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_TCB, 0); 2017c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_TCBS, 0); 2018c02f3f7aSCameron Grant 2019c02f3f7aSCameron Grant /* disable channel interrupt */ 2020c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_CLIEL, 0); 2021c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_CLIEH, 0); 2022c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_SOLEL, 0); 2023c7e0c9dbSPedro F. Giffuni emu_wrptr(sc, 0, EMU_SOLEH, 0); 2024c02f3f7aSCameron Grant 2025c02f3f7aSCameron Grant /* init envelope engine */ 2026c02f3f7aSCameron Grant if (!SLIST_EMPTY(&sc->mem.blocks)) 2027c02f3f7aSCameron Grant device_printf(sc->dev, "warning: memblock list not empty\n"); 2028c02f3f7aSCameron Grant emu_free(sc, sc->mem.ptb_pages); 2029c02f3f7aSCameron Grant emu_free(sc, sc->mem.silent_page); 2030c02f3f7aSCameron Grant 2031f510d240SAlexander Leidinger if(sc->mpu) 2032f510d240SAlexander Leidinger mpu401_uninit(sc->mpu); 2033c02f3f7aSCameron Grant return 0; 2034c02f3f7aSCameron Grant } 2035c02f3f7aSCameron Grant 2036c02f3f7aSCameron Grant static int 2037c067afaaSCameron Grant emu_pci_probe(device_t dev) 2038c067afaaSCameron Grant { 2039c067afaaSCameron Grant char *s = NULL; 2040c067afaaSCameron Grant 2041c067afaaSCameron Grant switch (pci_get_devid(dev)) { 2042c067afaaSCameron Grant case EMU10K1_PCI_ID: 2043c067afaaSCameron Grant s = "Creative EMU10K1"; 2044c067afaaSCameron Grant break; 204521f1e37cSDavid E. O'Brien 2046a791cfeeSCameron Grant case EMU10K2_PCI_ID: 204721f1e37cSDavid E. O'Brien if (pci_get_revid(dev) == 0x04) 204821f1e37cSDavid E. O'Brien s = "Creative Audigy 2 (EMU10K2)"; 204921f1e37cSDavid E. O'Brien else 205021f1e37cSDavid E. O'Brien s = "Creative Audigy (EMU10K2)"; 2051a791cfeeSCameron Grant break; 205221f1e37cSDavid E. O'Brien 2053d2b677bbSWarner Losh case EMU10K3_PCI_ID: 2054d2b677bbSWarner Losh s = "Creative Audigy 2 (EMU10K3)"; 2055d2b677bbSWarner Losh break; 2056d2b677bbSWarner Losh 2057a791cfeeSCameron Grant default: 2058a791cfeeSCameron Grant return ENXIO; 2059c067afaaSCameron Grant } 2060c067afaaSCameron Grant 2061a791cfeeSCameron Grant device_set_desc(dev, s); 206204d895e8SAlexander Leidinger return BUS_PROBE_LOW_PRIORITY; 2063c067afaaSCameron Grant } 2064c067afaaSCameron Grant 2065c067afaaSCameron Grant static int 2066c067afaaSCameron Grant emu_pci_attach(device_t dev) 2067c067afaaSCameron Grant { 2068306f91b6SCameron Grant struct ac97_info *codec = NULL; 2069a791cfeeSCameron Grant struct sc_info *sc; 2070a791cfeeSCameron Grant int i, gotmic; 2071c067afaaSCameron Grant char status[SND_STATUSLEN]; 2072c067afaaSCameron Grant 2073082f6383SAriff Abdullah sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 20744582b3a1SAriff Abdullah sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_emu10k1 softc"); 207570776a9cSCameron Grant sc->dev = dev; 2076c067afaaSCameron Grant sc->type = pci_get_devid(dev); 2077c067afaaSCameron Grant sc->rev = pci_get_revid(dev); 2078d2b677bbSWarner Losh sc->audigy = sc->type == EMU10K2_PCI_ID || sc->type == EMU10K3_PCI_ID; 207921f1e37cSDavid E. O'Brien sc->audigy2 = (sc->audigy && sc->rev == 0x04); 208021f1e37cSDavid E. O'Brien sc->nchans = sc->audigy ? 8 : 4; 2081c7e0c9dbSPedro F. Giffuni sc->addrmask = sc->audigy ? EMU_A_PTR_ADDR_MASK : EMU_PTR_ADDR_MASK; 2082c067afaaSCameron Grant 2083c68534f1SScott Long pci_enable_busmaster(dev); 2084c067afaaSCameron Grant 2085e27951b2SJohn Baldwin i = PCIR_BAR(0); 20865f96beb9SNate Lawson sc->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &i, RF_ACTIVE); 2087a791cfeeSCameron Grant if (sc->reg == NULL) { 2088c067afaaSCameron Grant device_printf(dev, "unable to map register space\n"); 2089c067afaaSCameron Grant goto bad; 2090c067afaaSCameron Grant } 2091a791cfeeSCameron Grant sc->st = rman_get_bustag(sc->reg); 2092a791cfeeSCameron Grant sc->sh = rman_get_bushandle(sc->reg); 2093a791cfeeSCameron Grant 2094a791cfeeSCameron Grant sc->bufsz = pcm_getbuffersize(dev, 4096, EMU_DEFAULT_BUFSZ, 65536); 2095c067afaaSCameron Grant 20960b989078SAlexander Leidinger if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, 20970b989078SAlexander Leidinger /*boundary*/0, 20981115e138SMarius Strobl /*lowaddr*/(1U << 31) - 1, /* can only access 0-2gb */ 2099c067afaaSCameron Grant /*highaddr*/BUS_SPACE_MAXADDR, 2100c067afaaSCameron Grant /*filter*/NULL, /*filterarg*/NULL, 2101a791cfeeSCameron Grant /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, 2102f6b1c44dSScott Long /*flags*/0, /*lockfunc*/busdma_lock_mutex, 2103f6b1c44dSScott Long /*lockarg*/&Giant, &sc->parent_dmat) != 0) { 2104c067afaaSCameron Grant device_printf(dev, "unable to create dma tag\n"); 2105c067afaaSCameron Grant goto bad; 2106c067afaaSCameron Grant } 2107c067afaaSCameron Grant 2108c067afaaSCameron Grant if (emu_init(sc) == -1) { 2109c067afaaSCameron Grant device_printf(dev, "unable to initialize the card\n"); 2110c067afaaSCameron Grant goto bad; 2111c067afaaSCameron Grant } 2112c067afaaSCameron Grant 21130f55ac6cSCameron Grant codec = AC97_CREATE(dev, sc, emu_ac97); 2114c067afaaSCameron Grant if (codec == NULL) goto bad; 2115a791cfeeSCameron Grant gotmic = (ac97_getcaps(codec) & AC97_CAP_MICCHANNEL) ? 1 : 0; 21160f55ac6cSCameron Grant if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; 2117c067afaaSCameron Grant 2118f510d240SAlexander Leidinger emu_midiattach(sc); 2119f510d240SAlexander Leidinger 2120a791cfeeSCameron Grant i = 0; 21215f96beb9SNate Lawson sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, 212221f1e37cSDavid E. O'Brien RF_ACTIVE | RF_SHAREABLE); 212321f1e37cSDavid E. O'Brien if (!sc->irq || 212405478063SMaxime Henrion snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih)) { 2125c067afaaSCameron Grant device_printf(dev, "unable to map interrupt\n"); 2126c067afaaSCameron Grant goto bad; 2127c067afaaSCameron Grant } 2128c067afaaSCameron Grant 21290d8ed52eSMathew Kanner snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", 21300d8ed52eSMathew Kanner rman_get_start(sc->reg), rman_get_start(sc->irq), 21310d8ed52eSMathew Kanner PCM_KLDSTRING(snd_emu10k1)); 2132c067afaaSCameron Grant 213321f1e37cSDavid E. O'Brien if (pcm_register(dev, sc, sc->nchans, gotmic ? 3 : 2)) goto bad; 213421f1e37cSDavid E. O'Brien for (i = 0; i < sc->nchans; i++) 21350f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc); 2136a791cfeeSCameron Grant for (i = 0; i < (gotmic ? 3 : 2); i++) 21370f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc); 2138c067afaaSCameron Grant 2139c067afaaSCameron Grant pcm_setstatus(dev, status); 2140c067afaaSCameron Grant 2141c067afaaSCameron Grant return 0; 2142c067afaaSCameron Grant 2143c067afaaSCameron Grant bad: 2144306f91b6SCameron Grant if (codec) ac97_destroy(codec); 2145e27951b2SJohn Baldwin if (sc->reg) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); 2146c067afaaSCameron Grant if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); 2147a791cfeeSCameron Grant if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 2148306f91b6SCameron Grant if (sc->parent_dmat) bus_dma_tag_destroy(sc->parent_dmat); 214966ef8af5SCameron Grant if (sc->lock) snd_mtxfree(sc->lock); 2150c067afaaSCameron Grant free(sc, M_DEVBUF); 2151c067afaaSCameron Grant return ENXIO; 2152c067afaaSCameron Grant } 2153c067afaaSCameron Grant 2154c02f3f7aSCameron Grant static int 2155c02f3f7aSCameron Grant emu_pci_detach(device_t dev) 2156c02f3f7aSCameron Grant { 2157c02f3f7aSCameron Grant int r; 2158c02f3f7aSCameron Grant struct sc_info *sc; 2159c02f3f7aSCameron Grant 2160c02f3f7aSCameron Grant r = pcm_unregister(dev); 2161c02f3f7aSCameron Grant if (r) 2162c02f3f7aSCameron Grant return r; 2163c02f3f7aSCameron Grant 2164c02f3f7aSCameron Grant sc = pcm_getdevinfo(dev); 2165c02f3f7aSCameron Grant /* shutdown chip */ 2166c02f3f7aSCameron Grant emu_uninit(sc); 2167c02f3f7aSCameron Grant 2168e27951b2SJohn Baldwin bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); 2169c02f3f7aSCameron Grant bus_teardown_intr(dev, sc->irq, sc->ih); 2170a791cfeeSCameron Grant bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 2171306f91b6SCameron Grant bus_dma_tag_destroy(sc->parent_dmat); 217266ef8af5SCameron Grant snd_mtxfree(sc->lock); 2173c02f3f7aSCameron Grant free(sc, M_DEVBUF); 2174c02f3f7aSCameron Grant 2175c02f3f7aSCameron Grant return 0; 2176c02f3f7aSCameron Grant } 2177c02f3f7aSCameron Grant 2178c02f3f7aSCameron Grant /* add suspend, resume */ 2179c067afaaSCameron Grant static device_method_t emu_methods[] = { 2180c067afaaSCameron Grant /* Device interface */ 2181c067afaaSCameron Grant DEVMETHOD(device_probe, emu_pci_probe), 2182c067afaaSCameron Grant DEVMETHOD(device_attach, emu_pci_attach), 2183c02f3f7aSCameron Grant DEVMETHOD(device_detach, emu_pci_detach), 2184c067afaaSCameron Grant 2185*3ac12483SMarius Strobl DEVMETHOD_END 2186c067afaaSCameron Grant }; 2187c067afaaSCameron Grant 2188c067afaaSCameron Grant static driver_t emu_driver = { 2189c067afaaSCameron Grant "pcm", 2190c067afaaSCameron Grant emu_methods, 219167b1dce3SCameron Grant PCM_SOFTC_SIZE, 2192c067afaaSCameron Grant }; 2193c067afaaSCameron Grant 2194*3ac12483SMarius Strobl DRIVER_MODULE(snd_emu10k1, pci, emu_driver, pcm_devclass, NULL, NULL); 21950739ea1dSSeigo Tanimura MODULE_DEPEND(snd_emu10k1, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 2196f314f3daSCameron Grant MODULE_VERSION(snd_emu10k1, 1); 2197f510d240SAlexander Leidinger MODULE_DEPEND(snd_emu10k1, midi, 1, 1, 1); 219870776a9cSCameron Grant 21996c1146c0SCameron Grant /* dummy driver to silence the joystick device */ 220070776a9cSCameron Grant static int 220170776a9cSCameron Grant emujoy_pci_probe(device_t dev) 220270776a9cSCameron Grant { 220370776a9cSCameron Grant char *s = NULL; 220470776a9cSCameron Grant 220570776a9cSCameron Grant switch (pci_get_devid(dev)) { 220670776a9cSCameron Grant case 0x70021102: 220770776a9cSCameron Grant s = "Creative EMU10K1 Joystick"; 220870776a9cSCameron Grant device_quiet(dev); 220970776a9cSCameron Grant break; 2210a791cfeeSCameron Grant case 0x70031102: 2211a791cfeeSCameron Grant s = "Creative EMU10K2 Joystick"; 2212a791cfeeSCameron Grant device_quiet(dev); 2213a791cfeeSCameron Grant break; 221470776a9cSCameron Grant } 221570776a9cSCameron Grant 221670776a9cSCameron Grant if (s) device_set_desc(dev, s); 2217a791cfeeSCameron Grant return s ? -1000 : ENXIO; 221870776a9cSCameron Grant } 221970776a9cSCameron Grant 222070776a9cSCameron Grant static int 222170776a9cSCameron Grant emujoy_pci_attach(device_t dev) 222270776a9cSCameron Grant { 2223*3ac12483SMarius Strobl 222470776a9cSCameron Grant return 0; 222570776a9cSCameron Grant } 222670776a9cSCameron Grant 2227c02f3f7aSCameron Grant static int 2228c02f3f7aSCameron Grant emujoy_pci_detach(device_t dev) 2229c02f3f7aSCameron Grant { 2230*3ac12483SMarius Strobl 2231c02f3f7aSCameron Grant return 0; 2232c02f3f7aSCameron Grant } 2233c02f3f7aSCameron Grant 223470776a9cSCameron Grant static device_method_t emujoy_methods[] = { 223570776a9cSCameron Grant DEVMETHOD(device_probe, emujoy_pci_probe), 223670776a9cSCameron Grant DEVMETHOD(device_attach, emujoy_pci_attach), 2237c02f3f7aSCameron Grant DEVMETHOD(device_detach, emujoy_pci_detach), 223870776a9cSCameron Grant 2239*3ac12483SMarius Strobl DEVMETHOD_END 224070776a9cSCameron Grant }; 224170776a9cSCameron Grant 224270776a9cSCameron Grant static driver_t emujoy_driver = { 224370776a9cSCameron Grant "emujoy", 224470776a9cSCameron Grant emujoy_methods, 2245*3ac12483SMarius Strobl 1 /* no softc */ 224670776a9cSCameron Grant }; 224770776a9cSCameron Grant 224870776a9cSCameron Grant static devclass_t emujoy_devclass; 224970776a9cSCameron Grant 2250*3ac12483SMarius Strobl DRIVER_MODULE(emujoy, pci, emujoy_driver, emujoy_devclass, NULL, NULL); 2251