/*- * Copyright (c) 1999 Cameron Grant * Copyright (c) 2003-2007 Yuriy Tsibizov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for DELAY */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_snd.h" #endif #include #include #include #include #include /* hw flags */ #define HAS_51 0x0001 #define HAS_71 0x0002 #define HAS_AC97 0x0004 #define IS_EMU10K1 0x0008 #define IS_EMU10K2 0x0010 #define IS_CA0102 0x0020 #define IS_CA0108 0x0040 #define IS_UNKNOWN 0x0080 #define BROKEN_DIGITAL 0x0100 #define DIGITAL_ONLY 0x0200 #define IS_CARDBUS 0x0400 #define MODE_ANALOG 1 #define MODE_DIGITAL 2 #define SPDIF_MODE_PCM 1 #define SPDIF_MODE_AC3 2 #define MACS 0x0 #define MACS1 0x1 #define MACW 0x2 #define MACW1 0x3 #define MACINTS 0x4 #define MACINTW 0x5 #define ACC3 0x6 #define MACMV 0x7 #define ANDXOR 0x8 #define TSTNEG 0x9 #define LIMIT 0xA #define LIMIT1 0xB #define LOG 0xC #define EXP 0xD #define INTERP 0xE #define SKIP 0xF #define GPR(i) (sc->gpr_base+(i)) #define INP(i) (sc->input_base+(i)) #define OUTP(i) (sc->output_base+(i)) #define FX(i) (i) #define FX2(i) (sc->efxc_base+(i)) #define DSP_CONST(i) (sc->dsp_zero+(i)) #define COND_NORMALIZED DSP_CONST(0x1) #define COND_BORROW DSP_CONST(0x2) #define COND_MINUS DSP_CONST(0x3) #define COND_LESS_ZERO DSP_CONST(0x4) #define COND_EQ_ZERO DSP_CONST(0x5) #define COND_SATURATION DSP_CONST(0x6) #define COND_NEQ_ZERO DSP_CONST(0x8) #define DSP_ACCUM DSP_CONST(0x16) #define DSP_CCR DSP_CONST(0x17) /* Live! Inputs */ #define IN_AC97_L 0x00 #define IN_AC97_R 0x01 #define IN_AC97 IN_AC97_L #define IN_SPDIF_CD_L 0x02 #define IN_SPDIF_CD_R 0x03 #define IN_SPDIF_CD IN_SPDIF_CD_L #define IN_ZOOM_L 0x04 #define IN_ZOOM_R 0x05 #define IN_ZOOM IN_ZOOM_L #define IN_TOSLINK_L 0x06 #define IN_TOSLINK_R 0x07 #define IN_TOSLINK IN_TOSLINK_L #define IN_LINE1_L 0x08 #define IN_LINE1_R 0x09 #define IN_LINE1 IN_LINE1_L #define IN_COAX_SPDIF_L 0x0a #define IN_COAX_SPDIF_R 0x0b #define IN_COAX_SPDIF IN_COAX_SPDIF_L #define IN_LINE2_L 0x0c #define IN_LINE2_R 0x0d #define IN_LINE2 IN_LINE2_L #define IN_0E 0x0e #define IN_0F 0x0f /* Outputs */ #define OUT_AC97_L 0x00 #define OUT_AC97_R 0x01 #define OUT_AC97 OUT_AC97_L #define OUT_A_FRONT OUT_AC97 #define OUT_TOSLINK_L 0x02 #define OUT_TOSLINK_R 0x03 #define OUT_TOSLINK OUT_TOSLINK_L #define OUT_D_CENTER 0x04 #define OUT_D_SUB 0x05 #define OUT_HEADPHONE_L 0x06 #define OUT_HEADPHONE_R 0x07 #define OUT_HEADPHONE OUT_HEADPHONE_L #define OUT_REAR_L 0x08 #define OUT_REAR_R 0x09 #define OUT_REAR OUT_REAR_L #define OUT_ADC_REC_L 0x0a #define OUT_ADC_REC_R 0x0b #define OUT_ADC_REC OUT_ADC_REC_L #define OUT_MIC_CAP 0x0c /* Live! 5.1 Digital, non-standard 5.1 (center & sub) outputs */ #define OUT_A_CENTER 0x11 #define OUT_A_SUB 0x12 /* Audigy Inputs */ #define A_IN_AC97_L 0x00 #define A_IN_AC97_R 0x01 #define A_IN_AC97 A_IN_AC97_L #define A_IN_SPDIF_CD_L 0x02 #define A_IN_SPDIF_CD_R 0x03 #define A_IN_SPDIF_CD A_IN_SPDIF_CD_L #define A_IN_O_SPDIF_L 0x04 #define A_IN_O_SPDIF_R 0x05 #define A_IN_O_SPDIF A_IN_O_SPDIF_L #define A_IN_LINE2_L 0x08 #define A_IN_LINE2_R 0x09 #define A_IN_LINE2 A_IN_LINE2_L #define A_IN_R_SPDIF_L 0x0a #define A_IN_R_SPDIF_R 0x0b #define A_IN_R_SPDIF A_IN_R_SPDIF_L #define A_IN_AUX2_L 0x0c #define A_IN_AUX2_R 0x0d #define A_IN_AUX2 A_IN_AUX2_L /* Audigy Outputs */ #define A_OUT_D_FRONT_L 0x00 #define A_OUT_D_FRONT_R 0x01 #define A_OUT_D_FRONT A_OUT_D_FRONT_L #define A_OUT_D_CENTER 0x02 #define A_OUT_D_SUB 0x03 #define A_OUT_D_SIDE_L 0x04 #define A_OUT_D_SIDE_R 0x05 #define A_OUT_D_SIDE A_OUT_D_SIDE_L #define A_OUT_D_REAR_L 0x06 #define A_OUT_D_REAR_R 0x07 #define A_OUT_D_REAR A_OUT_D_REAR_L /* on Audigy Platinum only */ #define A_OUT_HPHONE_L 0x04 #define A_OUT_HPHONE_R 0x05 #define A_OUT_HPHONE A_OUT_HPHONE_L #define A_OUT_A_FRONT_L 0x08 #define A_OUT_A_FRONT_R 0x09 #define A_OUT_A_FRONT A_OUT_A_FRONT_L #define A_OUT_A_CENTER 0x0a #define A_OUT_A_SUB 0x0b #define A_OUT_A_SIDE_L 0x0c #define A_OUT_A_SIDE_R 0x0d #define A_OUT_A_SIDE A_OUT_A_SIDE_L #define A_OUT_A_REAR_L 0x0e #define A_OUT_A_REAR_R 0x0f #define A_OUT_A_REAR A_OUT_A_REAR_L #define A_OUT_AC97_L 0x10 #define A_OUT_AC97_R 0x11 #define A_OUT_AC97 A_OUT_AC97_L #define A_OUT_ADC_REC_L 0x16 #define A_OUT_ADC_REC_R 0x17 #define A_OUT_ADC_REC A_OUT_ADC_REC_L #define EMU_DATA2 0x24 #define EMU_IPR2 0x28 #define EMU_INTE2 0x2c #define EMU_IPR3 0x38 #define EMU_INTE3 0x3c #define EMU_A2_SRCSel 0x60 #define EMU_A2_SRCMULTI_ENABLE 0x6e #define EMU_A_I2S_CAPTURE_96000 0x00000400 #define EMU_A2_MIXER_I2S_ENABLE 0x7B #define EMU_A2_MIXER_SPDIF_ENABLE 0x7A #define C_FRONT_L 0 #define C_FRONT_R 1 #define C_REC_L 2 #define C_REC_R 3 #define C_REAR_L 4 #define C_REAR_R 5 #define C_CENTER 6 #define C_SUB 7 #define C_SIDE_L 8 #define C_SIDE_R 9 #define NUM_CACHES 10 #define CDSPDIFMUTE 0 #define ANALOGMUTE 1 #define NUM_MUTE 2 #define EMU_MAX_GPR 512 #define EMU_MAX_IRQ_CONSUMERS 32 struct emu_voice { int vnum; unsigned int b16:1, stereo:1, busy:1, running:1, ismaster:1; int speed; int start; int end; int vol; uint32_t buf; void *vbuf; struct emu_voice *slave; uint32_t sa; uint32_t ea; uint32_t routing[8]; uint32_t amounts[8]; }; struct emu_memblk { SLIST_ENTRY(emu_memblk) link; void *buf; char owner[16]; bus_addr_t buf_addr; uint32_t pte_start, pte_size; bus_dmamap_t buf_map; }; struct emu_mem { uint8_t bmap[EMU_MAXPAGES / 8]; uint32_t *ptb_pages; void *silent_page; bus_addr_t ptb_pages_addr; bus_addr_t silent_page_addr; bus_dmamap_t ptb_map; bus_dmamap_t silent_map; bus_dma_tag_t dmat; struct emu_sc_info *card; SLIST_HEAD(, emu_memblk) blocks; }; /* rm */ struct emu_rm { struct emu_sc_info *card; struct mtx gpr_lock; signed int allocmap[EMU_MAX_GPR]; int num_gprs; int last_free_gpr; int num_used; }; struct emu_intr_handler { void* softc; uint32_t intr_mask; uint32_t inte_mask; uint32_t(*irq_func) (void *softc, uint32_t irq); }; struct emu_sc_info { struct mtx lock; struct mtx rw; /* Hardware exclusive access lock */ /* Hardware and subdevices */ device_t dev; device_t pcm[RT_COUNT]; device_t midi[2]; uint32_t type; uint32_t rev; bus_space_tag_t st; bus_space_handle_t sh; struct cdev *cdev; /* /dev/emu10k character device */ struct mtx emu10kx_lock; int emu10kx_isopen; struct sbuf emu10kx_sbuf; int emu10kx_bufptr; /* Resources */ struct resource *reg; struct resource *irq; void *ih; /* IRQ handlers */ struct emu_intr_handler ihandler[EMU_MAX_IRQ_CONSUMERS]; /* Card HW configuration */ unsigned int mode; /* analog / digital */ unsigned int mchannel_fx; unsigned int dsp_zero; unsigned int code_base; unsigned int code_size; unsigned int gpr_base; unsigned int num_gprs; unsigned int input_base; unsigned int output_base; unsigned int efxc_base; unsigned int opcode_shift; unsigned int high_operand_shift; unsigned int address_mask; uint32_t is_emu10k1:1, is_emu10k2, is_ca0102, is_ca0108:1, has_ac97:1, has_51:1, has_71:1, enable_ir:1, broken_digital:1, is_cardbus:1; signed int mch_disabled, mch_rec, dbg_level; signed int num_inputs; unsigned int num_outputs; unsigned int num_fxbuses; unsigned int routing_code_start; unsigned int routing_code_end; /* HW resources */ struct emu_voice voice[NUM_G]; /* Hardware voices */ uint32_t irq_mask[EMU_MAX_IRQ_CONSUMERS]; /* IRQ manager data */ int timer[EMU_MAX_IRQ_CONSUMERS]; /* timer */ int timerinterval; struct emu_rm *rm; struct emu_mem mem; /* memory */ /* Mixer */ int mixer_gpr[NUM_MIXERS]; int mixer_volcache[NUM_MIXERS]; int cache_gpr[NUM_CACHES]; int dummy_gpr; int mute_gpr[NUM_MUTE]; struct sysctl_ctx_list *ctx; struct sysctl_oid *root; }; static void emu_setmap(void *arg, bus_dma_segment_t * segs, int nseg, int error); static void* emu_malloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, bus_dmamap_t *map); static void emu_free(struct emu_mem *mem, void *dmabuf, bus_dmamap_t map); static void* emu_memalloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, const char * owner); static int emu_memfree(struct emu_mem *mem, void *membuf); static int emu_memstart(struct emu_mem *mem, void *membuf); /* /dev */ static int emu10kx_dev_init(struct emu_sc_info *sc); static int emu10kx_dev_uninit(struct emu_sc_info *sc); static int emu10kx_prepare(struct emu_sc_info *sc, struct sbuf *s); static void emumix_set_mode(struct emu_sc_info *sc, int mode); static void emumix_set_spdif_mode(struct emu_sc_info *sc, int mode); static void emumix_set_fxvol(struct emu_sc_info *sc, unsigned gpr, int32_t vol); static void emumix_set_gpr(struct emu_sc_info *sc, unsigned gpr, int32_t val); static int sysctl_emu_mixer_control(SYSCTL_HANDLER_ARGS); static int emu_rm_init(struct emu_sc_info *sc); static int emu_rm_uninit(struct emu_sc_info *sc); static int emu_rm_gpr_alloc(struct emu_rm *rm, int count); static unsigned int emu_getcard(device_t dev); static uint32_t emu_rd_nolock(struct emu_sc_info *sc, unsigned int regno, unsigned int size); static void emu_wr_nolock(struct emu_sc_info *sc, unsigned int regno, uint32_t data, unsigned int size); static void emu_wr_cbptr(struct emu_sc_info *sc, uint32_t data); static void emu_vstop(struct emu_sc_info *sc, char channel, int enable); static void emu_intr(void *p); static void emu_wrefx(struct emu_sc_info *sc, unsigned int pc, unsigned int data); static void emu_addefxop(struct emu_sc_info *sc, unsigned int op, unsigned int z, unsigned int w, unsigned int x, unsigned int y, uint32_t * pc); static void emu_initefx(struct emu_sc_info *sc); static int emu_cardbus_init(struct emu_sc_info *sc); static int emu_init(struct emu_sc_info *sc); static int emu_uninit(struct emu_sc_info *sc); static int emu_read_ivar(device_t bus __unused, device_t dev, int ivar_index, uintptr_t * result); static int emu_write_ivar(device_t bus __unused, device_t dev __unused, int ivar_index, uintptr_t value __unused); static int emu_pci_probe(device_t dev); static int emu_pci_attach(device_t dev); static int emu_pci_detach(device_t dev); static int emu_modevent(module_t mod __unused, int cmd, void *data __unused); #ifdef SND_EMU10KX_DEBUG #define EMU_MTX_DEBUG() do { \ if (mtx_owned(&sc->rw)) { \ printf("RW owned in %s line %d for %s\n", __func__, \ __LINE__ , device_get_nameunit(sc->dev)); \ printf("rw lock owned: %d\n", mtx_owned(&sc->rw)); \ printf("rw lock: value %x thread %x\n", \ ((&sc->rw)->mtx_lock & ~MTX_FLAGMASK), \ (uintptr_t)curthread); \ printf("rw lock: recursed %d\n", mtx_recursed(&sc->rw));\ db_show_mtx(&sc->rw); \ } \ } while (0) #else #define EMU_MTX_DEBUG() do { \ } while (0) #endif #define EMU_RWLOCK() do { \ EMU_MTX_DEBUG(); \ mtx_lock(&(sc->rw)); \ } while (0) #define EMU_RWUNLOCK() do { \ mtx_unlock(&(sc->rw)); \ EMU_MTX_DEBUG(); \ } while (0) /* Supported cards */ struct emu_hwinfo { uint16_t vendor; uint16_t device; uint16_t subvendor; uint16_t subdevice; char SBcode[8]; char desc[32]; int flags; }; static struct emu_hwinfo emu_cards[] = { {0xffff, 0xffff, 0xffff, 0xffff, "BADCRD", "Not a compatible card", 0}, /* 0x0020..0x002f 4.0 EMU10K1 cards */ {0x1102, 0x0002, 0x1102, 0x0020, "CT4850", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x0021, "CT4620", "SBLive!", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x002f, "CT????", "SBLive! mainboard implementation", HAS_AC97 | IS_EMU10K1}, /* (range unknown) 5.1 EMU10K1 cards */ {0x1102, 0x0002, 0x1102, 0x100a, "CT????", "SBLive! 5.1", HAS_AC97 | HAS_51 | IS_EMU10K1}, /* 0x80??..0x805? 4.0 EMU10K1 cards */ {0x1102, 0x0002, 0x1102, 0x8022, "CT4780", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8023, "CT4790", "SB PCI512", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8024, "CT4760", "SBLive!", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8025, "CT????", "SBLive! Mainboard Implementation", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8026, "CT4830", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8027, "CT4832", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8028, "CT4760", "SBLive! OEM version", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8031, "CT4831", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8040, "CT4760", "SBLive!", HAS_AC97 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8051, "CT4850", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, /* 0x8061..0x???? 5.1 EMU10K1 cards */ {0x1102, 0x0002, 0x1102, 0x8061, "SB????", "SBLive! Player 5.1", HAS_AC97 | HAS_51 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8062, "CT4830", "SBLive! 1024", HAS_AC97 | HAS_51 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8064, "SB????", "SBLive! 5.1", HAS_AC97 | HAS_51 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8065, "SB0220", "SBLive! 5.1 Digital", HAS_AC97 | HAS_51 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8066, "CT4780", "SBLive! 5.1 Digital", HAS_AC97 | HAS_51 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8067, "SB????", "SBLive!", HAS_AC97 | HAS_51 | IS_EMU10K1}, /* Generic SB Live! */ {0x1102, 0x0002, 0x1102, 0x0000, "SB????", "SBLive! (Unknown model)", HAS_AC97 | IS_EMU10K1}, /* 0x0041..0x0043 EMU10K2 (some kind of Audigy) cards */ /* 0x0051..0x0051 5.1 CA0100-IAF cards */ {0x1102, 0x0004, 0x1102, 0x0051, "SB0090", "Audigy", HAS_AC97 | HAS_51 | IS_EMU10K2}, /* ES is CA0100-IDF chip that don't work in digital mode */ {0x1102, 0x0004, 0x1102, 0x0052, "SB0160", "Audigy ES", HAS_AC97 | HAS_71 | IS_EMU10K2 | BROKEN_DIGITAL}, /* 0x0053..0x005C 5.1 CA0101-NAF cards */ {0x1102, 0x0004, 0x1102, 0x0053, "SB0090", "Audigy Player/OEM", HAS_AC97 | HAS_51 | IS_EMU10K2}, {0x1102, 0x0004, 0x1102, 0x0058, "SB0090", "Audigy Player/OEM", HAS_AC97 | HAS_51 | IS_EMU10K2}, /* 0x1002..0x1009 5.1 CA0102-IAT cards */ {0x1102, 0x0004, 0x1102, 0x1002, "SB????", "Audigy 2 Platinum", HAS_51 | IS_CA0102}, {0x1102, 0x0004, 0x1102, 0x1005, "SB????", "Audigy 2 Platinum EX", HAS_51 | IS_CA0102}, {0x1102, 0x0004, 0x1102, 0x1007, "SB0240", "Audigy 2", HAS_AC97 | HAS_51 | IS_CA0102}, /* 0x2001..0x2003 7.1 CA0102-ICT cards */ {0x1102, 0x0004, 0x1102, 0x2001, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, {0x1102, 0x0004, 0x1102, 0x2002, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, /* XXX No reports about 0x2003 & 0x2004 cards */ {0x1102, 0x0004, 0x1102, 0x2003, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, {0x1102, 0x0004, 0x1102, 0x2004, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, {0x1102, 0x0004, 0x1102, 0x2005, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, /* (range unknown) 7.1 CA0102-xxx Audigy 4 cards */ {0x1102, 0x0004, 0x1102, 0x2007, "SB0380", "Audigy 4 Pro", HAS_AC97 | HAS_71 | IS_CA0102}, /* Generic Audigy or Audigy 2 */ {0x1102, 0x0004, 0x1102, 0x0000, "SB????", "Audigy (Unknown model)", HAS_AC97 | HAS_51 | IS_EMU10K2}, /* We don't support CA0103-DAT (Audigy LS) cards */ /* There is NO CA0104-xxx cards */ /* There is NO CA0105-xxx cards */ /* We don't support CA0106-DAT (SB Live! 24 bit) cards */ /* There is NO CA0107-xxx cards */ /* 0x1000..0x1001 7.1 CA0108-IAT cards */ {0x1102, 0x0008, 0x1102, 0x1000, "SB????", "Audigy 2 LS", HAS_AC97 | HAS_51 | IS_CA0108 | DIGITAL_ONLY}, {0x1102, 0x0008, 0x1102, 0x1001, "SB0400", "Audigy 2 Value", HAS_AC97 | HAS_71 | IS_CA0108 | DIGITAL_ONLY}, {0x1102, 0x0008, 0x1102, 0x1021, "SB0610", "Audigy 4", HAS_AC97 | HAS_71 | IS_CA0108 | DIGITAL_ONLY}, {0x1102, 0x0008, 0x1102, 0x2001, "SB0530", "Audigy 2 ZS CardBus", HAS_AC97 | HAS_71 | IS_CA0108 | IS_CARDBUS}, {0x1102, 0x0008, 0x0000, 0x0000, "SB????", "Audigy 2 Value (Unknown model)", HAS_AC97 | HAS_51 | IS_CA0108}, }; /* Unsupported cards */ static struct emu_hwinfo emu_bad_cards[] = { /* APS cards should be possible to support */ {0x1102, 0x0002, 0x1102, 0x4001, "EMUAPS", "E-mu APS", 0}, {0x1102, 0x0002, 0x1102, 0x4002, "EMUAPS", "E-mu APS", 0}, {0x1102, 0x0004, 0x1102, 0x4001, "EMU???", "E-mu 1212m [4001]", 0}, /* Similar-named ("Live!" or "Audigy") cards on different chipsets */ {0x1102, 0x8064, 0x0000, 0x0000, "SB0100", "SBLive! 5.1 OEM", 0}, {0x1102, 0x0006, 0x0000, 0x0000, "SB0200", "DELL OEM SBLive! Value", 0}, {0x1102, 0x0007, 0x0000, 0x0000, "SB0310", "Audigy LS", 0}, }; /* * Get best known information about device. */ static unsigned int emu_getcard(device_t dev) { uint16_t device; uint16_t subdevice; int n_cards; unsigned int thiscard; int i; device = pci_read_config(dev, PCIR_DEVICE, /* bytes */ 2); subdevice = pci_read_config(dev, PCIR_SUBDEV_0, /* bytes */ 2); n_cards = sizeof(emu_cards) / sizeof(struct emu_hwinfo); thiscard = 0; for (i = 1; i < n_cards; i++) { if (device == emu_cards[i].device) { if (subdevice == emu_cards[i].subdevice) { thiscard = i; break; } if (0x0000 == emu_cards[i].subdevice) { thiscard = i; /* * don't break, we can get more specific card * later in the list. */ } } } n_cards = sizeof(emu_bad_cards) / sizeof(struct emu_hwinfo); for (i = 0; i < n_cards; i++) { if (device == emu_bad_cards[i].device) { if (subdevice == emu_bad_cards[i].subdevice) { thiscard = 0; break; } if (0x0000 == emu_bad_cards[i].subdevice) { thiscard = 0; break; /* we avoid all this cards */ } } } return (thiscard); } /* * Base hardware interface are 32 (Audigy) or 64 (Audigy2) registers. * Some of them are used directly, some of them provide pointer / data pairs. */ static uint32_t emu_rd_nolock(struct emu_sc_info *sc, unsigned int regno, unsigned int size) { KASSERT(sc != NULL, ("emu_rd: NULL sc")); switch (size) { case 1: return (bus_space_read_1(sc->st, sc->sh, regno)); case 2: return (bus_space_read_2(sc->st, sc->sh, regno)); case 4: return (bus_space_read_4(sc->st, sc->sh, regno)); } return (0xffffffff); } static void emu_wr_nolock(struct emu_sc_info *sc, unsigned int regno, uint32_t data, unsigned int size) { KASSERT(sc != NULL, ("emu_rd: NULL sc")); switch (size) { case 1: bus_space_write_1(sc->st, sc->sh, regno, data); break; case 2: bus_space_write_2(sc->st, sc->sh, regno, data); break; case 4: bus_space_write_4(sc->st, sc->sh, regno, data); break; } } /* * EMU_PTR / EMU_DATA interface. Access to EMU10Kx is made * via (channel, register) pair. Some registers are channel-specific, * some not. */ uint32_t emu_rdptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg) { uint32_t ptr, val, mask, size, offset; ptr = ((reg << 16) & sc->address_mask) | (chn & EMU_PTR_CHNO_MASK); EMU_RWLOCK(); emu_wr_nolock(sc, EMU_PTR, ptr, 4); val = emu_rd_nolock(sc, EMU_DATA, 4); EMU_RWUNLOCK(); /* * XXX Some register numbers has data size and offset encoded in * it to get only part of 32bit register. This use is not described * in register name, be careful! */ if (reg & 0xff000000) { size = (reg >> 24) & 0x3f; offset = (reg >> 16) & 0x1f; mask = ((1 << size) - 1) << offset; val &= mask; val >>= offset; } return (val); } void emu_wrptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg, uint32_t data) { uint32_t ptr, mask, size, offset; ptr = ((reg << 16) & sc->address_mask) | (chn & EMU_PTR_CHNO_MASK); EMU_RWLOCK(); emu_wr_nolock(sc, EMU_PTR, ptr, 4); /* * XXX Another kind of magic encoding in register number. This can * give you side effect - it will read previous data from register * and change only required bits. */ if (reg & 0xff000000) { size = (reg >> 24) & 0x3f; offset = (reg >> 16) & 0x1f; mask = ((1 << size) - 1) << offset; data <<= offset; data &= mask; data |= emu_rd_nolock(sc, EMU_DATA, 4) & ~mask; } emu_wr_nolock(sc, EMU_DATA, data, 4); EMU_RWUNLOCK(); } /* * EMU_A2_PTR / EMU_DATA2 interface. Access to P16v is made * via (channel, register) pair. Some registers are channel-specific, * some not. This interface is supported by CA0102 and CA0108 chips only. */ uint32_t emu_rd_p16vptr(struct emu_sc_info *sc, uint16_t chn, uint16_t reg) { uint32_t val; /* XXX separate lock? */ EMU_RWLOCK(); emu_wr_nolock(sc, EMU_A2_PTR, (reg << 16) | chn, 4); val = emu_rd_nolock(sc, EMU_DATA2, 4); EMU_RWUNLOCK(); return (val); } void emu_wr_p16vptr(struct emu_sc_info *sc, uint16_t chn, uint16_t reg, uint32_t data) { EMU_RWLOCK(); emu_wr_nolock(sc, EMU_A2_PTR, (reg << 16) | chn, 4); emu_wr_nolock(sc, EMU_DATA2, data, 4); EMU_RWUNLOCK(); } /* * XXX CardBus interface. Not tested on any real hardware. */ static void emu_wr_cbptr(struct emu_sc_info *sc, uint32_t data) { uint32_t val; /* * 0x38 is IPE3 (CD S/PDIF interrupt pending register) on CA0102. Seems * to be some reg/value accessible kind of config register on CardBus * CA0108, with value(?) in top 16 bit, address(?) in low 16 */ val = emu_rd_nolock(sc, 0x38, 4); emu_wr_nolock(sc, 0x38, data, 4); val = emu_rd_nolock(sc, 0x38, 4); } /* * Direct hardware register access * Assume that it is never used to access EMU_PTR-based registers and can run unlocked. */ void emu_wr(struct emu_sc_info *sc, unsigned int regno, uint32_t data, unsigned int size) { KASSERT(regno != EMU_PTR, ("emu_wr: attempt to write to EMU_PTR")); KASSERT(regno != EMU_A2_PTR, ("emu_wr: attempt to write to EMU_A2_PTR")); emu_wr_nolock(sc, regno, data, size); } uint32_t emu_rd(struct emu_sc_info *sc, unsigned int regno, unsigned int size) { uint32_t rd; KASSERT(regno != EMU_DATA, ("emu_rd: attempt to read DATA")); KASSERT(regno != EMU_DATA2, ("emu_rd: attempt to read DATA2")); rd = emu_rd_nolock(sc, regno, size); return (rd); } /* * Enabling IR MIDI messages is another kind of black magic. It just * has to be made this way. It really do it. */ void emu_enable_ir(struct emu_sc_info *sc) { uint32_t iocfg; if (sc->is_emu10k2 || sc->is_ca0102) { iocfg = emu_rd_nolock(sc, EMU_A_IOCFG, 2); emu_wr_nolock(sc, EMU_A_IOCFG, iocfg | EMU_A_IOCFG_GPOUT2, 2); DELAY(500); emu_wr_nolock(sc, EMU_A_IOCFG, iocfg | EMU_A_IOCFG_GPOUT1 | EMU_A_IOCFG_GPOUT2, 2); DELAY(500); emu_wr_nolock(sc, EMU_A_IOCFG, iocfg | EMU_A_IOCFG_GPOUT1, 2); DELAY(100); emu_wr_nolock(sc, EMU_A_IOCFG, iocfg, 2); device_printf(sc->dev, "Audigy IR MIDI events enabled.\n"); sc->enable_ir = 1; } if (sc->is_emu10k1) { iocfg = emu_rd_nolock(sc, EMU_HCFG, 4); emu_wr_nolock(sc, EMU_HCFG, iocfg | EMU_HCFG_GPOUT2, 4); DELAY(500); emu_wr_nolock(sc, EMU_HCFG, iocfg | EMU_HCFG_GPOUT1 | EMU_HCFG_GPOUT2, 4); DELAY(100); emu_wr_nolock(sc, EMU_HCFG, iocfg, 4); device_printf(sc->dev, "SB Live! IR MIDI events enabled.\n"); sc->enable_ir = 1; } } /* * emu_timer_ - HW timer management */ int emu_timer_create(struct emu_sc_info *sc) { int i, timer; timer = -1; mtx_lock(&sc->lock); for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) if (sc->timer[i] == 0) { sc->timer[i] = -1; /* disable it */ timer = i; mtx_unlock(&sc->lock); return (timer); } mtx_unlock(&sc->lock); return (-1); } int emu_timer_set(struct emu_sc_info *sc, int timer, int delay) { int i; if (timer < 0) return (-1); RANGE(delay, 16, 1024); RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); mtx_lock(&sc->lock); sc->timer[timer] = delay; for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) if (sc->timerinterval > sc->timer[i]) sc->timerinterval = sc->timer[i]; /* XXX */ emu_wr(sc, EMU_TIMER, sc->timerinterval & 0x03ff, 2); mtx_unlock(&sc->lock); return (timer); } int emu_timer_enable(struct emu_sc_info *sc, int timer, int go) { uint32_t x; int ena_int; int i; if (timer < 0) return (-1); RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); mtx_lock(&sc->lock); if ((go == 1) && (sc->timer[timer] < 0)) sc->timer[timer] = -sc->timer[timer]; if ((go == 0) && (sc->timer[timer] > 0)) sc->timer[timer] = -sc->timer[timer]; ena_int = 0; for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) { if (sc->timerinterval > sc->timer[i]) sc->timerinterval = sc->timer[i]; if (sc->timer[i] > 0) ena_int = 1; } emu_wr(sc, EMU_TIMER, sc->timerinterval & 0x03ff, 2); if (ena_int == 1) { x = emu_rd(sc, EMU_INTE, 4); x |= EMU_INTE_INTERTIMERENB; emu_wr(sc, EMU_INTE, x, 4); } else { x = emu_rd(sc, EMU_INTE, 4); x &= ~EMU_INTE_INTERTIMERENB; emu_wr(sc, EMU_INTE, x, 4); } mtx_unlock(&sc->lock); return (0); } int emu_timer_clear(struct emu_sc_info *sc, int timer) { if (timer < 0) return (-1); RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); emu_timer_enable(sc, timer, 0); mtx_lock(&sc->lock); if (sc->timer[timer] != 0) sc->timer[timer] = 0; mtx_unlock(&sc->lock); return (timer); } /* * emu_intr_ - HW interrupt handler management */ int emu_intr_register(struct emu_sc_info *sc, uint32_t inte_mask, uint32_t intr_mask, uint32_t(*func) (void *softc, uint32_t irq), void *isc) { int i; uint32_t x; mtx_lock(&sc->lock); for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) if (sc->ihandler[i].inte_mask == 0) { sc->ihandler[i].inte_mask = inte_mask; sc->ihandler[i].intr_mask = intr_mask; sc->ihandler[i].softc = isc; sc->ihandler[i].irq_func = func; x = emu_rd(sc, EMU_INTE, 4); x |= inte_mask; emu_wr(sc, EMU_INTE, x, 4); mtx_unlock(&sc->lock); if (sc->dbg_level > 1) device_printf(sc->dev, "ihandle %d registered\n", i); return (i); } mtx_unlock(&sc->lock); if (sc->dbg_level > 1) device_printf(sc->dev, "ihandle not registered\n"); return (-1); } int emu_intr_unregister(struct emu_sc_info *sc, int hnumber) { uint32_t x; int i; mtx_lock(&sc->lock); if (sc->ihandler[hnumber].inte_mask == 0) { mtx_unlock(&sc->lock); return (-1); } x = emu_rd(sc, EMU_INTE, 4); x &= ~sc->ihandler[hnumber].inte_mask; sc->ihandler[hnumber].inte_mask = 0; sc->ihandler[hnumber].intr_mask = 0; sc->ihandler[hnumber].softc = NULL; sc->ihandler[hnumber].irq_func = NULL; /* other interrupt handlers may use this EMU_INTE value */ for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) if (sc->ihandler[i].inte_mask != 0) x |= sc->ihandler[i].inte_mask; emu_wr(sc, EMU_INTE, x, 4); mtx_unlock(&sc->lock); return (hnumber); } static void emu_intr(void *p) { struct emu_sc_info *sc = (struct emu_sc_info *)p; uint32_t stat, ack; int i; for (;;) { stat = emu_rd(sc, EMU_IPR, 4); ack = 0; if (stat == 0) break; emu_wr(sc, EMU_IPR, stat, 4); for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) { if ((((sc->ihandler[i].intr_mask) & stat) != 0) && (((void *)sc->ihandler[i].irq_func) != NULL)) { ack |= sc->ihandler[i].irq_func(sc->ihandler[i].softc, (sc->ihandler[i].intr_mask) & stat); } } if (sc->dbg_level > 1) if (stat & (~ack)) device_printf(sc->dev, "Unhandled interrupt: %08x\n", stat & (~ack)); } if ((sc->is_ca0102) || (sc->is_ca0108)) for (;;) { stat = emu_rd(sc, EMU_IPR2, 4); ack = 0; if (stat == 0) break; emu_wr(sc, EMU_IPR2, stat, 4); if (sc->dbg_level > 1) device_printf(sc->dev, "EMU_IPR2: %08x\n", stat); break; /* to avoid infinite loop. should be removed * after completion of P16V interface. */ } if (sc->is_ca0102) for (;;) { stat = emu_rd(sc, EMU_IPR3, 4); ack = 0; if (stat == 0) break; emu_wr(sc, EMU_IPR3, stat, 4); if (sc->dbg_level > 1) device_printf(sc->dev, "EMU_IPR3: %08x\n", stat); break; /* to avoid infinite loop. should be removed * after completion of S/PDIF interface */ } } /* * Get data from private emu10kx structure for PCM buffer allocation. * Used by PCM code only. */ bus_dma_tag_t emu_gettag(struct emu_sc_info *sc) { return (sc->mem.dmat); } static void emu_setmap(void *arg, bus_dma_segment_t * segs, int nseg, int error) { bus_addr_t *phys = (bus_addr_t *) arg; *phys = error ? 0 : (bus_addr_t) segs->ds_addr; if (bootverbose) { printf("emu10kx: setmap (%lx, %lx), nseg=%d, error=%d\n", (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, nseg, error); } } static void * emu_malloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, bus_dmamap_t *map) { void *dmabuf; int error; *addr = 0; if ((error = bus_dmamem_alloc(mem->dmat, &dmabuf, BUS_DMA_NOWAIT, map))) { if (mem->card->dbg_level > 2) device_printf(mem->card->dev, "emu_malloc: failed to alloc DMA map: %d\n", error); return (NULL); } if ((error = bus_dmamap_load(mem->dmat, *map, dmabuf, sz, emu_setmap, addr, 0)) || !*addr) { if (mem->card->dbg_level > 2) device_printf(mem->card->dev, "emu_malloc: failed to load DMA memory: %d\n", error); bus_dmamem_free(mem->dmat, dmabuf, *map); return (NULL); } return (dmabuf); } static void emu_free(struct emu_mem *mem, void *dmabuf, bus_dmamap_t map) { bus_dmamap_unload(mem->dmat, map); bus_dmamem_free(mem->dmat, dmabuf, map); } static void * emu_memalloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, const char *owner) { uint32_t blksz, start, idx, ofs, tmp, found; struct emu_memblk *blk; void *membuf; blksz = sz / EMUPAGESIZE; if (sz > (blksz * EMUPAGESIZE)) blksz++; if (blksz > EMU_MAX_BUFSZ / EMUPAGESIZE) { if (mem->card->dbg_level > 2) device_printf(mem->card->dev, "emu_memalloc: memory request tool large\n"); return (NULL); } /* find a free block in the bitmap */ found = 0; start = 1; while (!found && start + blksz < EMU_MAXPAGES) { found = 1; for (idx = start; idx < start + blksz; idx++) if (mem->bmap[idx >> 3] & (1 << (idx & 7))) found = 0; if (!found) start++; } if (!found) { if (mem->card->dbg_level > 2) device_printf(mem->card->dev, "emu_memalloc: no free space in bitmap\n"); return (NULL); } blk = malloc(sizeof(*blk), M_DEVBUF, M_NOWAIT); if (blk == NULL) { if (mem->card->dbg_level > 2) device_printf(mem->card->dev, "emu_memalloc: buffer allocation failed\n"); return (NULL); } bzero(blk, sizeof(*blk)); membuf = emu_malloc(mem, sz, &blk->buf_addr, &blk->buf_map); *addr = blk->buf_addr; if (membuf == NULL) { if (mem->card->dbg_level > 2) device_printf(mem->card->dev, "emu_memalloc: can't setup HW memory\n"); free(blk, M_DEVBUF); return (NULL); } blk->buf = membuf; blk->pte_start = start; blk->pte_size = blksz; strncpy(blk->owner, owner, 15); blk->owner[15] = '\0'; ofs = 0; for (idx = start; idx < start + blksz; idx++) { mem->bmap[idx >> 3] |= 1 << (idx & 7); tmp = (uint32_t) (blk->buf_addr + ofs); mem->ptb_pages[idx] = (tmp << 1) | idx; ofs += EMUPAGESIZE; } SLIST_INSERT_HEAD(&mem->blocks, blk, link); return (membuf); } static int emu_memfree(struct emu_mem *mem, void *membuf) { uint32_t idx, tmp; struct emu_memblk *blk, *i; blk = NULL; SLIST_FOREACH(i, &mem->blocks, link) { if (i->buf == membuf) blk = i; } if (blk == NULL) return (EINVAL); SLIST_REMOVE(&mem->blocks, blk, emu_memblk, link); emu_free(mem, membuf, blk->buf_map); tmp = (uint32_t) (mem->silent_page_addr) << 1; for (idx = blk->pte_start; idx < blk->pte_start + blk->pte_size; idx++) { mem->bmap[idx >> 3] &= ~(1 << (idx & 7)); mem->ptb_pages[idx] = tmp | idx; } free(blk, M_DEVBUF); return (0); } static int emu_memstart(struct emu_mem *mem, void *membuf) { struct emu_memblk *blk, *i; blk = NULL; SLIST_FOREACH(i, &mem->blocks, link) { if (i->buf == membuf) blk = i; } if (blk == NULL) return (-1); return (blk->pte_start); } static uint32_t emu_rate_to_pitch(uint32_t rate) { static uint32_t logMagTable[128] = { 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df }; static char logSlopeTable[128] = { 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f }; int i; if (rate == 0) return (0); rate *= 11185; /* Scale 48000 to 0x20002380 */ for (i = 31; i > 0; i--) { if (rate & 0x80000000) { /* Detect leading "1" */ return (((uint32_t) (i - 15) << 20) + logMagTable[0x7f & (rate >> 24)] + (0x7f & (rate >> 17)) * logSlopeTable[0x7f & (rate >> 24)]); } rate <<= 1; } /* NOTREACHED */ return (0); } static uint32_t emu_rate_to_linearpitch(uint32_t rate) { rate = (rate << 8) / 375; return ((rate >> 1) + (rate & 1)); } struct emu_voice * emu_valloc(struct emu_sc_info *sc) { struct emu_voice *v; int i; v = NULL; mtx_lock(&sc->lock); for (i = 0; i < NUM_G && sc->voice[i].busy; i++); if (i < NUM_G) { v = &sc->voice[i]; v->busy = 1; } mtx_unlock(&sc->lock); return (v); } void emu_vfree(struct emu_sc_info *sc, struct emu_voice *v) { int i, r; mtx_lock(&sc->lock); for (i = 0; i < NUM_G; i++) { if (v == &sc->voice[i] && sc->voice[i].busy) { v->busy = 0; /* * XXX What we should do with mono channels? * See -pcm.c emupchan_init for other side of * this problem */ if (v->slave != NULL) r = emu_memfree(&sc->mem, v->vbuf); } } mtx_unlock(&sc->lock); } int emu_vinit(struct emu_sc_info *sc, struct emu_voice *m, struct emu_voice *s, uint32_t sz, struct snd_dbuf *b) { void *vbuf; bus_addr_t tmp_addr; vbuf = emu_memalloc(&sc->mem, sz, &tmp_addr, "vinit"); if (vbuf == NULL) { if(sc->dbg_level > 2) device_printf(sc->dev, "emu_memalloc returns NULL in enu_vinit\n"); return (ENOMEM); } if (b != NULL) sndbuf_setup(b, vbuf, sz); m->start = emu_memstart(&sc->mem, vbuf) * EMUPAGESIZE; if (m->start < 0) { if(sc->dbg_level > 2) device_printf(sc->dev, "emu_memstart returns (-1) in enu_vinit\n"); emu_memfree(&sc->mem, vbuf); return (ENOMEM); } m->end = m->start + sz; m->speed = 0; m->b16 = 0; m->stereo = 0; m->running = 0; m->ismaster = 1; m->vol = 0xff; m->buf = tmp_addr; m->vbuf = vbuf; m->slave = s; if (s != NULL) { s->start = m->start; s->end = m->end; s->speed = 0; s->b16 = 0; s->stereo = 0; s->running = 0; s->ismaster = 0; s->vol = m->vol; s->buf = m->buf; s->vbuf = NULL; s->slave = NULL; } return (0); } void emu_vsetup(struct emu_voice *v, int fmt, int spd) { if (fmt) { v->b16 = (fmt & AFMT_16BIT) ? 1 : 0; v->stereo = (AFMT_CHANNEL(fmt) > 1) ? 1 : 0; if (v->slave != NULL) { v->slave->b16 = v->b16; v->slave->stereo = v->stereo; } } if (spd) { v->speed = spd; if (v->slave != NULL) v->slave->speed = v->speed; } } void emu_vroute(struct emu_sc_info *sc, struct emu_route *rt, struct emu_voice *v) { int i; for (i = 0; i < 8; i++) { v->routing[i] = rt->routing_left[i]; v->amounts[i] = rt->amounts_left[i]; } if ((v->stereo) && (v->ismaster == 0)) for (i = 0; i < 8; i++) { v->routing[i] = rt->routing_right[i]; v->amounts[i] = rt->amounts_right[i]; } if ((v->stereo) && (v->slave != NULL)) emu_vroute(sc, rt, v->slave); } void emu_vwrite(struct emu_sc_info *sc, struct emu_voice *v) { int s; uint32_t start, val, silent_page; s = (v->stereo ? 1 : 0) + (v->b16 ? 1 : 0); v->sa = v->start >> s; v->ea = v->end >> s; if (v->stereo) { emu_wrptr(sc, v->vnum, EMU_CHAN_CPF, EMU_CHAN_CPF_STEREO_MASK); } else { emu_wrptr(sc, v->vnum, EMU_CHAN_CPF, 0); } val = v->stereo ? 28 : 30; val *= v->b16 ? 1 : 2; start = v->sa + val; if (sc->is_emu10k1) { emu_wrptr(sc, v->vnum, EMU_CHAN_FXRT, ((v->routing[3] << 12) | (v->routing[2] << 8) | (v->routing[1] << 4) | (v->routing[0] << 0)) << 16); } else { emu_wrptr(sc, v->vnum, EMU_A_CHAN_FXRT1, (v->routing[3] << 24) | (v->routing[2] << 16) | (v->routing[1] << 8) | (v->routing[0] << 0)); emu_wrptr(sc, v->vnum, EMU_A_CHAN_FXRT2, (v->routing[7] << 24) | (v->routing[6] << 16) | (v->routing[5] << 8) | (v->routing[4] << 0)); emu_wrptr(sc, v->vnum, EMU_A_CHAN_SENDAMOUNTS, (v->amounts[7] << 24) | (v->amounts[6] << 26) | (v->amounts[5] << 8) | (v->amounts[4] << 0)); } emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX, (v->amounts[0] << 8) | (v->amounts[1] << 0)); emu_wrptr(sc, v->vnum, EMU_CHAN_DSL, v->ea | (v->amounts[3] << 24)); emu_wrptr(sc, v->vnum, EMU_CHAN_PSST, v->sa | (v->amounts[2] << 24)); emu_wrptr(sc, v->vnum, EMU_CHAN_CCCA, start | (v->b16 ? 0 : EMU_CHAN_CCCA_8BITSELECT)); emu_wrptr(sc, v->vnum, EMU_CHAN_Z1, 0); emu_wrptr(sc, v->vnum, EMU_CHAN_Z2, 0); silent_page = ((uint32_t) (sc->mem.silent_page_addr) << 1) | EMU_CHAN_MAP_PTI_MASK; emu_wrptr(sc, v->vnum, EMU_CHAN_MAPA, silent_page); emu_wrptr(sc, v->vnum, EMU_CHAN_MAPB, silent_page); emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, EMU_CHAN_CVCF_CURRFILTER_MASK); emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, EMU_CHAN_VTFT_FILTERTARGET_MASK); emu_wrptr(sc, v->vnum, EMU_CHAN_ATKHLDM, 0); emu_wrptr(sc, v->vnum, EMU_CHAN_DCYSUSM, EMU_CHAN_DCYSUSM_DECAYTIME_MASK); emu_wrptr(sc, v->vnum, EMU_CHAN_LFOVAL1, 0x8000); emu_wrptr(sc, v->vnum, EMU_CHAN_LFOVAL2, 0x8000); emu_wrptr(sc, v->vnum, EMU_CHAN_FMMOD, 0); emu_wrptr(sc, v->vnum, EMU_CHAN_TREMFRQ, 0); emu_wrptr(sc, v->vnum, EMU_CHAN_FM2FRQ2, 0); emu_wrptr(sc, v->vnum, EMU_CHAN_ENVVAL, 0x8000); emu_wrptr(sc, v->vnum, EMU_CHAN_ATKHLDV, EMU_CHAN_ATKHLDV_HOLDTIME_MASK | EMU_CHAN_ATKHLDV_ATTACKTIME_MASK); emu_wrptr(sc, v->vnum, EMU_CHAN_ENVVOL, 0x8000); emu_wrptr(sc, v->vnum, EMU_CHAN_PEFE_FILTERAMOUNT, 0x7f); emu_wrptr(sc, v->vnum, EMU_CHAN_PEFE_PITCHAMOUNT, 0); if ((v->stereo) && (v->slave != NULL)) emu_vwrite(sc, v->slave); } static void emu_vstop(struct emu_sc_info *sc, char channel, int enable) { int reg; reg = (channel & 0x20) ? EMU_SOLEH : EMU_SOLEL; channel &= 0x1f; reg |= 1 << 24; reg |= channel << 16; emu_wrptr(sc, 0, reg, enable); } void emu_vtrigger(struct emu_sc_info *sc, struct emu_voice *v, int go) { uint32_t pitch_target, initial_pitch; uint32_t cra, cs, ccis; uint32_t sample, i; if (go) { cra = 64; cs = v->stereo ? 4 : 2; ccis = v->stereo ? 28 : 30; ccis *= v->b16 ? 1 : 2; sample = v->b16 ? 0x00000000 : 0x80808080; for (i = 0; i < cs; i++) emu_wrptr(sc, v->vnum, EMU_CHAN_CD0 + i, sample); emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_CACHEINVALIDSIZE, 0); emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_READADDRESS, cra); emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_CACHEINVALIDSIZE, ccis); emu_wrptr(sc, v->vnum, EMU_CHAN_IFATN, 0xff00); emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, 0xffffffff); emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, 0xffffffff); emu_wrptr(sc, v->vnum, EMU_CHAN_DCYSUSV, 0x00007f7f); emu_vstop(sc, v->vnum, 0); pitch_target = emu_rate_to_linearpitch(v->speed); initial_pitch = emu_rate_to_pitch(v->speed) >> 8; emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX_PITCHTARGET, pitch_target); emu_wrptr(sc, v->vnum, EMU_CHAN_CPF_PITCH, pitch_target); emu_wrptr(sc, v->vnum, EMU_CHAN_IP, initial_pitch); } else { emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX_PITCHTARGET, 0); emu_wrptr(sc, v->vnum, EMU_CHAN_CPF_PITCH, 0); emu_wrptr(sc, v->vnum, EMU_CHAN_IFATN, 0xffff); emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, 0x0000ffff); emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, 0x0000ffff); emu_wrptr(sc, v->vnum, EMU_CHAN_IP, 0); emu_vstop(sc, v->vnum, 1); } if ((v->stereo) && (v->slave != NULL)) emu_vtrigger(sc, v->slave, go); } int emu_vpos(struct emu_sc_info *sc, struct emu_voice *v) { int s, ptr; s = (v->b16 ? 1 : 0) + (v->stereo ? 1 : 0); ptr = (emu_rdptr(sc, v->vnum, EMU_CHAN_CCCA_CURRADDR) - (v->start >> s)) << s; return (ptr & ~0x0000001f); } /* fx */ static void emu_wrefx(struct emu_sc_info *sc, unsigned int pc, unsigned int data) { emu_wrptr(sc, 0, sc->code_base + pc, data); } static void emu_addefxop(struct emu_sc_info *sc, unsigned int op, unsigned int z, unsigned int w, unsigned int x, unsigned int y, uint32_t * pc) { if ((*pc) + 1 > sc->code_size) { device_printf(sc->dev, "DSP CODE OVERRUN: attept to write past code_size (pc=%d)\n", (*pc)); return; } emu_wrefx(sc, (*pc) * 2, (x << sc->high_operand_shift) | y); emu_wrefx(sc, (*pc) * 2 + 1, (op << sc->opcode_shift) | (z << sc->high_operand_shift) | w); (*pc)++; } static int sysctl_emu_mixer_control(SYSCTL_HANDLER_ARGS) { struct emu_sc_info *sc; int mixer_id; int new_vol; int err; sc = arg1; mixer_id = arg2; new_vol = emumix_get_volume(sc, mixer_id); err = sysctl_handle_int(oidp, &new_vol, 0, req); if (err || req->newptr == NULL) return (err); if (new_vol < 0 || new_vol > 100) return (EINVAL); emumix_set_volume(sc, mixer_id, new_vol); return (0); } static int emu_addefxmixer(struct emu_sc_info *sc, const char *mix_name, const int mix_id, uint32_t defvolume) { int volgpr; char sysctl_name[32]; volgpr = emu_rm_gpr_alloc(sc->rm, 1); emumix_set_fxvol(sc, volgpr, defvolume); /* * Mixer controls with NULL mix_name are handled * by AC97 emulation code or PCM mixer. */ if (mix_name != NULL) { /* * Temporary sysctls should start with underscore, * see freebsd-current mailing list, emu10kx driver * discussion around 2006-05-24. */ snprintf(sysctl_name, 32, "_%s", mix_name); SYSCTL_ADD_PROC(sc->ctx, SYSCTL_CHILDREN(sc->root), OID_AUTO, sysctl_name, CTLTYPE_INT | CTLFLAG_RW, sc, mix_id, sysctl_emu_mixer_control, "I", ""); } return (volgpr); } static int sysctl_emu_digitalswitch_control(SYSCTL_HANDLER_ARGS) { struct emu_sc_info *sc; int new_val; int err; sc = arg1; new_val = (sc->mode == MODE_DIGITAL) ? 1 : 0; err = sysctl_handle_int(oidp, &new_val, 0, req); if (err || req->newptr == NULL) return (err); if (new_val < 0 || new_val > 1) return (EINVAL); switch (new_val) { case 0: emumix_set_mode(sc, MODE_ANALOG); break; case 1: emumix_set_mode(sc, MODE_DIGITAL); break; } return (0); } static void emu_digitalswitch(struct emu_sc_info *sc) { /* XXX temporary? */ SYSCTL_ADD_PROC(sc->ctx, SYSCTL_CHILDREN(sc->root), OID_AUTO, "_digital", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_emu_digitalswitch_control, "I", "Enable digital output"); return; } /* * Allocate cache GPRs that will hold mixed output channels * and clear it on every DSP run. */ #define EFX_CACHE(CACHE_IDX) do { \ sc->cache_gpr[CACHE_IDX] = emu_rm_gpr_alloc(sc->rm, 1); \ emu_addefxop(sc, ACC3, \ GPR(sc->cache_gpr[CACHE_IDX]), \ DSP_CONST(0), \ DSP_CONST(0), \ DSP_CONST(0), \ &pc); \ } while (0) /* Allocate GPR for volume control and route sound: OUT = OUT + IN * VOL */ #define EFX_ROUTE(TITLE, INP_NR, IN_GPR_IDX, OUT_CACHE_IDX, DEF) do { \ sc->mixer_gpr[IN_GPR_IDX] = emu_addefxmixer(sc, TITLE, IN_GPR_IDX, DEF); \ sc->mixer_volcache[IN_GPR_IDX] = DEF; \ emu_addefxop(sc, MACS, \ GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ INP_NR, \ GPR(sc->mixer_gpr[IN_GPR_IDX]), \ &pc); \ } while (0) /* allocate GPR, OUT = IN * VOL */ #define EFX_OUTPUT(TITLE, OUT_CACHE_IDX, OUT_GPR_IDX, OUTP_NR, DEF) do { \ sc->mixer_gpr[OUT_GPR_IDX] = emu_addefxmixer(sc, TITLE, OUT_GPR_IDX, DEF); \ sc->mixer_volcache[OUT_GPR_IDX] = DEF; \ emu_addefxop(sc, MACS, \ OUTP(OUTP_NR), \ DSP_CONST(0), \ GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ GPR(sc->mixer_gpr[OUT_GPR_IDX]), \ &pc); \ } while (0) /* like EFX_OUTPUT, but don't allocate mixer gpr */ #define EFX_OUTPUTD(OUT_CACHE_IDX, OUT_GPR_IDX, OUTP_NR) do { \ emu_addefxop(sc, MACS, \ OUTP(OUTP_NR), \ DSP_CONST(0), \ GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ GPR(sc->mixer_gpr[OUT_GPR_IDX]), \ &pc); \ } while (0) /* skip next OPCOUNT instructions if FLAG != 0 */ #define EFX_SKIP(OPCOUNT, FLAG_GPR) do { \ emu_addefxop(sc, MACS, \ DSP_CONST(0), \ GPR(sc->mute_gpr[FLAG_GPR]), \ DSP_CONST(0), \ DSP_CONST(0), \ &pc); \ emu_addefxop(sc, SKIP, \ DSP_CCR, \ DSP_CCR, \ COND_NEQ_ZERO, \ OPCOUNT, \ &pc); \ } while (0) #define EFX_COPY(TO, FROM) do { \ emu_addefxop(sc, ACC3, \ TO, \ DSP_CONST(0), \ DSP_CONST(0), \ FROM, \ &pc); \ } while (0) static void emu_initefx(struct emu_sc_info *sc) { unsigned int i; uint32_t pc; /* stop DSP */ if (sc->is_emu10k1) { emu_wrptr(sc, 0, EMU_DBG, EMU_DBG_SINGLE_STEP); } else { emu_wrptr(sc, 0, EMU_A_DBG, EMU_A_DBG_SINGLE_STEP); } /* code size is in instructions */ pc = 0; for (i = 0; i < sc->code_size; i++) { if (sc->is_emu10k1) { emu_addefxop(sc, ACC3, DSP_CONST(0x0), DSP_CONST(0x0), DSP_CONST(0x0), DSP_CONST(0x0), &pc); } else { emu_addefxop(sc, SKIP, DSP_CONST(0x0), DSP_CONST(0x0), DSP_CONST(0xf), DSP_CONST(0x0), &pc); } } /* allocate GPRs for mute switches (EFX_SKIP). Mute by default */ for (i = 0; i < NUM_MUTE; i++) { sc->mute_gpr[i] = emu_rm_gpr_alloc(sc->rm, 1); emumix_set_gpr(sc, sc->mute_gpr[i], 1); } emu_digitalswitch(sc); pc = 0; /* * DSP code below is not good, because: * 1. It can be written smaller, if it can use DSP accumulator register * instead of cache_gpr[]. * 2. It can be more careful when volume is 100%, because in DSP * x*0x7fffffff may not be equal to x ! */ /* clean outputs */ for (i = 0; i < 16 ; i++) { emu_addefxop(sc, ACC3, OUTP(i), DSP_CONST(0), DSP_CONST(0), DSP_CONST(0), &pc); } if (sc->is_emu10k1) { EFX_CACHE(C_FRONT_L); EFX_CACHE(C_FRONT_R); EFX_CACHE(C_REC_L); EFX_CACHE(C_REC_R); /* fx0 to front/record, 100%/muted by default */ EFX_ROUTE("pcm_front_l", FX(0), M_FX0_FRONT_L, C_FRONT_L, 100); EFX_ROUTE("pcm_front_r", FX(1), M_FX1_FRONT_R, C_FRONT_R, 100); EFX_ROUTE(NULL, FX(0), M_FX0_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, FX(1), M_FX1_REC_R, C_REC_R, 0); /* in0, from AC97 codec output */ EFX_ROUTE("ac97_front_l", INP(IN_AC97_L), M_IN0_FRONT_L, C_FRONT_L, 0); EFX_ROUTE("ac97_front_r", INP(IN_AC97_R), M_IN0_FRONT_R, C_FRONT_R, 0); EFX_ROUTE("ac97_rec_l", INP(IN_AC97_L), M_IN0_REC_L, C_REC_L, 0); EFX_ROUTE("ac97_rec_r", INP(IN_AC97_R), M_IN0_REC_R, C_REC_R, 0); /* in1, from CD S/PDIF */ /* XXX EFX_SKIP 4 assumes that each EFX_ROUTE is one DSP op */ EFX_SKIP(4, CDSPDIFMUTE); EFX_ROUTE(NULL, INP(IN_SPDIF_CD_L), M_IN1_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(IN_SPDIF_CD_R), M_IN1_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(IN_SPDIF_CD_L), M_IN1_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(IN_SPDIF_CD_R), M_IN1_REC_R, C_REC_R, 0); if (sc->dbg_level > 0) { /* in2, ZoomVide (???) */ EFX_ROUTE("zoom_front_l", INP(IN_ZOOM_L), M_IN2_FRONT_L, C_FRONT_L, 0); EFX_ROUTE("zoom_front_r", INP(IN_ZOOM_R), M_IN2_FRONT_R, C_FRONT_R, 0); EFX_ROUTE("zoom_rec_l", INP(IN_ZOOM_L), M_IN2_REC_L, C_REC_L, 0); EFX_ROUTE("zoom_rec_r", INP(IN_ZOOM_R), M_IN2_REC_R, C_REC_R, 0); } /* in3, TOSLink */ EFX_ROUTE(NULL, INP(IN_TOSLINK_L), M_IN3_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(IN_TOSLINK_R), M_IN3_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(IN_TOSLINK_L), M_IN3_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(IN_TOSLINK_R), M_IN3_REC_R, C_REC_R, 0); /* in4, LineIn */ EFX_ROUTE(NULL, INP(IN_LINE1_L), M_IN4_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(IN_LINE1_R), M_IN4_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(IN_LINE1_L), M_IN4_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(IN_LINE1_R), M_IN4_REC_R, C_REC_R, 0); /* in5, on-card S/PDIF */ EFX_ROUTE(NULL, INP(IN_COAX_SPDIF_L), M_IN5_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(IN_COAX_SPDIF_R), M_IN5_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(IN_COAX_SPDIF_L), M_IN5_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(IN_COAX_SPDIF_R), M_IN5_REC_R, C_REC_R, 0); /* in6, Line2 on Live!Drive */ EFX_ROUTE(NULL, INP(IN_LINE2_L), M_IN6_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(IN_LINE2_R), M_IN6_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(IN_LINE2_L), M_IN6_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(IN_LINE2_R), M_IN6_REC_R, C_REC_R, 0); if (sc->dbg_level > 0) { /* in7, unknown */ EFX_ROUTE("in7_front_l", INP(0xE), M_IN7_FRONT_L, C_FRONT_L, 0); EFX_ROUTE("in7_front_r", INP(0xF), M_IN7_FRONT_R, C_FRONT_R, 0); EFX_ROUTE("in7_rec_l", INP(0xE), M_IN7_REC_L, C_REC_L, 0); EFX_ROUTE("in7_rec_r", INP(0xF), M_IN7_REC_R, C_REC_R, 0); } /* analog and digital */ EFX_OUTPUT("master_front_l", C_FRONT_L, M_MASTER_FRONT_L, OUT_AC97_L, 100); EFX_OUTPUT("master_front_r", C_FRONT_R, M_MASTER_FRONT_R, OUT_AC97_R, 100); /* S/PDIF */ EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, OUT_TOSLINK_L); EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, OUT_TOSLINK_R); /* Headphones */ EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, OUT_HEADPHONE_L); EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, OUT_HEADPHONE_R); /* rec output to "ADC" */ EFX_OUTPUT("master_rec_l", C_REC_L, M_MASTER_REC_L, OUT_ADC_REC_L, 100); EFX_OUTPUT("master_rec_r", C_REC_R, M_MASTER_REC_R, OUT_ADC_REC_R, 100); if (!(sc->mch_disabled)) { /* * Additional channel volume is controlled by mixer in * emu_dspmixer_set() in -pcm.c */ /* fx2/3 (pcm1) to rear */ EFX_CACHE(C_REAR_L); EFX_CACHE(C_REAR_R); EFX_ROUTE(NULL, FX(2), M_FX2_REAR_L, C_REAR_L, 100); EFX_ROUTE(NULL, FX(3), M_FX3_REAR_R, C_REAR_R, 100); EFX_OUTPUT(NULL, C_REAR_L, M_MASTER_REAR_L, OUT_REAR_L, 100); EFX_OUTPUT(NULL, C_REAR_R, M_MASTER_REAR_R, OUT_REAR_R, 100); if (sc->has_51) { /* fx4 (pcm2) to center */ EFX_CACHE(C_CENTER); EFX_ROUTE(NULL, FX(4), M_FX4_CENTER, C_CENTER, 100); EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, OUT_D_CENTER, 100); /* XXX in digital mode (default) this should be muted because this output is shared with digital out */ EFX_SKIP(1, ANALOGMUTE); EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, OUT_A_CENTER); /* fx5 (pcm3) to sub */ EFX_CACHE(C_SUB); EFX_ROUTE(NULL, FX(5), M_FX5_SUBWOOFER, C_SUB, 100); EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, OUT_D_SUB, 100); /* XXX in digital mode (default) this should be muted because this output is shared with digital out */ EFX_SKIP(1, ANALOGMUTE); EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, OUT_A_SUB); } } else { /* SND_EMU10KX_MULTICHANNEL_DISABLED */ EFX_OUTPUT(NULL, C_FRONT_L, M_MASTER_REAR_L, OUT_REAR_L, 57); /* 75%*75% */ EFX_OUTPUT(NULL, C_FRONT_R, M_MASTER_REAR_R, OUT_REAR_R, 57); /* 75%*75% */ #if 0 /* XXX 5.1 does not work */ if (sc->has_51) { /* (fx0+fx1)/2 to center */ EFX_CACHE(C_CENTER); emu_addefxop(sc, MACS, GPR(sc->cache_gpr[C_CENTER]), GPR(sc->cache_gpr[C_CENTER]), DSP_CONST(0xd), /* = 1/2 */ GPR(sc->cache_gpr[C_FRONT_L]), &pc); emu_addefxop(sc, MACS, GPR(sc->cache_gpr[C_CENTER]), GPR(sc->cache_gpr[C_CENTER]), DSP_CONST(0xd), /* = 1/2 */ GPR(sc->cache_gpr[C_FRONT_R]), &pc); EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, OUT_D_CENTER, 100); /* XXX in digital mode (default) this should be muted because this output is shared with digital out */ EFX_SKIP(1, ANALOGMUTE); EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, OUT_A_CENTER); /* (fx0+fx1)/2 to sub */ EFX_CACHE(C_SUB); emu_addefxop(sc, MACS, GPR(sc->cache_gpr[C_CENTER]), GPR(sc->cache_gpr[C_CENTER]), DSP_CONST(0xd), /* = 1/2 */ GPR(sc->cache_gpr[C_FRONT_L]), &pc); emu_addefxop(sc, MACS, GPR(sc->cache_gpr[C_CENTER]), GPR(sc->cache_gpr[C_CENTER]), DSP_CONST(0xd), /* = 1/2 */ GPR(sc->cache_gpr[C_FRONT_R]), &pc); /* XXX add lowpass filter here */ EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, OUT_D_SUB, 100); /* XXX in digital mode (default) this should be muted because this output is shared with digital out */ EFX_SKIP(1, ANALOGMUTE); EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, OUT_A_SUB); } #endif } /* !mch_disabled */ if (sc->mch_rec) { /* * MCH RECORDING , hight 16 slots. On 5.1 cards first 4 slots * are used as outputs and already filled with data */ /* * XXX On Live! cards stream does not begin at zero offset. * It can be HW, driver or sound buffering problem. * Use sync substream (offset 0x3E) to let userland find * correct data. */ /* * Substream map (in byte offsets, each substream is 2 bytes): * 0x00..0x1E - outputs * 0x20..0x3E - FX, inputs and sync stream */ /* First 2 channels (offset 0x20,0x22) are empty */ for(i = (sc->has_51 ? 2 : 0); i < 2; i++) EFX_COPY(FX2(i), DSP_CONST(0)); /* PCM Playback monitoring, offset 0x24..0x2A */ for(i = 0; i < 4; i++) EFX_COPY(FX2(i+2), FX(i)); /* Copy of some inputs, offset 0x2C..0x3C */ for(i = 0; i < 9; i++) EFX_COPY(FX2(i+8), INP(i)); /* sync data (0xc0de, offset 0x3E) */ sc->dummy_gpr = emu_rm_gpr_alloc(sc->rm, 1); emumix_set_gpr(sc, sc->dummy_gpr, 0xc0de0000); EFX_COPY(FX2(15), GPR(sc->dummy_gpr)); } /* mch_rec */ } else /* emu10k2 and later */ { EFX_CACHE(C_FRONT_L); EFX_CACHE(C_FRONT_R); EFX_CACHE(C_REC_L); EFX_CACHE(C_REC_R); /* fx0 to front/record, 100%/muted by default */ /* * FRONT_[L|R] is controlled by AC97 emulation in * emu_ac97_[read|write]_emulation in -pcm.c */ EFX_ROUTE(NULL, FX(0), M_FX0_FRONT_L, C_FRONT_L, 100); EFX_ROUTE(NULL, FX(1), M_FX1_FRONT_R, C_FRONT_R, 100); EFX_ROUTE(NULL, FX(0), M_FX0_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, FX(1), M_FX1_REC_R, C_REC_R, 0); /* in0, from AC97 codec output */ EFX_ROUTE(NULL, INP(A_IN_AC97_L), M_IN0_FRONT_L, C_FRONT_L, 100); EFX_ROUTE(NULL, INP(A_IN_AC97_R), M_IN0_FRONT_R, C_FRONT_R, 100); EFX_ROUTE(NULL, INP(A_IN_AC97_L), M_IN0_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(A_IN_AC97_R), M_IN0_REC_R, C_REC_R, 0); /* in1, from CD S/PDIF */ EFX_ROUTE(NULL, INP(A_IN_SPDIF_CD_L), M_IN1_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(A_IN_SPDIF_CD_R), M_IN1_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(A_IN_SPDIF_CD_L), M_IN1_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(A_IN_SPDIF_CD_R), M_IN1_REC_R, C_REC_R, 0); /* in2, optical & coax S/PDIF on AudigyDrive*/ /* XXX Should be muted when GPRSCS valid stream == 0 */ EFX_ROUTE(NULL, INP(A_IN_O_SPDIF_L), M_IN2_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(A_IN_O_SPDIF_R), M_IN2_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(A_IN_O_SPDIF_L), M_IN2_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(A_IN_O_SPDIF_R), M_IN2_REC_R, C_REC_R, 0); if (sc->dbg_level > 0) { /* in3, unknown */ EFX_ROUTE("in3_front_l", INP(0x6), M_IN3_FRONT_L, C_FRONT_L, 0); EFX_ROUTE("in3_front_r", INP(0x7), M_IN3_FRONT_R, C_FRONT_R, 0); EFX_ROUTE("in3_rec_l", INP(0x6), M_IN3_REC_L, C_REC_L, 0); EFX_ROUTE("in3_rec_r", INP(0x7), M_IN3_REC_R, C_REC_R, 0); } /* in4, LineIn 2 on AudigyDrive */ EFX_ROUTE(NULL, INP(A_IN_LINE2_L), M_IN4_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(A_IN_LINE2_R), M_IN4_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(A_IN_LINE2_L), M_IN4_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(A_IN_LINE2_R), M_IN4_REC_R, C_REC_R, 0); /* in5, on-card S/PDIF */ EFX_ROUTE(NULL, INP(A_IN_R_SPDIF_L), M_IN5_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(A_IN_R_SPDIF_R), M_IN5_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(A_IN_R_SPDIF_L), M_IN5_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(A_IN_R_SPDIF_R), M_IN5_REC_R, C_REC_R, 0); /* in6, AUX2 on AudigyDrive */ EFX_ROUTE(NULL, INP(A_IN_AUX2_L), M_IN6_FRONT_L, C_FRONT_L, 0); EFX_ROUTE(NULL, INP(A_IN_AUX2_R), M_IN6_FRONT_R, C_FRONT_R, 0); EFX_ROUTE(NULL, INP(A_IN_AUX2_L), M_IN6_REC_L, C_REC_L, 0); EFX_ROUTE(NULL, INP(A_IN_AUX2_R), M_IN6_REC_R, C_REC_R, 0); if (sc->dbg_level > 0) { /* in7, unknown */ EFX_ROUTE("in7_front_l", INP(0xE), M_IN7_FRONT_L, C_FRONT_L, 0); EFX_ROUTE("in7_front_r", INP(0xF), M_IN7_FRONT_R, C_FRONT_R, 0); EFX_ROUTE("in7_rec_l", INP(0xE), M_IN7_REC_L, C_REC_L, 0); EFX_ROUTE("in7_rec_r", INP(0xF), M_IN7_REC_R, C_REC_R, 0); } /* front output to headphones and alog and digital *front */ /* volume controlled by AC97 emulation */ EFX_OUTPUT(NULL, C_FRONT_L, M_MASTER_FRONT_L, A_OUT_A_FRONT_L, 100); EFX_OUTPUT(NULL, C_FRONT_R, M_MASTER_FRONT_R, A_OUT_A_FRONT_R, 100); EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_D_FRONT_L); EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_D_FRONT_R); EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_HPHONE_L); EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_HPHONE_R); /* rec output to "ADC" */ /* volume controlled by AC97 emulation */ EFX_OUTPUT(NULL, C_REC_L, M_MASTER_REC_L, A_OUT_ADC_REC_L, 100); EFX_OUTPUT(NULL, C_REC_R, M_MASTER_REC_R, A_OUT_ADC_REC_R, 100); if (!(sc->mch_disabled)) { /* * Additional channel volume is controlled by mixer in * emu_dspmixer_set() in -pcm.c */ /* fx2/3 (pcm1) to rear */ EFX_CACHE(C_REAR_L); EFX_CACHE(C_REAR_R); EFX_ROUTE(NULL, FX(2), M_FX2_REAR_L, C_REAR_L, 100); EFX_ROUTE(NULL, FX(3), M_FX3_REAR_R, C_REAR_R, 100); EFX_OUTPUT(NULL, C_REAR_L, M_MASTER_REAR_L, A_OUT_A_REAR_L, 100); EFX_OUTPUT(NULL, C_REAR_R, M_MASTER_REAR_R, A_OUT_A_REAR_R, 100); EFX_OUTPUTD(C_REAR_L, M_MASTER_REAR_L, A_OUT_D_REAR_L); EFX_OUTPUTD(C_REAR_R, M_MASTER_REAR_R, A_OUT_D_REAR_R); /* fx4 (pcm2) to center */ EFX_CACHE(C_CENTER); EFX_ROUTE(NULL, FX(4), M_FX4_CENTER, C_CENTER, 100); EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, A_OUT_D_CENTER, 100); #if 0 /* * XXX in digital mode (default) this should be muted * because this output is shared with digital out */ EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, A_OUT_A_CENTER); #endif /* fx5 (pcm3) to sub */ EFX_CACHE(C_SUB); EFX_ROUTE(NULL, FX(5), M_FX5_SUBWOOFER, C_SUB, 100); EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, A_OUT_D_SUB, 100); #if 0 /* * XXX in digital mode (default) this should be muted * because this output is shared with digital out */ EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, A_OUT_A_SUB); #endif if (sc->has_71) { /* XXX this will broke headphones on AudigyDrive */ /* fx6/7 (pcm4) to side */ EFX_CACHE(C_SIDE_L); EFX_CACHE(C_SIDE_R); EFX_ROUTE(NULL, FX(6), M_FX6_SIDE_L, C_SIDE_L, 100); EFX_ROUTE(NULL, FX(7), M_FX7_SIDE_R, C_SIDE_R, 100); EFX_OUTPUT(NULL, C_SIDE_L, M_MASTER_SIDE_L, A_OUT_A_SIDE_L, 100); EFX_OUTPUT(NULL, C_SIDE_R, M_MASTER_SIDE_R, A_OUT_A_SIDE_R, 100); EFX_OUTPUTD(C_SIDE_L, M_MASTER_SIDE_L, A_OUT_D_SIDE_L); EFX_OUTPUTD(C_SIDE_R, M_MASTER_SIDE_R, A_OUT_D_SIDE_R); } } else { /* mch_disabled */ EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_A_REAR_L); EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_A_REAR_R); EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_D_REAR_L); EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_D_REAR_R); if (sc->has_51) { /* (fx0+fx1)/2 to center */ EFX_CACHE(C_CENTER); emu_addefxop(sc, MACS, GPR(sc->cache_gpr[C_CENTER]), GPR(sc->cache_gpr[C_CENTER]), DSP_CONST(0xd), /* = 1/2 */ GPR(sc->cache_gpr[C_FRONT_L]), &pc); emu_addefxop(sc, MACS, GPR(sc->cache_gpr[C_CENTER]), GPR(sc->cache_gpr[C_CENTER]), DSP_CONST(0xd), /* = 1/2 */ GPR(sc->cache_gpr[C_FRONT_R]), &pc); EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, A_OUT_D_CENTER, 100); /* XXX in digital mode (default) this should be muted because this output is shared with digital out */ EFX_SKIP(1, ANALOGMUTE); EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, A_OUT_A_CENTER); /* (fx0+fx1)/2 to sub */ EFX_CACHE(C_SUB); emu_addefxop(sc, MACS, GPR(sc->cache_gpr[C_SUB]), GPR(sc->cache_gpr[C_SUB]), DSP_CONST(0xd), /* = 1/2 */ GPR(sc->cache_gpr[C_FRONT_L]), &pc); emu_addefxop(sc, MACS, GPR(sc->cache_gpr[C_SUB]), GPR(sc->cache_gpr[C_SUB]), DSP_CONST(0xd), /* = 1/2 */ GPR(sc->cache_gpr[C_FRONT_R]), &pc); /* XXX add lowpass filter here */ EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, A_OUT_D_SUB, 100); /* XXX in digital mode (default) this should be muted because this output is shared with digital out */ EFX_SKIP(1, ANALOGMUTE); EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, A_OUT_A_SUB); } } /* mch_disabled */ if (sc->mch_rec) { /* MCH RECORDING, high 32 slots */ /* * Stream map (in byte offsets): * 0x00..0x3E - outputs * 0x40..0x7E - FX, inputs * each substream is 2 bytes. */ /* * XXX Audigy 2 Value cards (and, possibly, * Audigy 4) write some unknown data in place of * some outputs (offsets 0x20..0x3F) and one * input (offset 0x7E). */ /* PCM Playback monitoring, offsets 0x40..0x5E */ for(i = 0; i < 16; i++) EFX_COPY(FX2(i), FX(i)); /* Copy of all inputs, offsets 0x60..0x7E */ for(i = 0; i < 16; i++) EFX_COPY(FX2(i+16), INP(i)); #if 0 /* XXX Audigy seems to work correct and does not need this */ /* sync data (0xc0de), offset 0x7E */ sc->dummy_gpr = emu_rm_gpr_alloc(sc->rm, 1); emumix_set_gpr(sc, sc->dummy_gpr, 0xc0de0000); EFX_COPY(FX2(31), GPR(sc->dummy_gpr)); #endif } /* mch_rec */ } sc->routing_code_end = pc; /* start DSP */ if (sc->is_emu10k1) { emu_wrptr(sc, 0, EMU_DBG, 0); } else { emu_wrptr(sc, 0, EMU_A_DBG, 0); } } /* /dev/em10kx */ static d_open_t emu10kx_open; static d_close_t emu10kx_close; static d_read_t emu10kx_read; static struct cdevsw emu10kx_cdevsw = { .d_open = emu10kx_open, .d_close = emu10kx_close, .d_read = emu10kx_read, .d_name = "emu10kx", .d_version = D_VERSION, }; static int emu10kx_open(struct cdev *i_dev, int flags __unused, int mode __unused, struct thread *td __unused) { int error; struct emu_sc_info *sc; sc = i_dev->si_drv1; mtx_lock(&sc->emu10kx_lock); if (sc->emu10kx_isopen) { mtx_unlock(&sc->emu10kx_lock); return (EBUSY); } sc->emu10kx_isopen = 1; mtx_unlock(&sc->emu10kx_lock); if (sbuf_new(&sc->emu10kx_sbuf, NULL, 4096, 0) == NULL) { error = ENXIO; goto out; } sc->emu10kx_bufptr = 0; error = (emu10kx_prepare(sc, &sc->emu10kx_sbuf) > 0) ? 0 : ENOMEM; out: if (error) { mtx_lock(&sc->emu10kx_lock); sc->emu10kx_isopen = 0; mtx_unlock(&sc->emu10kx_lock); } return (error); } static int emu10kx_close(struct cdev *i_dev, int flags __unused, int mode __unused, struct thread *td __unused) { struct emu_sc_info *sc; sc = i_dev->si_drv1; mtx_lock(&sc->emu10kx_lock); if (!(sc->emu10kx_isopen)) { mtx_unlock(&sc->emu10kx_lock); return (EBADF); } sbuf_delete(&sc->emu10kx_sbuf); sc->emu10kx_isopen = 0; mtx_unlock(&sc->emu10kx_lock); return (0); } static int emu10kx_read(struct cdev *i_dev, struct uio *buf, int flag __unused) { int l, err; struct emu_sc_info *sc; sc = i_dev->si_drv1; mtx_lock(&sc->emu10kx_lock); if (!(sc->emu10kx_isopen)) { mtx_unlock(&sc->emu10kx_lock); return (EBADF); } mtx_unlock(&sc->emu10kx_lock); l = min(buf->uio_resid, sbuf_len(&sc->emu10kx_sbuf) - sc->emu10kx_bufptr); err = (l > 0) ? uiomove(sbuf_data(&sc->emu10kx_sbuf) + sc->emu10kx_bufptr, l, buf) : 0; sc->emu10kx_bufptr += l; return (err); } static int emu10kx_prepare(struct emu_sc_info *sc, struct sbuf *s) { int i; sbuf_printf(s, "FreeBSD EMU10Kx Audio Driver\n"); sbuf_printf(s, "\nHardware resource usage:\n"); sbuf_printf(s, "DSP General Purpose Registers: %d used, %d total\n", sc->rm->num_used, sc->rm->num_gprs); sbuf_printf(s, "DSP Instruction Registers: %d used, %d total\n", sc->routing_code_end, sc->code_size); sbuf_printf(s, "Card supports"); if (sc->has_ac97) { sbuf_printf(s, " AC97 codec"); } else { sbuf_printf(s, " NO AC97 codec"); } if (sc->has_51) { if (sc->has_71) sbuf_printf(s, " and 7.1 output"); else sbuf_printf(s, " and 5.1 output"); } if (sc->is_emu10k1) sbuf_printf(s, ", SBLive! DSP code"); if (sc->is_emu10k2) sbuf_printf(s, ", Audigy DSP code"); if (sc->is_ca0102) sbuf_printf(s, ", Audigy DSP code with Audigy2 hacks"); if (sc->is_ca0108) sbuf_printf(s, ", Audigy DSP code with Audigy2Value hacks"); sbuf_printf(s, "\n"); if (sc->broken_digital) sbuf_printf(s, "Digital mode unsupported\n"); sbuf_printf(s, "\nInstalled devices:\n"); for (i = 0; i < RT_COUNT; i++) if (sc->pcm[i] != NULL) if (device_is_attached(sc->pcm[i])) { sbuf_printf(s, "%s on %s\n", device_get_desc(sc->pcm[i]), device_get_nameunit(sc->pcm[i])); } if (sc->midi[0] != NULL) if (device_is_attached(sc->midi[0])) { sbuf_printf(s, "EMU10Kx MIDI Interface\n"); sbuf_printf(s, "\tOn-card connector on %s\n", device_get_nameunit(sc->midi[0])); } if (sc->midi[1] != NULL) if (device_is_attached(sc->midi[1])) { sbuf_printf(s, "\tOn-Drive connector on %s\n", device_get_nameunit(sc->midi[1])); } if (sc->midi[0] != NULL) if (device_is_attached(sc->midi[0])) { sbuf_printf(s, "\tIR receiver MIDI events %s\n", sc->enable_ir ? "enabled" : "disabled"); } sbuf_printf(s, "Card is in %s mode\n", (sc->mode == MODE_ANALOG) ? "analog" : "digital"); sbuf_finish(s); return (sbuf_len(s)); } /* INIT & UNINIT */ static int emu10kx_dev_init(struct emu_sc_info *sc) { int unit; mtx_init(&sc->emu10kx_lock, device_get_nameunit(sc->dev), "kxdevlock", 0); unit = device_get_unit(sc->dev); sc->cdev = make_dev(&emu10kx_cdevsw, PCMMINOR(unit), UID_ROOT, GID_WHEEL, 0640, "emu10kx%d", unit); if (sc->cdev != NULL) { sc->cdev->si_drv1 = sc; return (0); } return (ENXIO); } static int emu10kx_dev_uninit(struct emu_sc_info *sc) { mtx_lock(&sc->emu10kx_lock); if (sc->emu10kx_isopen) { mtx_unlock(&sc->emu10kx_lock); return (EBUSY); } if (sc->cdev) destroy_dev(sc->cdev); sc->cdev = 0; mtx_destroy(&sc->emu10kx_lock); return (0); } /* resource manager */ int emu_rm_init(struct emu_sc_info *sc) { int i; int maxcount; struct emu_rm *rm; rm = malloc(sizeof(struct emu_rm), M_DEVBUF, M_NOWAIT | M_ZERO); if (rm == NULL) { return (ENOMEM); } sc->rm = rm; rm->card = sc; maxcount = sc->num_gprs; rm->num_used = 0; mtx_init(&(rm->gpr_lock), device_get_nameunit(sc->dev), "gpr alloc", MTX_DEF); rm->num_gprs = (maxcount < EMU_MAX_GPR ? maxcount : EMU_MAX_GPR); for (i = 0; i < rm->num_gprs; i++) rm->allocmap[i] = 0; /* pre-allocate gpr[0] */ rm->allocmap[0] = 1; rm->last_free_gpr = 1; return (0); } int emu_rm_uninit(struct emu_sc_info *sc) { int i; if (sc->dbg_level > 1) { mtx_lock(&(sc->rm->gpr_lock)); for (i = 1; i < sc->rm->last_free_gpr; i++) if (sc->rm->allocmap[i] > 0) device_printf(sc->dev, "rm: gpr %d not free before uninit\n", i); mtx_unlock(&(sc->rm->gpr_lock)); } mtx_destroy(&(sc->rm->gpr_lock)); free(sc->rm, M_DEVBUF); return (0); } static int emu_rm_gpr_alloc(struct emu_rm *rm, int count) { int i, j; int allocated_gpr; allocated_gpr = rm->num_gprs; /* try fast way first */ mtx_lock(&(rm->gpr_lock)); if (rm->last_free_gpr + count <= rm->num_gprs) { allocated_gpr = rm->last_free_gpr; rm->last_free_gpr += count; rm->allocmap[allocated_gpr] = count; for (i = 1; i < count; i++) rm->allocmap[allocated_gpr + i] = -(count - i); } else { /* longer */ i = 0; allocated_gpr = rm->num_gprs; while (i < rm->last_free_gpr - count) { if (rm->allocmap[i] > 0) { i += rm->allocmap[i]; } else { allocated_gpr = i; for (j = 1; j < count; j++) { if (rm->allocmap[i + j] != 0) allocated_gpr = rm->num_gprs; } if (allocated_gpr == i) break; } } if (allocated_gpr + count < rm->last_free_gpr) { rm->allocmap[allocated_gpr] = count; for (i = 1; i < count; i++) rm->allocmap[allocated_gpr + i] = -(count - i); } } if (allocated_gpr == rm->num_gprs) allocated_gpr = (-1); if (allocated_gpr >= 0) rm->num_used += count; mtx_unlock(&(rm->gpr_lock)); return (allocated_gpr); } /* mixer */ void emumix_set_mode(struct emu_sc_info *sc, int mode) { uint32_t a_iocfg; uint32_t hcfg; uint32_t tmp; switch (mode) { case MODE_DIGITAL: /* FALLTHROUGH */ case MODE_ANALOG: break; default: return; } hcfg = EMU_HCFG_AUDIOENABLE | EMU_HCFG_AUTOMUTE; a_iocfg = 0; if (sc->rev >= 6) hcfg |= EMU_HCFG_JOYENABLE; if (sc->is_emu10k1) hcfg |= EMU_HCFG_LOCKTANKCACHE_MASK; else hcfg |= EMU_HCFG_CODECFMT_I2S | EMU_HCFG_JOYENABLE; if (mode == MODE_DIGITAL) { if (sc->broken_digital) { device_printf(sc->dev, "Digital mode is reported as broken on this card.\n"); } a_iocfg |= EMU_A_IOCFG_GPOUT1; hcfg |= EMU_HCFG_GPOUT0; } if (mode == MODE_ANALOG) emumix_set_spdif_mode(sc, SPDIF_MODE_PCM); if (sc->is_emu10k2) a_iocfg |= 0x80; /* XXX */ if ((sc->is_ca0102) || (sc->is_ca0108)) /* * Setting EMU_A_IOCFG_DISABLE_ANALOG will do opposite things * on diffrerent cards. * "don't disable analog outs" on Audigy 2 (ca0102/ca0108) * "disable analog outs" on Audigy (emu10k2) */ a_iocfg |= EMU_A_IOCFG_DISABLE_ANALOG; if (sc->is_ca0108) a_iocfg |= 0x20; /* XXX */ /* Mute analog center & subwoofer before mode change */ if (mode == MODE_DIGITAL) emumix_set_gpr(sc, sc->mute_gpr[ANALOGMUTE], 1); emu_wr(sc, EMU_HCFG, hcfg, 4); if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { tmp = emu_rd(sc, EMU_A_IOCFG, 2); tmp = a_iocfg; emu_wr(sc, EMU_A_IOCFG, tmp, 2); } /* Unmute if we have changed mode to analog. */ if (mode == MODE_ANALOG) emumix_set_gpr(sc, sc->mute_gpr[ANALOGMUTE], 0); sc->mode = mode; } void emumix_set_spdif_mode(struct emu_sc_info *sc, int mode) { uint32_t spcs; switch (mode) { case SPDIF_MODE_PCM: break; case SPDIF_MODE_AC3: device_printf(sc->dev, "AC3 mode does not work and disabled\n"); return; default: return; } spcs = EMU_SPCS_CLKACCY_1000PPM | EMU_SPCS_SAMPLERATE_48 | EMU_SPCS_CHANNELNUM_LEFT | EMU_SPCS_SOURCENUM_UNSPEC | EMU_SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 | EMU_SPCS_EMPHASIS_NONE | EMU_SPCS_COPYRIGHT; mode = SPDIF_MODE_PCM; emu_wrptr(sc, 0, EMU_SPCS0, spcs); emu_wrptr(sc, 0, EMU_SPCS1, spcs); emu_wrptr(sc, 0, EMU_SPCS2, spcs); } #define L2L_POINTS 10 static int l2l_df[L2L_POINTS] = { 0x572C5CA, /* 100..90 */ 0x3211625, /* 90..80 */ 0x1CC1A76, /* 80..70 */ 0x108428F, /* 70..60 */ 0x097C70A, /* 60..50 */ 0x0572C5C, /* 50..40 */ 0x0321162, /* 40..30 */ 0x01CC1A7, /* 30..20 */ 0x0108428, /* 20..10 */ 0x016493D /* 10..0 */ }; static int l2l_f[L2L_POINTS] = { 0x4984461A, /* 90 */ 0x2A3968A7, /* 80 */ 0x18406003, /* 70 */ 0x0DEDC66D, /* 60 */ 0x07FFFFFF, /* 50 */ 0x04984461, /* 40 */ 0x02A3968A, /* 30 */ 0x01840600, /* 20 */ 0x00DEDC66, /* 10 */ 0x00000000 /* 0 */ }; static int log2lin(int log_t) { int lin_t; int idx, lin; if (log_t <= 0) { lin_t = 0x00000000; return (lin_t); } if (log_t >= 100) { lin_t = 0x7fffffff; return (lin_t); } idx = (L2L_POINTS - 1) - log_t / (L2L_POINTS); lin = log_t % (L2L_POINTS); lin_t = l2l_df[idx] * lin + l2l_f[idx]; return (lin_t); } void emumix_set_fxvol(struct emu_sc_info *sc, unsigned gpr, int32_t vol) { vol = log2lin(vol); emumix_set_gpr(sc, gpr, vol); } void emumix_set_gpr(struct emu_sc_info *sc, unsigned gpr, int32_t val) { if (sc->dbg_level > 1) if (gpr == 0) { device_printf(sc->dev, "Zero gpr write access\n"); #ifdef KDB kdb_backtrace(); #endif return; } emu_wrptr(sc, 0, GPR(gpr), val); } void emumix_set_volume(struct emu_sc_info *sc, int mixer_idx, int volume) { RANGE(volume, 0, 100); if (mixer_idx < NUM_MIXERS) { sc->mixer_volcache[mixer_idx] = volume; emumix_set_fxvol(sc, sc->mixer_gpr[mixer_idx], volume); } } int emumix_get_volume(struct emu_sc_info *sc, int mixer_idx) { if ((mixer_idx < NUM_MIXERS) && (mixer_idx >= 0)) return (sc->mixer_volcache[mixer_idx]); return (-1); } /* Init CardBus part */ static int emu_cardbus_init(struct emu_sc_info *sc) { /* * XXX May not need this if we have EMU_IPR3 handler. * Is it a real init calls, or EMU_IPR3 interrupt acknowledgments? * Looks much like "(data << 16) | register". */ emu_wr_cbptr(sc, (0x00d0 << 16) | 0x0000); emu_wr_cbptr(sc, (0x00d0 << 16) | 0x0001); emu_wr_cbptr(sc, (0x00d0 << 16) | 0x005f); emu_wr_cbptr(sc, (0x00d0 << 16) | 0x007f); emu_wr_cbptr(sc, (0x0090 << 16) | 0x007f); return (0); } /* Probe and attach the card */ static int emu_init(struct emu_sc_info *sc) { uint32_t ch, tmp; uint32_t spdif_sr; uint32_t ac97slot; int def_mode; int i; /* disable audio and lock cache */ emu_wr(sc, EMU_HCFG, EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE, 4); /* reset recording buffers */ emu_wrptr(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE); emu_wrptr(sc, 0, EMU_MICBA, 0); emu_wrptr(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE); emu_wrptr(sc, 0, EMU_FXBA, 0); emu_wrptr(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE); emu_wrptr(sc, 0, EMU_ADCBA, 0); /* disable channel interrupt */ emu_wr(sc, EMU_INTE, EMU_INTE_INTERTIMERENB | EMU_INTE_SAMPLERATER | EMU_INTE_PCIERRENABLE, 4); emu_wrptr(sc, 0, EMU_CLIEL, 0); emu_wrptr(sc, 0, EMU_CLIEH, 0); emu_wrptr(sc, 0, EMU_SOLEL, 0); emu_wrptr(sc, 0, EMU_SOLEH, 0); /* disable P16V and S/PDIF interrupts */ if ((sc->is_ca0102) || (sc->is_ca0108)) emu_wr(sc, EMU_INTE2, 0, 4); if (sc->is_ca0102) emu_wr(sc, EMU_INTE3, 0, 4); /* init phys inputs and outputs */ ac97slot = 0; if (sc->has_51) ac97slot = EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE; if (sc->has_71) ac97slot = EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE | EMU_AC97SLOT_REAR_LEFT | EMU_AC97SLOT_REAR_RIGHT; if (sc->is_emu10k2) ac97slot |= 0x40; emu_wrptr(sc, 0, EMU_AC97SLOT, ac97slot); if (sc->is_emu10k2) /* XXX for later cards? */ emu_wrptr(sc, 0, EMU_SPBYPASS, 0xf00); /* What will happen if * we write 1 here? */ if (bus_dma_tag_create( /* parent */ bus_get_dma_tag(sc->dev), /* alignment */ 2, /* boundary */ 0, /* lowaddr */ (1U << 31) - 1, /* can only access 0-2gb */ /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ EMU_MAX_BUFSZ, /* nsegments */ 1, /* maxsegz */ 0x3ffff, /* flags */ 0, /* lockfunc */ busdma_lock_mutex, /* lockarg */ &Giant, &(sc->mem.dmat)) != 0) { device_printf(sc->dev, "unable to create dma tag\n"); bus_dma_tag_destroy(sc->mem.dmat); return (ENOMEM); } sc->mem.card = sc; SLIST_INIT(&sc->mem.blocks); sc->mem.ptb_pages = emu_malloc(&sc->mem, EMU_MAXPAGES * sizeof(uint32_t), &sc->mem.ptb_pages_addr, &sc->mem.ptb_map); if (sc->mem.ptb_pages == NULL) return (ENOMEM); sc->mem.silent_page = emu_malloc(&sc->mem, EMUPAGESIZE, &sc->mem.silent_page_addr, &sc->mem.silent_map); if (sc->mem.silent_page == NULL) { emu_free(&sc->mem, sc->mem.ptb_pages, sc->mem.ptb_map); return (ENOMEM); } /* Clear page with silence & setup all pointers to this page */ bzero(sc->mem.silent_page, EMUPAGESIZE); tmp = (uint32_t) (sc->mem.silent_page_addr) << 1; for (i = 0; i < EMU_MAXPAGES; i++) sc->mem.ptb_pages[i] = tmp | i; for (ch = 0; ch < NUM_G; ch++) { emu_wrptr(sc, ch, EMU_CHAN_MAPA, tmp | EMU_CHAN_MAP_PTI_MASK); emu_wrptr(sc, ch, EMU_CHAN_MAPB, tmp | EMU_CHAN_MAP_PTI_MASK); } emu_wrptr(sc, 0, EMU_PTB, (sc->mem.ptb_pages_addr)); emu_wrptr(sc, 0, EMU_TCB, 0); /* taken from original driver */ emu_wrptr(sc, 0, EMU_TCBS, 0); /* taken from original driver */ /* init envelope engine */ for (ch = 0; ch < NUM_G; ch++) { emu_wrptr(sc, ch, EMU_CHAN_DCYSUSV, 0); emu_wrptr(sc, ch, EMU_CHAN_IP, 0); emu_wrptr(sc, ch, EMU_CHAN_VTFT, 0xffff); emu_wrptr(sc, ch, EMU_CHAN_CVCF, 0xffff); emu_wrptr(sc, ch, EMU_CHAN_PTRX, 0); emu_wrptr(sc, ch, EMU_CHAN_CPF, 0); emu_wrptr(sc, ch, EMU_CHAN_CCR, 0); emu_wrptr(sc, ch, EMU_CHAN_PSST, 0); emu_wrptr(sc, ch, EMU_CHAN_DSL, 0x10); emu_wrptr(sc, ch, EMU_CHAN_CCCA, 0); emu_wrptr(sc, ch, EMU_CHAN_Z1, 0); emu_wrptr(sc, ch, EMU_CHAN_Z2, 0); emu_wrptr(sc, ch, EMU_CHAN_FXRT, 0xd01c0000); emu_wrptr(sc, ch, EMU_CHAN_ATKHLDM, 0); emu_wrptr(sc, ch, EMU_CHAN_DCYSUSM, 0); emu_wrptr(sc, ch, EMU_CHAN_IFATN, 0xffff); emu_wrptr(sc, ch, EMU_CHAN_PEFE, 0); emu_wrptr(sc, ch, EMU_CHAN_FMMOD, 0); emu_wrptr(sc, ch, EMU_CHAN_TREMFRQ, 24); /* 1 Hz */ emu_wrptr(sc, ch, EMU_CHAN_FM2FRQ2, 24); /* 1 Hz */ emu_wrptr(sc, ch, EMU_CHAN_TEMPENV, 0); /*** these are last so OFF prevents writing ***/ emu_wrptr(sc, ch, EMU_CHAN_LFOVAL2, 0); emu_wrptr(sc, ch, EMU_CHAN_LFOVAL1, 0); emu_wrptr(sc, ch, EMU_CHAN_ATKHLDV, 0); emu_wrptr(sc, ch, EMU_CHAN_ENVVOL, 0); emu_wrptr(sc, ch, EMU_CHAN_ENVVAL, 0); if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { emu_wrptr(sc, ch, 0x4c, 0x0); emu_wrptr(sc, ch, 0x4d, 0x0); emu_wrptr(sc, ch, 0x4e, 0x0); emu_wrptr(sc, ch, 0x4f, 0x0); emu_wrptr(sc, ch, EMU_A_CHAN_FXRT1, 0x3f3f3f3f); emu_wrptr(sc, ch, EMU_A_CHAN_FXRT2, 0x3f3f3f3f); emu_wrptr(sc, ch, EMU_A_CHAN_SENDAMOUNTS, 0x0); } } emumix_set_spdif_mode(sc, SPDIF_MODE_PCM); if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) emu_wrptr(sc, 0, EMU_A_SPDIF_SAMPLERATE, EMU_A_SPDIF_48000); /* * CAxxxx cards needs additional setup: * 1. Set I2S capture sample rate to 96000 * 2. Disable P16v / P17v proceesing * 3. Allow EMU10K DSP inputs */ if ((sc->is_ca0102) || (sc->is_ca0108)) { spdif_sr = emu_rdptr(sc, 0, EMU_A_SPDIF_SAMPLERATE); spdif_sr &= 0xfffff1ff; spdif_sr |= EMU_A_I2S_CAPTURE_96000; emu_wrptr(sc, 0, EMU_A_SPDIF_SAMPLERATE, spdif_sr); /* Disable P16v processing */ emu_wr_p16vptr(sc, 0, EMU_A2_SRCSel, 0x14); /* Setup P16v/P17v sound routing */ if (sc->is_ca0102) emu_wr_p16vptr(sc, 0, EMU_A2_SRCMULTI_ENABLE, 0xFF00FF00); else { emu_wr_p16vptr(sc, 0, EMU_A2_MIXER_I2S_ENABLE, 0xFF000000); emu_wr_p16vptr(sc, 0, EMU_A2_MIXER_SPDIF_ENABLE, 0xFF000000); tmp = emu_rd(sc, EMU_A_IOCFG, 2); emu_wr(sc, EMU_A_IOCFG, tmp & ~0x8, 2); } } emu_initefx(sc); def_mode = MODE_ANALOG; if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) def_mode = MODE_DIGITAL; if (((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) && (sc->broken_digital)) { device_printf(sc->dev, "Audigy card initialized in analog mode.\n"); def_mode = MODE_ANALOG; } emumix_set_mode(sc, def_mode); if (bootverbose) { tmp = emu_rd(sc, EMU_HCFG, 4); device_printf(sc->dev, "Card Configuration ( 0x%08x )\n", tmp); device_printf(sc->dev, "Card Configuration ( & 0xff000000 ) : %s%s%s%s%s%s%s%s\n", (tmp & 0x80000000 ? "[Legacy MPIC] " : ""), (tmp & 0x40000000 ? "[0x40] " : ""), (tmp & 0x20000000 ? "[0x20] " : ""), (tmp & 0x10000000 ? "[0x10] " : ""), (tmp & 0x08000000 ? "[0x08] " : ""), (tmp & 0x04000000 ? "[0x04] " : ""), (tmp & 0x02000000 ? "[0x02] " : ""), (tmp & 0x01000000 ? "[0x01]" : " ")); device_printf(sc->dev, "Card Configuration ( & 0x00ff0000 ) : %s%s%s%s%s%s%s%s\n", (tmp & 0x00800000 ? "[0x80] " : ""), (tmp & 0x00400000 ? "[0x40] " : ""), (tmp & 0x00200000 ? "[Legacy INT] " : ""), (tmp & 0x00100000 ? "[0x10] " : ""), (tmp & 0x00080000 ? "[0x08] " : ""), (tmp & 0x00040000 ? "[Codec4] " : ""), (tmp & 0x00020000 ? "[Codec2] " : ""), (tmp & 0x00010000 ? "[I2S Codec]" : " ")); device_printf(sc->dev, "Card Configuration ( & 0x0000ff00 ) : %s%s%s%s%s%s%s%s\n", (tmp & 0x00008000 ? "[0x80] " : ""), (tmp & 0x00004000 ? "[GPINPUT0] " : ""), (tmp & 0x00002000 ? "[GPINPUT1] " : ""), (tmp & 0x00001000 ? "[GPOUT0] " : ""), (tmp & 0x00000800 ? "[GPOUT1] " : ""), (tmp & 0x00000400 ? "[GPOUT2] " : ""), (tmp & 0x00000200 ? "[Joystick] " : ""), (tmp & 0x00000100 ? "[0x01]" : " ")); device_printf(sc->dev, "Card Configuration ( & 0x000000ff ) : %s%s%s%s%s%s%s%s\n", (tmp & 0x00000080 ? "[0x80] " : ""), (tmp & 0x00000040 ? "[0x40] " : ""), (tmp & 0x00000020 ? "[0x20] " : ""), (tmp & 0x00000010 ? "[AUTOMUTE] " : ""), (tmp & 0x00000008 ? "[LOCKSOUNDCACHE] " : ""), (tmp & 0x00000004 ? "[LOCKTANKCACHE] " : ""), (tmp & 0x00000002 ? "[MUTEBUTTONENABLE] " : ""), (tmp & 0x00000001 ? "[AUDIOENABLE]" : " ")); if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { tmp = emu_rd(sc, EMU_A_IOCFG, 2); device_printf(sc->dev, "Audigy Card Configuration ( 0x%04x )\n", tmp); device_printf(sc->dev, "Audigy Card Configuration ( & 0xff00 )"); printf(" : %s%s%s%s%s%s%s%s\n", (tmp & 0x8000 ? "[Rear Speakers] " : ""), (tmp & 0x4000 ? "[Front Speakers] " : ""), (tmp & 0x2000 ? "[0x20] " : ""), (tmp & 0x1000 ? "[0x10] " : ""), (tmp & 0x0800 ? "[0x08] " : ""), (tmp & 0x0400 ? "[0x04] " : ""), (tmp & 0x0200 ? "[0x02] " : ""), (tmp & 0x0100 ? "[AudigyDrive Phones]" : " ")); device_printf(sc->dev, "Audigy Card Configuration ( & 0x00ff )"); printf(" : %s%s%s%s%s%s%s%s\n", (tmp & 0x0080 ? "[0x80] " : ""), (tmp & 0x0040 ? "[Mute AnalogOut] " : ""), (tmp & 0x0020 ? "[0x20] " : ""), (tmp & 0x0010 ? "[0x10] " : ""), (tmp & 0x0008 ? "[0x08] " : ""), (tmp & 0x0004 ? "[GPOUT0] " : ""), (tmp & 0x0002 ? "[GPOUT1] " : ""), (tmp & 0x0001 ? "[GPOUT2]" : " ")); } /* is_emu10k2 or ca* */ } /* bootverbose */ return (0); } static int emu_uninit(struct emu_sc_info *sc) { uint32_t ch; struct emu_memblk *blk; emu_wr(sc, EMU_INTE, 0, 4); for (ch = 0; ch < NUM_G; ch++) emu_wrptr(sc, ch, EMU_CHAN_DCYSUSV, 0); for (ch = 0; ch < NUM_G; ch++) { emu_wrptr(sc, ch, EMU_CHAN_VTFT, 0); emu_wrptr(sc, ch, EMU_CHAN_CVCF, 0); emu_wrptr(sc, ch, EMU_CHAN_PTRX, 0); emu_wrptr(sc, ch, EMU_CHAN_CPF, 0); } /* disable audio and lock cache */ emu_wr(sc, EMU_HCFG, EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE, 4); emu_wrptr(sc, 0, EMU_PTB, 0); /* reset recording buffers */ emu_wrptr(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE); emu_wrptr(sc, 0, EMU_MICBA, 0); emu_wrptr(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE); emu_wrptr(sc, 0, EMU_FXBA, 0); emu_wrptr(sc, 0, EMU_FXWC, 0); emu_wrptr(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE); emu_wrptr(sc, 0, EMU_ADCBA, 0); emu_wrptr(sc, 0, EMU_TCB, 0); emu_wrptr(sc, 0, EMU_TCBS, 0); /* disable channel interrupt */ emu_wrptr(sc, 0, EMU_CLIEL, 0); emu_wrptr(sc, 0, EMU_CLIEH, 0); emu_wrptr(sc, 0, EMU_SOLEL, 0); emu_wrptr(sc, 0, EMU_SOLEH, 0); if (!SLIST_EMPTY(&sc->mem.blocks)) device_printf(sc->dev, "warning: memblock list not empty\n"); SLIST_FOREACH(blk, &sc->mem.blocks, link) if (blk != NULL) device_printf(sc->dev, "lost %d for %s\n", blk->pte_size, blk->owner); emu_free(&sc->mem, sc->mem.ptb_pages, sc->mem.ptb_map); emu_free(&sc->mem, sc->mem.silent_page, sc->mem.silent_map); return (0); } static int emu_read_ivar(device_t bus, device_t dev, int ivar_index, uintptr_t * result) { struct sndcard_func *func = device_get_ivars(dev); struct emu_sc_info *sc = device_get_softc(bus); if (func==NULL) return (ENOMEM); if (sc == NULL) return (ENOMEM); switch (ivar_index) { case EMU_VAR_FUNC: *result = func->func; break; case EMU_VAR_ROUTE: if (func->varinfo == NULL) return (ENOMEM); *result = ((struct emu_pcminfo *)func->varinfo)->route; break; case EMU_VAR_ISEMU10K1: *result = sc->is_emu10k1; break; case EMU_VAR_MCH_DISABLED: *result = sc->mch_disabled; break; case EMU_VAR_MCH_REC: *result = sc->mch_rec; break; default: return (ENOENT); } return (0); } static int emu_write_ivar(device_t bus __unused, device_t dev __unused, int ivar_index, uintptr_t value __unused) { switch (ivar_index) { case 0: return (EINVAL); default: return (ENOENT); } } static int emu_pci_probe(device_t dev) { struct sbuf *s; unsigned int thiscard = 0; uint16_t vendor; vendor = pci_read_config(dev, PCIR_DEVVENDOR, /* bytes */ 2); if (vendor != 0x1102) return (ENXIO); /* Not Creative */ thiscard = emu_getcard(dev); if (thiscard == 0) return (ENXIO); s = sbuf_new(NULL, NULL, 4096, 0); if (s == NULL) return (ENOMEM); sbuf_printf(s, "Creative %s [%s]", emu_cards[thiscard].desc, emu_cards[thiscard].SBcode); sbuf_finish(s); device_set_desc_copy(dev, sbuf_data(s)); sbuf_delete(s); return (BUS_PROBE_DEFAULT); } static int emu_pci_attach(device_t dev) { struct sndcard_func *func; struct emu_sc_info *sc; struct emu_pcminfo *pcminfo; #if 0 struct emu_midiinfo *midiinfo; #endif int i; int device_flags; char status[255]; int error = ENXIO; int unit; sc = device_get_softc(dev); unit = device_get_unit(dev); /* Get configuration */ sc->ctx = device_get_sysctl_ctx(dev); if (sc->ctx == NULL) goto bad; sc->root = device_get_sysctl_tree(dev); if (sc->root == NULL) goto bad; if (resource_int_value("emu10kx", unit, "multichannel_disabled", &(sc->mch_disabled))) RANGE(sc->mch_disabled, 0, 1); SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "multichannel_disabled", CTLFLAG_RD, &(sc->mch_disabled), 0, "Multichannel playback setting"); if (resource_int_value("emu10kx", unit, "multichannel_recording", &(sc->mch_rec))) RANGE(sc->mch_rec, 0, 1); SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "multichannel_recording", CTLFLAG_RD, &(sc->mch_rec), 0, "Multichannel recording setting"); if (resource_int_value("emu10kx", unit, "debug", &(sc->dbg_level))) RANGE(sc->mch_rec, 0, 2); SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", CTLFLAG_RW, &(sc->dbg_level), 0, "Debug level"); /* Fill in the softc. */ mtx_init(&sc->lock, device_get_nameunit(dev), "bridge conf", MTX_DEF); mtx_init(&sc->rw, device_get_nameunit(dev), "exclusive io", MTX_DEF); sc->dev = dev; sc->type = pci_get_devid(dev); sc->rev = pci_get_revid(dev); sc->enable_ir = 0; sc->has_ac97 = 0; sc->has_51 = 0; sc->has_71 = 0; sc->broken_digital = 0; sc->is_emu10k1 = 0; sc->is_emu10k2 = 0; sc->is_ca0102 = 0; sc->is_ca0108 = 0; sc->is_cardbus = 0; device_flags = emu_cards[emu_getcard(dev)].flags; if (device_flags & HAS_51) sc->has_51 = 1; if (device_flags & HAS_71) { sc->has_51 = 1; sc->has_71 = 1; } if (device_flags & IS_EMU10K1) sc->is_emu10k1 = 1; if (device_flags & IS_EMU10K2) sc->is_emu10k2 = 1; if (device_flags & IS_CA0102) sc->is_ca0102 = 1; if (device_flags & IS_CA0108) sc->is_ca0108 = 1; if ((sc->is_emu10k2) && (sc->rev == 4)) { sc->is_emu10k2 = 0; sc->is_ca0102 = 1; /* for unknown Audigy 2 cards */ } if ((sc->is_ca0102 == 1) || (sc->is_ca0108 == 1)) if (device_flags & IS_CARDBUS) sc->is_cardbus = 1; if ((sc->is_emu10k1 + sc->is_emu10k2 + sc->is_ca0102 + sc->is_ca0108) != 1) { device_printf(sc->dev, "Unable to detect HW chipset\n"); goto bad; } if (device_flags & BROKEN_DIGITAL) sc->broken_digital = 1; if (device_flags & HAS_AC97) sc->has_ac97 = 1; sc->opcode_shift = 0; if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { sc->opcode_shift = 24; sc->high_operand_shift = 12; /* DSP map */ /* sc->fx_base = 0x0 */ sc->input_base = 0x40; /* sc->p16vinput_base = 0x50; */ sc->output_base = 0x60; sc->efxc_base = 0x80; /* sc->output32h_base = 0xa0; */ /* sc->output32l_base = 0xb0; */ sc->dsp_zero = 0xc0; /* 0xe0...0x100 are unknown */ /* sc->tram_base = 0x200 */ /* sc->tram_addr_base = 0x300 */ sc->gpr_base = EMU_A_FXGPREGBASE; sc->num_gprs = 0x200; sc->code_base = EMU_A_MICROCODEBASE; sc->code_size = 0x800 / 2; /* 0x600-0xdff, 2048 words, * 1024 instructions */ sc->mchannel_fx = 8; sc->num_fxbuses = 16; sc->num_inputs = 8; sc->num_outputs = 16; sc->address_mask = EMU_A_PTR_ADDR_MASK; } if (sc->is_emu10k1) { sc->has_51 = 0; /* We don't support 5.1 sound on SB Live! 5.1 */ sc->opcode_shift = 20; sc->high_operand_shift = 10; sc->code_base = EMU_MICROCODEBASE; sc->code_size = 0x400 / 2; /* 0x400-0x7ff, 1024 words, * 512 instructions */ sc->gpr_base = EMU_FXGPREGBASE; sc->num_gprs = 0x100; sc->input_base = 0x10; sc->output_base = 0x20; /* * XXX 5.1 Analog outputs are inside efxc address space! * They use output+0x11/+0x12 (=efxc+1/+2). * Don't use this efx registers for recording on SB Live! 5.1! */ sc->efxc_base = 0x30; sc->dsp_zero = 0x40; sc->mchannel_fx = 0; sc->num_fxbuses = 8; sc->num_inputs = 8; sc->num_outputs = 16; sc->address_mask = EMU_PTR_ADDR_MASK; } if (sc->opcode_shift == 0) goto bad; pci_enable_busmaster(dev); i = PCIR_BAR(0); sc->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &i, RF_ACTIVE); if (sc->reg == NULL) { device_printf(dev, "unable to map register space\n"); goto bad; } sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) sc->timer[i] = 0; /* disable it */ i = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE); if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, NULL, emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } if (emu_rm_init(sc) != 0) { device_printf(dev, "unable to create resource manager\n"); goto bad; } if (sc->is_cardbus) if (emu_cardbus_init(sc) != 0) { device_printf(dev, "unable to initialize CardBus interface\n"); goto bad; } if (emu_init(sc) != 0) { device_printf(dev, "unable to initialize the card\n"); goto bad; } if (emu10kx_dev_init(sc) != 0) { device_printf(dev, "unable to create control device\n"); goto bad; } snprintf(status, 255, "rev %d at io 0x%lx irq %ld", sc->rev, rman_get_start(sc->reg), rman_get_start(sc->irq)); /* Voices */ for (i = 0; i < NUM_G; i++) { sc->voice[i].vnum = i; sc->voice[i].slave = NULL; sc->voice[i].busy = 0; sc->voice[i].ismaster = 0; sc->voice[i].running = 0; sc->voice[i].b16 = 0; sc->voice[i].stereo = 0; sc->voice[i].speed = 0; sc->voice[i].start = 0; sc->voice[i].end = 0; } /* PCM Audio */ for (i = 0; i < RT_COUNT; i++) sc->pcm[i] = NULL; /* FRONT */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; goto bad; } pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (pcminfo == NULL) { error = ENOMEM; goto bad; } pcminfo->card = sc; pcminfo->route = RT_FRONT; func->func = SCF_PCM; func->varinfo = pcminfo; sc->pcm[RT_FRONT] = device_add_child(dev, "pcm", -1); device_set_ivars(sc->pcm[RT_FRONT], func); if (!(sc->mch_disabled)) { /* REAR */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; goto bad; } pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (pcminfo == NULL) { error = ENOMEM; goto bad; } pcminfo->card = sc; pcminfo->route = RT_REAR; func->func = SCF_PCM; func->varinfo = pcminfo; sc->pcm[RT_REAR] = device_add_child(dev, "pcm", -1); device_set_ivars(sc->pcm[RT_REAR], func); if (sc->has_51) { /* CENTER */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; goto bad; } pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (pcminfo == NULL) { error = ENOMEM; goto bad; } pcminfo->card = sc; pcminfo->route = RT_CENTER; func->func = SCF_PCM; func->varinfo = pcminfo; sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", -1); device_set_ivars(sc->pcm[RT_CENTER], func); /* SUB */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; goto bad; } pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (pcminfo == NULL) { error = ENOMEM; goto bad; } pcminfo->card = sc; pcminfo->route = RT_SUB; func->func = SCF_PCM; func->varinfo = pcminfo; sc->pcm[RT_SUB] = device_add_child(dev, "pcm", -1); device_set_ivars(sc->pcm[RT_SUB], func); } if (sc->has_71) { /* SIDE */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; goto bad; } pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (pcminfo == NULL) { error = ENOMEM; goto bad; } pcminfo->card = sc; pcminfo->route = RT_SIDE; func->func = SCF_PCM; func->varinfo = pcminfo; sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", -1); device_set_ivars(sc->pcm[RT_SIDE], func); } } /* mch_disabled */ if (sc->mch_rec) { func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; goto bad; } pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (pcminfo == NULL) { error = ENOMEM; goto bad; } pcminfo->card = sc; pcminfo->route = RT_MCHRECORD; func->func = SCF_PCM; func->varinfo = pcminfo; sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", -1); device_set_ivars(sc->pcm[RT_MCHRECORD], func); } /*mch_rec */ for (i = 0; i < 2; i++) sc->midi[i] = NULL; /* MIDI has some memory mangament and (possible) locking problems */ #if 0 /* Midi Interface 1: Live!, Audigy, Audigy 2 */ if ((sc->is_emu10k1) || (sc->is_emu10k2) || (sc->is_ca0102)) { func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; goto bad; } midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (midiinfo == NULL) { error = ENOMEM; goto bad; } midiinfo->card = sc; if (sc->is_emu10k2 || (sc->is_ca0102)) { midiinfo->port = EMU_A_MUDATA1; midiinfo->portnr = 1; } if (sc->is_emu10k1) { midiinfo->port = MUDATA; midiinfo->portnr = 1; } func->func = SCF_MIDI; func->varinfo = midiinfo; sc->midi[0] = device_add_child(dev, "midi", -1); device_set_ivars(sc->midi[0], func); } /* Midi Interface 2: Audigy, Audigy 2 (on AudigyDrive) */ if (sc->is_emu10k2 || (sc->is_ca0102)) { func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; goto bad; } midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (midiinfo == NULL) { error = ENOMEM; goto bad; } midiinfo->card = sc; midiinfo->port = EMU_A_MUDATA2; midiinfo->portnr = 2; func->func = SCF_MIDI; func->varinfo = midiinfo; sc->midi[1] = device_add_child(dev, "midi", -1); device_set_ivars(sc->midi[1], func); } #endif return (bus_generic_attach(dev)); bad: /* XXX can we just call emu_pci_detach here? */ if (sc->cdev) emu10kx_dev_uninit(sc); if (sc->rm != NULL) emu_rm_uninit(sc); if (sc->reg) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); mtx_destroy(&sc->rw); mtx_destroy(&sc->lock); return (error); } static int emu_pci_detach(device_t dev) { struct emu_sc_info *sc; struct sndcard_func *func; int devcount, i; device_t *childlist; int r = 0; sc = device_get_softc(dev); for (i = 0; i < RT_COUNT; i++) { if (sc->pcm[i] != NULL) { func = device_get_ivars(sc->pcm[i]); if (func != NULL && func->func == SCF_PCM) { device_set_ivars(sc->pcm[i], NULL); free(func->varinfo, M_DEVBUF); free(func, M_DEVBUF); } r = device_delete_child(dev, sc->pcm[i]); if (r) return (r); } } if (sc->midi[0] != NULL) { func = device_get_ivars(sc->midi[0]); if (func != NULL && func->func == SCF_MIDI) { device_set_ivars(sc->midi[0], NULL); free(func->varinfo, M_DEVBUF); free(func, M_DEVBUF); } r = device_delete_child(dev, sc->midi[0]); if (r) return (r); } if (sc->midi[1] != NULL) { func = device_get_ivars(sc->midi[1]); if (func != NULL && func->func == SCF_MIDI) { device_set_ivars(sc->midi[1], NULL); free(func->varinfo, M_DEVBUF); free(func, M_DEVBUF); } r = device_delete_child(dev, sc->midi[1]); if (r) return (r); } if (device_get_children(dev, &childlist, &devcount) == 0) for (i = 0; i < devcount - 1; i++) { device_printf(dev, "removing stale child %d (unit %d)\n", i, device_get_unit(childlist[i])); func = device_get_ivars(childlist[i]); if (func != NULL && (func->func == SCF_MIDI || func->func == SCF_PCM)) { device_set_ivars(childlist[i], NULL); free(func->varinfo, M_DEVBUF); free(func, M_DEVBUF); } device_delete_child(dev, childlist[i]); } if (childlist != NULL) free(childlist, M_TEMP); r = emu10kx_dev_uninit(sc); if (r) return (r); /* shutdown chip */ emu_uninit(sc); emu_rm_uninit(sc); if (sc->mem.dmat) bus_dma_tag_destroy(sc->mem.dmat); if (sc->reg) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); mtx_destroy(&sc->rw); mtx_destroy(&sc->lock); return (bus_generic_detach(dev)); } /* add suspend, resume */ static device_method_t emu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, emu_pci_probe), DEVMETHOD(device_attach, emu_pci_attach), DEVMETHOD(device_detach, emu_pci_detach), /* Bus methods */ DEVMETHOD(bus_read_ivar, emu_read_ivar), DEVMETHOD(bus_write_ivar, emu_write_ivar), DEVMETHOD_END }; static driver_t emu_driver = { "emu10kx", emu_methods, sizeof(struct emu_sc_info), NULL, 0, NULL }; static int emu_modevent(module_t mod __unused, int cmd, void *data __unused) { int err = 0; switch (cmd) { case MOD_LOAD: break; /* Success */ case MOD_UNLOAD: case MOD_SHUTDOWN: /* XXX Should we check state of pcm & midi subdevices here? */ break; /* Success */ default: err = EINVAL; break; } return (err); } static devclass_t emu_devclass; DRIVER_MODULE(snd_emu10kx, pci, emu_driver, emu_devclass, emu_modevent, NULL); MODULE_VERSION(snd_emu10kx, SND_EMU10KX_PREFVER);