root/sys/dev/sound/pci/emu10kx.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
 * Copyright (c) 2003-2007 Yuriy Tsibizov <yuriy.tsibizov@gfk.ru>
 * 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.
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/bus.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/systm.h>
#include <sys/sbuf.h>
#include <sys/queue.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/kdb.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>

#include <machine/clock.h>      /* for DELAY */

#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
#endif

#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>

#include <dev/sound/pci/emuxkireg.h>
#include <dev/sound/pci/emu10kx.h>

/* 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;
        unsigned int thiscard;
        int i;

        device = pci_read_config(dev, PCIR_DEVICE, /* bytes */ 2);
        subdevice = pci_read_config(dev, PCIR_SUBDEV_0, /* bytes */ 2);

        thiscard = 0;
        for (i = 1; i < nitems(emu_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.
                                 */
                        }
                }
        }

        for (i = 0; i < nitems(emu_bad_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)
{

        /*
         * 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
         */

        emu_rd_nolock(sc, 0x38, 4);
        emu_wr_nolock(sc, 0x38, data, 4);
        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)
{

        mtx_lock(&sc->lock);
        for (int 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)
                                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 | CTLFLAG_MPSAFE, 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 | CTLFLAG_MPSAFE,
            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, 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 = NULL;

        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 */NULL, /* lockarg */NULL,
             &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)
{
        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);

        device_set_descf(dev, "Creative %s [%s]",
            emu_cards[thiscard].desc, emu_cards[thiscard].SBcode);

        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%jx irq %jd", 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_WAITOK | M_ZERO);
        pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
        pcminfo->card = sc;
        pcminfo->route = RT_FRONT;

        func->func = SCF_PCM;
        func->varinfo = pcminfo;
        sc->pcm[RT_FRONT] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
        device_set_ivars(sc->pcm[RT_FRONT], func);

        if (!(sc->mch_disabled)) {
                /* REAR */
                func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
                pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
                pcminfo->card = sc;
                pcminfo->route = RT_REAR;

                func->func = SCF_PCM;
                func->varinfo = pcminfo;
                sc->pcm[RT_REAR] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
                device_set_ivars(sc->pcm[RT_REAR], func);
                if (sc->has_51) {
                        /* CENTER */
                        func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
                        pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
                        pcminfo->card = sc;
                        pcminfo->route = RT_CENTER;

                        func->func = SCF_PCM;
                        func->varinfo = pcminfo;
                        sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
                        device_set_ivars(sc->pcm[RT_CENTER], func);
                        /* SUB */
                        func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
                        pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
                        pcminfo->card = sc;
                        pcminfo->route = RT_SUB;

                        func->func = SCF_PCM;
                        func->varinfo = pcminfo;
                        sc->pcm[RT_SUB] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
                        device_set_ivars(sc->pcm[RT_SUB], func);
                }
                if (sc->has_71) {
                        /* SIDE */
                        func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
                        pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
                        pcminfo->card = sc;
                        pcminfo->route = RT_SIDE;

                        func->func = SCF_PCM;
                        func->varinfo = pcminfo;
                        sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
                        device_set_ivars(sc->pcm[RT_SIDE], func);
                }
        } /* mch_disabled */

        if (sc->mch_rec) {
                func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
                pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
                pcminfo->card = sc;
                pcminfo->route = RT_MCHRECORD;

                func->func = SCF_PCM;
                func->varinfo = pcminfo;
                sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
                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_WAITOK | M_ZERO);
                midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_WAITOK | M_ZERO);
                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", DEVICE_UNIT_ANY);
                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_WAITOK | M_ZERO);
                midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_WAITOK | M_ZERO);
                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", DEVICE_UNIT_ANY);
                device_set_ivars(sc->midi[1], func);
        }
#endif
        bus_attach_children(dev);
        return (0);

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 void
emu_pci_child_deleted(device_t dev, device_t child)
{
        struct sndcard_func *func;

        func = device_get_ivars(child);
        if (func != NULL) {
                free(func->varinfo, M_DEVBUF);
                free(func, M_DEVBUF);
        }
}

static int
emu_pci_detach(device_t dev)
{
        struct emu_sc_info *sc;
        int r = 0;

        sc = device_get_softc(dev);

        r = bus_generic_detach(dev);
        if (r != 0)
                return (r);

        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 (0);
}
/* 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_child_deleted, emu_pci_child_deleted),
        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);

}

DRIVER_MODULE(snd_emu10kx, pci, emu_driver, emu_modevent, NULL);
MODULE_VERSION(snd_emu10kx, SND_EMU10KX_PREFVER);