Bring in the latest sound changes from RELENG_6.
authorHasso Tepper <hasso@dragonflybsd.org>
Sat, 16 Jun 2007 19:48:05 +0000 (19:48 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Sat, 16 Jun 2007 19:48:05 +0000 (19:48 +0000)
Obtained-from: FreeBSD

33 files changed:
sys/conf/files
sys/dev/sound/driver.c
sys/dev/sound/driver/Makefile
sys/dev/sound/driver/envy24/Makefile [new file with mode: 0644]
sys/dev/sound/driver/envy24ht/Makefile [new file with mode: 0644]
sys/dev/sound/driver/spicds/Makefile [new file with mode: 0644]
sys/dev/sound/pci/atiixp.c
sys/dev/sound/pci/envy24.c [new file with mode: 0644]
sys/dev/sound/pci/envy24.h [new file with mode: 0644]
sys/dev/sound/pci/envy24ht.c [new file with mode: 0644]
sys/dev/sound/pci/envy24ht.h [new file with mode: 0644]
sys/dev/sound/pci/hda/hda_reg.h
sys/dev/sound/pci/hda/hdac.c
sys/dev/sound/pci/hda/hdac_private.h
sys/dev/sound/pci/ich.c
sys/dev/sound/pci/maestro3.c
sys/dev/sound/pci/spicds.c [new file with mode: 0644]
sys/dev/sound/pci/spicds.h [new file with mode: 0644]
sys/dev/sound/pci/via8233.c
sys/dev/sound/pci/via82c686.c
sys/dev/sound/pcm/ac97.c
sys/dev/sound/pcm/ac97.h
sys/dev/sound/pcm/ac97_patch.c
sys/dev/sound/pcm/ac97_patch.h
sys/dev/sound/pcm/buffer.c
sys/dev/sound/pcm/buffer.h
sys/dev/sound/pcm/channel.c
sys/dev/sound/pcm/mixer.c
sys/dev/sound/pcm/mixer.h
sys/dev/sound/pcm/sound.c
sys/dev/sound/pcm/sound.h
sys/dev/sound/pcm/vchan.c
sys/dev/sound/usb/uaudio_pcm.c

index b4e95ba..455d244 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/sys/conf/files,v 1.161 2007/06/16 18:55:28 dillon Exp $
+# $DragonFly: src/sys/conf/files,v 1.162 2007/06/16 19:48:04 hasso Exp $
 #
 # The long compile-with and dependency lines are required because of
 # limitations in config: backslash-newline doesn't work in strings, and
@@ -1224,6 +1224,8 @@ dev/sound/pci/csapcm.c            optional snd_csa pci
 dev/sound/pci/ds1.c            optional snd_ds1 pci
 dev/sound/pci/emu10k1.c                optional snd_emu10k1 pci \
        dependency "emu10k1-alsa%diked.h"
+dev/sound/pci/envy24.c         optional snd_envy24 pci
+dev/sound/pci/envy24ht.c       optional snd_envy24ht pci
 dev/sound/pci/es137x.c         optional snd_es137x pci
 dev/sound/pci/fm801.c          optional snd_fm801 pci
 dev/sound/pci/hda/hdac.c       optional snd_hda pci
@@ -1232,6 +1234,7 @@ dev/sound/pci/maestro.c   optional snd_maestro pci
 dev/sound/pci/maestro3.c       optional snd_maestro3 pci
 dev/sound/pci/neomagic.c       optional snd_neomagic pci
 dev/sound/pci/solo.c           optional snd_solo pci
+dev/sound/pci/spicds.c         optional snd_spicds pci
 dev/sound/pci/t4dwave.c                optional snd_t4dwave pci
 dev/sound/pci/via8233.c                optional snd_via8233 pci
 dev/sound/pci/via82c686.c      optional snd_via82c686 pci
index c498585..6284b10 100644 (file)
@@ -23,8 +23,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/sound/driver.c,v 1.13.2.2 2006/01/24 18:56:11 joel Exp $
- * $DragonFly: src/sys/dev/sound/driver.c,v 1.3 2007/01/04 21:47:00 corecode Exp $
+ * $FreeBSD: src/sys/dev/sound/driver.c,v 1.13.2.3 2007/05/13 21:11:40 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/driver.c,v 1.4 2007/06/16 19:48:04 hasso Exp $
  */
 
 #include <dev/sound/pcm/sound.h>
@@ -63,6 +63,8 @@ MODULE_DEPEND(snd_driver, snd_csa, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_csapcm, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_ds1, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_emu10k1, 1, 1, 1);
+MODULE_DEPEND(snd_driver, snd_envy24, 1, 1, 1);
+MODULE_DEPEND(snd_driver, snd_envy24ht, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_es137x, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_es1888, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_ess, 1, 1, 1);
@@ -78,6 +80,7 @@ MODULE_DEPEND(snd_driver, snd_sb16, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_sb8, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_sbc, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_solo, 1, 1, 1);
+MODULE_DEPEND(snd_driver, snd_spicds, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_t4dwave, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_via8233, 1, 1, 1);
 MODULE_DEPEND(snd_driver, snd_via82c686, 1, 1, 1);
index af2333c..ce4c2dd 100644 (file)
@@ -1,15 +1,13 @@
-# $FreeBSD: src/sys/modules/sound/driver/Makefile,v 1.16.2.1 2005/12/30 19:55:54 netchild Exp $
-# $DragonFly: src/sys/dev/sound/driver/Makefile,v 1.3 2007/01/04 21:47:00 corecode Exp $
+# $FreeBSD: src/sys/modules/sound/driver/Makefile,v 1.16.2.2 2007/05/13 21:11:40 ariff Exp $
+# $DragonFly: src/sys/dev/sound/driver/Makefile,v 1.4 2007/06/16 19:48:04 hasso Exp $
 
 .if ${MACHINE_ARCH} == "sparc64"
 SUBDIR  = audiocs es137x
 .else
-SUBDIR  = als4000 ad1816 atiixp cmi cs4281 csa ds1 emu10k1 es137x ess
-SUBDIR += fm801 ich maestro maestro3 mss neomagic sb16 sb8 sbc solo
-SUBDIR += t4dwave via8233 via82c686 vibes
-SUBDIR += hda
-SUBDIR += driver
-SUBDIR += uaudio
+SUBDIR  = als4000 ad1816 atiixp cmi cs4281 csa ds1 emu10k1 envy24 
+SUBDIR += envy24ht es137x ess fm801 hda ich maestro maestro3 mss neomagic
+SUBDIR += sb16 sb8 sbc solo spicds t4dwave via8233 via82c686 vibes
+SUBDIR += driver uaudio
 .endif
 
 .include <bsd.subdir.mk>
diff --git a/sys/dev/sound/driver/envy24/Makefile b/sys/dev/sound/driver/envy24/Makefile
new file mode 100644 (file)
index 0000000..c777971
--- /dev/null
@@ -0,0 +1,11 @@
+# $FreeBSD: src/sys/modules/sound/driver/envy24/Makefile,v 1.3.2.1 2007/05/13 21:03:46 ariff Exp $
+# $DragonFly: src/sys/dev/sound/driver/envy24/Makefile,v 1.1 2007/06/16 19:48:05 hasso Exp $
+
+.PATH: ${.CURDIR}/../../../../dev/sound/pci
+
+KMOD=  snd_envy24
+SRCS=  device_if.h bus_if.h pci_if.h
+SRCS+= envy24.c
+
+KMODDEPS=      sound
+.include <bsd.kmod.mk>
diff --git a/sys/dev/sound/driver/envy24ht/Makefile b/sys/dev/sound/driver/envy24ht/Makefile
new file mode 100644 (file)
index 0000000..8dab4cf
--- /dev/null
@@ -0,0 +1,11 @@
+# $FreeBSD: src/sys/modules/sound/driver/envy24ht/Makefile,v 1.2.2.1 2007/05/13 21:03:46 ariff Exp $
+# $DragonFly: src/sys/dev/sound/driver/envy24ht/Makefile,v 1.1 2007/06/16 19:48:05 hasso Exp $
+
+.PATH: ${.CURDIR}/../../../../dev/sound/pci
+
+KMOD=  snd_envy24ht
+SRCS=  device_if.h bus_if.h pci_if.h
+SRCS+= envy24ht.c
+
+KMODDEPS=      sound
+.include <bsd.kmod.mk>
diff --git a/sys/dev/sound/driver/spicds/Makefile b/sys/dev/sound/driver/spicds/Makefile
new file mode 100644 (file)
index 0000000..37f0c14
--- /dev/null
@@ -0,0 +1,11 @@
+# $FreeBSD: src/sys/modules/sound/driver/spicds/Makefile,v 1.2.2.1 2007/05/13 21:03:46 ariff Exp $
+# $DragonFly: src/sys/dev/sound/driver/spicds/Makefile,v 1.1 2007/06/16 19:48:05 hasso Exp $
+
+.PATH: ${.CURDIR}/../../../../dev/sound/pci
+
+KMOD=  snd_spicds
+SRCS=  device_if.h bus_if.h isa_if.h pci_if.h
+SRCS+= spicds.c
+
+KMODDEPS=      sound
+.include <bsd.kmod.mk>
index a0e1e66..be387ef 100644 (file)
@@ -23,8 +23,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/sound/pci/atiixp.c,v 1.2.2.5 2006/11/14 13:08:11 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.3 2007/05/13 18:33:58 swildner Exp $
+ * $FreeBSD: src/sys/dev/sound/pci/atiixp.c,v 1.2.2.7 2007/04/26 08:21:44 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.4 2007/06/16 19:48:05 hasso Exp $
  */
 
 /*
@@ -66,7 +66,7 @@
 
 #include <dev/sound/pci/atiixp.h>
 
-SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.3 2007/05/13 18:33:58 swildner Exp $");
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.4 2007/06/16 19:48:05 hasso Exp $");
 
 struct atiixp_dma_op {
        volatile uint32_t addr;
@@ -800,6 +800,7 @@ atiixp_chip_post_init(void *arg)
 
        subdev = (pci_get_subdevice(sc->dev) << 16) | pci_get_subvendor(sc->dev);
        switch (subdev) {
+       case 0x11831043:        /* ASUS A6R */
        case 0x2043161f:        /* Maxselect x710s - http://maxselect.ru/ */
                ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV);
                break;
@@ -844,27 +845,30 @@ atiixp_release_resource(struct atiixp_info *sc)
        }
        if (sc->ih) {
                bus_teardown_intr(sc->dev, sc->irq, sc->ih);
-               sc->ih = 0;
+               sc->ih = NULL;
        }
        if (sc->reg) {
                bus_release_resource(sc->dev, sc->regtype, sc->regid, sc->reg);
-               sc->reg = 0;
+               sc->reg = NULL;
        }
        if (sc->irq) {
                bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqid, sc->irq);
-               sc->irq = 0;
+               sc->irq = NULL;
        }
        if (sc->parent_dmat) {
                bus_dma_tag_destroy(sc->parent_dmat);
-               sc->parent_dmat = 0;
+               sc->parent_dmat = NULL;
        }
-       if (sc->sgd_dmamap) {
+       if (sc->sgd_dmamap)
                bus_dmamap_unload(sc->sgd_dmat, sc->sgd_dmamap);
-               sc->sgd_dmamap = 0;
+       if (sc->sgd_table) {
+               bus_dmamem_free(sc->sgd_dmat, sc->sgd_table, sc->sgd_dmamap);
+               sc->sgd_table = NULL;
        }
+       sc->sgd_dmamap = NULL;
        if (sc->sgd_dmat) {
                bus_dma_tag_destroy(sc->sgd_dmat);
-               sc->sgd_dmat = 0;
+               sc->sgd_dmat = NULL;
        }
        if (sc->lock) {
                snd_mtxfree(sc->lock);
diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c
new file mode 100644 (file)
index 0000000..3ede549
--- /dev/null
@@ -0,0 +1,2657 @@
+/*
+ * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
+ * 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 THEPOSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/sound/pci/envy24.c,v 1.11.2.2 2007/06/11 19:33:27 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/envy24.c,v 1.1 2007/06/16 19:48:05 hasso Exp $
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+#include <dev/sound/pci/spicds.h>
+#include <dev/sound/pci/envy24.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+#include "mixer_if.h"
+
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/envy24.c,v 1.1 2007/06/16 19:48:05 hasso Exp $");
+
+MALLOC_DEFINE(M_ENVY24, "envy24", "envy24 audio");
+
+/* -------------------------------------------------------------------- */
+
+struct sc_info;
+
+#define ENVY24_PLAY_CHNUM 10
+#define ENVY24_REC_CHNUM 12
+#define ENVY24_PLAY_BUFUNIT (4 /* byte/sample */ * 10 /* channel */)
+#define ENVY24_REC_BUFUNIT  (4 /* byte/sample */ * 12 /* channel */)
+#define ENVY24_SAMPLE_NUM   4096
+
+#define ENVY24_TIMEOUT 1000
+
+#define ENVY24_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE)
+
+#define ENVY24_NAMELEN 32
+
+#define SDA_GPIO 0x10
+#define SCL_GPIO 0x20
+
+#define abs(i) (i < 0 ? -i : i)
+
+struct envy24_sample {
+        volatile u_int32_t buffer;
+};
+
+typedef struct envy24_sample sample32_t;
+
+/* channel registers */
+struct sc_chinfo {
+       struct snd_dbuf         *buffer;
+       struct pcm_channel      *channel;
+       struct sc_info          *parent;
+       int                     dir;
+       unsigned                num; /* hw channel number */
+
+       /* channel information */
+       u_int32_t               format;
+       u_int32_t               speed;
+       u_int32_t               blk; /* hw block size(dword) */
+
+       /* format conversion structure */
+       u_int8_t                *data;
+       unsigned int            size; /* data buffer size(byte) */
+       int                     unit; /* sample size(byte) */
+       unsigned int            offset; /* samples number offset */
+       void                    (*emldma)(struct sc_chinfo *);
+
+       /* flags */
+       int                     run;
+};
+
+/* codec interface entrys */
+struct codec_entry {
+       void *(*create)(device_t dev, void *devinfo, int dir, int num);
+       void (*destroy)(void *codec);
+       void (*init)(void *codec);
+       void (*reinit)(void *codec);
+       void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right);
+       void (*setrate)(void *codec, int which, int rate);
+};
+
+/* system configuration information */
+struct cfg_info {
+       char *name;
+       u_int16_t subvendor, subdevice;
+       u_int8_t scfg, acl, i2s, spdif;
+       u_int8_t gpiomask, gpiostate, gpiodir;
+       u_int8_t cdti, cclk, cs, cif, type;
+       u_int8_t free;
+       struct codec_entry *codec;
+};
+
+/* device private data */
+struct sc_info {
+       device_t        dev;
+       struct spinlock *lock;
+
+       /* Control/Status registor */
+       struct resource *cs;
+       int             csid;
+       bus_space_tag_t cst;
+       bus_space_handle_t csh;
+       /* DDMA registor */
+       struct resource *ddma;
+       int             ddmaid;
+       bus_space_tag_t ddmat;
+       bus_space_handle_t ddmah;
+       /* Consumer Section DMA Channel Registers */
+       struct resource *ds;
+       int             dsid;
+       bus_space_tag_t dst;
+       bus_space_handle_t dsh;
+       /* MultiTrack registor */
+       struct resource *mt;
+       int             mtid;
+       bus_space_tag_t mtt;
+       bus_space_handle_t mth;
+       /* DMA tag */
+       bus_dma_tag_t dmat;
+       /* IRQ resource */
+       struct resource *irq;
+       int             irqid;
+       void            *ih;
+
+       /* system configuration data */
+       struct cfg_info *cfg;
+
+       /* ADC/DAC number and info */
+       int             adcn, dacn;
+       void            *adc[4], *dac[4];
+
+       /* mixer control data */
+       u_int32_t       src;
+       u_int8_t        left[ENVY24_CHAN_NUM];
+       u_int8_t        right[ENVY24_CHAN_NUM];
+
+       /* Play/Record DMA fifo */
+       sample32_t      *pbuf;
+       sample32_t      *rbuf;
+       u_int32_t       psize, rsize; /* DMA buffer size(byte) */
+       u_int16_t       blk[2]; /* transfer check blocksize(dword) */
+       bus_dmamap_t    pmap, rmap;
+
+       /* current status */
+       u_int32_t       speed;
+       int             run[2];
+       u_int16_t       intr[2];
+       struct pcmchan_caps     caps[2];
+
+       /* channel info table */
+       unsigned        chnum;
+       struct sc_chinfo chan[11];
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * prototypes
+ */
+
+/* DMA emulator */
+static void envy24_p8u(struct sc_chinfo *);
+static void envy24_p16sl(struct sc_chinfo *);
+static void envy24_p32sl(struct sc_chinfo *);
+static void envy24_r16sl(struct sc_chinfo *);
+static void envy24_r32sl(struct sc_chinfo *);
+
+/* channel interface */
+static void *envy24chan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
+static int envy24chan_setformat(kobj_t, void *, u_int32_t);
+static int envy24chan_setspeed(kobj_t, void *, u_int32_t);
+static int envy24chan_setblocksize(kobj_t, void *, u_int32_t);
+static int envy24chan_trigger(kobj_t, void *, int);
+static int envy24chan_getptr(kobj_t, void *);
+static struct pcmchan_caps *envy24chan_getcaps(kobj_t, void *);
+
+/* mixer interface */
+static int envy24mixer_init(struct snd_mixer *);
+static int envy24mixer_reinit(struct snd_mixer *);
+static int envy24mixer_uninit(struct snd_mixer *);
+static int envy24mixer_set(struct snd_mixer *, unsigned, unsigned, unsigned);
+static u_int32_t envy24mixer_setrecsrc(struct snd_mixer *, u_int32_t);
+
+/* M-Audio Delta series AK4524 access interface */
+static void *envy24_delta_ak4524_create(device_t, void *, int, int);
+static void envy24_delta_ak4524_destroy(void *);
+static void envy24_delta_ak4524_init(void *);
+static void envy24_delta_ak4524_reinit(void *);
+static void envy24_delta_ak4524_setvolume(void *, int, unsigned int, unsigned int);
+
+/* -------------------------------------------------------------------- */
+
+/*
+  system constant tables
+*/
+
+/* API -> hardware channel map */
+static unsigned envy24_chanmap[ENVY24_CHAN_NUM] = {
+       ENVY24_CHAN_PLAY_SPDIF, /* 0 */
+       ENVY24_CHAN_PLAY_DAC1,  /* 1 */
+       ENVY24_CHAN_PLAY_DAC2,  /* 2 */
+       ENVY24_CHAN_PLAY_DAC3,  /* 3 */
+       ENVY24_CHAN_PLAY_DAC4,  /* 4 */
+       ENVY24_CHAN_REC_MIX,    /* 5 */
+       ENVY24_CHAN_REC_SPDIF,  /* 6 */
+       ENVY24_CHAN_REC_ADC1,   /* 7 */
+       ENVY24_CHAN_REC_ADC2,   /* 8 */
+       ENVY24_CHAN_REC_ADC3,   /* 9 */
+       ENVY24_CHAN_REC_ADC4,   /* 10 */
+};
+
+/* mixer -> API channel map. see above */
+static int envy24_mixmap[] = {
+       -1, /* Master output level. It is depend on codec support */
+       -1, /* Treble level of all output channels */
+       -1, /* Bass level of all output channels */
+       -1, /* Volume of synthesier input */
+       0,  /* Output level for the audio device */
+       -1, /* Output level for the PC speaker */
+       7,  /* line in jack */
+       -1, /* microphone jack */
+       -1, /* CD audio input */
+       -1, /* Recording monitor */
+       1,  /* alternative codec */
+       -1, /* global recording level */
+       -1, /* Input gain */
+       -1, /* Output gain */
+       8,  /* Input source 1 */
+       9,  /* Input source 2 */
+       10, /* Input source 3 */
+       6,  /* Digital (input) 1 */
+       -1, /* Digital (input) 2 */
+       -1, /* Digital (input) 3 */
+       -1, /* Phone input */
+       -1, /* Phone output */
+       -1, /* Video/TV (audio) in */
+       -1, /* Radio in */
+       -1, /* Monitor volume */
+};
+
+/* variable rate audio */
+static u_int32_t envy24_speed[] = {
+    96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
+    12000, 11025, 9600, 8000, 0
+};
+
+/* known boards configuration */
+static struct codec_entry delta_codec = {
+       envy24_delta_ak4524_create,
+       envy24_delta_ak4524_destroy,
+       envy24_delta_ak4524_init,
+       envy24_delta_ak4524_reinit,
+       envy24_delta_ak4524_setvolume,
+       NULL, /* setrate */
+};
+
+static struct cfg_info cfg_table[] = {
+       {
+               "Envy24 audio (M Audio Delta Dio 2496)",
+               0x1412, 0xd631,
+               0x10, 0x80, 0xf0, 0x03,
+               0xff, 0x00, 0x00,
+               0x10, 0x20, 0x40, 0x00, 0x00,
+               0x00,
+               &delta_codec,
+       },
+       {
+               "Envy24 audio (Terratec DMX 6fire)",
+               0x153b, 0x1138,
+               0x2f, 0x80, 0xf0, 0x03,
+               0xc0, 0xff, 0x7f,
+               0x10, 0x20, 0x01, 0x01, 0x00,
+               0x00,
+               &delta_codec,
+       },
+       {
+               "Envy24 audio (M Audio Audiophile 2496)",
+               0x1412, 0xd634,
+               0x10, 0x80, 0x72, 0x03,
+               0x04, 0xfe, 0xfb,
+               0x08, 0x02, 0x20, 0x00, 0x01,
+               0x00,
+               &delta_codec,
+       },
+       {
+               "Envy24 audio (Generic)",
+               0, 0,
+               0x0f, 0x00, 0x01, 0x03,
+               0xff, 0x00, 0x00,
+               0x10, 0x20, 0x40, 0x00, 0x00,
+               0x00,
+               &delta_codec, /* default codec routines */
+       }
+};
+
+static u_int32_t envy24_recfmt[] = {
+       AFMT_STEREO | AFMT_S16_LE,
+       AFMT_STEREO | AFMT_S32_LE,
+       0
+};
+static struct pcmchan_caps envy24_reccaps = {8000, 96000, envy24_recfmt, 0};
+
+static u_int32_t envy24_playfmt[] = {
+       AFMT_STEREO | AFMT_U8,
+       AFMT_STEREO | AFMT_S16_LE,
+       AFMT_STEREO | AFMT_S32_LE,
+       0
+};
+
+static struct pcmchan_caps envy24_playcaps = {8000, 96000, envy24_playfmt, 0};
+
+struct envy24_emldma {
+       u_int32_t       format;
+       void            (*emldma)(struct sc_chinfo *);
+       int             unit;
+};
+
+static struct envy24_emldma envy24_pemltab[] = {
+       {AFMT_STEREO | AFMT_U8, envy24_p8u, 2},
+       {AFMT_STEREO | AFMT_S16_LE, envy24_p16sl, 4},
+       {AFMT_STEREO | AFMT_S32_LE, envy24_p32sl, 8},
+       {0, NULL, 0}
+};
+
+static struct envy24_emldma envy24_remltab[] = {
+       {AFMT_STEREO | AFMT_S16_LE, envy24_r16sl, 4},
+       {AFMT_STEREO | AFMT_S32_LE, envy24_r32sl, 8},
+       {0, NULL, 0}
+};
+
+/* -------------------------------------------------------------------- */
+
+/* common routines */
+static u_int32_t
+envy24_rdcs(struct sc_info *sc, int regno, int size)
+{
+       switch (size) {
+       case 1:
+               return bus_space_read_1(sc->cst, sc->csh, regno);
+       case 2:
+               return bus_space_read_2(sc->cst, sc->csh, regno);
+       case 4:
+               return bus_space_read_4(sc->cst, sc->csh, regno);
+       default:
+               return 0xffffffff;
+       }
+}
+
+static void
+envy24_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size)
+{
+       switch (size) {
+       case 1:
+               bus_space_write_1(sc->cst, sc->csh, regno, data);
+               break;
+       case 2:
+               bus_space_write_2(sc->cst, sc->csh, regno, data);
+               break;
+       case 4:
+               bus_space_write_4(sc->cst, sc->csh, regno, data);
+               break;
+       }
+}
+
+static u_int32_t
+envy24_rdmt(struct sc_info *sc, int regno, int size)
+{
+       switch (size) {
+       case 1:
+               return bus_space_read_1(sc->mtt, sc->mth, regno);
+       case 2:
+               return bus_space_read_2(sc->mtt, sc->mth, regno);
+       case 4:
+               return bus_space_read_4(sc->mtt, sc->mth, regno);
+       default:
+               return 0xffffffff;
+       }
+}
+
+static void
+envy24_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size)
+{
+       switch (size) {
+       case 1:
+               bus_space_write_1(sc->mtt, sc->mth, regno, data);
+               break;
+       case 2:
+               bus_space_write_2(sc->mtt, sc->mth, regno, data);
+               break;
+       case 4:
+               bus_space_write_4(sc->mtt, sc->mth, regno, data);
+               break;
+       }
+}
+
+static u_int32_t
+envy24_rdci(struct sc_info *sc, int regno)
+{
+       envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1);
+       return envy24_rdcs(sc, ENVY24_CCS_DATA, 1);
+}
+
+static void
+envy24_wrci(struct sc_info *sc, int regno, u_int32_t data)
+{
+       envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1);
+       envy24_wrcs(sc, ENVY24_CCS_DATA, data, 1);
+}
+
+/* -------------------------------------------------------------------- */
+
+/* I2C port/E2PROM access routines */
+
+static int
+envy24_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr)
+{
+       u_int32_t data;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
+#endif
+       for (i = 0; i < ENVY24_TIMEOUT; i++) {
+               data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+               if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
+                       break;
+               DELAY(32); /* 31.25kHz */
+       }
+       if (i == ENVY24_TIMEOUT) {
+               return -1;
+       }
+       envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1);
+       envy24_wrcs(sc, ENVY24_CCS_I2CDEV,
+           (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_RD, 1);
+       for (i = 0; i < ENVY24_TIMEOUT; i++) {
+               data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+               if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
+                       break;
+               DELAY(32); /* 31.25kHz */
+       }
+       if (i == ENVY24_TIMEOUT) {
+               return -1;
+       }
+       data = envy24_rdcs(sc, ENVY24_CCS_I2CDATA, 1);
+
+#if(0)
+       device_printf(sc->dev, "envy24_rdi2c(): return 0x%x\n", data);
+#endif
+       return (int)data;
+}
+
+#if 0
+static int
+envy24_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data)
+{
+       u_int32_t tmp;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
+#endif
+       for (i = 0; i < ENVY24_TIMEOUT; i++) {
+               tmp = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+               if ((tmp & ENVY24_CCS_I2CSTAT_BSY) == 0)
+                       break;
+               DELAY(32); /* 31.25kHz */
+       }
+       if (i == ENVY24_TIMEOUT) {
+               return -1;
+       }
+       envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1);
+       envy24_wrcs(sc, ENVY24_CCS_I2CDATA, data, 1);
+       envy24_wrcs(sc, ENVY24_CCS_I2CDEV,
+           (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_WR, 1);
+       for (i = 0; i < ENVY24_TIMEOUT; i++) {
+               data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+               if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
+                       break;
+               DELAY(32); /* 31.25kHz */
+       }
+       if (i == ENVY24_TIMEOUT) {
+               return -1;
+       }
+
+       return 0;
+}
+#endif
+
+static int
+envy24_rdrom(struct sc_info *sc, u_int32_t addr)
+{
+       u_int32_t data;
+
+#if(0)
+       device_printf(sc->dev, "envy24_rdrom(sc, 0x%02x)\n", addr);
+#endif
+       data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
+       if ((data & ENVY24_CCS_I2CSTAT_ROM) == 0) {
+#if(0)
+               device_printf(sc->dev, "envy24_rdrom(): E2PROM not presented\n");
+#endif
+               return -1;
+       }
+
+       return envy24_rdi2c(sc, ENVY24_CCS_I2CDEV_ROM, addr);
+}
+
+static struct cfg_info *
+envy24_rom2cfg(struct sc_info *sc)
+{
+       struct cfg_info *buff;
+       int size;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24_rom2cfg(sc)\n");
+#endif
+       size = envy24_rdrom(sc, ENVY24_E2PROM_SIZE);
+       if (size < ENVY24_E2PROM_GPIODIR + 1) {
+#if(0)
+               device_printf(sc->dev, "envy24_rom2cfg(): ENVY24_E2PROM_SIZE-->%d\n", size);
+#endif
+               return NULL;
+       }
+       buff = kmalloc(sizeof(*buff), M_ENVY24, M_NOWAIT);
+       if (buff == NULL) {
+#if(0)
+               device_printf(sc->dev, "envy24_rom2cfg(): malloc()\n");
+#endif
+               return NULL;
+       }
+       buff->free = 1;
+
+       buff->subvendor = envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR) << 8;
+       buff->subvendor += envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR + 1);
+       buff->subdevice = envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE) << 8;
+       buff->subdevice += envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE + 1);
+       buff->scfg = envy24_rdrom(sc, ENVY24_E2PROM_SCFG);
+       buff->acl = envy24_rdrom(sc, ENVY24_E2PROM_ACL);
+       buff->i2s = envy24_rdrom(sc, ENVY24_E2PROM_I2S);
+       buff->spdif = envy24_rdrom(sc, ENVY24_E2PROM_SPDIF);
+       buff->gpiomask = envy24_rdrom(sc, ENVY24_E2PROM_GPIOMASK);
+       buff->gpiostate = envy24_rdrom(sc, ENVY24_E2PROM_GPIOSTATE);
+       buff->gpiodir = envy24_rdrom(sc, ENVY24_E2PROM_GPIODIR);
+
+       for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++)
+               if (cfg_table[i].subvendor == buff->subvendor &&
+                   cfg_table[i].subdevice == buff->subdevice)
+                       break;
+       buff->name = cfg_table[i].name;
+       buff->codec = cfg_table[i].codec;
+
+       return buff;
+}
+
+static void
+envy24_cfgfree(struct cfg_info *cfg) {
+       if (cfg == NULL)
+               return;
+       if (cfg->free)
+               kfree(cfg, M_ENVY24);
+       return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* AC'97 codec access routines */
+
+#if 0
+static int
+envy24_coldcd(struct sc_info *sc)
+{
+       u_int32_t data;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24_coldcd()\n");
+#endif
+       envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_CLD, 1);
+       DELAY(10);
+       envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1);
+       DELAY(1000);
+       for (i = 0; i < ENVY24_TIMEOUT; i++) {
+               data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
+               if (data & ENVY24_MT_AC97CMD_RDY) {
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+#endif
+
+static int
+envy24_slavecd(struct sc_info *sc)
+{
+       u_int32_t data;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24_slavecd()\n");
+#endif
+       envy24_wrmt(sc, ENVY24_MT_AC97CMD,
+           ENVY24_MT_AC97CMD_CLD | ENVY24_MT_AC97CMD_WRM, 1);
+       DELAY(10);
+       envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1);
+       DELAY(1000);
+       for (i = 0; i < ENVY24_TIMEOUT; i++) {
+               data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
+               if (data & ENVY24_MT_AC97CMD_RDY) {
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+#if 0
+static int
+envy24_rdcd(kobj_t obj, void *devinfo, int regno)
+{
+       struct sc_info *sc = (struct sc_info *)devinfo;
+       u_int32_t data;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24_rdcd(obj, sc, 0x%02x)\n", regno);
+#endif
+       envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1);
+       envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_RD, 1);
+       for (i = 0; i < ENVY24_TIMEOUT; i++) {
+               data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
+               if ((data & ENVY24_MT_AC97CMD_RD) == 0)
+                       break;
+       }
+       data = envy24_rdmt(sc, ENVY24_MT_AC97DLO, 2);
+
+#if(0)
+       device_printf(sc->dev, "envy24_rdcd(): return 0x%x\n", data);
+#endif
+       return (int)data;
+}
+
+static int
+envy24_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
+{
+       struct sc_info *sc = (struct sc_info *)devinfo;
+       u_int32_t cmd;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data);
+#endif
+       envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1);
+       envy24_wrmt(sc, ENVY24_MT_AC97DLO, (u_int32_t)data, 2);
+       envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_WR, 1);
+       for (i = 0; i < ENVY24_TIMEOUT; i++) {
+               cmd = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
+               if ((cmd & ENVY24_MT_AC97CMD_WR) == 0)
+                       break;
+       }
+
+       return 0;
+}
+
+static kobj_method_t envy24_ac97_methods[] = {
+       KOBJMETHOD(ac97_read,   envy24_rdcd),
+       KOBJMETHOD(ac97_write,  envy24_wrcd),
+       {0, 0}
+};
+AC97_DECLARE(envy24_ac97);
+#endif
+
+/* -------------------------------------------------------------------- */
+
+/* GPIO access routines */
+
+static u_int32_t
+envy24_gpiord(struct sc_info *sc)
+{
+       return envy24_rdci(sc, ENVY24_CCI_GPIODAT);
+}
+
+static void
+envy24_gpiowr(struct sc_info *sc, u_int32_t data)
+{
+#if(0)
+       device_printf(sc->dev, "envy24_gpiowr(sc, 0x%02x)\n", data & 0xff);
+       return;
+#endif
+       envy24_wrci(sc, ENVY24_CCI_GPIODAT, data);
+       return;
+}
+
+#if 0
+static u_int32_t
+envy24_gpiogetmask(struct sc_info *sc)
+{
+       return envy24_rdci(sc, ENVY24_CCI_GPIOMASK);
+}
+#endif
+
+static void
+envy24_gpiosetmask(struct sc_info *sc, u_int32_t mask)
+{
+       envy24_wrci(sc, ENVY24_CCI_GPIOMASK, mask);
+       return;
+}
+
+#if 0
+static u_int32_t
+envy24_gpiogetdir(struct sc_info *sc)
+{
+       return envy24_rdci(sc, ENVY24_CCI_GPIOCTL);
+}
+#endif
+
+static void
+envy24_gpiosetdir(struct sc_info *sc, u_int32_t dir)
+{
+       envy24_wrci(sc, ENVY24_CCI_GPIOCTL, dir);
+       return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* Envy24 I2C through GPIO bit-banging */
+
+struct envy24_delta_ak4524_codec {
+       struct spicds_info *info;
+       struct sc_info *parent;
+       int dir;
+       int num;
+       int cs, cclk, cdti;
+};
+
+static void
+envy24_gpio_i2c_ctl(void *codec, unsigned int scl, unsigned int sda)
+{
+        u_int32_t data = 0;
+        struct envy24_delta_ak4524_codec *ptr = codec;
+#if(0)
+        device_printf(ptr->parent->dev, "--> %d, %d\n", scl, sda);
+#endif
+        data = envy24_gpiord(ptr->parent);
+        data &= ~(SDA_GPIO | SCL_GPIO);
+        if (scl) data += SCL_GPIO;
+        if (sda) data += SDA_GPIO;
+        envy24_gpiowr(ptr->parent, data);
+        return;
+}
+
+static void
+i2c_wrbit(void *codec, void (*ctrl)(void*, unsigned int, unsigned int), int bit)
+{
+        struct envy24_delta_ak4524_codec *ptr = codec;
+        unsigned int sda;
+
+        if (bit)
+                sda = 1;
+        else
+                sda = 0;
+
+        ctrl(ptr, 0, sda);
+        DELAY(I2C_DELAY);
+        ctrl(ptr, 1, sda);
+        DELAY(I2C_DELAY);
+        ctrl(ptr, 0, sda);
+        DELAY(I2C_DELAY);
+}
+
+static void
+i2c_start(void *codec, void (*ctrl)(void*, unsigned int, unsigned int))
+{
+        struct envy24_delta_ak4524_codec *ptr = codec;
+
+        ctrl(ptr, 1, 1);
+        DELAY(I2C_DELAY);
+        ctrl(ptr, 1, 0);
+        DELAY(I2C_DELAY);
+        ctrl(ptr, 0, 0);
+        DELAY(I2C_DELAY);
+}
+
+static void
+i2c_stop(void *codec, void (*ctrl)(void*, unsigned int, unsigned int))
+{
+        struct envy24_delta_ak4524_codec *ptr = codec;
+
+        ctrl(ptr, 0, 0);
+        DELAY(I2C_DELAY);
+        ctrl(ptr, 1, 0);
+        DELAY(I2C_DELAY);
+        ctrl(ptr, 1, 1);
+        DELAY(I2C_DELAY);
+}
+
+static void
+i2c_ack(void *codec, void (*ctrl)(void*, unsigned int, unsigned int))
+{
+        struct envy24_delta_ak4524_codec *ptr = codec;
+
+        ctrl(ptr, 0, 1);
+        DELAY(I2C_DELAY);
+        ctrl(ptr, 1, 1);
+        DELAY(I2C_DELAY);
+        /* dummy, need routine to change gpio direction */
+        ctrl(ptr, 0, 1);
+        DELAY(I2C_DELAY);
+}
+
+static void
+i2c_wr(void *codec,  void (*ctrl)(void*, unsigned int, unsigned int), u_int32_t dev, int reg, u_int8_t val)
+{
+        struct envy24_delta_ak4524_codec *ptr = codec;
+        int mask;
+
+        i2c_start(ptr, ctrl);
+
+        for (mask = 0x80; mask != 0; mask >>= 1)
+                i2c_wrbit(ptr, ctrl, dev & mask);
+        i2c_ack(ptr, ctrl);
+
+        if (reg != 0xff) {
+        for (mask = 0x80; mask != 0; mask >>= 1)
+                i2c_wrbit(ptr, ctrl, reg & mask);
+        i2c_ack(ptr, ctrl);
+        }
+
+        for (mask = 0x80; mask != 0; mask >>= 1)
+                i2c_wrbit(ptr, ctrl, val & mask);
+        i2c_ack(ptr, ctrl);
+
+        i2c_stop(ptr, ctrl);
+}
+
+/* -------------------------------------------------------------------- */
+
+/* M-Audio Delta series AK4524 access interface routine */
+
+static void
+envy24_delta_ak4524_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti)
+{
+       u_int32_t data = 0;
+       struct envy24_delta_ak4524_codec *ptr = codec;
+
+#if(0)
+       device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti);
+#endif
+       data = envy24_gpiord(ptr->parent);
+       data &= ~(ptr->cs | ptr->cclk | ptr->cdti);
+       if (cs) data += ptr->cs;
+       if (cclk) data += ptr->cclk;
+       if (cdti) data += ptr->cdti;
+       envy24_gpiowr(ptr->parent, data);
+       return;
+}
+
+static void *
+envy24_delta_ak4524_create(device_t dev, void *info, int dir, int num)
+{
+       struct sc_info *sc = info;
+       struct envy24_delta_ak4524_codec *buff = NULL;
+
+#if(0)
+       device_printf(sc->dev, "envy24_delta_ak4524_create(dev, sc, %d, %d)\n", dir, num);
+#endif
+       
+       buff = kmalloc(sizeof(*buff), M_ENVY24, M_NOWAIT);
+       if (buff == NULL)
+               return NULL;
+
+       if (dir == PCMDIR_REC && sc->adc[num] != NULL)
+               buff->info = ((struct envy24_delta_ak4524_codec *)sc->adc[num])->info;
+       else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL)
+               buff->info = ((struct envy24_delta_ak4524_codec *)sc->dac[num])->info;
+       else
+               buff->info = spicds_create(dev, buff, num, envy24_delta_ak4524_ctl);
+       if (buff->info == NULL) {
+               kfree(buff, M_ENVY24);
+               return NULL;
+       }
+
+       buff->parent = sc;
+       buff->dir = dir;
+       buff->num = num;
+
+       return (void *)buff;
+}
+
+static void
+envy24_delta_ak4524_destroy(void *codec)
+{
+       struct envy24_delta_ak4524_codec *ptr = codec;
+       if (ptr == NULL)
+               return;
+#if(0)
+       device_printf(ptr->parent->dev, "envy24_delta_ak4524_destroy()\n");
+#endif
+
+       if (ptr->dir == PCMDIR_PLAY) {
+               if (ptr->parent->dac[ptr->num] != NULL)
+                       spicds_destroy(ptr->info);
+       }
+       else {
+               if (ptr->parent->adc[ptr->num] != NULL)
+                       spicds_destroy(ptr->info);
+       }
+
+       kfree(codec, M_ENVY24);
+}
+
+static void
+envy24_delta_ak4524_init(void *codec)
+{
+#if 0
+       u_int32_t gpiomask, gpiodir;
+#endif
+       struct envy24_delta_ak4524_codec *ptr = codec;
+       if (ptr == NULL)
+               return;
+#if(0)
+       device_printf(ptr->parent->dev, "envy24_delta_ak4524_init()\n");
+#endif
+
+       /*
+       gpiomask = envy24_gpiogetmask(ptr->parent);
+       gpiomask &= ~(ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1);
+       envy24_gpiosetmask(ptr->parent, gpiomask);
+       gpiodir = envy24_gpiogetdir(ptr->parent);
+       gpiodir |= ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1;
+       envy24_gpiosetdir(ptr->parent, gpiodir);
+       */
+       ptr->cs = ptr->parent->cfg->cs;
+#if 0
+       envy24_gpiosetmask(ptr->parent, ENVY24_GPIO_CS8414_STATUS);
+       envy24_gpiosetdir(ptr->parent, ~ENVY24_GPIO_CS8414_STATUS);
+       if (ptr->num == 0)
+               ptr->cs = ENVY24_GPIO_AK4524_CS0;
+       else
+               ptr->cs = ENVY24_GPIO_AK4524_CS1;
+       ptr->cclk = ENVY24_GPIO_AK4524_CCLK;
+#endif
+       ptr->cclk = ptr->parent->cfg->cclk;
+       ptr->cdti = ptr->parent->cfg->cdti;
+       spicds_settype(ptr->info,  ptr->parent->cfg->type);
+       spicds_setcif(ptr->info, ptr->parent->cfg->cif);
+       spicds_setformat(ptr->info,
+           AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X);
+       spicds_setdvc(ptr->info, AK452X_DVC_DEMOFF);
+       /* for the time being, init only first codec */
+       if (ptr->num == 0)
+               spicds_init(ptr->info);
+
+        /* 6fire rear input init test, set ptr->num to 1 for test */
+        if (ptr->parent->cfg->subvendor == 0x153b && \
+                ptr->parent->cfg->subdevice == 0x1138 && ptr->num == 100) {
+                ptr->cs = 0x02;  
+                spicds_init(ptr->info);
+                device_printf(ptr->parent->dev, "6fire rear input init\n");
+                i2c_wr(ptr, envy24_gpio_i2c_ctl, \
+                        PCA9554_I2CDEV, PCA9554_DIR, 0x80);
+                i2c_wr(ptr, envy24_gpio_i2c_ctl, \
+                        PCA9554_I2CDEV, PCA9554_OUT, 0x02);
+        }
+}
+
+static void
+envy24_delta_ak4524_reinit(void *codec)
+{
+       struct envy24_delta_ak4524_codec *ptr = codec;
+       if (ptr == NULL)
+               return;
+#if(0)
+       device_printf(ptr->parent->dev, "envy24_delta_ak4524_reinit()\n");
+#endif
+
+       spicds_reinit(ptr->info);
+}
+
+static void
+envy24_delta_ak4524_setvolume(void *codec, int dir, unsigned int left, unsigned int right)
+{
+       struct envy24_delta_ak4524_codec *ptr = codec;
+       if (ptr == NULL)
+               return;
+#if(0)
+       device_printf(ptr->parent->dev, "envy24_delta_ak4524_set()\n");
+#endif
+
+       spicds_set(ptr->info, dir, left, right);
+}
+
+/*
+  There is no need for AK452[48] codec to set sample rate
+  static void
+  envy24_delta_ak4524_setrate(struct envy24_delta_ak4524_codec *codec, int which, int rate)
+  {
+  }
+*/
+
+/* -------------------------------------------------------------------- */
+
+/* hardware access routeines */
+
+static struct {
+       u_int32_t speed;
+       u_int32_t code;
+} envy24_speedtab[] = {
+       {48000, ENVY24_MT_RATE_48000},
+       {24000, ENVY24_MT_RATE_24000},
+       {12000, ENVY24_MT_RATE_12000},
+       {9600, ENVY24_MT_RATE_9600},
+       {32000, ENVY24_MT_RATE_32000},
+       {16000, ENVY24_MT_RATE_16000},
+       {8000, ENVY24_MT_RATE_8000},
+       {96000, ENVY24_MT_RATE_96000},
+       {64000, ENVY24_MT_RATE_64000},
+       {44100, ENVY24_MT_RATE_44100},
+       {22050, ENVY24_MT_RATE_22050},
+       {11025, ENVY24_MT_RATE_11025},
+       {88200, ENVY24_MT_RATE_88200},
+       {0, 0x10}
+};
+
+static int
+envy24_setspeed(struct sc_info *sc, u_int32_t speed) {
+       u_int32_t code;
+       int i = 0;
+
+#if(0)
+       device_printf(sc->dev, "envy24_setspeed(sc, %d)\n", speed);
+#endif
+       if (speed == 0) {
+               code = ENVY24_MT_RATE_SPDIF; /* external master clock */
+               envy24_slavecd(sc);
+       }
+       else {
+               for (i = 0; envy24_speedtab[i].speed != 0; i++) {
+                       if (envy24_speedtab[i].speed == speed)
+                               break;
+               }
+               code = envy24_speedtab[i].code;
+       }
+#if(0)
+       device_printf(sc->dev, "envy24_setspeed(): speed %d/code 0x%04x\n", envy24_speedtab[i].speed, code);
+#endif
+       if (code < 0x10) {
+               envy24_wrmt(sc, ENVY24_MT_RATE, code, 1);
+               code = envy24_rdmt(sc, ENVY24_MT_RATE, 1);
+               code &= ENVY24_MT_RATE_MASK;
+               for (i = 0; envy24_speedtab[i].code < 0x10; i++) {
+                       if (envy24_speedtab[i].code == code)
+                               break;
+               }
+               speed = envy24_speedtab[i].speed;
+       }
+       else
+               speed = 0;
+
+#if(0)
+       device_printf(sc->dev, "envy24_setspeed(): return %d\n", speed);
+#endif
+       return speed;
+}
+
+static void
+envy24_setvolume(struct sc_info *sc, unsigned ch)
+{
+#if(0)
+       device_printf(sc->dev, "envy24_setvolume(sc, %d)\n", ch);
+#endif
+if (sc->cfg->subvendor==0x153b  && sc->cfg->subdevice==0x1138 ) {
+        envy24_wrmt(sc, ENVY24_MT_VOLIDX, 16, 1);
+        envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f7f, 2);
+        envy24_wrmt(sc, ENVY24_MT_VOLIDX, 17, 1);
+        envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f7f, 2);
+       }
+
+       envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1);
+       envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f00 | sc->left[ch], 2);
+       envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1);
+       envy24_wrmt(sc, ENVY24_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2);
+}
+
+static void
+envy24_mutevolume(struct sc_info *sc, unsigned ch)
+{
+       u_int32_t vol;
+
+#if(0)
+       device_printf(sc->dev, "envy24_mutevolume(sc, %d)\n", ch);
+#endif
+       vol = ENVY24_VOL_MUTE << 8 | ENVY24_VOL_MUTE;
+       envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1);
+       envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2);
+       envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1);
+       envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2);
+}
+
+static u_int32_t
+envy24_gethwptr(struct sc_info *sc, int dir)
+{
+       int unit, regno;
+       u_int32_t ptr, rtn;
+
+#if(0)
+       device_printf(sc->dev, "envy24_gethwptr(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY) {
+               rtn = sc->psize / 4;
+               unit = ENVY24_PLAY_BUFUNIT / 4;
+               regno = ENVY24_MT_PCNT;
+       }
+       else {
+               rtn = sc->rsize / 4;
+               unit = ENVY24_REC_BUFUNIT / 4;
+               regno = ENVY24_MT_RCNT;
+       }
+
+       ptr = envy24_rdmt(sc, regno, 2);
+       rtn -= (ptr + 1);
+       rtn /= unit;
+
+#if(0)
+       device_printf(sc->dev, "envy24_gethwptr(): return %d\n", rtn);
+#endif
+       return rtn;
+}
+
+static void
+envy24_updintr(struct sc_info *sc, int dir)
+{
+       int regptr, regintr;
+       u_int32_t mask, intr;
+       u_int32_t ptr, size, cnt;
+       u_int16_t blk;
+
+#if(0)
+       device_printf(sc->dev, "envy24_updintr(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY) {
+               blk = sc->blk[0];
+               size = sc->psize / 4;
+               regptr = ENVY24_MT_PCNT;
+               regintr = ENVY24_MT_PTERM;
+               mask = ~ENVY24_MT_INT_PMASK;
+       }
+       else {
+               blk = sc->blk[1];
+               size = sc->rsize / 4;
+               regptr = ENVY24_MT_RCNT;
+               regintr = ENVY24_MT_RTERM;
+               mask = ~ENVY24_MT_INT_RMASK;
+       }
+
+       ptr = size - envy24_rdmt(sc, regptr, 2) - 1;
+       /*
+       cnt = blk - ptr % blk - 1;
+       if (cnt == 0)
+               cnt = blk - 1;
+       */
+       cnt = blk - 1;
+#if(0)
+       device_printf(sc->dev, "envy24_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt);
+#endif
+       envy24_wrmt(sc, regintr, cnt, 2);
+       intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
+#if(0)
+       device_printf(sc->dev, "envy24_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask);
+#endif
+       envy24_wrmt(sc, ENVY24_MT_INT, intr & mask, 1);
+#if(0)
+       device_printf(sc->dev, "envy24_updintr():INT-->0x%02x\n",
+                     envy24_rdmt(sc, ENVY24_MT_INT, 1));
+#endif
+
+       return;
+}
+
+#if 0
+static void
+envy24_maskintr(struct sc_info *sc, int dir)
+{
+       u_int32_t mask, intr;
+
+#if(0)
+       device_printf(sc->dev, "envy24_maskintr(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY)
+               mask = ENVY24_MT_INT_PMASK;
+       else
+               mask = ENVY24_MT_INT_RMASK;
+       intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
+       envy24_wrmt(sc, ENVY24_MT_INT, intr | mask, 1);
+
+       return;
+}
+#endif
+
+static int
+envy24_checkintr(struct sc_info *sc, int dir)
+{
+       u_int32_t mask, stat, intr, rtn;
+
+#if(0)
+       device_printf(sc->dev, "envy24_checkintr(sc, %d)\n", dir);
+#endif
+       intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
+       if (dir == PCMDIR_PLAY) {
+               if ((rtn = intr & ENVY24_MT_INT_PSTAT) != 0) {
+                       mask = ~ENVY24_MT_INT_RSTAT;
+                       stat = ENVY24_MT_INT_PSTAT | ENVY24_MT_INT_PMASK;
+                       envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1);
+               }
+       }
+       else {
+               if ((rtn = intr & ENVY24_MT_INT_RSTAT) != 0) {
+                       mask = ~ENVY24_MT_INT_PSTAT;
+                       stat = ENVY24_MT_INT_RSTAT | ENVY24_MT_INT_RMASK;
+                       envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1);
+               }
+       }
+
+       return rtn;
+}
+
+static void
+envy24_start(struct sc_info *sc, int dir)
+{
+       u_int32_t stat, sw;
+
+#if(0)
+       device_printf(sc->dev, "envy24_start(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY)
+               sw = ENVY24_MT_PCTL_PSTART;
+       else
+               sw = ENVY24_MT_PCTL_RSTART;
+
+       stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1);
+       envy24_wrmt(sc, ENVY24_MT_PCTL, stat | sw, 1);
+#if(0)
+       DELAY(100);
+       device_printf(sc->dev, "PADDR:0x%08x\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4));
+       device_printf(sc->dev, "PCNT:%ld\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2));
+#endif
+
+       return;
+}
+
+static void
+envy24_stop(struct sc_info *sc, int dir)
+{
+       u_int32_t stat, sw;
+
+#if(0)
+       device_printf(sc->dev, "envy24_stop(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY)
+               sw = ~ENVY24_MT_PCTL_PSTART;
+       else
+               sw = ~ENVY24_MT_PCTL_RSTART;
+
+       stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1);
+       envy24_wrmt(sc, ENVY24_MT_PCTL, stat & sw, 1);
+
+       return;
+}
+
+static int
+envy24_route(struct sc_info *sc, int dac, int class, int adc, int rev)
+{
+       u_int32_t reg, mask;
+       u_int32_t left, right;
+
+#if(0)
+       device_printf(sc->dev, "envy24_route(sc, %d, %d, %d, %d)\n",
+           dac, class, adc, rev);
+#endif
+       /* parameter pattern check */
+       if (dac < 0 || ENVY24_ROUTE_DAC_SPDIF < dac)
+               return -1;
+       if (class == ENVY24_ROUTE_CLASS_MIX &&
+           (dac != ENVY24_ROUTE_DAC_1 && dac != ENVY24_ROUTE_DAC_SPDIF))
+               return -1;
+       if (rev) {
+               left = ENVY24_ROUTE_RIGHT;
+               right = ENVY24_ROUTE_LEFT;
+       }
+       else {
+               left = ENVY24_ROUTE_LEFT;
+               right = ENVY24_ROUTE_RIGHT;
+       }
+
+       if (dac == ENVY24_ROUTE_DAC_SPDIF) {
+               reg = class | class << 2 |
+                       ((adc << 1 | left) | left << 3) << 8 |
+                       ((adc << 1 | right) | right << 3) << 12;
+#if(0)
+               device_printf(sc->dev, "envy24_route(): MT_SPDOUT-->0x%04x\n", reg);
+#endif
+               envy24_wrmt(sc, ENVY24_MT_SPDOUT, reg, 2);
+       }
+       else {
+               mask = ~(0x0303 << dac * 2);
+               reg = envy24_rdmt(sc, ENVY24_MT_PSDOUT, 2);
+               reg = (reg & mask) | ((class | class << 8) << dac * 2);
+#if(0)
+               device_printf(sc->dev, "envy24_route(): MT_PSDOUT-->0x%04x\n", reg);
+#endif
+               envy24_wrmt(sc, ENVY24_MT_PSDOUT, reg, 2);
+               mask = ~(0xff << dac * 8);
+               reg = envy24_rdmt(sc, ENVY24_MT_RECORD, 4);
+               reg = (reg & mask) |
+                       (((adc << 1 | left) | left << 3) |
+                        ((adc << 1 | right) | right << 3) << 4) << dac * 8;
+#if(0)
+               device_printf(sc->dev, "envy24_route(): MT_RECORD-->0x%08x\n", reg);
+#endif
+               envy24_wrmt(sc, ENVY24_MT_RECORD, reg, 4);
+
+               /* 6fire rear input init test */
+               envy24_wrmt(sc, ENVY24_MT_RECORD, 0x00, 4);
+       }
+
+       return 0;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* buffer copy routines */
+static void
+envy24_p32sl(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int32_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+       length = sndbuf_getready(ch->buffer) / 8;
+       dmabuf = ch->parent->pbuf;
+       data = (u_int32_t *)ch->data;
+       src = sndbuf_getreadyptr(ch->buffer) / 4;
+       dst = src / 2 + ch->offset;
+       ssize = ch->size / 4;
+       dsize = ch->size / 8;
+       slot = ch->num * 2;
+
+       for (i = 0; i < length; i++) {
+               dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = data[src];
+               dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = data[src + 1];
+               dst++;
+               dst %= dsize;
+               src += 2;
+               src %= ssize;
+       }
+       
+       return;
+}
+
+static void
+envy24_p16sl(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int16_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+#if(0)
+       device_printf(ch->parent->dev, "envy24_p16sl()\n");
+#endif
+       length = sndbuf_getready(ch->buffer) / 4;
+       dmabuf = ch->parent->pbuf;
+       data = (u_int16_t *)ch->data;
+       src = sndbuf_getreadyptr(ch->buffer) / 2;
+       dst = src / 2 + ch->offset;
+       ssize = ch->size / 2;
+       dsize = ch->size / 4;
+       slot = ch->num * 2;
+#if(0)
+       device_printf(ch->parent->dev, "envy24_p16sl():%lu-->%lu(%lu)\n", src, dst, length);
+#endif
+       
+       for (i = 0; i < length; i++) {
+               dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16;
+               dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16;
+#if(0)
+               if (i < 16) {
+                       printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot]);
+                       printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1]);
+               }
+#endif
+               dst++;
+               dst %= dsize;
+               src += 2;
+               src %= ssize;
+       }
+#if(0)
+       printf("\n");
+#endif
+       
+       return;
+}
+
+static void
+envy24_p8u(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int8_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+       length = sndbuf_getready(ch->buffer) / 2;
+       dmabuf = ch->parent->pbuf;
+       data = (u_int8_t *)ch->data;
+       src = sndbuf_getreadyptr(ch->buffer);
+       dst = src / 2 + ch->offset;
+       ssize = ch->size;
+       dsize = ch->size / 4;
+       slot = ch->num * 2;
+       
+       for (i = 0; i < length; i++) {
+               dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24;
+               dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24;
+               dst++;
+               dst %= dsize;
+               src += 2;
+               src %= ssize;
+       }
+       
+       return;
+}
+
+static void
+envy24_r32sl(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int32_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+       length = sndbuf_getfree(ch->buffer) / 8;
+       dmabuf = ch->parent->rbuf;
+       data = (u_int32_t *)ch->data;
+       dst = sndbuf_getfreeptr(ch->buffer) / 4;
+       src = dst / 2 + ch->offset;
+       dsize = ch->size / 4;
+       ssize = ch->size / 8;
+       slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2;
+
+       for (i = 0; i < length; i++) {
+               data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot].buffer;
+               data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1].buffer;
+               dst += 2;
+               dst %= dsize;
+               src++;
+               src %= ssize;
+       }
+       
+       return;
+}
+
+static void
+envy24_r16sl(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int16_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+       length = sndbuf_getfree(ch->buffer) / 4;
+       dmabuf = ch->parent->rbuf;
+       data = (u_int16_t *)ch->data;
+       dst = sndbuf_getfreeptr(ch->buffer) / 2;
+       src = dst / 2 + ch->offset;
+       dsize = ch->size / 2;
+       ssize = ch->size / 8;
+       slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2;
+
+       for (i = 0; i < length; i++) {
+               data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot].buffer;
+               data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1].buffer;
+               dst += 2;
+               dst %= dsize;
+               src++;
+               src %= ssize;
+       }
+       
+       return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* channel interface */
+static void *
+envy24chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+{
+       struct sc_info  *sc = (struct sc_info *)devinfo;
+       struct sc_chinfo *ch;
+       unsigned num;
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_init(obj, devinfo, b, c, %d)\n", dir);
+#endif
+       snd_mtxlock(sc->lock);
+       if ((sc->chnum > ENVY24_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) ||
+           (sc->chnum < ENVY24_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) {
+               snd_mtxunlock(sc->lock);
+               return NULL;
+       }
+       num = sc->chnum;
+
+       ch = &sc->chan[num];
+       ch->size = 8 * ENVY24_SAMPLE_NUM;
+       ch->data = kmalloc(ch->size, M_ENVY24, M_NOWAIT);
+       if (ch->data == NULL) {
+               ch->size = 0;
+               ch = NULL;
+       }
+       else {
+               ch->buffer = b;
+               ch->channel = c;
+               ch->parent = sc;
+               ch->dir = dir;
+               /* set channel map */
+               ch->num = envy24_chanmap[num];
+               snd_mtxunlock(sc->lock);
+               sndbuf_setup(ch->buffer, ch->data, ch->size);
+               snd_mtxlock(sc->lock);
+               /* these 2 values are dummy */
+               ch->unit = 4;
+               ch->blk = 10240;
+       }
+       snd_mtxunlock(sc->lock);
+
+       return ch;
+}
+
+static int
+envy24chan_free(kobj_t obj, void *data)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_free()\n");
+#endif
+       snd_mtxlock(sc->lock);
+       if (ch->data != NULL) {
+               kfree(ch->data, M_ENVY24);
+               ch->data = NULL;
+       }
+       snd_mtxunlock(sc->lock);
+
+       return 0;
+}
+
+static int
+envy24chan_setformat(kobj_t obj, void *data, u_int32_t format)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+       struct envy24_emldma *emltab;
+       /* unsigned int bcnt, bsize; */
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_setformat(obj, data, 0x%08x)\n", format);
+#endif
+       snd_mtxlock(sc->lock);
+       /* check and get format related information */
+       if (ch->dir == PCMDIR_PLAY)
+               emltab = envy24_pemltab;
+       else
+               emltab = envy24_remltab;
+       if (emltab == NULL) {
+               snd_mtxunlock(sc->lock);
+               return -1;
+       }
+       for (i = 0; emltab[i].format != 0; i++)
+               if (emltab[i].format == format)
+                       break;
+       if (emltab[i].format == 0) {
+               snd_mtxunlock(sc->lock);
+               return -1;
+       }
+
+       /* set format information */
+       ch->format = format;
+       ch->emldma = emltab[i].emldma;
+       if (ch->unit > emltab[i].unit)
+               ch->blk *= ch->unit / emltab[i].unit;
+       else
+               ch->blk /= emltab[i].unit / ch->unit;
+       ch->unit = emltab[i].unit;
+
+       /* set channel buffer information */
+       ch->size = ch->unit * ENVY24_SAMPLE_NUM;
+#if 0
+       if (ch->dir == PCMDIR_PLAY)
+               bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT;
+       else
+               bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT;
+       bsize *= ch->unit;
+       bcnt = ch->size / bsize;
+       sndbuf_resize(ch->buffer, bcnt, bsize);
+#endif
+       snd_mtxunlock(sc->lock);
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_setformat(): return 0x%08x\n", 0);
+#endif
+       return 0;
+}
+
+/*
+  IMPLEMENT NOTICE: In this driver, setspeed function only do setting
+  of speed information value. And real hardware speed setting is done
+  at start triggered(see envy24chan_trigger()). So, at this function
+  is called, any value that ENVY24 can use is able to set. But, at
+  start triggerd, some other channel is running, and that channel's
+  speed isn't same with, then trigger function will fail.
+*/
+static int
+envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+{
+       struct sc_chinfo *ch = data;
+       u_int32_t val, prev;
+       int i;
+
+#if(0)
+       device_printf(ch->parent->dev, "envy24chan_setspeed(obj, data, %d)\n", speed);
+#endif
+       prev = 0x7fffffff;
+       for (i = 0; (val = envy24_speed[i]) != 0; i++) {
+               if (abs(val - speed) < abs(prev - speed))
+                       prev = val;
+               else
+                       break;
+       }
+       ch->speed = prev;
+       
+#if(0)
+       device_printf(ch->parent->dev, "envy24chan_setspeed(): return %d\n", ch->speed);
+#endif
+       return ch->speed;
+}
+
+static int
+envy24chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+{
+       struct sc_chinfo *ch = data;
+       /* struct sc_info *sc = ch->parent; */
+       u_int32_t size, prev;
+        unsigned int bcnt, bsize;
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_setblocksize(obj, data, %d)\n", blocksize);
+#endif
+       prev = 0x7fffffff;
+       /* snd_mtxlock(sc->lock); */
+       for (size = ch->size / 2; size > 0; size /= 2) {
+               if (abs(size - blocksize) < abs(prev - blocksize))
+                       prev = size;
+               else
+                       break;
+       }
+
+       ch->blk = prev / ch->unit;
+       if (ch->dir == PCMDIR_PLAY)
+               ch->blk *= ENVY24_PLAY_BUFUNIT / 4;
+       else
+               ch->blk *= ENVY24_REC_BUFUNIT / 4;
+       /* set channel buffer information */
+       /* ch->size = ch->unit * ENVY24_SAMPLE_NUM; */
+        if (ch->dir == PCMDIR_PLAY)
+                bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT;
+        else
+                bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT;
+        bsize *= ch->unit;
+        bcnt = ch->size / bsize;
+        sndbuf_resize(ch->buffer, bcnt, bsize);
+       /* snd_mtxunlock(sc->lock); */
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_setblocksize(): return %d\n", prev);
+#endif
+       return prev;
+}
+
+/* semantic note: must start at beginning of buffer */
+static int
+envy24chan_trigger(kobj_t obj, void *data, int go)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+       u_int32_t ptr;
+       int slot;
+#if 0
+       int i;
+
+       device_printf(sc->dev, "envy24chan_trigger(obj, data, %d)\n", go);
+#endif
+       snd_mtxlock(sc->lock);
+       if (ch->dir == PCMDIR_PLAY)
+               slot = 0;
+       else
+               slot = 1;
+       switch (go) {
+       case PCMTRIG_START:
+#if(0)
+               device_printf(sc->dev, "envy24chan_trigger(): start\n");
+#endif
+               /* check or set channel speed */
+               if (sc->run[0] == 0 && sc->run[1] == 0) {
+                       sc->speed = envy24_setspeed(sc, ch->speed);
+                       sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed;
+                       sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed;
+               }
+               else if (ch->speed != 0 && ch->speed != sc->speed)
+                       return -1;
+               if (ch->speed == 0)
+                       ch->channel->speed = sc->speed;
+               /* start or enable channel */
+               sc->run[slot]++;
+               if (sc->run[slot] == 1) {
+                       /* first channel */
+                       ch->offset = 0;
+                       sc->blk[slot] = ch->blk;
+               }
+               else {
+                       ptr = envy24_gethwptr(sc, ch->dir);
+                       ch->offset = ((ptr / ch->blk + 1) * ch->blk %
+                           (ch->size / 4)) * 4 / ch->unit;
+                       if (ch->blk < sc->blk[slot])
+                               sc->blk[slot] = ch->blk;
+               }
+               if (ch->dir == PCMDIR_PLAY) {
+                       ch->emldma(ch);
+                       envy24_setvolume(sc, ch->num);
+               }
+               envy24_updintr(sc, ch->dir);
+               if (sc->run[slot] == 1)
+                       envy24_start(sc, ch->dir);
+               ch->run = 1;
+               break;
+       case PCMTRIG_EMLDMAWR:
+#if(0)
+               device_printf(sc->dev, "envy24chan_trigger(): emldmawr\n");
+#endif
+               if (ch->run != 1)
+                       return -1;
+               ch->emldma(ch);
+               break;
+       case PCMTRIG_EMLDMARD:
+#if(0)
+               device_printf(sc->dev, "envy24chan_trigger(): emldmard\n");
+#endif
+               if (ch->run != 1)
+                       return -1;
+               ch->emldma(ch);
+               break;
+       case PCMTRIG_ABORT:
+               if (ch->run) {
+#if(0)
+               device_printf(sc->dev, "envy24chan_trigger(): abort\n");
+#endif
+               ch->run = 0;
+               sc->run[slot]--;
+               if (ch->dir == PCMDIR_PLAY)
+                       envy24_mutevolume(sc, ch->num);
+               if (sc->run[slot] == 0) {
+                       envy24_stop(sc, ch->dir);
+                       sc->intr[slot] = 0;
+               }
+#if 0
+               else if (ch->blk == sc->blk[slot]) {
+                       sc->blk[slot] = ENVY24_SAMPLE_NUM / 2;
+                       for (i = 0; i < ENVY24_CHAN_NUM; i++) {
+                               if (sc->chan[i].dir == ch->dir &&
+                                   sc->chan[i].run == 1 &&
+                                   sc->chan[i].blk < sc->blk[slot])
+                                       sc->blk[slot] = sc->chan[i].blk;
+                       }
+                       if (ch->blk != sc->blk[slot])
+                               envy24_updintr(sc, ch->dir);
+               }
+#endif
+               }
+               break;
+       }
+       snd_mtxunlock(sc->lock);
+
+       return 0;
+}
+
+static int
+envy24chan_getptr(kobj_t obj, void *data)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+       u_int32_t ptr;
+       int rtn;
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_getptr()\n");
+#endif
+       snd_mtxlock(sc->lock);
+       ptr = envy24_gethwptr(sc, ch->dir);
+       rtn = ptr * ch->unit;
+       snd_mtxunlock(sc->lock);
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_getptr(): return %d\n",
+           rtn);
+#endif
+       return rtn;
+}
+
+static struct pcmchan_caps *
+envy24chan_getcaps(kobj_t obj, void *data)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+       struct pcmchan_caps *rtn;
+
+#if(0)
+       device_printf(sc->dev, "envy24chan_getcaps()\n");
+#endif
+       snd_mtxlock(sc->lock);
+       if (ch->dir == PCMDIR_PLAY) {
+               if (sc->run[0] == 0)
+                       rtn = &envy24_playcaps;
+               else
+                       rtn = &sc->caps[0];
+       }
+       else {
+               if (sc->run[1] == 0)
+                       rtn = &envy24_reccaps;
+               else
+                       rtn = &sc->caps[1];
+       }
+       snd_mtxunlock(sc->lock);
+
+       return rtn;
+}
+
+static kobj_method_t envy24chan_methods[] = {
+       KOBJMETHOD(channel_init,                envy24chan_init),
+       KOBJMETHOD(channel_free,                envy24chan_free),
+       KOBJMETHOD(channel_setformat,           envy24chan_setformat),
+       KOBJMETHOD(channel_setspeed,            envy24chan_setspeed),
+       KOBJMETHOD(channel_setblocksize,        envy24chan_setblocksize),
+       KOBJMETHOD(channel_trigger,             envy24chan_trigger),
+       KOBJMETHOD(channel_getptr,              envy24chan_getptr),
+       KOBJMETHOD(channel_getcaps,             envy24chan_getcaps),
+       { 0, 0 }
+};
+CHANNEL_DECLARE(envy24chan);
+
+/* -------------------------------------------------------------------- */
+
+/* mixer interface */
+
+static int
+envy24mixer_init(struct snd_mixer *m)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+
+#if(0)
+       device_printf(sc->dev, "envy24mixer_init()\n");
+#endif
+       if (sc == NULL)
+               return -1;
+
+       /* set volume control rate */
+       snd_mtxlock(sc->lock);
+       envy24_wrmt(sc, ENVY24_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */
+
+       mix_setdevs(m, ENVY24_MIX_MASK);
+       mix_setrecdevs(m, ENVY24_MIX_REC_MASK);
+       snd_mtxunlock(sc->lock);
+
+       return 0;
+}
+
+static int
+envy24mixer_reinit(struct snd_mixer *m)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+
+       if (sc == NULL)
+               return -1;
+#if(0)
+       device_printf(sc->dev, "envy24mixer_reinit()\n");
+#endif
+
+       return 0;
+}
+
+static int
+envy24mixer_uninit(struct snd_mixer *m)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+
+       if (sc == NULL)
+               return -1;
+#if(0)
+       device_printf(sc->dev, "envy24mixer_uninit()\n");
+#endif
+
+       return 0;
+}
+
+static int
+envy24mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+       int ch = envy24_mixmap[dev];
+       int hwch;
+       int i;
+
+       if (sc == NULL)
+               return -1;
+       if (dev == 0 && sc->cfg->codec->setvolume == NULL)
+               return -1;
+       if (dev != 0 && ch == -1)
+               return -1;
+       hwch = envy24_chanmap[ch];
+#if(0)
+       device_printf(sc->dev, "envy24mixer_set(m, %d, %d, %d)\n",
+           dev, left, right);
+#endif
+
+       snd_mtxlock(sc->lock);
+       if (dev == 0) {
+               for (i = 0; i < sc->dacn; i++) {
+                       sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right);
+               }
+       }
+       else {
+               /* set volume value for hardware */
+               if ((sc->left[hwch] = 100 - left) > ENVY24_VOL_MIN)
+                       sc->left[hwch] = ENVY24_VOL_MUTE;
+               if ((sc->right[hwch] = 100 - right) > ENVY24_VOL_MIN)
+                       sc->right[hwch] = ENVY24_VOL_MUTE;
+
+               /* set volume for record channel and running play channel */
+               if (hwch > ENVY24_CHAN_PLAY_SPDIF || sc->chan[ch].run)
+                       envy24_setvolume(sc, hwch);
+       }
+       snd_mtxunlock(sc->lock);
+
+       return right << 8 | left;
+}
+
+static u_int32_t
+envy24mixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+       int ch = envy24_mixmap[src];
+#if(0)
+       device_printf(sc->dev, "envy24mixer_setrecsrc(m, %d)\n", src);
+#endif
+
+       if (ch > ENVY24_CHAN_PLAY_SPDIF)
+               sc->src = ch;
+       return src;
+}
+
+static kobj_method_t envy24mixer_methods[] = {
+       KOBJMETHOD(mixer_init,          envy24mixer_init),
+       KOBJMETHOD(mixer_reinit,        envy24mixer_reinit),
+       KOBJMETHOD(mixer_uninit,        envy24mixer_uninit),
+       KOBJMETHOD(mixer_set,           envy24mixer_set),
+       KOBJMETHOD(mixer_setrecsrc,     envy24mixer_setrecsrc),
+       { 0, 0 }
+};
+MIXER_DECLARE(envy24mixer);
+
+/* -------------------------------------------------------------------- */
+
+/* The interrupt handler */
+static void
+envy24_intr(void *p)
+{
+       struct sc_info *sc = (struct sc_info *)p;
+       struct sc_chinfo *ch;
+       u_int32_t ptr, dsize, feed;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24_intr()\n");
+#endif
+       snd_mtxlock(sc->lock);
+       if (envy24_checkintr(sc, PCMDIR_PLAY)) {
+#if(0)
+               device_printf(sc->dev, "envy24_intr(): play\n");
+#endif
+               dsize = sc->psize / 4;
+               ptr = dsize - envy24_rdmt(sc, ENVY24_MT_PCNT, 2) - 1;
+#if(0)
+               device_printf(sc->dev, "envy24_intr(): ptr = %d-->", ptr);
+#endif
+               ptr -= ptr % sc->blk[0];
+               feed = (ptr + dsize - sc->intr[0]) % dsize; 
+#if(0)
+               printf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed);
+#endif
+               for (i = ENVY24_CHAN_PLAY_DAC1; i <= ENVY24_CHAN_PLAY_SPDIF; i++) {
+                       ch = &sc->chan[i];
+#if(0)
+                       if (ch->run)
+                               device_printf(sc->dev, "envy24_intr(): chan[%d].blk = %d\n", i, ch->blk);
+#endif
+                       if (ch->run && ch->blk <= feed) {
+                               snd_mtxunlock(sc->lock);
+                               chn_intr(ch->channel);
+                               snd_mtxlock(sc->lock);
+                       }
+               }
+               sc->intr[0] = ptr;
+               envy24_updintr(sc, PCMDIR_PLAY);
+       }
+       if (envy24_checkintr(sc, PCMDIR_REC)) {
+#if(0)
+               device_printf(sc->dev, "envy24_intr(): rec\n");
+#endif
+               dsize = sc->rsize / 4;
+               ptr = dsize - envy24_rdmt(sc, ENVY24_MT_RCNT, 2) - 1;
+               ptr -= ptr % sc->blk[1];
+               feed = (ptr + dsize - sc->intr[1]) % dsize; 
+               for (i = ENVY24_CHAN_REC_ADC1; i <= ENVY24_CHAN_REC_SPDIF; i++) {
+                       ch = &sc->chan[i];
+                       if (ch->run && ch->blk <= feed) {
+                               snd_mtxunlock(sc->lock);
+                               chn_intr(ch->channel);
+                               snd_mtxlock(sc->lock);
+                       }
+               }
+               sc->intr[1] = ptr;
+               envy24_updintr(sc, PCMDIR_REC);
+       }
+       snd_mtxunlock(sc->lock);
+
+       return;
+}
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+envy24_pci_probe(device_t dev)
+{
+       u_int16_t sv, sd;
+       int i;
+
+#if(0)
+       printf("envy24_pci_probe()\n");
+#endif
+       if (pci_get_device(dev) == PCID_ENVY24 &&
+           pci_get_vendor(dev) == PCIV_ENVY24) {
+               sv = pci_get_subvendor(dev);
+               sd = pci_get_subdevice(dev);
+               for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
+                       if (cfg_table[i].subvendor == sv &&
+                           cfg_table[i].subdevice == sd) {
+                               break;
+                       }
+               }
+               device_set_desc(dev, cfg_table[i].name);
+#if(0)
+               printf("envy24_pci_probe(): return 0\n");
+#endif
+               return 0;
+       }
+       else {
+#if(0)
+               printf("envy24_pci_probe(): return ENXIO\n");
+#endif
+               return ENXIO;
+       }
+}
+
+static void
+envy24_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+       /* struct sc_info *sc = (struct sc_info *)arg; */
+
+#if(0)
+       device_printf(sc->dev, "envy24_dmapsetmap()\n");
+       if (bootverbose) {
+               printf("envy24(play): setmap %lx, %lx; ",
+                   (unsigned long)segs->ds_addr,
+                   (unsigned long)segs->ds_len);
+               printf("%p -> %lx\n", sc->pmap, (unsigned long)vtophys(sc->pmap));
+       }
+#endif
+}
+
+static void
+envy24_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+       /* struct sc_info *sc = (struct sc_info *)arg; */
+
+#if(0)
+       device_printf(sc->dev, "envy24_dmarsetmap()\n");
+       if (bootverbose) {
+               printf("envy24(record): setmap %lx, %lx; ",
+                   (unsigned long)segs->ds_addr,
+                   (unsigned long)segs->ds_len);
+               printf("%p -> %lx\n", sc->rmap, (unsigned long)vtophys(sc->pmap));
+       }
+#endif
+}
+
+static void
+envy24_dmafree(struct sc_info *sc)
+{
+#if(0)
+       device_printf(sc->dev, "envy24_dmafree():");
+       if (sc->rmap) printf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap);
+       else printf(" sc->rmap(null)");
+       if (sc->pmap) printf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap);
+       else printf(" sc->pmap(null)");
+       if (sc->rbuf) printf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf);
+       else printf(" sc->rbuf(null)");
+       if (sc->pbuf) printf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf);
+       else printf(" sc->pbuf(null)\n");
+#endif
+#if(0)
+       if (sc->rmap)
+               bus_dmamap_unload(sc->dmat, sc->rmap);
+       if (sc->pmap)
+               bus_dmamap_unload(sc->dmat, sc->pmap);
+       if (sc->rbuf)
+               bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+       if (sc->pbuf)
+               bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+#else
+       bus_dmamap_unload(sc->dmat, sc->rmap);
+       bus_dmamap_unload(sc->dmat, sc->pmap);
+       bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+       bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+#endif
+
+       sc->rmap = sc->pmap = NULL;
+       sc->pbuf = NULL;
+       sc->rbuf = NULL;
+
+       return;
+}
+
+static int
+envy24_dmainit(struct sc_info *sc)
+{
+       u_int32_t addr;
+
+#if(0)
+       device_printf(sc->dev, "envy24_dmainit()\n");
+#endif
+       /* init values */
+       sc->psize = ENVY24_PLAY_BUFUNIT * ENVY24_SAMPLE_NUM;
+       sc->rsize = ENVY24_REC_BUFUNIT * ENVY24_SAMPLE_NUM;
+       sc->pbuf = NULL;
+       sc->rbuf = NULL;
+       sc->pmap = sc->rmap = NULL;
+       sc->blk[0] = sc->blk[1] = 0;
+
+       /* allocate DMA buffer */
+#if(0)
+       device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->pbuf\n");
+#endif
+       if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap))
+               goto bad;
+#if(0)
+       device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->rbuf\n");
+#endif
+       if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap))
+               goto bad;
+#if(0)
+       device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->pmap\n");
+#endif
+       if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24_dmapsetmap, sc, 0))
+               goto bad;
+#if(0)
+       device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->rmap\n");
+#endif
+       if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24_dmarsetmap, sc, 0))
+               goto bad;
+       bzero(sc->pbuf, sc->psize);
+       bzero(sc->rbuf, sc->rsize);
+
+       /* set values to register */
+       addr = vtophys(sc->pbuf);
+#if(0)
+       device_printf(sc->dev, "pbuf(0x%08x)\n", addr);
+#endif
+       envy24_wrmt(sc, ENVY24_MT_PADDR, addr, 4);
+#if(0)
+       device_printf(sc->dev, "PADDR-->(0x%08x)\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4));
+       device_printf(sc->dev, "psize(%ld)\n", sc->psize / 4 - 1);
+#endif
+       envy24_wrmt(sc, ENVY24_MT_PCNT, sc->psize / 4 - 1, 2);
+#if(0)
+       device_printf(sc->dev, "PCNT-->(%ld)\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2));
+#endif
+       addr = vtophys(sc->rbuf);
+       envy24_wrmt(sc, ENVY24_MT_RADDR, addr, 4);
+       envy24_wrmt(sc, ENVY24_MT_RCNT, sc->rsize / 4 - 1, 2);
+
+       return 0;
+ bad:
+       envy24_dmafree(sc);
+       return ENOSPC;
+}
+
+static void
+envy24_putcfg(struct sc_info *sc)
+{
+       device_printf(sc->dev, "system configuration\n");
+       kprintf("  SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n",
+           sc->cfg->subvendor, sc->cfg->subdevice);
+       kprintf("  XIN2 Clock Source: ");
+       switch (sc->cfg->scfg & PCIM_SCFG_XIN2) {
+       case 0x00:
+               kprintf("22.5792MHz(44.1kHz*512)\n");
+               break;
+       case 0x40:
+               kprintf("16.9344MHz(44.1kHz*384)\n");
+               break;
+       case 0x80:
+               kprintf("from external clock synthesizer chip\n");
+               break;
+       default:
+               kprintf("illeagal system setting\n");
+       }
+       kprintf("  MPU-401 UART(s) #: ");
+       if (sc->cfg->scfg & PCIM_SCFG_MPU)
+               kprintf("2\n");
+       else
+               kprintf("1\n");
+       kprintf("  AC'97 codec: ");
+       if (sc->cfg->scfg & PCIM_SCFG_AC97)
+               kprintf("not exist\n");
+       else
+               kprintf("exist\n");
+       kprintf("  ADC #: ");
+       kprintf("%d\n", sc->adcn);
+       kprintf("  DAC #: ");
+       kprintf("%d\n", sc->dacn);
+       kprintf("  Multi-track converter type: ");
+       if ((sc->cfg->acl & PCIM_ACL_MTC) == 0) {
+               kprintf("AC'97(SDATA_OUT:");
+               if (sc->cfg->acl & PCIM_ACL_OMODE)
+                       kprintf("packed");
+               else
+                       kprintf("split");
+               kprintf("|SDATA_IN:");
+               if (sc->cfg->acl & PCIM_ACL_IMODE)
+                       kprintf("packed");
+               else
+                       kprintf("split");
+               kprintf(")\n");
+       }
+       else {
+               kprintf("I2S(");
+               if (sc->cfg->i2s & PCIM_I2S_VOL)
+                       kprintf("with volume, ");
+               if (sc->cfg->i2s & PCIM_I2S_96KHZ)
+                       kprintf("96KHz support, ");
+               switch (sc->cfg->i2s & PCIM_I2S_RES) {
+               case PCIM_I2S_16BIT:
+                       kprintf("16bit resolution, ");
+                       break;
+               case PCIM_I2S_18BIT:
+                       kprintf("18bit resolution, ");
+                       break;
+               case PCIM_I2S_20BIT:
+                       kprintf("20bit resolution, ");
+                       break;
+               case PCIM_I2S_24BIT:
+                       kprintf("24bit resolution, ");
+                       break;
+               }
+               kprintf("ID#0x%x)\n", sc->cfg->i2s & PCIM_I2S_ID);
+       }
+       kprintf("  S/PDIF(IN/OUT): ");
+       if (sc->cfg->spdif & PCIM_SPDIF_IN)
+               kprintf("1/");
+       else
+               kprintf("0/");
+       if (sc->cfg->spdif & PCIM_SPDIF_OUT)
+               kprintf("1 ");
+       else
+               kprintf("0 ");
+       if (sc->cfg->spdif & (PCIM_SPDIF_IN | PCIM_SPDIF_OUT))
+               kprintf("ID# 0x%02x\n", (sc->cfg->spdif & PCIM_SPDIF_ID) >> 2);
+       kprintf("  GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n",
+           sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate);
+}
+
+static int
+envy24_init(struct sc_info *sc)
+{
+       u_int32_t data;
+#if(0)
+       int rtn;
+#endif
+       int i;
+       u_int32_t sv, sd;
+
+
+#if(0)
+       device_printf(sc->dev, "envy24_init()\n");
+#endif
+
+       /* reset chip */
+       envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_RESET | ENVY24_CCS_CTL_NATIVE, 1);
+       DELAY(200);
+       envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_NATIVE, 1);
+       DELAY(200);
+
+       /* legacy hardware disable */
+       data = pci_read_config(sc->dev, PCIR_LAC, 2);
+       data |= PCIM_LAC_DISABLE;
+       pci_write_config(sc->dev, PCIR_LAC, data, 2);
+
+       /* check system configuration */
+       sc->cfg = NULL;
+       for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
+               /* 1st: search configuration from table */
+               sv = pci_get_subvendor(sc->dev);
+               sd = pci_get_subdevice(sc->dev);
+               if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) {
+#if(0)
+                       device_printf(sc->dev, "Set configuration from table\n");
+#endif
+                       sc->cfg = &cfg_table[i];
+                       break;
+               }
+       }
+       if (sc->cfg == NULL) {
+               /* 2nd: read configuration from table */
+               sc->cfg = envy24_rom2cfg(sc);
+       }
+       sc->adcn = ((sc->cfg->scfg & PCIM_SCFG_ADC) >> 2) + 1;
+       sc->dacn = (sc->cfg->scfg & PCIM_SCFG_DAC) + 1;
+
+       if (1 /* bootverbose */) {
+               envy24_putcfg(sc);
+       }
+
+       /* set system configuration */
+       pci_write_config(sc->dev, PCIR_SCFG, sc->cfg->scfg, 1);
+       pci_write_config(sc->dev, PCIR_ACL, sc->cfg->acl, 1);
+       pci_write_config(sc->dev, PCIR_I2S, sc->cfg->i2s, 1);
+       pci_write_config(sc->dev, PCIR_SPDIF, sc->cfg->spdif, 1);
+       envy24_gpiosetmask(sc, sc->cfg->gpiomask);
+       envy24_gpiosetdir(sc, sc->cfg->gpiodir);
+       envy24_gpiowr(sc, sc->cfg->gpiostate);
+       for (i = 0; i < sc->adcn; i++) {
+               sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i);
+               sc->cfg->codec->init(sc->adc[i]);
+       }
+       for (i = 0; i < sc->dacn; i++) {
+               sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i);
+               sc->cfg->codec->init(sc->dac[i]);
+       }
+
+       /* initialize DMA buffer */
+#if(0)
+       device_printf(sc->dev, "envy24_init(): initialize DMA buffer\n");
+#endif
+       if (envy24_dmainit(sc))
+               return ENOSPC;
+
+       /* initialize status */
+       sc->run[0] = sc->run[1] = 0;
+       sc->intr[0] = sc->intr[1] = 0;
+       sc->speed = 0;
+       sc->caps[0].fmtlist = envy24_playfmt;
+       sc->caps[1].fmtlist = envy24_recfmt;
+
+       /* set channel router */
+       envy24_route(sc, ENVY24_ROUTE_DAC_1, ENVY24_ROUTE_CLASS_MIX, 0, 0);
+       envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_DMA, 0, 0);
+       /* envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_MIX, 0, 0); */
+
+       /* set macro interrupt mask */
+       data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1);
+       envy24_wrcs(sc, ENVY24_CCS_IMASK, data & ~ENVY24_CCS_IMASK_PMT, 1);
+       data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1);
+#if(0)
+       device_printf(sc->dev, "envy24_init(): CCS_IMASK-->0x%02x\n", data);
+#endif
+
+       return 0;
+}
+
+static int
+envy24_alloc_resource(struct sc_info *sc)
+{
+       /* allocate I/O port resource */
+       sc->csid = PCIR_CCS;
+       sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+           &sc->csid, 0, ~0, 1, RF_ACTIVE);
+       sc->ddmaid = PCIR_DDMA;
+       sc->ddma = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+           &sc->ddmaid, 0, ~0, 1, RF_ACTIVE);
+       sc->dsid = PCIR_DS;
+       sc->ds = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+           &sc->dsid, 0, ~0, 1, RF_ACTIVE);
+       sc->mtid = PCIR_MT;
+       sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+           &sc->mtid, 0, ~0, 1, RF_ACTIVE);
+       if (!sc->cs || !sc->ddma || !sc->ds || !sc->mt) {
+               device_printf(sc->dev, "unable to map IO port space\n");
+               return ENXIO;
+       }
+       sc->cst = rman_get_bustag(sc->cs);
+       sc->csh = rman_get_bushandle(sc->cs);
+       sc->ddmat = rman_get_bustag(sc->ddma);
+       sc->ddmah = rman_get_bushandle(sc->ddma);
+       sc->dst = rman_get_bustag(sc->ds);
+       sc->dsh = rman_get_bushandle(sc->ds);
+       sc->mtt = rman_get_bustag(sc->mt);
+       sc->mth = rman_get_bushandle(sc->mt);
+#if(0)
+       device_printf(sc->dev,
+           "IO port register values\nCCS: 0x%lx\nDDMA: 0x%lx\nDS: 0x%lx\nMT: 0x%lx\n",
+           pci_read_config(sc->dev, PCIR_CCS, 4),
+           pci_read_config(sc->dev, PCIR_DDMA, 4),
+           pci_read_config(sc->dev, PCIR_DS, 4),
+           pci_read_config(sc->dev, PCIR_MT, 4));
+#endif
+
+       /* allocate interupt resource */
+       sc->irqid = 0;
+       sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid,
+                                0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+       if (!sc->irq ||
+           snd_setup_intr(sc->dev, sc->irq, INTR_MPSAFE, envy24_intr, sc, &sc->ih)) {
+               device_printf(sc->dev, "unable to map interrupt\n");
+               return ENXIO;
+       }
+
+       /* allocate DMA resource */
+       if (bus_dma_tag_create(/*parent*/NULL,
+           /*alignment*/4,
+           /*boundary*/0,
+           /*lowaddr*/BUS_SPACE_MAXADDR_ENVY24,
+           /*highaddr*/BUS_SPACE_MAXADDR_ENVY24,
+           /*filter*/NULL, /*filterarg*/NULL,
+           /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24,
+           /*nsegments*/1, /*maxsegsz*/0x3ffff,
+           /*flags*/0, &sc->dmat) != 0) {
+               device_printf(sc->dev, "unable to create dma tag\n");
+               return ENXIO;
+       }
+
+       return 0;
+}
+
+static int
+envy24_pci_attach(device_t dev)
+{
+       u_int32_t               data;
+       struct sc_info          *sc;
+       char                    status[SND_STATUSLEN];
+       int                     err = 0;
+       int                     i;
+
+#if(0)
+       device_printf(dev, "envy24_pci_attach()\n");
+#endif
+       /* get sc_info data area */
+       if ((sc = kmalloc(sizeof(*sc), M_ENVY24, M_NOWAIT)) == NULL) {
+               device_printf(dev, "cannot allocate softc\n");
+               return ENXIO;
+       }
+
+       bzero(sc, sizeof(*sc));
+       sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_envy24 softc");
+       sc->dev = dev;
+
+       /* initialize PCI interface */
+       data = pci_read_config(dev, PCIR_COMMAND, 2);
+       data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN);
+       pci_write_config(dev, PCIR_COMMAND, data, 2);
+       data = pci_read_config(dev, PCIR_COMMAND, 2);
+
+       /* allocate resources */
+       err = envy24_alloc_resource(sc);
+       if (err) {
+               device_printf(dev, "unable to allocate system resources\n");
+               goto bad;
+       }
+
+       /* initialize card */
+       err = envy24_init(sc);
+       if (err) {
+               device_printf(dev, "unable to initialize the card\n");
+               goto bad;
+       }
+
+       /* set multi track mixer */
+       mixer_init(dev, &envy24mixer_class, sc);
+
+       /* set channel information */
+       err = pcm_register(dev, sc, 5, 2 + sc->adcn);
+       if (err)
+               goto bad;
+       sc->chnum = 0;
+       for (i = 0; i < 5; i++) {
+               pcm_addchan(dev, PCMDIR_PLAY, &envy24chan_class, sc);
+               sc->chnum++;
+       }
+       for (i = 0; i < 2 + sc->adcn; i++) {
+               pcm_addchan(dev, PCMDIR_REC, &envy24chan_class, sc);
+               sc->chnum++;
+       }
+
+       /* set status iformation */
+       ksnprintf(status, SND_STATUSLEN,
+           "at io 0x%lx:%ld,0x%lx:%ld,0x%lx:%ld,0x%lx:%ld irq %ld",
+           rman_get_start(sc->cs),
+           rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1,
+           rman_get_start(sc->ddma),
+           rman_get_end(sc->ddma) - rman_get_start(sc->ddma) + 1,
+           rman_get_start(sc->ds),
+           rman_get_end(sc->ds) - rman_get_start(sc->ds) + 1,
+           rman_get_start(sc->mt),
+           rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
+           rman_get_start(sc->irq));
+       pcm_setstatus(dev, status);
+
+       return 0;
+
+bad:
+       if (sc->ih)
+               bus_teardown_intr(dev, sc->irq, sc->ih);
+       if (sc->irq)
+               bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+       envy24_dmafree(sc);
+       if (sc->dmat)
+               bus_dma_tag_destroy(sc->dmat);
+       if (sc->cfg->codec->destroy != NULL) {
+                for (i = 0; i < sc->adcn; i++)
+                        sc->cfg->codec->destroy(sc->adc[i]);
+                for (i = 0; i < sc->dacn; i++)
+                        sc->cfg->codec->destroy(sc->dac[i]);
+        }
+       envy24_cfgfree(sc->cfg);
+       if (sc->cs)
+               bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
+       if (sc->ddma)
+               bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma);
+       if (sc->ds)
+               bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds);
+       if (sc->mt)
+               bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
+       if (sc->lock)
+               snd_mtxfree(sc->lock);
+       kfree(sc, M_ENVY24);
+       return err;
+}
+
+static int
+envy24_pci_detach(device_t dev)
+{
+       struct sc_info *sc;
+       int r;
+       int i;
+
+#if(0)
+       device_printf(dev, "envy24_pci_detach()\n");
+#endif
+       sc = pcm_getdevinfo(dev);
+       if (sc == NULL)
+               return 0;
+       r = pcm_unregister(dev);
+       if (r)
+               return r;
+
+       envy24_dmafree(sc);
+       if (sc->cfg->codec->destroy != NULL) {
+               for (i = 0; i < sc->adcn; i++)
+                       sc->cfg->codec->destroy(sc->adc[i]);
+               for (i = 0; i < sc->dacn; i++)
+                       sc->cfg->codec->destroy(sc->dac[i]);
+       }
+       envy24_cfgfree(sc->cfg);
+       bus_dma_tag_destroy(sc->dmat);
+       bus_teardown_intr(dev, sc->irq, sc->ih);
+       bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+       bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
+       bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma);
+       bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds);
+       bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
+       snd_mtxfree(sc->lock);
+       kfree(sc, M_ENVY24);
+       return 0;
+}
+
+static device_method_t envy24_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         envy24_pci_probe),
+       DEVMETHOD(device_attach,        envy24_pci_attach),
+       DEVMETHOD(device_detach,        envy24_pci_detach),
+       { 0, 0 }
+};
+
+static driver_t envy24_driver = {
+       "pcm",
+       envy24_methods,
+#if __FreeBSD_version > 500000
+       PCM_SOFTC_SIZE,
+#else
+       sizeof(struct snddev_info),
+#endif
+};
+
+DRIVER_MODULE(snd_envy24, pci, envy24_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(snd_envy24, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_DEPEND(snd_envy24, snd_spicds, 1, 1, 1);
+MODULE_VERSION(snd_envy24, 1);
diff --git a/sys/dev/sound/pci/envy24.h b/sys/dev/sound/pci/envy24.h
new file mode 100644 (file)
index 0000000..eda43dd
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
+ * 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 THEPOSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/sound/pci/envy24.h,v 1.1.2.2 2007/06/11 19:33:27 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/envy24.h,v 1.1 2007/06/16 19:48:05 hasso Exp $
+ */
+
+
+/* -------------------------------------------------------------------- */
+
+/* PCI device ID */
+#define PCIV_ENVY24 0x1412
+#define PCID_ENVY24 0x1712
+
+/* PCI Registers */
+
+#define PCIR_CCS   0x10 /* Controller I/O Base Address */
+#define PCIR_DDMA  0x14 /* DDMA I/O Base Address */
+#define PCIR_DS    0x18 /* DMA Path Registers I/O Base Address */
+#define PCIR_MT    0x1c /* Professional Multi-Track I/O Base Address */
+
+#define PCIR_LAC   0x40 /* Legacy Audio Control */
+#define PCIM_LAC_DISABLE    0x8000 /* Legacy Audio Hardware disabled */
+#define PCIM_LAC_SBDMA0     0x0000 /* SB DMA Channel Select: 0 */
+#define PCIM_LAC_SBDMA1     0x0040 /* SB DMA Channel Select: 1 */
+#define PCIM_LAC_SBDMA3     0x00c0 /* SB DMA Channel Select: 3 */
+#define PCIM_LAC_IOADDR10   0x0020 /* I/O Address Alias Control */ 
+#define PCIM_LAC_MPU401     0x0008 /* MPU-401 I/O enable */
+#define PCIM_LAC_GAME       0x0004 /* Game Port enable (200h) */
+#define PCIM_LAC_FM         0x0002 /* FM I/O enable (AdLib 388h base) */
+#define PCIM_LAC_SB         0x0001 /* SB I/O enable */
+
+#define PCIR_LCC   0x42 /* Legacy Configuration Control */
+#define PCIM_LCC_VINT       0xff00 /* Interrupt vector to be snooped */
+#define PCIM_LCC_SVIDRW     0x0080 /* SVID read/write enable */
+#define PCIM_LCC_SNPSB      0x0040 /* snoop SB 22C/24Ch I/O write cycle */
+#define PCIM_LCC_SNPPIC     0x0020 /* snoop PIC I/O R/W cycle */
+#define PCIM_LCC_SNPPCI     0x0010 /* snoop PCI bus interrupt acknowledge cycle */
+#define PCIM_LCC_SBBASE     0x0008 /* SB base 240h(1)/220h(0) */
+#define PCIM_LCC_MPUBASE    0x0006 /* MPU-401 base 300h-330h */
+#define PCIM_LCC_LDMA       0x0001 /* Legacy DMA enable */
+
+#define PCIR_SCFG  0x60 /* System Configuration Register */
+#define PCIM_SCFG_XIN2      0xc0 /* XIN2 Clock Source Configuration */
+                                 /* 00: 22.5792MHz(44.1kHz*512) */
+                                 /* 01: 16.9344MHz(44.1kHz*384) */
+                                 /* 10: from external clock synthesizer chip */
+#define PCIM_SCFG_MPU       0x20 /* 1(0)/2(1) MPU-401 UART(s) */
+#define PCIM_SCFG_AC97      0x10 /* 0: AC'97 codec exist */
+                                 /* 1: AC'97 codec not exist */
+#define PCIM_SCFG_ADC       0x0c /* 1-4 stereo ADC connected */
+#define PCIM_SCFG_DAC       0x03 /* 1-4 stereo DAC connected */
+
+#define PCIR_ACL   0x61 /* AC-Link Configuration Register */
+#define PCIM_ACL_MTC        0x80 /* Multi-track converter type: 0:AC'97 1:I2S */
+#define PCIM_ACL_OMODE      0x02 /* AC 97 codec SDATA_OUT 0:split 1:packed */
+#define PCIM_ACL_IMODE      0x01 /* AC 97 codec SDATA_IN 0:split 1:packed */
+
+#define PCIR_I2S   0x62 /* I2S Converters Features Register */
+#define PCIM_I2S_VOL        0x80 /* I2S codec Volume and mute */
+#define PCIM_I2S_96KHZ      0x40 /* I2S converter 96kHz sampling rate support */
+#define PCIM_I2S_RES        0x30 /* Converter resolution */
+#define PCIM_I2S_16BIT      0x00 /* 16bit */
+#define PCIM_I2S_18BIT      0x10 /* 18bit */
+#define PCIM_I2S_20BIT      0x20 /* 20bit */
+#define PCIM_I2S_24BIT      0x30 /* 24bit */
+#define PCIM_I2S_ID         0x0f /* Other I2S IDs */
+
+#define PCIR_SPDIF 0x63 /* S/PDIF Configuration Register */
+#define PCIM_SPDIF_ID       0xfc /* S/PDIF chip ID */
+#define PCIM_SPDIF_IN       0x02 /* S/PDIF Stereo In is present */
+#define PCIM_SPDIF_OUT      0x01 /* S/PDIF Stereo Out is present */
+
+#define PCIR_POWER_STAT     0x84 /* Power Management Control and Status */
+
+/* Controller Registers */
+
+#define ENVY24_CCS_CTL      0x00 /* Control/Status Register */
+#define ENVY24_CCS_CTL_RESET   0x80 /* Entire Chip soft reset */
+#define ENVY24_CCS_CTL_DMAINT  0x40 /* DS DMA Channel-C interrupt */
+#define ENVY24_CCS_CTL_DOSVOL  0x10 /* set the DOS WT volume control */
+#define ENVY24_CCS_CTL_EDGE    0x08 /* SERR# edge (only one PCI clock width) */
+#define ENVY24_CCS_CTL_SBINT   0x02 /* SERR# assertion for SB interrupt */
+#define ENVY24_CCS_CTL_NATIVE  0x01 /* Mode select: 0:SB mode 1:native mode */
+
+#define ENVY24_CCS_IMASK    0x01 /* Interrupt Mask Register */
+#define ENVY24_CCS_IMASK_PMIDI 0x80 /* Primary MIDI */
+#define ENVY24_CCS_IMASK_TIMER 0x40 /* Timer */
+#define ENVY24_CCS_IMASK_SMIDI 0x20 /* Secondary MIDI */
+#define ENVY24_CCS_IMASK_PMT   0x10 /* Professional Multi-track */
+#define ENVY24_CCS_IMASK_FM    0x08 /* FM/MIDI trapping */
+#define ENVY24_CCS_IMASK_PDMA  0x04 /* Playback DS DMA */
+#define ENVY24_CCS_IMASK_RDMA  0x02 /* Consumer record DMA */
+#define ENVY24_CCS_IMASK_SB    0x01 /* Consumer/SB mode playback */
+
+#define ENVY24_CCS_ISTAT    0x02 /* Interrupt Status Register */
+#define ENVY24_CCS_ISTAT_PMIDI 0x80 /* Primary MIDI */
+#define ENVY24_CCS_ISTAT_TIMER 0x40 /* Timer */
+#define ENVY24_CCS_ISTAT_SMIDI 0x20 /* Secondary MIDI */
+#define ENVY24_CCS_ISTAT_PMT   0x10 /* Professional Multi-track */
+#define ENVY24_CCS_ISTAT_FM    0x08 /* FM/MIDI trapping */
+#define ENVY24_CCS_ISTAT_PDMA  0x04 /* Playback DS DMA */
+#define ENVY24_CCS_ISTAT_RDMA  0x02 /* Consumer record DMA */
+#define ENVY24_CCS_ISTAT_SB    0x01 /* Consumer/SB mode playback */
+
+#define ENVY24_CCS_INDEX    0x03 /* Envy24 Index Register */
+#define ENVY24_CCS_DATA     0x04 /* Envy24 Data Register */
+
+#define ENVY24_CCS_NMI1     0x05 /* NMI Status Register 1 */
+#define ENVY24_CCS_NMI1_PCI    0x80 /* PCI I/O read/write cycle */
+#define ENVY24_CCS_NMI1_SB     0x40 /* SB 22C/24C write */
+#define ENVY24_CCS_NMI1_SBDMA  0x10 /* SB interrupt (SB DMA/SB F2 command) */
+#define ENVY24_CCS_NMI1_DSDMA  0x08 /* DS channel C DMA interrupt */
+#define ENVY24_CCS_NMI1_MIDI   0x04 /* MIDI 330h or [PCI_10]h+Ch write */
+#define ENVY24_CCS_NMI1_FM     0x01 /* FM data register write */
+
+#define ENVY24_CCS_NMIDAT   0x06 /* NMI Data Register */
+#define ENVY24_CCS_NMIIDX   0x07 /* NMI Index Register */
+#define ENVY24_CCS_AC97IDX  0x08 /* Consumer AC'97 Index Register */
+
+#define ENVY24_CCS_AC97CMD  0x09 /* Consumer AC'97 Command/Status Register */
+#define ENVY24_CCS_AC97CMD_COLD    0x80 /* Cold reset */
+#define ENVY24_CCS_AC97CMD_WARM    0x40 /* Warm reset */
+#define ENVY24_CCS_AC97CMD_WRCODEC 0x20 /* Write to AC'97 codec registers */
+#define ENVY24_CCS_AC97CMD_RDCODEC 0x10 /* Read from AC'97 codec registers */
+#define ENVY24_CCS_AC97CMD_READY   0x08 /* AC'97 codec ready status bit */
+#define ENVY24_CCS_AC97CMD_PVSR    0x02 /* VSR for Playback */
+#define ENVY24_CCS_AC97CMD_RVSR    0x01 /* VSR for Record */
+
+#define ENVY24_CCS_AC97DAT  0x0a /* Consumer AC'97 Data Port Register */
+#define ENVY24_CCS_PMIDIDAT 0x0c /* Primary MIDI UART Data Register */
+#define ENVY24_CCS_PMIDICMD 0x0d /* Primary MIDI UART Command/Status Register */
+
+#define ENVY24_CCS_NMI2     0x0e /* NMI Status Register 2 */
+#define ENVY24_CCS_NMI2_FMBANK 0x30 /* FM bank indicator */
+#define ENVY24_CCS_NMI2_FM0    0x10 /* FM bank 0 (388h/220h/228h) */
+#define ENVY24_CCS_NMI2_FM1    0x20 /* FM bank 1 (38ah/222h) */
+#define ENVY24_CCS_NMI2_PICIO  0x0f /* PIC I/O cycle */
+#define ENVY24_CCS_NMI2_PIC20W 0x01 /* 20h write */
+#define ENVY24_CCS_NMI2_PICA0W 0x02 /* a0h write */
+#define ENVY24_CCS_NMI2_PIC21W 0x05 /* 21h write */
+#define ENVY24_CCS_NMI2_PICA1W 0x06 /* a1h write */
+#define ENVY24_CCS_NMI2_PIC20R 0x09 /* 20h read */
+#define ENVY24_CCS_NMI2_PICA0R 0x0a /* a0h read */
+#define ENVY24_CCS_NMI2_PIC21R 0x0d /* 21h read */
+#define ENVY24_CCS_NMI2_PICA1R 0x0e /* a1h read */
+
+#define ENVY24_CCS_JOY      0x0f /* Game port register */
+
+#define ENVY24_CCS_I2CDEV   0x10 /* I2C Port Device Address Register */
+#define ENVY24_CCS_I2CDEV_ADDR 0xfe /* I2C device address */
+#define ENVY24_CCS_I2CDEV_ROM  0xa0 /* reserved for the external I2C E2PROM */
+#define ENVY24_CCS_I2CDEV_WR   0x01 /* write */
+#define ENVY24_CCS_I2CDEV_RD   0x00 /* read */
+
+#define ENVY24_CCS_I2CADDR  0x11 /* I2C Port Byte Address Register */
+#define ENVY24_CCS_I2CDATA  0x12 /* I2C Port Read/Write Data Register */
+
+#define ENVY24_CCS_I2CSTAT  0x13 /* I2C Port Control and Status Register */
+#define ENVY24_CCS_I2CSTAT_ROM 0x80 /* external E2PROM exists */
+#define ENVY24_CCS_I2CSTAT_BSY 0x01 /* I2C port read/write status busy */
+
+#define ENVY24_CCS_CDMABASE 0x14 /* Consumer Record DMA Current/Base Address Register */
+#define ENVY24_CCS_CDMACNT  0x18 /* Consumer Record DMA Current/Base Count Register */
+#define ENVY24_CCS_SERR     0x1b /* PCI Configuration SERR# Shadow Register */
+#define ENVY24_CCS_SMIDIDAT 0x1c /* Secondary MIDI UART Data Register */
+#define ENVY24_CCS_SMIDICMD 0x1d /* Secondary MIDI UART Command/Status Register */
+
+#define ENVY24_CCS_TIMER    0x1e /* Timer Register */
+#define ENVY24_CCS_TIMER_EN    0x8000 /* Timer count enable */
+#define ENVY24_CCS_TIMER_MASK  0x7fff /* Timer counter mask */
+
+/* Controller Indexed Registers */
+
+#define ENVY24_CCI_PTCHIGH  0x00 /* Playback Terminal Count Register (High Byte) */
+#define ENVY24_CCI_PTCLOW   0x01 /* Playback Terminal Count Register (Low Byte) */
+
+#define ENVY24_CCI_PCTL     0x02 /* Playback Control Register */
+#define ENVY24_CCI_PCTL_TURBO  0x80 /* 4x up sampling in the host by software */
+#define ENVY24_CCI_PCTL_U8     0x10 /* 8 bits unsigned */
+#define ENVY24_CCI_PCTL_S16    0x00 /* 16 bits signed */
+#define ENVY24_CCI_PCTL_STEREO 0x08 /* stereo */
+#define ENVY24_CCI_PCTL_MONO   0x00 /* mono */
+#define ENVY24_CCI_PCTL_FLUSH  0x04 /* FIFO flush (sticky bit. Requires toggling) */
+#define ENVY24_CCI_PCTL_PAUSE  0x02 /* Pause */
+#define ENVY24_CCI_PCTL_ENABLE 0x01 /* Playback enable */
+
+#define ENVY24_CCI_PLVOL    0x03 /* Playback Left Volume/Pan Register */
+#define ENVY24_CCI_PRVOL    0x04 /* Playback Right Volume/Pan Register */
+#define ENVY24_CCI_VOL_MASK    0x3f /* Volume value mask */
+
+#define ENVY24_CCI_SOFTVOL  0x05 /* Soft Volume/Mute Control Register */
+#define ENVY24_CCI_PSRLOW   0x06 /* Playback Sampling Rate Register (Low Byte) */
+#define ENVY24_CCI_PSRMID   0x07 /* Playback Sampling Rate Register (Middle Byte) */
+#define ENVY24_CCI_PSRHIGH  0x08 /* Playback Sampling Rate Register (High Byte) */
+#define ENVY24_CCI_RTCHIGH  0x10 /* Record Terminal Count Register (High Byte) */
+#define ENVY24_CCI_RTCLOW   0x11 /* Record Terminal Count Register (Low Byte) */
+
+#define ENVY24_CCI_RCTL     0x12 /* Record Control Register */
+#define ENVY24_CCI_RCTL_DRTN   0x80 /* Digital return enable */
+#define ENVY24_CCI_RCTL_U8     0x04 /* 8 bits unsigned */
+#define ENVY24_CCI_RCTL_S16    0x00 /* 16 bits signed */
+#define ENVY24_CCI_RCTL_STEREO 0x00 /* stereo */
+#define ENVY24_CCI_RCTL_MONO   0x02 /* mono */
+#define ENVY24_CCI_RCTL_ENABLE 0x01 /* Record enable */
+
+#define ENVY24_CCI_GPIODAT  0x20 /* GPIO Data Register */
+#define ENVY24_CCI_GPIOMASK 0x21 /* GPIO Write Mask Register */
+
+#define ENVY24_CCI_GPIOCTL  0x22 /* GPIO Direction Control Register */
+#define ENVY24_CCI_GPIO_OUT    1 /* output */
+#define ENVY24_CCI_GPIO_IN     0 /* input */
+
+#define ENVY24_CCI_CPDWN   0x30 /* Consumer Section Power Down Register */
+#define ENVY24_CCI_CPDWN_XTAL  0x80 /* Crystal clock generation power down for XTAL_1 */
+#define ENVY24_CCI_CPDWN_GAME  0x40 /* Game port analog power down */
+#define ENVY24_CCI_CPDWN_I2C   0x10 /* I2C port clock */
+#define ENVY24_CCI_CPDWN_MIDI  0x08 /* MIDI clock */
+#define ENVY24_CCI_CPDWN_AC97  0x04 /* AC'97 clock */
+#define ENVY24_CCI_CPDWN_DS    0x02 /* DS Block clock */
+#define ENVY24_CCI_CPDWN_PCI   0x01 /* PCI clock for SB, DMA controller */
+
+#define ENVY24_CCI_MTPDWN  0x31 /* Multi-Track Section Power Down Register */
+#define ENVY24_CCI_MTPDWN_XTAL 0x80 /* Crystal clock generation power down for XTAL_2 */
+#define ENVY24_CCI_MTPDWN_SPDIF 0x04 /* S/PDIF clock */
+#define ENVY24_CCI_MTPDWN_MIX  0x02 /* Professional digital mixer clock */
+#define ENVY24_CCI_MTPDWN_I2S  0x01 /* Multi-track I2S serial interface clock */
+
+/* DDMA Registers */
+
+#define ENVY24_DDMA_ADDR0  0x00 /* DMA Base and Current Address bit 0-7 */
+#define ENVY24_DDMA_ADDR8  0x01 /* DMA Base and Current Address bit 8-15 */
+#define ENVY24_DDMA_ADDR16 0x02 /* DMA Base and Current Address bit 16-23 */
+#define ENVY24_DDMA_ADDR24 0x03 /* DMA Base and Current Address bit 24-31 */
+#define ENVY24_DDMA_CNT0   0x04 /* DMA Base and Current Count 0-7 */
+#define ENVY24_DDMA_CNT8   0x05 /* DMA Base and Current Count 8-15 */
+#define ENVY24_DDMA_CNT16  0x06 /* (not supported) */
+#define ENVY24_DDMA_CMD    0x08 /* Status and Command */
+#define ENVY24_DDMA_MODE   0x0b /* Mode */
+#define ENVY24_DDMA_RESET  0x0c /* Master reset */
+#define ENVY24_DDMA_CHAN   0x0f /* Channel Mask */
+
+/* Consumer Section DMA Channel Registers */
+
+#define ENVY24_CS_INTMASK  0x00 /* DirectSound DMA Interrupt Mask Register */
+#define ENVY24_CS_INTSTAT  0x02 /* DirectSound DMA Interrupt Status Register */
+#define ENVY24_CS_CHDAT    0x04 /* Channel Data register */
+
+#define ENVY24_CS_CHIDX    0x08 /* Channel Index Register */
+#define ENVY24_CS_CHIDX_NUM   0xf0 /* Channel number */
+#define ENVY24_CS_CHIDX_ADDR0 0x00 /* Buffer_0 DMA base address */
+#define ENVY24_CS_CHIDX_CNT0  0x01 /* Buffer_0 DMA base count */
+#define ENVY24_CS_CHIDX_ADDR1 0x02 /* Buffer_1 DMA base address */
+#define ENVY24_CS_CHIDX_CNT1  0x03 /* Buffer_1 DMA base count */
+#define ENVY24_CS_CHIDX_CTL   0x04 /* Channel Control and Status register */
+#define ENVY24_CS_CHIDX_RATE  0x05 /* Channel Sampling Rate */
+#define ENVY24_CS_CHIDX_VOL   0x06 /* Channel left and right volume/pan control */
+/* Channel Control and Status Register at Index 4h */
+#define ENVY24_CS_CTL_BUF     0x80 /* indicating that the current active buffer */
+#define ENVY24_CS_CTL_AUTO1   0x40 /* Buffer_1 auto init. enable */
+#define ENVY24_CS_CTL_AUTO0   0x20 /* Buffer_0 auto init. enable */
+#define ENVY24_CS_CTL_FLUSH   0x10 /* Flush FIFO */
+#define ENVY24_CS_CTL_STEREO  0x08 /* stereo(or mono) */
+#define ENVY24_CS_CTL_U8      0x04 /* 8-bit unsigned(or 16-bit signed) */
+#define ENVY24_CS_CTL_PAUSE   0x02 /* DMA request 1:pause */
+#define ENVY24_CS_CTL_START   0x01 /* DMA request 1: start, 0:stop */
+/* Consumer mode Left/Right Volume Register at Index 06h */
+#define ENVY24_CS_VOL_RIGHT   0x3f00
+#define ENVY24_CS_VOL_LEFT    0x003f
+
+/* Professional Multi-Track Control Registers */
+
+#define ENVY24_MT_INT      0x00 /* DMA Interrupt Mask and Status Register */
+#define ENVY24_MT_INT_RMASK   0x80 /* Multi-track record interrupt mask */
+#define ENVY24_MT_INT_PMASK   0x40 /* Multi-track playback interrupt mask */
+#define ENVY24_MT_INT_RSTAT   0x02 /* Multi-track record interrupt status */
+#define ENVY24_MT_INT_PSTAT   0x01 /* Multi-track playback interrupt status */
+
+#define ENVY24_MT_RATE     0x01 /* Sampling Rate Select Register */
+#define ENVY24_MT_RATE_SPDIF  0x10 /* S/PDIF input clock as the master */
+#define ENVY24_MT_RATE_48000  0x00
+#define ENVY24_MT_RATE_24000  0x01
+#define ENVY24_MT_RATE_12000  0x02
+#define ENVY24_MT_RATE_9600   0x03
+#define ENVY24_MT_RATE_32000  0x04
+#define ENVY24_MT_RATE_16000  0x05
+#define ENVY24_MT_RATE_8000   0x06
+#define ENVY24_MT_RATE_96000  0x07
+#define ENVY24_MT_RATE_64000  0x0f
+#define ENVY24_MT_RATE_44100  0x08
+#define ENVY24_MT_RATE_22050  0x09
+#define ENVY24_MT_RATE_11025  0x0a
+#define ENVY24_MT_RATE_88200  0x0b
+#define ENVY24_MT_RATE_MASK   0x0f
+
+#define ENVY24_MT_I2S      0x02 /* I2S Data Format Register */
+#define ENVY24_MT_I2S_MLR128  0x08 /* MCLK/LRCLK ratio 128x(or 256x) */
+#define ENVY24_MT_I2S_SLR48   0x04 /* SCLK/LRCLK ratio 48bpf(or 64bpf) */
+#define ENVY24_MT_I2S_FORM    0x00 /* I2S data format */
+
+#define ENVY24_MT_AC97IDX  0x04 /* Index Register for AC'97 Codecs */
+
+#define ENVY24_MT_AC97CMD  0x05 /* Command and Status Register for AC'97 Codecs */
+#define ENVY24_MT_AC97CMD_CLD 0x80 /* Cold reset */
+#define ENVY24_MT_AC97CMD_WRM 0x40 /* Warm reset */
+#define ENVY24_MT_AC97CMD_WR  0x20 /* write to AC'97 codec register */
+#define ENVY24_MT_AC97CMD_RD  0x10 /* read AC'97 CODEC register */
+#define ENVY24_MT_AC97CMD_RDY 0x08 /* AC'97 codec ready status bit */
+#define ENVY24_MT_AC97CMD_ID  0x03 /* ID(0-3) for external AC 97 registers */
+
+#define ENVY24_MT_AC97DLO  0x06 /* AC'97 codec register data low byte */
+#define ENVY24_MT_AC97DHI  0x07 /* AC'97 codec register data high byte */
+#define ENVY24_MT_PADDR    0x10 /* Playback DMA Current/Base Address Register */
+#define ENVY24_MT_PCNT     0x14 /* Playback DMA Current/Base Count Register */
+#define ENVY24_MT_PTERM    0x16 /* Playback Current/Base Terminal Count Register */
+#define ENVY24_MT_PCTL     0x18 /* Playback and Record Control Register */
+#define ENVY24_MT_PCTL_RSTART 0x04 /* 1: Record start; 0: Record stop */
+#define ENVY24_MT_PCTL_PAUSE  0x02 /* 1: Pause; 0: Resume */
+#define ENVY24_MT_PCTL_PSTART 0x01 /* 1: Playback start; 0: Playback stop */
+
+#define ENVY24_MT_RADDR    0x20 /* Record DMA Current/Base Address Register */
+#define ENVY24_MT_RCNT     0x24 /* Record DMA Current/Base Count Register */
+#define ENVY24_MT_RTERM    0x26 /* Record Current/Base Terminal Count Register */
+#define ENVY24_MT_RCTL     0x28 /* Record Control Register */
+#define ENVY24_MT_RCTL_RSTART 0x01 /* 1: Record start; 0: Record stop */
+
+#define ENVY24_MT_PSDOUT   0x30 /* Routing Control Register for Data to PSDOUT[0:3] */
+#define ENVY24_MT_SPDOUT   0x32 /* Routing Control Register for SPDOUT */
+#define ENVY24_MT_RECORD   0x34 /* Captured (Recorded) data Routing Selection Register */
+
+#define BUS_SPACE_MAXADDR_ENVY24 0x0fffffff /* Address space beyond 256MB is not supported */
+#define BUS_SPACE_MAXSIZE_ENVY24 0x3fffc /* 64k x 4byte(1dword) */
+
+#define ENVY24_MT_VOLUME   0x38 /* Left/Right Volume Control Data Register */
+#define ENVY24_MT_VOLUME_L    0x007f /* Left Volume Mask */
+#define ENVY24_MT_VOLUME_R    0x7f00 /* Right Volume Mask */
+
+#define ENVY24_MT_VOLIDX   0x3a /* Volume Control Stream Index Register */
+#define ENVY24_MT_VOLRATE  0x3b /* Volume Control Rate Register */
+#define ENVY24_MT_MONAC97  0x3c /* Digital Mixer Monitor Routing Control Register */
+#define ENVY24_MT_PEAKIDX  0x3e /* Peak Meter Index Register */
+#define ENVY24_MT_PEAKDAT  0x3f /* Peak Meter Data Register */
+
+/* -------------------------------------------------------------------- */
+
+/* ENVY24 mixer channel defines */
+/*
+  ENVY24 mixer has original line matrix. So, general mixer command is not
+  able to use for this. If system has consumer AC'97 output, AC'97 line is
+  used as master mixer, and it is able to control.
+*/
+#define ENVY24_CHAN_NUM  11 /* Play * 5 + Record * 5 + Mix * 1 */
+
+#define ENVY24_CHAN_PLAY_DAC1  0
+#define ENVY24_CHAN_PLAY_DAC2  1
+#define ENVY24_CHAN_PLAY_DAC3  2
+#define ENVY24_CHAN_PLAY_DAC4  3
+#define ENVY24_CHAN_PLAY_SPDIF 4
+#define ENVY24_CHAN_REC_ADC1   5
+#define ENVY24_CHAN_REC_ADC2   6
+#define ENVY24_CHAN_REC_ADC3   7
+#define ENVY24_CHAN_REC_ADC4   8
+#define ENVY24_CHAN_REC_SPDIF  9
+#define ENVY24_CHAN_REC_MIX   10
+
+#define ENVY24_MIX_MASK     0x3ff
+#define ENVY24_MIX_REC_MASK 0x3e0
+
+/* volume value constants */
+#define ENVY24_VOL_MAX    0 /* 0db(negate) */
+#define ENVY24_VOL_MIN   96 /* -144db(negate) */
+#define ENVY24_VOL_MUTE 127 /* mute */
+
+/* -------------------------------------------------------------------- */
+
+/* ENVY24 routing control defines */
+/*
+  ENVY24 has input->output data routing matrix switch. But original ENVY24
+  matrix control is so complex. So, in this driver, matrix control is
+  defined 4 parameters.
+
+  1: output DAC channels (include S/PDIF output)
+  2: output data classes
+     a. direct output from DMA
+     b. MIXER output which mixed the DMA outputs and input channels
+        (NOTICE: this class is able to set only DAC-1 and S/PDIF output)
+     c. direct input from ADC
+     d. direct input from S/PDIF
+  3: input ADC channel selection(when 2:c. is selected)
+  4: left/right reverse
+
+  These parameters matrix is bit reduced from original ENVY24 matrix
+  pattern(ex. route different ADC input to one DAC). But almost case
+  this is enough to use.
+*/
+#define ENVY24_ROUTE_DAC_1       0
+#define ENVY24_ROUTE_DAC_2       1
+#define ENVY24_ROUTE_DAC_3       2
+#define ENVY24_ROUTE_DAC_4       3
+#define ENVY24_ROUTE_DAC_SPDIF   4
+
+#define ENVY24_ROUTE_CLASS_DMA   0
+#define ENVY24_ROUTE_CLASS_MIX   1
+#define ENVY24_ROUTE_CLASS_ADC   2
+#define ENVY24_ROUTE_CLASS_SPDIF 3
+
+#define ENVY24_ROUTE_ADC_1       0
+#define ENVY24_ROUTE_ADC_2       1
+#define ENVY24_ROUTE_ADC_3       2
+#define ENVY24_ROUTE_ADC_4       3
+
+#define ENVY24_ROUTE_NORMAL      0
+#define ENVY24_ROUTE_REVERSE     1
+#define ENVY24_ROUTE_LEFT        0
+#define ENVY24_ROUTE_RIGHT       1
+
+/* -------------------------------------------------------------------- */
+
+/*
+  These map values are refferd from ALSA sound driver.
+*/
+/* ENVY24 configuration E2PROM map */
+#define ENVY24_E2PROM_SUBVENDOR  0x00
+#define ENVY24_E2PROM_SUBDEVICE  0x02
+#define ENVY24_E2PROM_SIZE       0x04
+#define ENVY24_E2PROM_VERSION    0x05
+#define ENVY24_E2PROM_SCFG       0x06
+#define ENVY24_E2PROM_ACL        0x07
+#define ENVY24_E2PROM_I2S        0x08
+#define ENVY24_E2PROM_SPDIF      0x09
+#define ENVY24_E2PROM_GPIOMASK   0x0a
+#define ENVY24_E2PROM_GPIOSTATE  0x0b
+#define ENVY24_E2PROM_GPIODIR    0x0c
+#define ENVY24_E2PROM_AC97MAIN   0x0d
+#define ENVY24_E2PROM_AC97PCM    0x0f
+#define ENVY24_E2PROM_AC97REC    0x11
+#define ENVY24_E2PROM_AC97RECSRC 0x13
+#define ENVY24_E2PROM_DACID      0x14
+#define ENVY24_E2PROM_ADCID      0x18
+#define ENVY24_E2PROM_EXTRA      0x1c
+
+/* GPIO connect map of M-Audio Delta series */
+#define ENVY24_GPIO_CS84X4_PRO    0x01
+#define ENVY24_GPIO_CS8414_STATUS 0x02
+#define ENVY24_GPIO_CS84X4_CLK    0x04
+#define ENVY24_GPIO_CS84X4_DATA   0x08
+#define ENVY24_GPIO_AK4524_CDTI   0x10 /* this value is duplicated to input select */
+#define ENVY24_GPIO_AK4524_CCLK   0x20
+#define ENVY24_GPIO_AK4524_CS0    0x40
+#define ENVY24_GPIO_AK4524_CS1    0x80
+
+/* M-Audio Delta series S/PDIF(CS84[01]4) control pin values */
+#define ENVY24_CS8404_PRO_RATE    0x18
+#define ENVY24_CS8404_PRO_RATE32  0x00
+#define ENVY24_CS8404_PRO_RATE441 0x10
+#define ENVY24_CS8404_PRO_RATE48  0x08
+
+/* M-Audio Delta series parameter */
+#define ENVY24_DELTA_AK4524_CIF 0
+
+#define I2C_DELAY 1000
+
+/* PCA9554 registers */
+#define PCA9554_I2CDEV          0x40    /* I2C device address */
+#define PCA9554_IN              0x00    /* input port */
+#define PCA9554_OUT             0x01    /* output port */
+#define PCA9554_INVERT          0x02    /* polarity invert */
+#define PCA9554_DIR             0x03    /* port directions */
+
+/* PCF8574 registers */
+#define PCF8574_I2CDEV_DAC      0x48
+#define PCF8574_SENSE_MASK      0x40
+
+/* end of file */
diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c
new file mode 100644 (file)
index 0000000..2e69b89
--- /dev/null
@@ -0,0 +1,2604 @@
+/*
+ * Copyright (c) 2006 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
+ * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/sound/pci/envy24ht.c,v 1.11.2.2 2007/06/11 19:33:27 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/envy24ht.c,v 1.1 2007/06/16 19:48:05 hasso Exp $
+ */
+
+/*
+ * Konstantin Dimitrov's thanks list:
+ *
+ * A huge thanks goes to Spas Filipov for his friendship, support and his
+ * generous gift - an 'Audiotrak Prodigy HD2' audio card! I also want to
+ * thank Keiichi Iwasaki and his parents, because they helped Spas to get
+ * the card from Japan! Having hardware sample of Prodigy HD2 made adding
+ * support for that great card very easy and real fun and pleasure.
+ *
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+#include <dev/sound/pci/spicds.h>
+#include <dev/sound/pci/envy24ht.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+#include "mixer_if.h"
+
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/envy24ht.c,v 1.1 2007/06/16 19:48:05 hasso Exp $");
+
+MALLOC_DEFINE(M_ENVY24HT, "envy24ht", "envy24ht audio");
+
+/* -------------------------------------------------------------------- */
+
+struct sc_info;
+
+#define ENVY24HT_PLAY_CHNUM 8
+#define ENVY24HT_REC_CHNUM 2
+#define ENVY24HT_PLAY_BUFUNIT (4 /* byte/sample */ * 8 /* channel */)
+#define ENVY24HT_REC_BUFUNIT  (4 /* byte/sample */ * 2 /* channel */)
+#define ENVY24HT_SAMPLE_NUM   4096
+
+#define ENVY24HT_TIMEOUT 1000
+
+#define ENVY24HT_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE)
+
+#define ENVY24HT_NAMELEN 32
+
+#define abs(i) (i < 0 ? -i : i)
+
+struct envy24ht_sample {
+        volatile u_int32_t buffer;
+};
+
+typedef struct envy24ht_sample sample32_t;
+
+/* channel registers */
+struct sc_chinfo {
+       struct snd_dbuf         *buffer;
+       struct pcm_channel      *channel;
+       struct sc_info          *parent;
+       int                     dir;
+       unsigned                num; /* hw channel number */
+
+       /* channel information */
+       u_int32_t               format;
+       u_int32_t               speed;
+       u_int32_t               blk; /* hw block size(dword) */
+
+       /* format conversion structure */
+       u_int8_t                *data;
+       unsigned int            size; /* data buffer size(byte) */
+       int                     unit; /* sample size(byte) */
+       unsigned int            offset; /* samples number offset */
+       void                    (*emldma)(struct sc_chinfo *);
+
+       /* flags */
+       int                     run;
+};
+
+/* codec interface entrys */
+struct codec_entry {
+       void *(*create)(device_t dev, void *devinfo, int dir, int num);
+       void (*destroy)(void *codec);
+       void (*init)(void *codec);
+       void (*reinit)(void *codec);
+       void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right);
+       void (*setrate)(void *codec, int which, int rate);
+};
+
+/* system configuration information */
+struct cfg_info {
+       char *name;
+       u_int16_t subvendor, subdevice;
+       u_int8_t scfg, acl, i2s, spdif;
+       u_int32_t gpiomask, gpiostate, gpiodir;
+       u_int32_t cdti, cclk, cs;
+       u_int8_t cif, type, free;
+       struct codec_entry *codec;
+};
+
+/* device private data */
+struct sc_info {
+       device_t        dev;
+       struct spinlock *lock;
+
+       /* Control/Status registor */
+       struct resource *cs;
+       int             csid;
+       bus_space_tag_t cst;
+       bus_space_handle_t csh;
+       /* MultiTrack registor */
+       struct resource *mt;
+       int             mtid;
+       bus_space_tag_t mtt;
+       bus_space_handle_t mth;
+       /* DMA tag */
+       bus_dma_tag_t dmat;
+       /* IRQ resource */
+       struct resource *irq;
+       int             irqid;
+       void            *ih;
+
+       /* system configuration data */
+       struct cfg_info *cfg;
+
+       /* ADC/DAC number and info */
+       int             adcn, dacn;
+       void            *adc[4], *dac[4];
+
+       /* mixer control data */
+       u_int32_t       src;
+       u_int8_t        left[ENVY24HT_CHAN_NUM];
+       u_int8_t        right[ENVY24HT_CHAN_NUM];
+
+       /* Play/Record DMA fifo */
+       sample32_t      *pbuf;
+       sample32_t      *rbuf;
+       u_int32_t       psize, rsize; /* DMA buffer size(byte) */
+       u_int16_t       blk[2]; /* transfer check blocksize(dword) */
+       bus_dmamap_t    pmap, rmap;
+
+       /* current status */
+       u_int32_t       speed;
+       int             run[2];
+       u_int16_t       intr[2];
+       struct pcmchan_caps     caps[2];
+
+       /* channel info table */
+       unsigned        chnum;
+       struct sc_chinfo chan[11];
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * prototypes
+ */
+
+/* DMA emulator */
+static void envy24ht_p8u(struct sc_chinfo *);
+static void envy24ht_p16sl(struct sc_chinfo *);
+static void envy24ht_p32sl(struct sc_chinfo *);
+static void envy24ht_r16sl(struct sc_chinfo *);
+static void envy24ht_r32sl(struct sc_chinfo *);
+
+/* channel interface */
+static void *envy24htchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
+static int envy24htchan_setformat(kobj_t, void *, u_int32_t);
+static int envy24htchan_setspeed(kobj_t, void *, u_int32_t);
+static int envy24htchan_setblocksize(kobj_t, void *, u_int32_t);
+static int envy24htchan_trigger(kobj_t, void *, int);
+static int envy24htchan_getptr(kobj_t, void *);
+static struct pcmchan_caps *envy24htchan_getcaps(kobj_t, void *);
+
+/* mixer interface */
+static int envy24htmixer_init(struct snd_mixer *);
+static int envy24htmixer_reinit(struct snd_mixer *);
+static int envy24htmixer_uninit(struct snd_mixer *);
+static int envy24htmixer_set(struct snd_mixer *, unsigned, unsigned, unsigned);
+static u_int32_t envy24htmixer_setrecsrc(struct snd_mixer *, u_int32_t);
+
+/* SPI codec access interface */
+static void *envy24ht_spi_create(device_t, void *, int, int);
+static void envy24ht_spi_destroy(void *);
+static void envy24ht_spi_init(void *);
+static void envy24ht_spi_reinit(void *);
+static void envy24ht_spi_setvolume(void *, int, unsigned int, unsigned int);
+
+/* -------------------------------------------------------------------- */
+
+/*
+  system constant tables
+*/
+
+/* API -> hardware channel map */
+static unsigned envy24ht_chanmap[ENVY24HT_CHAN_NUM] = {
+       ENVY24HT_CHAN_PLAY_DAC1,  /* 1 */
+       ENVY24HT_CHAN_PLAY_DAC2,  /* 2 */
+       ENVY24HT_CHAN_PLAY_DAC3,  /* 3 */
+       ENVY24HT_CHAN_PLAY_DAC4,  /* 4 */
+       ENVY24HT_CHAN_PLAY_SPDIF, /* 0 */
+       ENVY24HT_CHAN_REC_MIX,    /* 5 */
+       ENVY24HT_CHAN_REC_SPDIF,  /* 6 */
+       ENVY24HT_CHAN_REC_ADC1,   /* 7 */
+       ENVY24HT_CHAN_REC_ADC2,   /* 8 */
+       ENVY24HT_CHAN_REC_ADC3,   /* 9 */
+       ENVY24HT_CHAN_REC_ADC4,   /* 10 */
+};
+
+/* mixer -> API channel map. see above */
+static int envy24ht_mixmap[] = {
+       -1, /* Master output level. It is depend on codec support */
+       -1, /* Treble level of all output channels */
+       -1, /* Bass level of all output channels */
+       -1, /* Volume of synthesier input */
+       0,  /* Output level for the audio device */
+       -1, /* Output level for the PC speaker */
+       7,  /* line in jack */
+       -1, /* microphone jack */
+       -1, /* CD audio input */
+       -1, /* Recording monitor */
+       1,  /* alternative codec */
+       -1, /* global recording level */
+       -1, /* Input gain */
+       -1, /* Output gain */
+       8,  /* Input source 1 */
+       9,  /* Input source 2 */
+       10, /* Input source 3 */
+       6,  /* Digital (input) 1 */
+       -1, /* Digital (input) 2 */
+       -1, /* Digital (input) 3 */
+       -1, /* Phone input */
+       -1, /* Phone output */
+       -1, /* Video/TV (audio) in */
+       -1, /* Radio in */
+       -1, /* Monitor volume */
+};
+
+/* variable rate audio */
+static u_int32_t envy24ht_speed[] = {
+    192000, 176400, 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
+    12000, 11025, 9600, 8000, 0
+};
+
+/* known boards configuration */
+static struct codec_entry spi_codec = {
+       envy24ht_spi_create,
+       envy24ht_spi_destroy,
+       envy24ht_spi_init,
+       envy24ht_spi_reinit,
+       envy24ht_spi_setvolume,
+       NULL, /* setrate */
+};
+
+static struct cfg_info cfg_table[] = {
+       {
+               "Envy24HT audio (Terratec Aureon 7.1 Space)",
+               0x153b, 0x1145,
+               0x0b, 0x80, 0xfc, 0xc3,
+               0x21efff, 0x7fffff, 0x5e1000,
+               0x40000, 0x80000, 0x1000, 0x00, 0x02,
+               0,
+               &spi_codec,
+       },
+        {
+                "Envy24HT audio (Terratec Aureon 5.1 Sky)",
+                0x153b, 0x1147,
+                0x0a, 0x80, 0xfc, 0xc3,
+                0x21efff, 0x7fffff, 0x5e1000,
+                0x40000, 0x80000, 0x1000, 0x00, 0x02,
+                0,
+                &spi_codec,
+        },
+               {
+                "Envy24HT audio (Terratec Aureon 7.1 Universe)",
+                0x153b, 0x1153,
+                0x0b, 0x80, 0xfc, 0xc3,
+                0x21efff, 0x7fffff, 0x5e1000,
+                0x40000, 0x80000, 0x1000, 0x00, 0x02,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24HT audio (AudioTrak Prodigy 7.1)",
+                0x4933, 0x4553,
+                0x0b, 0x80, 0xfc, 0xc3,
+                0x21efff, 0x7fffff, 0x5e1000,
+                0x40000, 0x80000, 0x1000, 0x00, 0x02,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24HT audio (Terratec PHASE 28)",
+                0x153b, 0x1149,
+                0x0b, 0x80, 0xfc, 0xc3,
+                0x21efff, 0x7fffff, 0x5e1000,
+                0x40000, 0x80000, 0x1000, 0x00, 0x02,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24HT-S audio (Terratec PHASE 22)",
+                0x153b, 0x1150,
+                0x10, 0x80, 0xf0, 0xc3,
+                0x7ffbc7, 0x7fffff, 0x438,
+                0x20, 0x10, 0x400, 0x00, 0x00,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24HT audio (AudioTrak Prodigy 7.1 LT)",
+                0x3132, 0x4154,   
+                0x4b, 0x80, 0xfc, 0xc3,
+                0x7ff8ff, 0x7fffff, 0x700,
+                0x400, 0x200, 0x100, 0x00, 0x02,
+                0,
+                &spi_codec, 
+        },
+        {
+                "Envy24HT audio (AudioTrak Prodigy 7.1 XT)",
+                0x3136, 0x4154,  
+                0x4b, 0x80, 0xfc, 0xc3,
+                0x7ff8ff, 0x7fffff, 0x700,
+                0x400, 0x200, 0x100, 0x00, 0x02,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24HT audio (M-Audio Revolution 7.1)",
+                0x1412, 0x3630,
+                0x43, 0x80, 0xf8, 0xc1,
+                0x3fff85, 0x72, 0x4000fa,
+                0x08, 0x02, 0x20, 0x00, 0x04,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24GT audio (M-Audio Revolution 5.1)",
+                0x1412, 0x3631,
+                0x42, 0x80, 0xf8, 0xc1,
+                0x3fff85, 0x72, 0x4000fa,
+                0x08, 0x02, 0x10, 0x00, 0x03,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24HT audio (M-Audio Audiophile 192)",
+                0x1412, 0x3632,
+                0x68, 0x80, 0xf8, 0xc3,
+                0x45, 0x4000b5, 0x7fffba,
+                0x08, 0x02, 0x10, 0x00, 0x03,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24HT audio (AudioTrak Prodigy HD2)",
+                0x3137, 0x4154,
+                0x68, 0x80, 0x78, 0xc3,
+                0xfff8ff, 0x200700, 0xdfffff,
+                0x400, 0x200, 0x100, 0x00, 0x05,
+                0,
+                &spi_codec,
+        },
+        {
+                "Envy24HT audio (ESI Juli@)",
+                0x3031, 0x4553,
+                0x20, 0x80, 0xf8, 0xc3,
+                0x7fff9f, 0x8016, 0x7fff9f,
+                0x08, 0x02, 0x10, 0x00, 0x03,
+                0,
+                &spi_codec,
+        },
+       {
+               "Envy24HT audio (Generic)",
+               0, 0,
+               0x0b, 0x80, 0xfc, 0xc3,
+               0x21efff, 0x7fffff, 0x5e1000,
+                0x40000, 0x80000, 0x1000, 0x00, 0x02,
+               0,
+               &spi_codec, /* default codec routines */
+       }
+};
+
+static u_int32_t envy24ht_recfmt[] = {
+       AFMT_STEREO | AFMT_S16_LE,
+       AFMT_STEREO | AFMT_S32_LE,
+       0
+};
+static struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0};
+
+static u_int32_t envy24ht_playfmt[] = {
+       AFMT_STEREO | AFMT_U8,
+       AFMT_STEREO | AFMT_S16_LE,
+       AFMT_STEREO | AFMT_S32_LE,
+       0
+};
+
+static struct pcmchan_caps envy24ht_playcaps = {8000, 192000, envy24ht_playfmt, 0};
+
+struct envy24ht_emldma {
+       u_int32_t       format;
+       void            (*emldma)(struct sc_chinfo *);
+       int             unit;
+};
+
+static struct envy24ht_emldma envy24ht_pemltab[] = {
+       {AFMT_STEREO | AFMT_U8, envy24ht_p8u, 2},
+       {AFMT_STEREO | AFMT_S16_LE, envy24ht_p16sl, 4},
+       {AFMT_STEREO | AFMT_S32_LE, envy24ht_p32sl, 8},
+       {0, NULL, 0}
+};
+
+static struct envy24ht_emldma envy24ht_remltab[] = {
+       {AFMT_STEREO | AFMT_S16_LE, envy24ht_r16sl, 4},
+       {AFMT_STEREO | AFMT_S32_LE, envy24ht_r32sl, 8},
+       {0, NULL, 0}
+};
+
+/* -------------------------------------------------------------------- */
+
+/* common routines */
+static u_int32_t
+envy24ht_rdcs(struct sc_info *sc, int regno, int size)
+{
+       switch (size) {
+       case 1:
+               return bus_space_read_1(sc->cst, sc->csh, regno);
+       case 2:
+               return bus_space_read_2(sc->cst, sc->csh, regno);
+       case 4:
+               return bus_space_read_4(sc->cst, sc->csh, regno);
+       default:
+               return 0xffffffff;
+       }
+}
+
+static void
+envy24ht_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size)
+{
+       switch (size) {
+       case 1:
+               bus_space_write_1(sc->cst, sc->csh, regno, data);
+               break;
+       case 2:
+               bus_space_write_2(sc->cst, sc->csh, regno, data);
+               break;
+       case 4:
+               bus_space_write_4(sc->cst, sc->csh, regno, data);
+               break;
+       }
+}
+
+static u_int32_t
+envy24ht_rdmt(struct sc_info *sc, int regno, int size)
+{
+       switch (size) {
+       case 1:
+               return bus_space_read_1(sc->mtt, sc->mth, regno);
+       case 2:
+               return bus_space_read_2(sc->mtt, sc->mth, regno);
+       case 4:
+               return bus_space_read_4(sc->mtt, sc->mth, regno);
+       default:
+               return 0xffffffff;
+       }
+}
+
+static void
+envy24ht_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size)
+{
+       switch (size) {
+       case 1:
+               bus_space_write_1(sc->mtt, sc->mth, regno, data);
+               break;
+       case 2:
+               bus_space_write_2(sc->mtt, sc->mth, regno, data);
+               break;
+       case 4:
+               bus_space_write_4(sc->mtt, sc->mth, regno, data);
+               break;
+       }
+}
+
+/* -------------------------------------------------------------------- */
+
+/* I2C port/E2PROM access routines */
+
+static int
+envy24ht_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr)
+{
+       u_int32_t data;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
+#endif
+       for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
+               data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
+               if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
+                       break;
+               DELAY(32); /* 31.25kHz */
+       }
+       if (i == ENVY24HT_TIMEOUT) {
+               return -1;
+       }
+       envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1);
+       envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV,
+           (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_RD, 1);
+       for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
+               data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
+               if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
+                       break;
+               DELAY(32); /* 31.25kHz */
+       }
+       if (i == ENVY24HT_TIMEOUT) {
+               return -1;
+       }
+       data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CDATA, 1);
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_rdi2c(): return 0x%x\n", data);
+#endif
+       return (int)data;
+}
+
+static int
+envy24ht_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data)
+{
+       u_int32_t tmp;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
+#endif
+       for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
+               tmp = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
+               if ((tmp & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
+                       break;
+               DELAY(32); /* 31.25kHz */
+       }
+       if (i == ENVY24HT_TIMEOUT) {
+               return -1;
+       }
+       envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1);
+       envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDATA, data, 1);
+       envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV,
+           (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_WR, 1);
+       for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
+               data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
+               if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
+                       break;
+               DELAY(32); /* 31.25kHz */
+       }
+       if (i == ENVY24HT_TIMEOUT) {
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+envy24ht_rdrom(struct sc_info *sc, u_int32_t addr)
+{
+       u_int32_t data;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_rdrom(sc, 0x%02x)\n", addr);
+#endif
+       data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
+       if ((data & ENVY24HT_CCS_I2CSTAT_ROM) == 0) {
+#if(0)
+               device_printf(sc->dev, "envy24ht_rdrom(): E2PROM not presented\n");
+#endif
+               return -1;
+       }
+
+       return envy24ht_rdi2c(sc, ENVY24HT_CCS_I2CDEV_ROM, addr);
+}
+
+static struct cfg_info *
+envy24ht_rom2cfg(struct sc_info *sc)
+{
+       struct cfg_info *buff;
+       int size;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_rom2cfg(sc)\n");
+#endif
+       size = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SIZE);
+       if ((size < ENVY24HT_E2PROM_GPIOSTATE + 3) || (size == 0x78)) {
+#if(0)
+               device_printf(sc->dev, "envy24ht_rom2cfg(): ENVY24HT_E2PROM_SIZE-->%d\n", size);
+#endif
+        buff = kmalloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
+        if (buff == NULL) {
+#if(0)
+                device_printf(sc->dev, "envy24ht_rom2cfg(): kmalloc()\n");
+#endif
+                return NULL;
+        }
+        buff->free = 1;
+
+       /* no valid e2prom, using default values */
+        buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8;
+        buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1);
+        buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8;
+        buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1);
+        buff->scfg = 0x0b;
+        buff->acl = 0x80;
+        buff->i2s = 0xfc;
+        buff->spdif = 0xc3;
+        buff->gpiomask = 0x21efff;
+        buff->gpiostate = 0x7fffff;
+        buff->gpiodir = 0x5e1000;
+       buff->cdti = 0x40000;
+       buff->cclk = 0x80000;
+       buff->cs = 0x1000;
+       buff->cif = 0x00;
+       buff->type = 0x02;
+
+        for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0;
+i++)
+                if (cfg_table[i].subvendor == buff->subvendor &&
+                    cfg_table[i].subdevice == buff->subdevice)
+                        break;
+        buff->name = cfg_table[i].name;
+        buff->codec = cfg_table[i].codec;
+
+               return buff;
+#if 0
+               return NULL;
+#endif
+       }
+       buff = kmalloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
+       if (buff == NULL) {
+#if(0)
+               device_printf(sc->dev, "envy24ht_rom2cfg(): kmalloc()\n");
+#endif
+               return NULL;
+       }
+       buff->free = 1;
+
+       buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8;
+       buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1);
+       buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8;
+       buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1);
+       buff->scfg = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SCFG);
+       buff->acl = envy24ht_rdrom(sc, ENVY24HT_E2PROM_ACL);
+       buff->i2s = envy24ht_rdrom(sc, ENVY24HT_E2PROM_I2S);
+       buff->spdif = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SPDIF);
+       buff->gpiomask = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK) | \
+       envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 1) << 8 | \
+       envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 2) << 16;
+       buff->gpiostate = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE) | \
+       envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 1) << 8 | \
+       envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 2) << 16;
+       buff->gpiodir = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR) | \
+       envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 1) << 8 | \
+       envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 2) << 16;
+
+       for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++)
+               if (cfg_table[i].subvendor == buff->subvendor &&
+                   cfg_table[i].subdevice == buff->subdevice)
+                       break;
+       buff->name = cfg_table[i].name;
+       buff->codec = cfg_table[i].codec;
+
+       return buff;
+}
+
+static void
+envy24ht_cfgfree(struct cfg_info *cfg) {
+       if (cfg == NULL)
+               return;
+       if (cfg->free)
+               kfree(cfg, M_ENVY24HT);
+       return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* AC'97 codec access routines */
+
+#if 0
+static int
+envy24ht_coldcd(struct sc_info *sc)
+{
+       u_int32_t data;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_coldcd()\n");
+#endif
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_CLD, 1);
+       DELAY(10);
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1);
+       DELAY(1000);
+       for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
+               data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
+               if (data & ENVY24HT_MT_AC97CMD_RDY) {
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static int
+envy24ht_slavecd(struct sc_info *sc)
+{
+       u_int32_t data;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_slavecd()\n");
+#endif
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD,
+           ENVY24HT_MT_AC97CMD_CLD | ENVY24HT_MT_AC97CMD_WRM, 1);
+       DELAY(10);
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1);
+       DELAY(1000);
+       for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
+               data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
+               if (data & ENVY24HT_MT_AC97CMD_RDY) {
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static int
+envy24ht_rdcd(kobj_t obj, void *devinfo, int regno)
+{
+       struct sc_info *sc = (struct sc_info *)devinfo;
+       u_int32_t data;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_rdcd(obj, sc, 0x%02x)\n", regno);
+#endif
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_RD, 1);
+       for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
+               data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
+               if ((data & ENVY24HT_MT_AC97CMD_RD) == 0)
+                       break;
+       }
+       data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97DLO, 2);
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_rdcd(): return 0x%x\n", data);
+#endif
+       return (int)data;
+}
+
+static int
+envy24ht_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
+{
+       struct sc_info *sc = (struct sc_info *)devinfo;
+       u_int32_t cmd;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data);
+#endif
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97DLO, (u_int32_t)data, 2);
+       envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_WR, 1);
+       for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
+               cmd = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
+               if ((cmd & ENVY24HT_MT_AC97CMD_WR) == 0)
+                       break;
+       }
+
+       return 0;
+}
+
+static kobj_method_t envy24ht_ac97_methods[] = {
+       KOBJMETHOD(ac97_read,   envy24ht_rdcd),
+       KOBJMETHOD(ac97_write,  envy24ht_wrcd),
+       {0, 0}
+};
+AC97_DECLARE(envy24ht_ac97);
+#endif
+
+/* -------------------------------------------------------------------- */
+
+/* GPIO access routines */
+
+static u_int32_t
+envy24ht_gpiord(struct sc_info *sc)
+{
+       if (sc->cfg->subvendor == 0x153b  && sc->cfg->subdevice == 0x1150) 
+       return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2);
+       else
+       return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HDATA, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2));
+}
+
+static void
+envy24ht_gpiowr(struct sc_info *sc, u_int32_t data)
+{
+#if(0)
+       device_printf(sc->dev, "envy24ht_gpiowr(sc, 0x%02x)\n", data & 0x7FFFFF);
+       return;
+#endif
+       envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LDATA, data, 2);
+       if (sc->cfg->subdevice != 0x1150)
+       envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HDATA, data >> 16, 1);
+       return;
+}
+
+#if 0
+static u_int32_t
+envy24ht_gpiogetmask(struct sc_info *sc)
+{
+       return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HMASK, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LMASK, 2));
+}
+#endif
+
+static void
+envy24ht_gpiosetmask(struct sc_info *sc, u_int32_t mask)
+{
+        envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LMASK, mask, 2);
+       if (sc->cfg->subdevice != 0x1150)
+        envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HMASK, mask >> 16, 1);
+       return;
+}
+
+#if 0
+static u_int32_t
+envy24ht_gpiogetdir(struct sc_info *sc)
+{
+       return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, 4);
+}
+#endif
+
+static void
+envy24ht_gpiosetdir(struct sc_info *sc, u_int32_t dir)
+{
+       if (sc->cfg->subvendor == 0x153b  && sc->cfg->subdevice == 0x1150)
+       envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 2);
+       else 
+       envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 4);
+       return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* SPI codec access interface routine */
+
+struct envy24ht_spi_codec {
+       struct spicds_info *info;
+       struct sc_info *parent;
+       int dir;
+       int num;
+       int cs, cclk, cdti;
+};
+
+static void
+envy24ht_spi_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti)
+{
+       u_int32_t data = 0;
+       struct envy24ht_spi_codec *ptr = codec;
+
+#if(0)
+       device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti);
+#endif
+       data = envy24ht_gpiord(ptr->parent);
+       data &= ~(ptr->cs | ptr->cclk | ptr->cdti);
+       if (cs) data += ptr->cs;
+       if (cclk) data += ptr->cclk;
+       if (cdti) data += ptr->cdti;
+       envy24ht_gpiowr(ptr->parent, data);
+       return;
+}
+
+static void *
+envy24ht_spi_create(device_t dev, void *info, int dir, int num)
+{
+       struct sc_info *sc = info;
+       struct envy24ht_spi_codec *buff = NULL;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_spi_create(dev, sc, %d, %d)\n", dir, num);
+#endif
+       
+       buff = kmalloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
+       if (buff == NULL)
+               return NULL;
+
+       if (dir == PCMDIR_REC && sc->adc[num] != NULL)
+               buff->info = ((struct envy24ht_spi_codec *)sc->adc[num])->info;
+       else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL)
+               buff->info = ((struct envy24ht_spi_codec *)sc->dac[num])->info;
+       else
+               buff->info = spicds_create(dev, buff, num, envy24ht_spi_ctl);
+       if (buff->info == NULL) {
+               kfree(buff, M_ENVY24HT);
+               return NULL;
+       }
+
+       buff->parent = sc;
+       buff->dir = dir;
+       buff->num = num;
+
+       return (void *)buff;
+}
+
+static void
+envy24ht_spi_destroy(void *codec)
+{
+       struct envy24ht_spi_codec *ptr = codec;
+       if (ptr == NULL)
+               return;
+#if(0)
+       device_printf(ptr->parent->dev, "envy24ht_spi_destroy()\n");
+#endif
+
+       if (ptr->dir == PCMDIR_PLAY) {
+               if (ptr->parent->dac[ptr->num] != NULL)
+                       spicds_destroy(ptr->info);
+       }
+       else {
+               if (ptr->parent->adc[ptr->num] != NULL)
+                       spicds_destroy(ptr->info);
+       }
+
+       kfree(codec, M_ENVY24HT);
+}
+
+static void
+envy24ht_spi_init(void *codec)
+{
+       struct envy24ht_spi_codec *ptr = codec;
+       if (ptr == NULL)
+               return;
+#if(0)
+       device_printf(ptr->parent->dev, "envy24ht_spicds_init()\n");
+#endif
+        ptr->cs = ptr->parent->cfg->cs;
+       ptr->cclk = ptr->parent->cfg->cclk;
+       ptr->cdti =  ptr->parent->cfg->cdti;
+       spicds_settype(ptr->info, ptr->parent->cfg->type);
+       spicds_setcif(ptr->info, ptr->parent->cfg->cif);
+       if (ptr->parent->cfg->type == SPICDS_TYPE_AK4524 || \
+       ptr->parent->cfg->type == SPICDS_TYPE_AK4528) {
+       spicds_setformat(ptr->info,
+           AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X);
+       spicds_setdvc(ptr->info, AK452X_DVC_DEMOFF);
+       }
+
+       /* for the time being, init only first codec */
+       if (ptr->num == 0)
+       spicds_init(ptr->info);
+}
+
+static void
+envy24ht_spi_reinit(void *codec)
+{
+       struct envy24ht_spi_codec *ptr = codec;
+       if (ptr == NULL)
+               return;
+#if(0)
+       device_printf(ptr->parent->dev, "envy24ht_spi_reinit()\n");
+#endif
+
+       spicds_reinit(ptr->info);
+}
+
+static void
+envy24ht_spi_setvolume(void *codec, int dir, unsigned int left, unsigned int right)
+{
+       struct envy24ht_spi_codec *ptr = codec;
+       if (ptr == NULL)
+               return;
+#if(0)
+       device_printf(ptr->parent->dev, "envy24ht_spi_set()\n");
+#endif
+
+       spicds_set(ptr->info, dir, left, right);
+}
+
+/* -------------------------------------------------------------------- */
+
+/* hardware access routeines */
+
+static struct {
+       u_int32_t speed;
+       u_int32_t code;
+} envy24ht_speedtab[] = {
+       {48000, ENVY24HT_MT_RATE_48000},
+       {24000, ENVY24HT_MT_RATE_24000},
+       {12000, ENVY24HT_MT_RATE_12000},
+       {9600, ENVY24HT_MT_RATE_9600},
+       {32000, ENVY24HT_MT_RATE_32000},
+       {16000, ENVY24HT_MT_RATE_16000},
+       {8000, ENVY24HT_MT_RATE_8000},
+       {96000, ENVY24HT_MT_RATE_96000},
+       {192000, ENVY24HT_MT_RATE_192000},
+       {64000, ENVY24HT_MT_RATE_64000},
+       {44100, ENVY24HT_MT_RATE_44100},
+       {22050, ENVY24HT_MT_RATE_22050},
+       {11025, ENVY24HT_MT_RATE_11025},
+       {88200, ENVY24HT_MT_RATE_88200},
+       {176400, ENVY24HT_MT_RATE_176400},
+       {0, 0x10}
+};
+
+static int
+envy24ht_setspeed(struct sc_info *sc, u_int32_t speed) {
+       u_int32_t code, i2sfmt;
+       int i = 0;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_setspeed(sc, %d)\n", speed);
+       if (speed == 0) {
+               code = ENVY24HT_MT_RATE_SPDIF; /* external master clock */
+               envy24ht_slavecd(sc);
+       }
+       else {
+#endif
+               for (i = 0; envy24ht_speedtab[i].speed != 0; i++) {
+                       if (envy24ht_speedtab[i].speed == speed)
+                               break;
+               }
+               code = envy24ht_speedtab[i].code;
+#if 0
+       }
+       device_printf(sc->dev, "envy24ht_setspeed(): speed %d/code 0x%04x\n", envy24ht_speedtab[i].speed, code);
+#endif
+       if (code < 0x10) {
+               envy24ht_wrmt(sc, ENVY24HT_MT_RATE, code, 1);
+               if ((((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) == 0x00) && (code == ENVY24HT_MT_RATE_192000)) || \
+                                                                           (code == ENVY24HT_MT_RATE_176400)) {
+                       i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1);
+                       i2sfmt |= ENVY24HT_MT_I2S_MLR128;
+                       envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1);
+               }
+               else {
+                       i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1);
+                       i2sfmt &= ~ENVY24HT_MT_I2S_MLR128;
+                       envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1);
+               }
+               code = envy24ht_rdmt(sc, ENVY24HT_MT_RATE, 1);
+               code &= ENVY24HT_MT_RATE_MASK;
+               for (i = 0; envy24ht_speedtab[i].code < 0x10; i++) {
+                       if (envy24ht_speedtab[i].code == code)
+                               break;
+               }
+               speed = envy24ht_speedtab[i].speed;
+       }
+       else
+               speed = 0;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_setspeed(): return %d\n", speed);
+#endif
+       return speed;
+}
+
+static void
+envy24ht_setvolume(struct sc_info *sc, unsigned ch)
+{
+#if(0)
+       device_printf(sc->dev, "envy24ht_setvolume(sc, %d)\n", ch);
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, 0x7f00 | sc->left[ch], 2);
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2);
+#endif
+}
+
+static void
+envy24ht_mutevolume(struct sc_info *sc, unsigned ch)
+{
+#if 0
+       u_int32_t vol;
+
+       device_printf(sc->dev, "envy24ht_mutevolume(sc, %d)\n", ch);
+       vol = ENVY24HT_VOL_MUTE << 8 | ENVY24HT_VOL_MUTE;
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2);
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2);
+#endif
+}
+
+static u_int32_t
+envy24ht_gethwptr(struct sc_info *sc, int dir)
+{
+       int unit, regno;
+       u_int32_t ptr, rtn;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_gethwptr(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY) {
+               rtn = sc->psize / 4;
+               unit = ENVY24HT_PLAY_BUFUNIT / 4;
+               regno = ENVY24HT_MT_PCNT;
+       }
+       else {
+               rtn = sc->rsize / 4;
+               unit = ENVY24HT_REC_BUFUNIT / 4;
+               regno = ENVY24HT_MT_RCNT;
+       }
+
+       ptr = envy24ht_rdmt(sc, regno, 2);
+       rtn -= (ptr + 1);
+       rtn /= unit;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_gethwptr(): return %d\n", rtn);
+#endif
+       return rtn;
+}
+
+static void
+envy24ht_updintr(struct sc_info *sc, int dir)
+{
+       int regptr, regintr;
+       u_int32_t mask, intr;
+       u_int32_t ptr, size, cnt;
+       u_int16_t blk;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_updintr(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY) {
+               blk = sc->blk[0];
+               size = sc->psize / 4;
+               regptr = ENVY24HT_MT_PCNT;
+               regintr = ENVY24HT_MT_PTERM;
+               mask = ~ENVY24HT_MT_INT_PMASK;
+       }
+       else {
+               blk = sc->blk[1];
+               size = sc->rsize / 4;
+               regptr = ENVY24HT_MT_RCNT;
+               regintr = ENVY24HT_MT_RTERM;
+               mask = ~ENVY24HT_MT_INT_RMASK;
+       }
+
+       ptr = size - envy24ht_rdmt(sc, regptr, 2) - 1;
+       /*
+       cnt = blk - ptr % blk - 1;
+       if (cnt == 0)
+               cnt = blk - 1;
+       */
+       cnt = blk - 1;
+#if(0)
+       device_printf(sc->dev, "envy24ht_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt);
+#endif
+       envy24ht_wrmt(sc, regintr, cnt, 2);
+       intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
+#if(0)
+       device_printf(sc->dev, "envy24ht_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask);
+#endif
+       envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, intr & mask, 1);
+#if(0)
+       device_printf(sc->dev, "envy24ht_updintr():INT-->0x%02x\n",
+                     envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1));
+#endif
+
+       return;
+}
+
+#if 0
+static void
+envy24ht_maskintr(struct sc_info *sc, int dir)
+{
+       u_int32_t mask, intr;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_maskintr(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY)
+               mask = ENVY24HT_MT_INT_PMASK;
+       else
+               mask = ENVY24HT_MT_INT_RMASK;
+       intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_INT, intr | mask, 1);
+
+       return;
+}
+#endif
+
+static int
+envy24ht_checkintr(struct sc_info *sc, int dir)
+{
+       u_int32_t mask, stat, intr, rtn;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_checkintr(sc, %d)\n", dir);
+#endif
+       intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_STAT, 1);
+       if (dir == PCMDIR_PLAY) {
+               if ((rtn = intr & ENVY24HT_MT_INT_PSTAT) != 0) {
+                       mask = ~ENVY24HT_MT_INT_RSTAT;
+                       envy24ht_wrmt(sc, 0x1a, 0x01, 1);
+                       envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_PSTAT | 0x08, 1);       
+                       stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
+                       envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_PMASK, 1);
+               }
+       }
+       else {
+               if ((rtn = intr & ENVY24HT_MT_INT_RSTAT) != 0) {
+                       mask = ~ENVY24HT_MT_INT_PSTAT;
+#if 0
+                       stat = ENVY24HT_MT_INT_RSTAT | ENVY24HT_MT_INT_RMASK;
+#endif
+                       envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_RSTAT, 1);
+                       stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
+                       envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_RMASK, 1);
+               }
+       }
+
+       return rtn;
+}
+
+static void
+envy24ht_start(struct sc_info *sc, int dir)
+{
+       u_int32_t stat, sw;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_start(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY)
+               sw = ENVY24HT_MT_PCTL_PSTART;
+       else
+               sw = ENVY24HT_MT_PCTL_RSTART;
+
+       stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat | sw, 1);
+#if(0)
+       DELAY(100);
+       device_printf(sc->dev, "PADDR:0x%08x\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4));
+       device_printf(sc->dev, "PCNT:%ld\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2));
+#endif
+
+       return;
+}
+
+static void
+envy24ht_stop(struct sc_info *sc, int dir)
+{
+       u_int32_t stat, sw;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_stop(sc, %d)\n", dir);
+#endif
+       if (dir == PCMDIR_PLAY)
+               sw = ~ENVY24HT_MT_PCTL_PSTART;
+       else
+               sw = ~ENVY24HT_MT_PCTL_RSTART;
+
+       stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1);
+       envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat & sw, 1);
+
+       return;
+}
+
+#if 0
+static int
+envy24ht_route(struct sc_info *sc, int dac, int class, int adc, int rev)
+{
+       return 0;
+}
+#endif
+
+/* -------------------------------------------------------------------- */
+
+/* buffer copy routines */
+static void
+envy24ht_p32sl(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int32_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+       length = sndbuf_getready(ch->buffer) / 8;
+       dmabuf = ch->parent->pbuf;
+       data = (u_int32_t *)ch->data;
+       src = sndbuf_getreadyptr(ch->buffer) / 4;
+       dst = src / 2 + ch->offset;
+       ssize = ch->size / 4;
+       dsize = ch->size / 8;
+       slot = ch->num * 2;
+
+       for (i = 0; i < length; i++) {
+               dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = data[src];
+               dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = data[src + 1];
+               dst++;
+               dst %= dsize;
+               src += 2;
+               src %= ssize;
+       }
+       
+       return;
+}
+
+static void
+envy24ht_p16sl(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int16_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+#if(0)
+       device_printf(ch->parent->dev, "envy24ht_p16sl()\n");
+#endif
+       length = sndbuf_getready(ch->buffer) / 4;
+       dmabuf = ch->parent->pbuf;
+       data = (u_int16_t *)ch->data;
+       src = sndbuf_getreadyptr(ch->buffer) / 2;
+       dst = src / 2 + ch->offset;
+       ssize = ch->size / 2;
+       dsize = ch->size / 4;
+       slot = ch->num * 2;
+#if(0)
+       device_printf(ch->parent->dev, "envy24ht_p16sl():%lu-->%lu(%lu)\n", src, dst, length);
+#endif
+       
+       for (i = 0; i < length; i++) {
+               dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16;
+               dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16;
+#if(0)
+               if (i < 16) {
+                       kprintf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot]);
+                       kprintf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1]);
+               }
+#endif
+               dst++;
+               dst %= dsize;
+               src += 2;
+               src %= ssize;
+       }
+#if(0)
+       kprintf("\n");
+#endif
+       
+       return;
+}
+
+static void
+envy24ht_p8u(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int8_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+       length = sndbuf_getready(ch->buffer) / 2;
+       dmabuf = ch->parent->pbuf;
+       data = (u_int8_t *)ch->data;
+       src = sndbuf_getreadyptr(ch->buffer);
+       dst = src / 2 + ch->offset;
+       ssize = ch->size;
+       dsize = ch->size / 4;
+       slot = ch->num * 2;
+       
+       for (i = 0; i < length; i++) {
+               dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24;
+               dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24;
+               dst++;
+               dst %= dsize;
+               src += 2;
+               src %= ssize;
+       }
+       
+       return;
+}
+
+static void
+envy24ht_r32sl(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int32_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+       length = sndbuf_getfree(ch->buffer) / 8;
+       dmabuf = ch->parent->rbuf;
+       data = (u_int32_t *)ch->data;
+       dst = sndbuf_getfreeptr(ch->buffer) / 4;
+       src = dst / 2 + ch->offset;
+       dsize = ch->size / 4;
+       ssize = ch->size / 8;
+       slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2;
+
+       for (i = 0; i < length; i++) {
+               data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer;
+               data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer;
+               dst += 2;
+               dst %= dsize;
+               src++;
+               src %= ssize;
+       }
+       
+       return;
+}
+
+static void
+envy24ht_r16sl(struct sc_chinfo *ch)
+{
+       int length;
+       sample32_t *dmabuf;
+       u_int16_t *data;
+       int src, dst, ssize, dsize, slot;
+       int i;
+
+       length = sndbuf_getfree(ch->buffer) / 4;
+       dmabuf = ch->parent->rbuf;
+       data = (u_int16_t *)ch->data;
+       dst = sndbuf_getfreeptr(ch->buffer) / 2;
+       src = dst / 2 + ch->offset;
+       dsize = ch->size / 2;
+       ssize = ch->size / 8;
+       slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2;
+
+       for (i = 0; i < length; i++) {
+               data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer;
+               data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer;
+               dst += 2;
+               dst %= dsize;
+               src++;
+               src %= ssize;
+       }
+       
+       return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* channel interface */
+static void *
+envy24htchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+{
+       struct sc_info  *sc = (struct sc_info *)devinfo;
+       struct sc_chinfo *ch;
+       unsigned num;
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_init(obj, devinfo, b, c, %d)\n", dir);
+#endif
+       snd_mtxlock(sc->lock);
+#if 0
+       if ((sc->chnum > ENVY24HT_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) ||
+           (sc->chnum < ENVY24HT_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) {
+               snd_mtxunlock(sc->lock);
+               return NULL;
+       }
+#endif
+       num = sc->chnum;
+
+       ch = &sc->chan[num];
+       ch->size = 8 * ENVY24HT_SAMPLE_NUM;
+       ch->data = kmalloc(ch->size, M_ENVY24HT, M_NOWAIT);
+       if (ch->data == NULL) {
+               ch->size = 0;
+               ch = NULL;
+       }
+       else {
+               ch->buffer = b;
+               ch->channel = c;
+               ch->parent = sc;
+               ch->dir = dir;
+               /* set channel map */
+               ch->num = envy24ht_chanmap[num];
+               snd_mtxunlock(sc->lock);
+               sndbuf_setup(ch->buffer, ch->data, ch->size);
+               snd_mtxlock(sc->lock);
+               /* these 2 values are dummy */
+               ch->unit = 4;
+               ch->blk = 10240;
+       }
+       snd_mtxunlock(sc->lock);
+
+       return ch;
+}
+
+static int
+envy24htchan_free(kobj_t obj, void *data)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_free()\n");
+#endif
+       snd_mtxlock(sc->lock);
+       if (ch->data != NULL) {
+               kfree(ch->data, M_ENVY24HT);
+               ch->data = NULL;
+       }
+       snd_mtxunlock(sc->lock);
+
+       return 0;
+}
+
+static int
+envy24htchan_setformat(kobj_t obj, void *data, u_int32_t format)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+       struct envy24ht_emldma *emltab;
+       /* unsigned int bcnt, bsize; */
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_setformat(obj, data, 0x%08x)\n", format);
+#endif
+       snd_mtxlock(sc->lock);
+       /* check and get format related information */
+       if (ch->dir == PCMDIR_PLAY)
+               emltab = envy24ht_pemltab;
+       else
+               emltab = envy24ht_remltab;
+       if (emltab == NULL) {
+               snd_mtxunlock(sc->lock);
+               return -1;
+       }
+       for (i = 0; emltab[i].format != 0; i++)
+               if (emltab[i].format == format)
+                       break;
+       if (emltab[i].format == 0) {
+               snd_mtxunlock(sc->lock);
+               return -1;
+       }
+
+       /* set format information */
+       ch->format = format;
+       ch->emldma = emltab[i].emldma;
+       if (ch->unit > emltab[i].unit)
+               ch->blk *= ch->unit / emltab[i].unit;
+       else
+               ch->blk /= emltab[i].unit / ch->unit;
+       ch->unit = emltab[i].unit;
+
+       /* set channel buffer information */
+       ch->size = ch->unit * ENVY24HT_SAMPLE_NUM;
+#if 0
+       if (ch->dir == PCMDIR_PLAY)
+               bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT;
+       else
+               bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT;
+       bsize *= ch->unit;
+       bcnt = ch->size / bsize;
+       sndbuf_resize(ch->buffer, bcnt, bsize);
+#endif
+       snd_mtxunlock(sc->lock);
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_setformat(): return 0x%08x\n", 0);
+#endif
+       return 0;
+}
+
+/*
+  IMPLEMENT NOTICE: In this driver, setspeed function only do setting
+  of speed information value. And real hardware speed setting is done
+  at start triggered(see envy24htchan_trigger()). So, at this function
+  is called, any value that ENVY24 can use is able to set. But, at
+  start triggerd, some other channel is running, and that channel's
+  speed isn't same with, then trigger function will fail.
+*/
+static int
+envy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+{
+       struct sc_chinfo *ch = data;
+       u_int32_t val, prev;
+       int i;
+
+#if(0)
+       device_printf(ch->parent->dev, "envy24htchan_setspeed(obj, data, %d)\n", speed);
+#endif
+       prev = 0x7fffffff;
+       for (i = 0; (val = envy24ht_speed[i]) != 0; i++) {
+               if (abs(val - speed) < abs(prev - speed))
+                       prev = val;
+               else
+                       break;
+       }
+       ch->speed = prev;
+       
+#if(0)
+       device_printf(ch->parent->dev, "envy24htchan_setspeed(): return %d\n", ch->speed);
+#endif
+       return ch->speed;
+}
+
+static int
+envy24htchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+{
+       struct sc_chinfo *ch = data;
+       /* struct sc_info *sc = ch->parent; */
+       u_int32_t size, prev;
+       unsigned int bcnt, bsize;
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_setblocksize(obj, data, %d)\n", blocksize);
+#endif
+       prev = 0x7fffffff;
+       /* snd_mtxlock(sc->lock); */
+       for (size = ch->size / 2; size > 0; size /= 2) {
+               if (abs(size - blocksize) < abs(prev - blocksize))
+                       prev = size;
+               else
+                       break;
+       }
+
+       ch->blk = prev / ch->unit;
+       if (ch->dir == PCMDIR_PLAY)
+               ch->blk *= ENVY24HT_PLAY_BUFUNIT / 4;
+       else
+               ch->blk *= ENVY24HT_REC_BUFUNIT / 4;
+        /* set channel buffer information */
+        /* ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; */
+        if (ch->dir == PCMDIR_PLAY)
+                bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT;
+        else
+                bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT;
+        bsize *= ch->unit;
+        bcnt = ch->size / bsize;
+        sndbuf_resize(ch->buffer, bcnt, bsize);
+       /* snd_mtxunlock(sc->lock); */
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_setblocksize(): return %d\n", prev);
+#endif
+       return prev;
+}
+
+/* semantic note: must start at beginning of buffer */
+static int
+envy24htchan_trigger(kobj_t obj, void *data, int go)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+       u_int32_t ptr;
+       int slot;
+#if 0
+       int i;
+
+       device_printf(sc->dev, "envy24htchan_trigger(obj, data, %d)\n", go);
+#endif
+       snd_mtxlock(sc->lock);
+       if (ch->dir == PCMDIR_PLAY)
+               slot = 0;
+       else
+               slot = 1;
+       switch (go) {
+       case PCMTRIG_START:
+#if(0)
+               device_printf(sc->dev, "envy24htchan_trigger(): start\n");
+#endif
+               /* check or set channel speed */
+               if (sc->run[0] == 0 && sc->run[1] == 0) {
+                       sc->speed = envy24ht_setspeed(sc, ch->speed);
+                       sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed;
+                       sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed;
+               }
+               else if (ch->speed != 0 && ch->speed != sc->speed)
+                       return -1;
+               if (ch->speed == 0)
+                       ch->channel->speed = sc->speed;
+               /* start or enable channel */
+               sc->run[slot]++;
+               if (sc->run[slot] == 1) {
+                       /* first channel */
+                       ch->offset = 0;
+                       sc->blk[slot] = ch->blk;
+               }
+               else {
+                       ptr = envy24ht_gethwptr(sc, ch->dir);
+                       ch->offset = ((ptr / ch->blk + 1) * ch->blk %
+                           (ch->size / 4)) * 4 / ch->unit;
+                       if (ch->blk < sc->blk[slot])
+                               sc->blk[slot] = ch->blk;
+               }
+               if (ch->dir == PCMDIR_PLAY) {
+                       ch->emldma(ch);
+                       envy24ht_setvolume(sc, ch->num);
+               }
+               envy24ht_updintr(sc, ch->dir);
+               if (sc->run[slot] == 1)
+                       envy24ht_start(sc, ch->dir);
+               ch->run = 1;
+               break;
+       case PCMTRIG_EMLDMAWR:
+#if(0)
+               device_printf(sc->dev, "envy24htchan_trigger(): emldmawr\n");
+#endif
+               if (ch->run != 1)
+                       return -1;
+               ch->emldma(ch);
+               break;
+       case PCMTRIG_EMLDMARD:
+#if(0)
+               device_printf(sc->dev, "envy24htchan_trigger(): emldmard\n");
+#endif
+               if (ch->run != 1)
+                       return -1;
+               ch->emldma(ch);
+               break;
+       case PCMTRIG_ABORT:
+               if (ch->run) {
+#if(0)
+               device_printf(sc->dev, "envy24htchan_trigger(): abort\n");
+#endif
+               ch->run = 0;
+               sc->run[slot]--;
+               if (ch->dir == PCMDIR_PLAY)
+                       envy24ht_mutevolume(sc, ch->num);
+               if (sc->run[slot] == 0) {
+                       envy24ht_stop(sc, ch->dir);
+                       sc->intr[slot] = 0;
+               }
+/*             else if (ch->blk == sc->blk[slot]) {
+                       sc->blk[slot] = ENVY24HT_SAMPLE_NUM / 2;
+                       for (i = 0; i < ENVY24HT_CHAN_NUM; i++) {
+                               if (sc->chan[i].dir == ch->dir &&
+                                   sc->chan[i].run == 1 &&
+                                   sc->chan[i].blk < sc->blk[slot])
+                                       sc->blk[slot] = sc->chan[i].blk;
+                       }
+                       if (ch->blk != sc->blk[slot])
+                               envy24ht_updintr(sc, ch->dir);
+               }*/
+               }
+               break;
+       }
+       snd_mtxunlock(sc->lock);
+
+       return 0;
+}
+
+static int
+envy24htchan_getptr(kobj_t obj, void *data)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+       u_int32_t ptr;
+       int rtn;
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_getptr()\n");
+#endif
+       snd_mtxlock(sc->lock);
+       ptr = envy24ht_gethwptr(sc, ch->dir);
+       rtn = ptr * ch->unit;
+       snd_mtxunlock(sc->lock);
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_getptr(): return %d\n",
+           rtn);
+#endif
+       return rtn;
+}
+
+static struct pcmchan_caps *
+envy24htchan_getcaps(kobj_t obj, void *data)
+{
+       struct sc_chinfo *ch = data;
+       struct sc_info *sc = ch->parent;
+       struct pcmchan_caps *rtn;
+
+#if(0)
+       device_printf(sc->dev, "envy24htchan_getcaps()\n");
+#endif
+       snd_mtxlock(sc->lock);
+       if (ch->dir == PCMDIR_PLAY) {
+               if (sc->run[0] == 0)
+                       rtn = &envy24ht_playcaps;
+               else
+                       rtn = &sc->caps[0];
+       }
+       else {
+               if (sc->run[1] == 0)
+                       rtn = &envy24ht_reccaps;
+               else
+                       rtn = &sc->caps[1];
+       }
+       snd_mtxunlock(sc->lock);
+
+       return rtn;
+}
+
+static kobj_method_t envy24htchan_methods[] = {
+       KOBJMETHOD(channel_init,                envy24htchan_init),
+       KOBJMETHOD(channel_free,                envy24htchan_free),
+       KOBJMETHOD(channel_setformat,           envy24htchan_setformat),
+       KOBJMETHOD(channel_setspeed,            envy24htchan_setspeed),
+       KOBJMETHOD(channel_setblocksize,        envy24htchan_setblocksize),
+       KOBJMETHOD(channel_trigger,             envy24htchan_trigger),
+       KOBJMETHOD(channel_getptr,              envy24htchan_getptr),
+       KOBJMETHOD(channel_getcaps,             envy24htchan_getcaps),
+       { 0, 0 }
+};
+CHANNEL_DECLARE(envy24htchan);
+
+/* -------------------------------------------------------------------- */
+
+/* mixer interface */
+
+static int
+envy24htmixer_init(struct snd_mixer *m)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+
+#if(0)
+       device_printf(sc->dev, "envy24htmixer_init()\n");
+#endif
+       if (sc == NULL)
+               return -1;
+
+       /* set volume control rate */
+       snd_mtxlock(sc->lock);
+#if 0
+       envy24ht_wrmt(sc, ENVY24HT_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */
+#endif
+
+       pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL);
+
+       mix_setdevs(m, ENVY24HT_MIX_MASK);
+       mix_setrecdevs(m, ENVY24HT_MIX_REC_MASK);
+       
+       snd_mtxunlock(sc->lock);
+
+       return 0;
+}
+
+static int
+envy24htmixer_reinit(struct snd_mixer *m)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+
+       if (sc == NULL)
+               return -1;
+#if(0)
+       device_printf(sc->dev, "envy24htmixer_reinit()\n");
+#endif
+
+       return 0;
+}
+
+static int
+envy24htmixer_uninit(struct snd_mixer *m)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+
+       if (sc == NULL)
+               return -1;
+#if(0)
+       device_printf(sc->dev, "envy24htmixer_uninit()\n");
+#endif
+
+       return 0;
+}
+
+static int
+envy24htmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+       int ch = envy24ht_mixmap[dev];
+       int hwch;
+       int i;
+
+       if (sc == NULL)
+               return -1;
+       if (dev == 0 && sc->cfg->codec->setvolume == NULL)
+               return -1;
+       if (dev != 0 && ch == -1)
+               return -1;
+       hwch = envy24ht_chanmap[ch];
+#if(0)
+       device_printf(sc->dev, "envy24htmixer_set(m, %d, %d, %d)\n",
+           dev, left, right);
+#endif
+
+       snd_mtxlock(sc->lock);
+       if (dev == 0) {
+               for (i = 0; i < sc->dacn; i++) {
+                       sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right);
+               }
+       }
+       else {
+               /* set volume value for hardware */
+               if ((sc->left[hwch] = 100 - left) > ENVY24HT_VOL_MIN)
+                       sc->left[hwch] = ENVY24HT_VOL_MUTE;
+               if ((sc->right[hwch] = 100 - right) > ENVY24HT_VOL_MIN)
+                       sc->right[hwch] = ENVY24HT_VOL_MUTE;
+
+               /* set volume for record channel and running play channel */
+               if (hwch > ENVY24HT_CHAN_PLAY_SPDIF || sc->chan[ch].run)
+                       envy24ht_setvolume(sc, hwch);
+       }
+       snd_mtxunlock(sc->lock);
+
+       return right << 8 | left;
+}
+
+static u_int32_t
+envy24htmixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
+{
+       struct sc_info *sc = mix_getdevinfo(m);
+       int ch = envy24ht_mixmap[src];
+#if(0)
+       device_printf(sc->dev, "envy24htmixer_setrecsrc(m, %d)\n", src);
+#endif
+
+       if (ch > ENVY24HT_CHAN_PLAY_SPDIF)
+               sc->src = ch;
+       return src;
+}
+
+static kobj_method_t envy24htmixer_methods[] = {
+       KOBJMETHOD(mixer_init,          envy24htmixer_init),
+       KOBJMETHOD(mixer_reinit,        envy24htmixer_reinit),
+       KOBJMETHOD(mixer_uninit,        envy24htmixer_uninit),
+       KOBJMETHOD(mixer_set,           envy24htmixer_set),
+       KOBJMETHOD(mixer_setrecsrc,     envy24htmixer_setrecsrc),
+       { 0, 0 }
+};
+MIXER_DECLARE(envy24htmixer);
+
+/* -------------------------------------------------------------------- */
+
+/* The interrupt handler */
+static void
+envy24ht_intr(void *p)
+{
+       struct sc_info *sc = (struct sc_info *)p;
+       struct sc_chinfo *ch;
+       u_int32_t ptr, dsize, feed;
+       int i;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_intr()\n");
+#endif
+       snd_mtxlock(sc->lock);
+       if (envy24ht_checkintr(sc, PCMDIR_PLAY)) {
+#if(0)
+               device_printf(sc->dev, "envy24ht_intr(): play\n");
+#endif
+               dsize = sc->psize / 4;
+               ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2) - 1;
+#if(0)
+               device_printf(sc->dev, "envy24ht_intr(): ptr = %d-->", ptr);
+#endif
+               ptr -= ptr % sc->blk[0];
+               feed = (ptr + dsize - sc->intr[0]) % dsize; 
+#if(0)
+               kprintf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed);
+#endif
+               for (i = ENVY24HT_CHAN_PLAY_DAC1; i <= ENVY24HT_CHAN_PLAY_SPDIF; i++) {
+                       ch = &sc->chan[i];
+#if(0)
+                       if (ch->run)
+                               device_printf(sc->dev, "envy24ht_intr(): chan[%d].blk = %d\n", i, ch->blk);
+#endif
+                       if (ch->run && ch->blk <= feed) {
+                               snd_mtxunlock(sc->lock);
+                               chn_intr(ch->channel);
+                               snd_mtxlock(sc->lock);
+                       }
+               }
+               sc->intr[0] = ptr;
+               envy24ht_updintr(sc, PCMDIR_PLAY);
+       }
+       if (envy24ht_checkintr(sc, PCMDIR_REC)) {
+#if(0)
+               device_printf(sc->dev, "envy24ht_intr(): rec\n");
+#endif
+               dsize = sc->rsize / 4;
+               ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_RCNT, 2) - 1;
+               ptr -= ptr % sc->blk[1];
+               feed = (ptr + dsize - sc->intr[1]) % dsize; 
+               for (i = ENVY24HT_CHAN_REC_ADC1; i <= ENVY24HT_CHAN_REC_SPDIF; i++) {
+                       ch = &sc->chan[i];
+                       if (ch->run && ch->blk <= feed) {
+                               snd_mtxunlock(sc->lock);
+                               chn_intr(ch->channel);
+                               snd_mtxlock(sc->lock);
+                       }
+               }
+               sc->intr[1] = ptr;
+               envy24ht_updintr(sc, PCMDIR_REC);
+       }
+       snd_mtxunlock(sc->lock);
+
+       return;
+}
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+envy24ht_pci_probe(device_t dev)
+{
+       u_int16_t sv, sd;
+       int i;
+
+#if(0)
+       kprintf("envy24ht_pci_probe()\n");
+#endif
+       if (pci_get_device(dev) == PCID_ENVY24HT &&
+           pci_get_vendor(dev) == PCIV_ENVY24) {
+               sv = pci_get_subvendor(dev);
+               sd = pci_get_subdevice(dev);
+               for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
+                       if (cfg_table[i].subvendor == sv &&
+                           cfg_table[i].subdevice == sd) {
+                               break;
+                       }
+               }
+               device_set_desc(dev, cfg_table[i].name);
+#if(0)
+               kprintf("envy24ht_pci_probe(): return 0\n");
+#endif
+               return 0;
+       }
+       else {
+#if(0)
+               kprintf("envy24ht_pci_probe(): return ENXIO\n");
+#endif
+               return ENXIO;
+       }
+}
+
+static void
+envy24ht_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+       /* struct sc_info *sc = (struct sc_info *)arg; */
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_dmapsetmap()\n");
+       if (bootverbose) {
+               kprintf("envy24ht(play): setmap %lx, %lx; ",
+                   (unsigned long)segs->ds_addr,
+                   (unsigned long)segs->ds_len);
+               kprintf("%p -> %lx\n", sc->pmap, (unsigned long)vtophys(sc->pmap));
+       }
+#endif
+}
+
+static void
+envy24ht_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+       /* struct sc_info *sc = (struct sc_info *)arg; */
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_dmarsetmap()\n");
+       if (bootverbose) {
+               kprintf("envy24ht(record): setmap %lx, %lx; ",
+                   (unsigned long)segs->ds_addr,
+                   (unsigned long)segs->ds_len);
+               kprintf("%p -> %lx\n", sc->rmap, (unsigned long)vtophys(sc->pmap));
+       }
+#endif
+}
+
+static void
+envy24ht_dmafree(struct sc_info *sc)
+{
+#if(0)
+       device_printf(sc->dev, "envy24ht_dmafree():");
+       if (sc->rmap) kprintf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap);
+       else kprintf(" sc->rmap(null)");
+       if (sc->pmap) kprintf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap);
+       else kprintf(" sc->pmap(null)");
+       if (sc->rbuf) kprintf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf);
+       else kprintf(" sc->rbuf(null)");
+       if (sc->pbuf) kprintf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf);
+       else kprintf(" sc->pbuf(null)\n");
+#endif
+#if(0)
+       if (sc->rmap)
+               bus_dmamap_unload(sc->dmat, sc->rmap);
+       if (sc->pmap)
+               bus_dmamap_unload(sc->dmat, sc->pmap);
+       if (sc->rbuf)
+               bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+       if (sc->pbuf)
+               bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+#else
+       bus_dmamap_unload(sc->dmat, sc->rmap);
+       bus_dmamap_unload(sc->dmat, sc->pmap);
+       bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+       bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+#endif
+
+       sc->rmap = sc->pmap = NULL;
+       sc->pbuf = NULL;
+       sc->rbuf = NULL;
+
+       return;
+}
+
+static int
+envy24ht_dmainit(struct sc_info *sc)
+{
+       u_int32_t addr;
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_dmainit()\n");
+#endif
+       /* init values */
+       sc->psize = ENVY24HT_PLAY_BUFUNIT * ENVY24HT_SAMPLE_NUM;
+       sc->rsize = ENVY24HT_REC_BUFUNIT * ENVY24HT_SAMPLE_NUM;
+       sc->pbuf = NULL;
+       sc->rbuf = NULL;
+       sc->pmap = sc->rmap = NULL;
+       sc->blk[0] = sc->blk[1] = 0;
+
+       /* allocate DMA buffer */
+#if(0)
+       device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->pbuf\n");
+#endif
+       if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap))
+               goto bad;
+#if(0)
+       device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->rbuf\n");
+#endif
+       if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap))
+               goto bad;
+#if(0)
+       device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->pmap\n");
+#endif
+       if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24ht_dmapsetmap, sc, 0))
+               goto bad;
+#if(0)
+       device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->rmap\n");
+#endif
+       if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24ht_dmarsetmap, sc, 0))
+               goto bad;
+       bzero(sc->pbuf, sc->psize);
+       bzero(sc->rbuf, sc->rsize);
+
+       /* set values to register */
+       addr = vtophys(sc->pbuf);
+#if(0)
+       device_printf(sc->dev, "pbuf(0x%08x)\n", addr);
+#endif
+       envy24ht_wrmt(sc, ENVY24HT_MT_PADDR, addr, 4);
+#if(0)
+       device_printf(sc->dev, "PADDR-->(0x%08x)\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4));
+       device_printf(sc->dev, "psize(%ld)\n", sc->psize / 4 - 1);
+#endif
+       envy24ht_wrmt(sc, ENVY24HT_MT_PCNT, sc->psize / 4 - 1, 2);
+#if(0)
+       device_printf(sc->dev, "PCNT-->(%ld)\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2));
+#endif
+       addr = vtophys(sc->rbuf);
+       envy24ht_wrmt(sc, ENVY24HT_MT_RADDR, addr, 4);
+       envy24ht_wrmt(sc, ENVY24HT_MT_RCNT, sc->rsize / 4 - 1, 2);
+
+       return 0;
+ bad:
+       envy24ht_dmafree(sc);
+       return ENOSPC;
+}
+
+static void
+envy24ht_putcfg(struct sc_info *sc)
+{
+       device_printf(sc->dev, "system configuration\n");
+       kprintf("  SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n",
+           sc->cfg->subvendor, sc->cfg->subdevice);
+       kprintf("  XIN2 Clock Source: ");
+       switch (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) {
+       case 0x00:
+               kprintf("24.576MHz(96kHz*256)\n");
+               break;
+       case 0x40:
+               kprintf("49.152MHz(192kHz*256)\n");
+               break;
+       case 0x80:
+               kprintf("reserved\n");
+               break;
+       default:
+               kprintf("illeagal system setting\n");
+       }
+       kprintf("  MPU-401 UART(s) #: ");
+       if (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_MPU)
+               kprintf("1\n");
+       else
+               kprintf("not implemented\n");
+        switch (sc->adcn) {
+        case 0x01 || 0x02:
+                kprintf("  ADC #: ");
+                kprintf("%d\n", sc->adcn);
+                break;
+        case 0x03:
+                kprintf("  ADC #: ");
+                kprintf("%d", 1);
+                kprintf(" and SPDIF receiver connected\n");
+                break;
+        default:
+                kprintf("  no physical inputs\n");
+        }
+       kprintf("  DAC #: ");
+       kprintf("%d\n", sc->dacn);
+       kprintf("  Multi-track converter type: ");
+       if ((sc->cfg->acl & ENVY24HT_CCSM_ACL_MTC) == 0) {
+               kprintf("AC'97(SDATA_OUT:");
+               if (sc->cfg->acl & ENVY24HT_CCSM_ACL_OMODE)
+                       kprintf("packed");
+               else
+                       kprintf("split");
+               kprintf(")\n");
+       }
+       else {
+               kprintf("I2S(");
+               if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_VOL)
+                       kprintf("with volume, ");
+                if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_192KHZ)
+                        kprintf("192KHz support, ");
+                else
+                if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_96KHZ)
+                        kprintf("192KHz support, ");
+                else
+                        kprintf("48KHz support, ");
+               switch (sc->cfg->i2s & ENVY24HT_CCSM_I2S_RES) {
+               case ENVY24HT_CCSM_I2S_16BIT:
+                       kprintf("16bit resolution, ");
+                       break;
+               case ENVY24HT_CCSM_I2S_18BIT:
+                       kprintf("18bit resolution, ");
+                       break;
+               case ENVY24HT_CCSM_I2S_20BIT:
+                       kprintf("20bit resolution, ");
+                       break;
+               case ENVY24HT_CCSM_I2S_24BIT:
+                       kprintf("24bit resolution, ");
+                       break;
+               }
+               kprintf("ID#0x%x)\n", sc->cfg->i2s & ENVY24HT_CCSM_I2S_ID);
+       }
+       kprintf("  S/PDIF(IN/OUT): ");
+       if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_IN)
+               kprintf("1/");
+       else
+               kprintf("0/");
+       if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_OUT)
+               kprintf("1 ");
+       else
+               kprintf("0 ");
+       if (sc->cfg->spdif & (ENVY24HT_CCSM_SPDIF_IN | ENVY24HT_CCSM_SPDIF_OUT))
+               kprintf("ID# 0x%02x\n", (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_ID) >> 2);
+       kprintf("  GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n",
+           sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate);
+}
+
+static int
+envy24ht_init(struct sc_info *sc)
+{
+       u_int32_t data;
+#if(0)
+       int rtn;
+#endif
+       int i;
+       u_int32_t sv, sd;
+
+
+#if(0)
+       device_printf(sc->dev, "envy24ht_init()\n");
+#endif
+
+       /* reset chip */
+#if 0
+       envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_RESET, 1);
+       DELAY(200);
+       envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_NATIVE, 1);
+       DELAY(200);
+
+       /* legacy hardware disable */
+       data = pci_read_config(sc->dev, PCIR_LAC, 2);
+       data |= PCIM_LAC_DISABLE;
+       pci_write_config(sc->dev, PCIR_LAC, data, 2);
+#endif
+
+       /* check system configuration */
+       sc->cfg = NULL;
+       for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
+               /* 1st: search configuration from table */
+               sv = pci_get_subvendor(sc->dev);
+               sd = pci_get_subdevice(sc->dev);
+               if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) {
+#if(0)
+                       device_printf(sc->dev, "Set configuration from table\n");
+#endif
+                       sc->cfg = &cfg_table[i];
+                       break;
+               }
+       }
+       if (sc->cfg == NULL) {
+               /* 2nd: read configuration from table */
+               sc->cfg = envy24ht_rom2cfg(sc);
+       }
+       sc->adcn = ((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_ADC) >> 2) + 1; /* need to be fixed */
+       sc->dacn = (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_DAC) + 1;
+
+       if (1 /* bootverbose */) {
+               envy24ht_putcfg(sc);
+       }
+
+       /* set system configuration */
+       envy24ht_wrcs(sc, ENVY24HT_CCS_SCFG, sc->cfg->scfg, 1);
+       envy24ht_wrcs(sc, ENVY24HT_CCS_ACL, sc->cfg->acl, 1);
+       envy24ht_wrcs(sc, ENVY24HT_CCS_I2S, sc->cfg->i2s, 1);
+       envy24ht_wrcs(sc, ENVY24HT_CCS_SPDIF, sc->cfg->spdif, 1);
+       envy24ht_gpiosetmask(sc, sc->cfg->gpiomask);
+       envy24ht_gpiosetdir(sc, sc->cfg->gpiodir);
+       envy24ht_gpiowr(sc, sc->cfg->gpiostate);
+
+       if ((sc->cfg->subvendor == 0x3031) && (sc->cfg->subdevice == 0x4553)) {
+               envy24ht_wri2c(sc, 0x22, 0x00, 0x07);
+               envy24ht_wri2c(sc, 0x22, 0x04, 0x5f | 0x80);
+               envy24ht_wri2c(sc, 0x22, 0x05, 0x5f | 0x80);
+       }
+       
+       for (i = 0; i < sc->adcn; i++) {
+               sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i);
+               sc->cfg->codec->init(sc->adc[i]);
+       }
+       for (i = 0; i < sc->dacn; i++) {
+               sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i);
+               sc->cfg->codec->init(sc->dac[i]);
+       }
+
+       /* initialize DMA buffer */
+#if(0)
+       device_printf(sc->dev, "envy24ht_init(): initialize DMA buffer\n");
+#endif
+       if (envy24ht_dmainit(sc))
+               return ENOSPC;
+
+       /* initialize status */
+       sc->run[0] = sc->run[1] = 0;
+       sc->intr[0] = sc->intr[1] = 0;
+       sc->speed = 0;
+       sc->caps[0].fmtlist = envy24ht_playfmt;
+       sc->caps[1].fmtlist = envy24ht_recfmt;
+
+       /* set channel router */
+#if 0
+       envy24ht_route(sc, ENVY24HT_ROUTE_DAC_1, ENVY24HT_ROUTE_CLASS_MIX, 0, 0);
+       envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_DMA, 0, 0);
+       envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_MIX, 0, 0);
+#endif
+
+       /* set macro interrupt mask */
+       data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1);
+       envy24ht_wrcs(sc, ENVY24HT_CCS_IMASK, data & ~ENVY24HT_CCS_IMASK_PMT, 1);
+       data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1);
+#if(0)
+       device_printf(sc->dev, "envy24ht_init(): CCS_IMASK-->0x%02x\n", data);
+#endif
+
+       return 0;
+}
+
+static int
+envy24ht_alloc_resource(struct sc_info *sc)
+{
+       /* allocate I/O port resource */
+       sc->csid = PCIR_CCS;
+       sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+           &sc->csid, 0, ~0, 1, RF_ACTIVE);
+       sc->mtid = ENVY24HT_PCIR_MT;
+       sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
+           &sc->mtid, 0, ~0, 1, RF_ACTIVE);
+       if (!sc->cs || !sc->mt) {
+               device_printf(sc->dev, "unable to map IO port space\n");
+               return ENXIO;
+       }
+       sc->cst = rman_get_bustag(sc->cs);
+       sc->csh = rman_get_bushandle(sc->cs);
+       sc->mtt = rman_get_bustag(sc->mt);
+       sc->mth = rman_get_bushandle(sc->mt);
+#if(0)
+       device_printf(sc->dev,
+           "IO port register values\nCCS: 0x%lx\nMT: 0x%lx\n",
+           pci_read_config(sc->dev, PCIR_CCS, 4),
+           pci_read_config(sc->dev, PCIR_MT, 4));
+#endif
+
+       /* allocate interupt resource */
+       sc->irqid = 0;
+       sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid,
+                                0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+       if (!sc->irq ||
+           snd_setup_intr(sc->dev, sc->irq, 0, envy24ht_intr, sc, &sc->ih)) {
+               device_printf(sc->dev, "unable to map interrupt\n");
+               return ENXIO;
+       }
+
+       /* allocate DMA resource */
+       if (bus_dma_tag_create(/*parent*/NULL,
+           /*alignment*/4,
+           /*boundary*/0,
+           /*lowaddr*/BUS_SPACE_MAXADDR_ENVY24,
+           /*highaddr*/BUS_SPACE_MAXADDR_ENVY24,
+           /*filter*/NULL, /*filterarg*/NULL,
+           /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24,
+           /*nsegments*/1, /*maxsegsz*/0x3ffff,
+           /*flags*/0 , &sc->dmat) != 0) {
+               device_printf(sc->dev, "unable to create dma tag\n");
+               return ENXIO;
+       }
+
+       return 0;
+}
+
+static int
+envy24ht_pci_attach(device_t dev)
+{
+       u_int32_t               data;
+       struct sc_info          *sc;
+       char                    status[SND_STATUSLEN];
+       int                     err = 0;
+       int                     i;
+
+#if(0)
+       device_printf(dev, "envy24ht_pci_attach()\n");
+#endif
+       /* get sc_info data area */
+       if ((sc = kmalloc(sizeof(*sc), M_ENVY24HT, M_NOWAIT)) == NULL) {
+               device_printf(dev, "cannot allocate softc\n");
+               return ENXIO;
+       }
+
+       bzero(sc, sizeof(*sc));
+       sc->lock = snd_mtxcreate(device_get_nameunit(dev),
+           "snd_envy24ht softc");
+       sc->dev = dev;
+
+       /* initialize PCI interface */
+       data = pci_read_config(dev, PCIR_COMMAND, 2);
+       data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN);
+       pci_write_config(dev, PCIR_COMMAND, data, 2);
+       data = pci_read_config(dev, PCIR_COMMAND, 2);
+
+       /* allocate resources */
+       err = envy24ht_alloc_resource(sc);
+       if (err) {
+               device_printf(dev, "unable to allocate system resources\n");
+               goto bad;
+       }
+
+       /* initialize card */
+       err = envy24ht_init(sc);
+       if (err) {
+               device_printf(dev, "unable to initialize the card\n");
+               goto bad;
+       }
+
+       /* set multi track mixer */
+       mixer_init(dev, &envy24htmixer_class, sc);
+
+       /* set channel information */
+       /* err = pcm_register(dev, sc, 5, 2 + sc->adcn); */
+       err = pcm_register(dev, sc, 1, 2 + sc->adcn);
+       if (err)
+               goto bad;
+       sc->chnum = 0;
+       /* for (i = 0; i < 5; i++) { */
+               pcm_addchan(dev, PCMDIR_PLAY, &envy24htchan_class, sc);
+               sc->chnum++;
+       /* } */
+       for (i = 0; i < 2 + sc->adcn; i++) {
+               pcm_addchan(dev, PCMDIR_REC, &envy24htchan_class, sc);
+               sc->chnum++;
+       }
+
+       /* set status iformation */
+       ksnprintf(status, SND_STATUSLEN,
+           "at io 0x%lx:%ld,0x%lx:%ld irq %ld",
+           rman_get_start(sc->cs),
+           rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1,
+           rman_get_start(sc->mt),
+           rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
+           rman_get_start(sc->irq));
+       pcm_setstatus(dev, status);
+
+       return 0;
+
+bad:
+       if (sc->ih)
+               bus_teardown_intr(dev, sc->irq, sc->ih);
+       if (sc->irq)
+               bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+       envy24ht_dmafree(sc);
+       if (sc->dmat)
+               bus_dma_tag_destroy(sc->dmat);
+        if (sc->cfg->codec->destroy != NULL) {
+                for (i = 0; i < sc->adcn; i++)
+                        sc->cfg->codec->destroy(sc->adc[i]);
+                for (i = 0; i < sc->dacn; i++)
+                        sc->cfg->codec->destroy(sc->dac[i]);
+        }
+       envy24ht_cfgfree(sc->cfg);
+       if (sc->cs)
+               bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
+       if (sc->mt)
+               bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
+       if (sc->lock)
+               snd_mtxfree(sc->lock);
+       kfree(sc, M_ENVY24HT);
+       return err;
+}
+
+static int
+envy24ht_pci_detach(device_t dev)
+{
+       struct sc_info *sc;
+       int r;
+       int i;
+
+#if(0)
+       device_printf(dev, "envy24ht_pci_detach()\n");
+#endif
+       sc = pcm_getdevinfo(dev);
+       if (sc == NULL)
+               return 0;
+       r = pcm_unregister(dev);
+       if (r)
+               return r;
+
+       envy24ht_dmafree(sc);
+       if (sc->cfg->codec->destroy != NULL) {
+               for (i = 0; i < sc->adcn; i++)
+                       sc->cfg->codec->destroy(sc->adc[i]);
+               for (i = 0; i < sc->dacn; i++)
+                       sc->cfg->codec->destroy(sc->dac[i]);
+       }
+       envy24ht_cfgfree(sc->cfg);
+       bus_dma_tag_destroy(sc->dmat);
+       bus_teardown_intr(dev, sc->irq, sc->ih);
+       bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+       bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
+       bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
+       snd_mtxfree(sc->lock);
+       kfree(sc, M_ENVY24HT);
+       return 0;
+}
+
+static device_method_t envy24ht_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         envy24ht_pci_probe),
+       DEVMETHOD(device_attach,        envy24ht_pci_attach),
+       DEVMETHOD(device_detach,        envy24ht_pci_detach),
+       { 0, 0 }
+};
+
+static driver_t envy24ht_driver = {
+       "pcm",
+       envy24ht_methods,
+#if __FreeBSD_version > 500000
+       PCM_SOFTC_SIZE,
+#else
+       sizeof(struct snddev_info),
+#endif
+};
+
+DRIVER_MODULE(snd_envy24ht, pci, envy24ht_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(snd_envy24ht, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_DEPEND(snd_envy24ht, snd_spicds, 1, 1, 1);
+MODULE_VERSION(snd_envy24ht, 1);
diff --git a/sys/dev/sound/pci/envy24ht.h b/sys/dev/sound/pci/envy24ht.h
new file mode 100644 (file)
index 0000000..17bd792
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2006 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
+ * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
+ * 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 THEPOSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/sound/pci/envy24ht.h,v 1.4.2.2 2007/06/11 19:33:27 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/envy24ht.h,v 1.1 2007/06/16 19:48:05 hasso Exp $
+ */
+
+
+/* -------------------------------------------------------------------- */
+
+/* PCI device ID */
+#define PCIV_ENVY24 0x1412
+#define PCID_ENVY24HT 0x1724
+
+#define PCIR_CCS               0x10 /* Controller I/O Base Address */
+#define ENVY24HT_PCIR_MT       0x14 /* Multi-Track I/O Base Address */
+
+/* Controller Registers */
+
+#define ENVY24HT_CCS_CTL      0x00 /* Control/Status Register */
+#define ENVY24HT_CCS_CTL_RESET   0x80 /* Entire Chip soft reset */
+
+#define ENVY24HT_CCS_IMASK    0x01 /* Interrupt Mask Register */
+#define ENVY24HT_CCS_IMASK_PMT   0x10 /* Professional Multi-track */
+
+#define ENVY24HT_CCS_I2CDEV   0x10 /* I2C Port Device Address Register */ 
+#define ENVY24HT_CCS_I2CDEV_ADDR 0xfe /* I2C device address */
+#define ENVY24HT_CCS_I2CDEV_ROM  0xa0 /* reserved for the external I2C E2PROM */
+#define ENVY24HT_CCS_I2CDEV_WR   0x01 /* write */
+#define ENVY24HT_CCS_I2CDEV_RD   0x00 /* read */
+  
+#define ENVY24HT_CCS_I2CADDR  0x11 /* I2C Port Byte Address Register */
+#define ENVY24HT_CCS_I2CDATA  0x12 /* I2C Port Read/Write Data Register */
+
+#define ENVY24HT_CCS_I2CSTAT  0x13 /* I2C Port Control and Status Register */
+#define ENVY24HT_CCS_I2CSTAT_ROM 0x80 /* external E2PROM exists */
+#define ENVY24HT_CCS_I2CSTAT_BSY 0x01 /* I2C port read/write status busy */
+
+#define ENVY24HT_CCS_SCFG  0x04 /* System Configuration Register */
+#define ENVY24HT_CCSM_SCFG_XIN2      0xc0 /* XIN2 Clock Source Configuration */
+                                         /* 00: 24.576MHz(96kHz*256) */
+                                         /* 01: 49.152MHz(192kHz*256) */
+                                         /* 1x: Reserved */
+#define ENVY24HT_CCSM_SCFG_MPU       0x20 /* 0(not implemented)/1(1) MPU-401 UART */
+#define ENVY24HT_CCSM_SCFG_ADC       0x0c /* 1-2 stereo ADC connected, S/PDIF receiver connected */
+#define ENVY24HT_CCSM_SCFG_DAC       0x03 /* 1-4 stereo DAC connected */
+
+#define ENVY24HT_CCS_ACL   0x05 /* AC-Link Configuration Register */
+#define ENVY24HT_CCSM_ACL_MTC        0x80 /* Multi-track converter type: 0:AC'97 1:I2S */
+#define ENVY24HT_CCSM_ACL_OMODE      0x02 /* AC 97 codec SDATA_OUT 0:split 1:packed */
+
+#define ENVY24HT_CCS_I2S   0x06 /* I2S Converters Features Register */
+#define ENVY24HT_CCSM_I2S_VOL        0x80 /* I2S codec Volume and mute */
+#define ENVY24HT_CCSM_I2S_96KHZ      0x40 /* I2S converter 96kHz sampling rate support */
+#define ENVY24HT_CCSM_I2S_192KHZ     0x08 /* I2S converter 192kHz sampling rate support */
+#define ENVY24HT_CCSM_I2S_RES        0x30 /* Converter resolution */
+#define ENVY24HT_CCSM_I2S_16BIT      0x00 /* 16bit */
+#define ENVY24HT_CCSM_I2S_18BIT      0x10 /* 18bit */
+#define ENVY24HT_CCSM_I2S_20BIT      0x20 /* 20bit */
+#define ENVY24HT_CCSM_I2S_24BIT      0x30 /* 24bit */
+#define ENVY24HT_CCSM_I2S_ID         0x07 /* Other I2S IDs */
+
+#define ENVY24HT_CCS_SPDIF 0x07 /* S/PDIF Configuration Register */
+#define ENVY24HT_CCSM_SPDIF_INT_EN   0x80 /* Enable integrated S/PDIF transmitter */
+#define ENVY24HT_CCSM_SPDIF_INT_OUT  0x40 /* Internal S/PDIF Out implemented */
+#define ENVY24HT_CCSM_SPDIF_ID       0x3c /* S/PDIF chip ID */
+#define ENVY24HT_CCSM_SPDIF_IN       0x02 /* S/PDIF Stereo In is present */
+#define ENVY24HT_CCSM_SPDIF_OUT      0x01 /* External S/PDIF Out implemented */
+
+/* Professional Multi-Track Control Registers */
+#define ENVY24HT_MT_INT_STAT    0x00 /* DMA Interrupt Mask and Status Register */ 
+#define ENVY24HT_MT_INT_RSTAT   0x02 /* Multi-track record interrupt status */
+#define ENVY24HT_MT_INT_PSTAT   0x01 /* Multi-track playback interrupt status */
+#define ENVY24HT_MT_INT_MASK   0x03
+#define ENVY24HT_MT_INT_RMASK   0x02 /* Multi-track record interrupt mask */
+#define ENVY24HT_MT_INT_PMASK   0x01 /* Multi-track playback interrupt mask */
+
+#define ENVY24HT_MT_RATE     0x01 /* Sampling Rate Select Register */ 
+#define ENVY24HT_MT_RATE_SPDIF  0x10 /* S/PDIF input clock as the master */
+#define ENVY24HT_MT_RATE_48000  0x00
+#define ENVY24HT_MT_RATE_24000  0x01
+#define ENVY24HT_MT_RATE_12000  0x02
+#define ENVY24HT_MT_RATE_9600   0x03
+#define ENVY24HT_MT_RATE_32000  0x04
+#define ENVY24HT_MT_RATE_16000  0x05
+#define ENVY24HT_MT_RATE_8000   0x06
+#define ENVY24HT_MT_RATE_96000  0x07
+#define ENVY24HT_MT_RATE_192000 0x0e
+#define ENVY24HT_MT_RATE_64000  0x0f
+#define ENVY24HT_MT_RATE_44100  0x08
+#define ENVY24HT_MT_RATE_22050  0x09
+#define ENVY24HT_MT_RATE_11025  0x0a
+#define ENVY24HT_MT_RATE_88200  0x0b
+#define ENVY24HT_MT_RATE_176400 0x0c
+#define ENVY24HT_MT_RATE_MASK   0x0f
+
+#define ENVY24HT_MT_I2S      0x02 /* I2S Data Format Register */
+#define ENVY24HT_MT_I2S_MLR128  0x08 /* MCLK/LRCLK ratio 128x (or 256x) */
+
+#define ENVY24HT_MT_PADDR    0x10 /* Playback DMA Current/Base Address Register */
+#define ENVY24HT_MT_PCNT     0x14 /* Playback DMA Current/Base Count Register */
+#define ENVY24HT_MT_PTERM    0x1C /* Playback Current/Base Terminal Count Register */
+
+#define ENVY24HT_MT_PCTL     0x18 /* Global Playback and Record DMA Start/Stop Register */
+#define ENVY24HT_MT_PCTL_RSTART 0x02 /* 1: Record start; 0: Record stop */
+#define ENVY24HT_MT_PCTL_PSTART 0x01 /* 1: Playback start; 0: Playback stop */
+
+#define ENVY24HT_MT_RADDR    0x20 /* Record DMA Current/Base Address Register */
+#define ENVY24HT_MT_RCNT     0x24 /* Record DMA Current/Base Count Register */
+#define ENVY24HT_MT_RTERM    0x26 /* Record Current/Base Terminal Count Register */
+
+/*
+  These map values are refferd from ALSA sound driver.
+*/
+/* ENVY24 configuration E2PROM map */
+#define ENVY24HT_E2PROM_SUBVENDOR  0x02
+#define ENVY24HT_E2PROM_SUBDEVICE  0x00
+#define ENVY24HT_E2PROM_SIZE       0x04
+#define ENVY24HT_E2PROM_VERSION    0x05
+#define ENVY24HT_E2PROM_SCFG       0x06
+#define ENVY24HT_E2PROM_ACL        0x07
+#define ENVY24HT_E2PROM_I2S        0x08
+#define ENVY24HT_E2PROM_SPDIF      0x09
+#define ENVY24HT_E2PROM_GPIOMASK   0x0d
+#define ENVY24HT_E2PROM_GPIOSTATE  0x10
+#define ENVY24HT_E2PROM_GPIODIR    0x0a
+
+/* ENVY24 mixer channel defines */
+/*
+  ENVY24 mixer has original line matrix. So, general mixer command is not
+  able to use for this. If system has consumer AC'97 output, AC'97 line is
+  used as master mixer, and it is able to control.
+*/
+#define ENVY24HT_CHAN_NUM  11 /* Play * 5 + Record * 5 + Mix * 1 */
+
+#define ENVY24HT_CHAN_PLAY_DAC1  0
+#define ENVY24HT_CHAN_PLAY_DAC2  1
+#define ENVY24HT_CHAN_PLAY_DAC3  2
+#define ENVY24HT_CHAN_PLAY_DAC4  3
+#define ENVY24HT_CHAN_PLAY_SPDIF 4
+#define ENVY24HT_CHAN_REC_ADC1   5
+#define ENVY24HT_CHAN_REC_ADC2   6
+#define ENVY24HT_CHAN_REC_ADC3   7
+#define ENVY24HT_CHAN_REC_ADC4   8
+#define ENVY24HT_CHAN_REC_SPDIF  9
+#define ENVY24HT_CHAN_REC_MIX   10
+
+#define ENVY24HT_MIX_MASK     0x3fd
+#define ENVY24HT_MIX_REC_MASK 0x3e0
+
+/* volume value constants */
+#define ENVY24HT_VOL_MAX    0 /* 0db(negate) */
+#define ENVY24HT_VOL_MIN   96 /* -144db(negate) */
+#define ENVY24HT_VOL_MUTE 127 /* mute */
+
+#define BUS_SPACE_MAXADDR_ENVY24 0x0fffffff /* Address space beyond 256MB is not
+ supported */
+#define BUS_SPACE_MAXSIZE_ENVY24 0x3fffc /* 64k x 4byte(1dword) */
+
+#define ENVY24HT_CCS_GPIO_HDATA 0x1E
+#define ENVY24HT_CCS_GPIO_LDATA 0x14
+#define ENVY24HT_CCS_GPIO_LMASK 0x16
+#define ENVY24HT_CCS_GPIO_HMASK 0x1F
+#define ENVY24HT_CCS_GPIO_CTLDIR 0x18
+
index 04bc7ce..172fc9c 100644 (file)
@@ -23,8 +23,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.1 2006/10/01 11:12:59 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.1 2007/01/04 21:47:03 corecode Exp $
+ * $FreeBSD: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.2.2.1 2007/05/13 21:09:24 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.2 2007/06/16 19:48:05 hasso Exp $
  */
 
 #ifndef _HDA_REG_H_
 #define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT       17
 #define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK         0x00010000
 #define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT                16
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK         0x00000800
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT                11
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK                0x00000400
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT       10
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK                0x00000200
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT       9
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK                0x00000100
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT       8
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK                0x00000080
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT       7
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK                0x00000040
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT       6
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK                0x00000020
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT       5
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK                0x00000010
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT       4
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK                0x00000008
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT       3
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK       0x000000004
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT      2
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK       0x000000002
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT      1
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK       0x000000001
-#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT      0
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK         0x00000001
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT                0
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK                0x00000002
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT       1
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK                0x00000004
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT       2
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK                0x00000008
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT       3
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK                0x00000010
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT       4
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK                0x00000020
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT       5
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK                0x00000040
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT       6
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK                0x00000080
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT       7
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK                0x00000100
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT       8
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK       0x00000200
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT      9
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK       0x00000400
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT      10
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK       0x00000800
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT      11
 
 #define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(param)                      \
     (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK) >>            \
index 2c21898..8a00f74 100644 (file)
@@ -24,8 +24,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/sound/pci/hda/hdac.c,v 1.6 2006/10/12 04:19:37 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 01:03:06 dillon Exp $
+ * $FreeBSD: src/sys/dev/sound/pci/hda/hdac.c,v 1.36.2.2 2007/06/13 01:52:26 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.4 2007/06/16 19:48:05 hasso Exp $
  */
 
 /*
@@ -47,7 +47,7 @@
  *        http://people.freebsd.org/~ariff/HDA/parser.rb . This crude
  *        ruby parser take the verbose dmesg dump as its input. Refer to
  *        http://www.microsoft.com/whdc/device/audio/default.mspx for various
- *        interesting documents, especiall UAA (Universal Audio Architecture).
+ *        interesting documents, especially UAA (Universal Audio Architecture).
  *     4) Possible vendor specific support.
  *        (snd_hda_intel, snd_hda_ati, etc..)
  *
 
 #include "mixer_if.h"
 
-#define HDA_DRV_TEST_REV       "20061009_0031"
+#define HDA_DRV_TEST_REV       "20070611_0045"
 #define HDA_WIDGET_PARSER_REV  1
 
-SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 01:03:06 dillon Exp $");
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.4 2007/06/16 19:48:05 hasso Exp $");
 
-#undef HDA_DEBUG_ENABLED
-#define HDA_DEBUG_ENABLED      1
-
-#ifdef HDA_DEBUG_ENABLED
-#define HDA_DEBUG(stmt)        do {    \
-       stmt                    \
-} while(0)
-#else
-#define HDA_DEBUG(stmt)
-#endif
-
-#define HDA_BOOTVERBOSE(stmt)  do {    \
-       if (bootverbose) {                      \
-               stmt                            \
-       }                                       \
+#define HDA_BOOTVERBOSE(stmt)  do {                    \
+       if (bootverbose != 0) {                         \
+               stmt                                    \
+       }                                               \
 } while(0)
 
 #if 1
@@ -113,7 +102,19 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11
 #define hdac_lock(sc)          snd_mtxlock((sc)->lock)
 #define hdac_unlock(sc)                snd_mtxunlock((sc)->lock)
 #define hdac_lockassert(sc)    snd_mtxassert((sc)->lock)
-#define hdac_lockowned(sc)     (1)/*mtx_owned((sc)->lock)*/
+#define hdac_lockowned(sc)     (1)/* mtx_owned((sc)->lock) */
+
+#if 0 /* TODO: No uncacheable DMA support in DragonFly. */
+#include <machine/specialreg.h>
+#define HDAC_DMA_ATTR(sc, v, s, attr)  do {                            \
+       vm_offset_t va = (vm_offset_t)(v);                              \
+       vm_size_t sz = (vm_size_t)(s);                                  \
+       if ((sc) != NULL && (sc)->nocache != 0 && va != 0 && sz != 0)   \
+               (void)pmap_change_attr(va, sz, (attr));                 \
+} while(0)
+#else
+#define HDAC_DMA_ATTR(...)
+#endif
 
 #define HDA_FLAG_MATCH(fl, v)  (((fl) & (v)) == (v))
 #define HDA_DEV_MATCH(fl, v)   ((fl) == (v) || \
@@ -125,6 +126,9 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11
 #define HDA_MATCH_ALL          0xffffffff
 #define HDAC_INVALID           0xffffffff
 
+/* Default controller / jack sense poll: 250ms */
+#define HDAC_POLL_INTERVAL     max(hz >> 2, 1)
+
 #define HDA_MODEL_CONSTRUCT(vendor, model)     \
                (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff))
 
@@ -166,17 +170,28 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11
 
 /* OEM/subvendors */
 
+/* Intel */
+#define INTEL_D101GGC_SUBVENDOR        HDA_MODEL_CONSTRUCT(INTEL, 0xd600)
+
 /* HP/Compaq */
 #define HP_VENDORID            0x103c
 #define HP_V3000_SUBVENDOR     HDA_MODEL_CONSTRUCT(HP, 0x30b5)
 #define HP_NX7400_SUBVENDOR    HDA_MODEL_CONSTRUCT(HP, 0x30a2)
 #define HP_NX6310_SUBVENDOR    HDA_MODEL_CONSTRUCT(HP, 0x30aa)
+#define HP_NX6325_SUBVENDOR    HDA_MODEL_CONSTRUCT(HP, 0x30b0)
+#define HP_XW4300_SUBVENDOR    HDA_MODEL_CONSTRUCT(HP, 0x3013)
+#define HP_3010_SUBVENDOR      HDA_MODEL_CONSTRUCT(HP, 0x3010)
+#define HP_DV5000_SUBVENDOR    HDA_MODEL_CONSTRUCT(HP, 0x30a5)
 #define HP_ALL_SUBVENDOR       HDA_MODEL_CONSTRUCT(HP, 0xffff)
+/* What is wrong with XN 2563 anyway? (Got the picture ?) */
+#define HP_NX6325_SUBVENDORX   0x103c30b0
 
 /* Dell */
 #define DELL_VENDORID          0x1028
 #define DELL_D820_SUBVENDOR    HDA_MODEL_CONSTRUCT(DELL, 0x01cc)
 #define DELL_I1300_SUBVENDOR   HDA_MODEL_CONSTRUCT(DELL, 0x01c9)
+#define DELL_XPSM1210_SUBVENDOR        HDA_MODEL_CONSTRUCT(DELL, 0x01d7)
+#define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da)
 #define DELL_ALL_SUBVENDOR     HDA_MODEL_CONSTRUCT(DELL, 0xffff)
 
 /* Clevo */
@@ -186,11 +201,25 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11
 
 /* Acer */
 #define ACER_VENDORID          0x1025
+#define ACER_A5050_SUBVENDOR   HDA_MODEL_CONSTRUCT(ACER, 0x010f)
+#define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110)
 #define ACER_ALL_SUBVENDOR     HDA_MODEL_CONSTRUCT(ACER, 0xffff)
 
 /* Asus */
 #define ASUS_VENDORID          0x1043
 #define ASUS_M5200_SUBVENDOR   HDA_MODEL_CONSTRUCT(ASUS, 0x1993)
+#define ASUS_U5F_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0x1263)
+#define ASUS_A8JC_SUBVENDOR    HDA_MODEL_CONSTRUCT(ASUS, 0x1153)
+#define ASUS_P1AH2_SUBVENDOR   HDA_MODEL_CONSTRUCT(ASUS, 0x81cb)
+#define ASUS_A7M_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0x1323)
+#define ASUS_A7T_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0x13c2)
+#define ASUS_W6F_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0x1263)
+#define ASUS_W2J_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0x1971)
+#define ASUS_F3JC_SUBVENDOR    HDA_MODEL_CONSTRUCT(ASUS, 0x1338)
+#define ASUS_M2V_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0x81e7)
+#define ASUS_M2N_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0x8234)
+#define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb)
+#define ASUS_P5BWD_SUBVENDOR   HDA_MODEL_CONSTRUCT(ASUS, 0x81ec)
 #define ASUS_ALL_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0xffff)
 
 /* IBM / Lenovo */
@@ -198,6 +227,52 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11
 #define IBM_M52_SUBVENDOR      HDA_MODEL_CONSTRUCT(IBM, 0x02f6)
 #define IBM_ALL_SUBVENDOR      HDA_MODEL_CONSTRUCT(IBM, 0xffff)
 
+/* Lenovo */
+#define LENOVO_VENDORID                0x17aa
+#define LENOVO_3KN100_SUBVENDOR        HDA_MODEL_CONSTRUCT(LENOVO, 0x2066)
+#define LENOVO_ALL_SUBVENDOR   HDA_MODEL_CONSTRUCT(LENOVO, 0xffff)
+
+/* Samsung */
+#define SAMSUNG_VENDORID       0x144d
+#define SAMSUNG_Q1_SUBVENDOR   HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027)
+#define SAMSUNG_ALL_SUBVENDOR  HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff)
+
+/* Medion ? */
+#define MEDION_VENDORID                        0x161f
+#define MEDION_MD95257_SUBVENDOR       HDA_MODEL_CONSTRUCT(MEDION, 0x203d)
+#define MEDION_ALL_SUBVENDOR           HDA_MODEL_CONSTRUCT(MEDION, 0xffff)
+
+/*
+ * Apple Intel MacXXXX seems using Sigmatel codec/vendor id
+ * instead of their own, which is beyond my comprehension
+ * (see HDA_CODEC_STAC9221 below).
+ */
+#define APPLE_INTEL_MAC                0x76808384
+
+/* LG Electronics */
+#define LG_VENDORID            0x1854
+#define LG_LW20_SUBVENDOR      HDA_MODEL_CONSTRUCT(LG, 0x0018)
+#define LG_ALL_SUBVENDOR       HDA_MODEL_CONSTRUCT(LG, 0xffff)
+
+/* Fujitsu Siemens */
+#define FS_VENDORID            0x1734
+#define FS_PA1510_SUBVENDOR    HDA_MODEL_CONSTRUCT(FS, 0x10b8)
+#define FS_ALL_SUBVENDOR       HDA_MODEL_CONSTRUCT(FS, 0xffff)
+
+/* Toshiba */
+#define TOSHIBA_VENDORID       0x1179
+#define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001)
+#define TOSHIBA_ALL_SUBVENDOR  HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff)
+
+/* Micro-Star International (MSI) */
+#define MSI_VENDORID           0x1462
+#define MSI_MS1034_SUBVENDOR   HDA_MODEL_CONSTRUCT(MSI, 0x0349)
+#define MSI_ALL_SUBVENDOR      HDA_MODEL_CONSTRUCT(MSI, 0xffff)
+
+/* Uniwill ? */
+#define UNIWILL_VENDORID       0x1584
+#define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075)
+
 
 /* Misc constants.. */
 #define HDA_AMP_MUTE_DEFAULT   (0xffffffff)
@@ -213,18 +288,48 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11
 #define HDA_ADC_PATH   (1 << 1)
 #define HDA_ADC_RECSEL (1 << 2)
 
+#define HDA_DAC_LOCKED (1 << 3)
+#define HDA_ADC_LOCKED (1 << 4)
+
 #define HDA_CTL_OUT    (1 << 0)
 #define HDA_CTL_IN     (1 << 1)
 #define HDA_CTL_BOTH   (HDA_CTL_IN | HDA_CTL_OUT)
 
-#define HDA_GPIO_MAX           15
-/* 0 - 14 = GPIO */
+#define HDA_GPIO_MAX           8
+/* 0 - 7 = GPIO , 8 = Flush */
 #define HDA_QUIRK_GPIO0                (1 << 0)
 #define HDA_QUIRK_GPIO1                (1 << 1)
 #define HDA_QUIRK_GPIO2                (1 << 2)
-#define HDA_QUIRK_SOFTPCMVOL   (1 << 15)
-#define HDA_QUIRK_FIXEDRATE    (1 << 16)
-#define HDA_QUIRK_FORCESTEREO  (1 << 17)
+#define HDA_QUIRK_GPIO3                (1 << 3)
+#define HDA_QUIRK_GPIO4                (1 << 4)
+#define HDA_QUIRK_GPIO5                (1 << 5)
+#define HDA_QUIRK_GPIO6                (1 << 6)
+#define HDA_QUIRK_GPIO7                (1 << 7)
+#define HDA_QUIRK_GPIOFLUSH    (1 << 8)
+
+/* 9 - 25 = anything else */
+#define HDA_QUIRK_SOFTPCMVOL   (1 << 9)
+#define HDA_QUIRK_FIXEDRATE    (1 << 10)
+#define HDA_QUIRK_FORCESTEREO  (1 << 11)
+#define HDA_QUIRK_EAPDINV      (1 << 12)
+#define HDA_QUIRK_DMAPOS       (1 << 13)
+
+/* 26 - 31 = vrefs */
+#define HDA_QUIRK_IVREF50      (1 << 26)
+#define HDA_QUIRK_IVREF80      (1 << 27)
+#define HDA_QUIRK_IVREF100     (1 << 28)
+#define HDA_QUIRK_OVREF50      (1 << 29)
+#define HDA_QUIRK_OVREF80      (1 << 30)
+#define HDA_QUIRK_OVREF100     (1 << 31)
+
+#define HDA_QUIRK_IVREF                (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 | \
+                                                       HDA_QUIRK_IVREF100)
+#define HDA_QUIRK_OVREF                (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 | \
+                                                       HDA_QUIRK_OVREF100)
+#define HDA_QUIRK_VREF         (HDA_QUIRK_IVREF | HDA_QUIRK_OVREF)
+
+#define SOUND_MASK_SKIP                (1 << 30)
+#define SOUND_MASK_DISABLE     (1 << 31)
 
 static const struct {
        char *key;
@@ -233,9 +338,26 @@ static const struct {
        { "gpio0", HDA_QUIRK_GPIO0 },
        { "gpio1", HDA_QUIRK_GPIO1 },
        { "gpio2", HDA_QUIRK_GPIO2 },
+       { "gpio3", HDA_QUIRK_GPIO3 },
+       { "gpio4", HDA_QUIRK_GPIO4 },
+       { "gpio5", HDA_QUIRK_GPIO5 },
+       { "gpio6", HDA_QUIRK_GPIO6 },
+       { "gpio7", HDA_QUIRK_GPIO7 },
+       { "gpioflush", HDA_QUIRK_GPIOFLUSH },
        { "softpcmvol", HDA_QUIRK_SOFTPCMVOL },
        { "fixedrate", HDA_QUIRK_FIXEDRATE },
        { "forcestereo", HDA_QUIRK_FORCESTEREO },
+       { "eapdinv", HDA_QUIRK_EAPDINV },
+       { "dmapos", HDA_QUIRK_DMAPOS },
+       { "ivref50", HDA_QUIRK_IVREF50 },
+       { "ivref80", HDA_QUIRK_IVREF80 },
+       { "ivref100", HDA_QUIRK_IVREF100 },
+       { "ovref50", HDA_QUIRK_OVREF50 },
+       { "ovref80", HDA_QUIRK_OVREF80 },
+       { "ovref100", HDA_QUIRK_OVREF100 },
+       { "ivref", HDA_QUIRK_IVREF },
+       { "ovref", HDA_QUIRK_OVREF },
+       { "vref", HDA_QUIRK_VREF },
 };
 #define HDAC_QUIRKS_TAB_LEN    \
                (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0]))
@@ -244,15 +366,19 @@ static const struct {
 #define HDA_BDL_MAX    256
 #define HDA_BDL_DEFAULT        HDA_BDL_MIN
 
+#define HDA_BLK_MIN    HDAC_DMA_ALIGNMENT
+#define HDA_BLK_ALIGN  (~(HDA_BLK_MIN - 1))
+
 #define HDA_BUFSZ_MIN          4096
 #define HDA_BUFSZ_MAX          65536
 #define HDA_BUFSZ_DEFAULT      16384
 
 #define HDA_PARSE_MAXDEPTH     10
 
-#define HDAC_UNSOLTAG_EVENT_HP 0x00
+#define HDAC_UNSOLTAG_EVENT_HP         0x00
+#define HDAC_UNSOLTAG_EVENT_TEST       0x01
 
-static MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller");
+MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller");
 
 enum {
        HDA_PARSE_MIXER,
@@ -294,6 +420,19 @@ static const struct {
 };
 #define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0]))
 
+static const struct {
+       uint16_t vendor;
+       uint8_t reg;
+       uint8_t mask;
+       uint8_t enable;
+} hdac_pcie_snoop[] = {
+       {  INTEL_VENDORID, 0x00, 0x00, 0x00 },
+       {    ATI_VENDORID, 0x42, 0xf8, 0x02 },
+       { NVIDIA_VENDORID, 0x4e, 0xf0, 0x0f },
+};
+#define HDAC_PCIESNOOP_LEN     \
+                       (sizeof(hdac_pcie_snoop) / sizeof(hdac_pcie_snoop[0]))
+
 static const struct {
        uint32_t        rate;
        int             valid;
@@ -346,19 +485,25 @@ static const struct {
 /* Realtek */
 #define REALTEK_VENDORID       0x10ec
 #define HDA_CODEC_ALC260       HDA_CODEC_CONSTRUCT(REALTEK, 0x0260)
+#define HDA_CODEC_ALC262       HDA_CODEC_CONSTRUCT(REALTEK, 0x0262)
+#define HDA_CODEC_ALC660       HDA_CODEC_CONSTRUCT(REALTEK, 0x0660)
 #define HDA_CODEC_ALC861       HDA_CODEC_CONSTRUCT(REALTEK, 0x0861)
-#define HDA_CODEC_ALC862       HDA_CODEC_CONSTRUCT(REALTEK, 0x0862)
+#define HDA_CODEC_ALC861VD     HDA_CODEC_CONSTRUCT(REALTEK, 0x0862)
 #define HDA_CODEC_ALC880       HDA_CODEC_CONSTRUCT(REALTEK, 0x0880)
 #define HDA_CODEC_ALC882       HDA_CODEC_CONSTRUCT(REALTEK, 0x0882)
 #define HDA_CODEC_ALC883       HDA_CODEC_CONSTRUCT(REALTEK, 0x0883)
+#define HDA_CODEC_ALC885       HDA_CODEC_CONSTRUCT(REALTEK, 0x0885)
+#define HDA_CODEC_ALC888       HDA_CODEC_CONSTRUCT(REALTEK, 0x0888)
 #define HDA_CODEC_ALCXXXX      HDA_CODEC_CONSTRUCT(REALTEK, 0xffff)
 
-/* Analog Device */
-#define ANALOGDEVICE_VENDORID  0x11d4
-#define HDA_CODEC_AD1981HD     HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1981)
-#define HDA_CODEC_AD1983       HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1983)
-#define HDA_CODEC_AD1986A      HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1986)
-#define HDA_CODEC_ADXXXX       HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0xffff)
+/* Analog Devices */
+#define ANALOGDEVICES_VENDORID 0x11d4
+#define HDA_CODEC_AD1981HD     HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981)
+#define HDA_CODEC_AD1983       HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983)
+#define HDA_CODEC_AD1986A      HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986)
+#define HDA_CODEC_AD1988       HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988)
+#define HDA_CODEC_AD1988B      HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x198b)
+#define HDA_CODEC_ADXXXX       HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0xffff)
 
 /* CMedia */
 #define CMEDIA_VENDORID                0x434d
@@ -371,6 +516,8 @@ static const struct {
 #define HDA_CODEC_STAC9221D    HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683)
 #define HDA_CODEC_STAC9220     HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690)
 #define HDA_CODEC_STAC922XD    HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681)
+#define HDA_CODEC_STAC9227     HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618)
+#define HDA_CODEC_STAC9271D    HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627)
 #define HDA_CODEC_STACXXXX     HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff)
 
 /*
@@ -388,6 +535,21 @@ static const struct {
 #define HDA_CODEC_CXWAIKIKI    HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047)
 #define HDA_CODEC_CXXXXX       HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff)
 
+/* VIA */
+#define HDA_CODEC_VT1708_8     HDA_CODEC_CONSTRUCT(VIA, 0x1708)
+#define HDA_CODEC_VT1708_9     HDA_CODEC_CONSTRUCT(VIA, 0x1709)
+#define HDA_CODEC_VT1708_A     HDA_CODEC_CONSTRUCT(VIA, 0x170a)
+#define HDA_CODEC_VT1708_B     HDA_CODEC_CONSTRUCT(VIA, 0x170b)
+#define HDA_CODEC_VT1709_0     HDA_CODEC_CONSTRUCT(VIA, 0xe710)
+#define HDA_CODEC_VT1709_1     HDA_CODEC_CONSTRUCT(VIA, 0xe711)
+#define HDA_CODEC_VT1709_2     HDA_CODEC_CONSTRUCT(VIA, 0xe712)
+#define HDA_CODEC_VT1709_3     HDA_CODEC_CONSTRUCT(VIA, 0xe713)
+#define HDA_CODEC_VT1709_4     HDA_CODEC_CONSTRUCT(VIA, 0xe714)
+#define HDA_CODEC_VT1709_5     HDA_CODEC_CONSTRUCT(VIA, 0xe715)
+#define HDA_CODEC_VT1709_6     HDA_CODEC_CONSTRUCT(VIA, 0xe716)
+#define HDA_CODEC_VT1709_7     HDA_CODEC_CONSTRUCT(VIA, 0xe717)
+#define HDA_CODEC_VTXXXX       HDA_CODEC_CONSTRUCT(VIA, 0xffff)
+
 
 /* Codecs */
 static const struct {
@@ -395,64 +557,119 @@ static const struct {
        char *name;
 } hdac_codecs[] = {
        { HDA_CODEC_ALC260,    "Realtek ALC260" },
+       { HDA_CODEC_ALC262,    "Realtek ALC262" },
+       { HDA_CODEC_ALC660,    "Realtek ALC660" },
        { HDA_CODEC_ALC861,    "Realtek ALC861" },
-       { HDA_CODEC_ALC862,    "Realtek ALC862" },
+       { HDA_CODEC_ALC861VD,  "Realtek ALC861-VD" },
        { HDA_CODEC_ALC880,    "Realtek ALC880" },
        { HDA_CODEC_ALC882,    "Realtek ALC882" },
        { HDA_CODEC_ALC883,    "Realtek ALC883" },
-       { HDA_CODEC_AD1981HD,  "Analog Device AD1981HD" },
-       { HDA_CODEC_AD1983,    "Analog Device AD1983" },
-       { HDA_CODEC_AD1986A,   "Analog Device AD1986A" },
+       { HDA_CODEC_ALC885,    "Realtek ALC885" },
+       { HDA_CODEC_ALC888,    "Realtek ALC888" },
+       { HDA_CODEC_AD1981HD,  "Analog Devices AD1981HD" },
+       { HDA_CODEC_AD1983,    "Analog Devices AD1983" },
+       { HDA_CODEC_AD1986A,   "Analog Devices AD1986A" },
+       { HDA_CODEC_AD1988,    "Analog Devices AD1988" },
+       { HDA_CODEC_AD1988B,   "Analog Devices AD1988B" },
        { HDA_CODEC_CMI9880,   "CMedia CMI9880" },
        { HDA_CODEC_STAC9221,  "Sigmatel STAC9221" },
        { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" },
        { HDA_CODEC_STAC9220,  "Sigmatel STAC9220" },
        { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" },
+       { HDA_CODEC_STAC9227,  "Sigmatel STAC9227" },
+       { HDA_CODEC_STAC9271D, "Sigmatel STAC9271D" },
        { HDA_CODEC_CXVENICE,  "Conexant Venice" },
        { HDA_CODEC_CXWAIKIKI, "Conexant Waikiki" },
+       { HDA_CODEC_VT1708_8,  "VIA VT1708_8" },
+       { HDA_CODEC_VT1708_9,  "VIA VT1708_9" },
+       { HDA_CODEC_VT1708_A,  "VIA VT1708_A" },
+       { HDA_CODEC_VT1708_B,  "VIA VT1708_B" },
+       { HDA_CODEC_VT1709_0,  "VIA VT1709_0" },
+       { HDA_CODEC_VT1709_1,  "VIA VT1709_1" },
+       { HDA_CODEC_VT1709_2,  "VIA VT1709_2" },
+       { HDA_CODEC_VT1709_3,  "VIA VT1709_3" },
+       { HDA_CODEC_VT1709_4,  "VIA VT1709_4" },
+       { HDA_CODEC_VT1709_5,  "VIA VT1709_5" },
+       { HDA_CODEC_VT1709_6,  "VIA VT1709_6" },
+       { HDA_CODEC_VT1709_7,  "VIA VT1709_7" },
        /* Unknown codec */
        { HDA_CODEC_ALCXXXX,   "Realtek (Unknown)" },
-       { HDA_CODEC_ADXXXX,    "Analog Device (Unknown)" },
+       { HDA_CODEC_ADXXXX,    "Analog Devices (Unknown)" },
        { HDA_CODEC_CMIXXXX,   "CMedia (Unknown)" },
        { HDA_CODEC_STACXXXX,  "Sigmatel (Unknown)" },
        { HDA_CODEC_CXXXXX,    "Conexant (Unknown)" },
+       { HDA_CODEC_VTXXXX,    "VIA (Unknown)" },
 };
 #define HDAC_CODECS_LEN        (sizeof(hdac_codecs) / sizeof(hdac_codecs[0]))
 
 enum {
        HDAC_HP_SWITCH_CTL,
-       HDAC_HP_SWITCH_CTRL
+       HDAC_HP_SWITCH_CTRL,
+       HDAC_HP_SWITCH_DEBUG
 };
 
 static const struct {
        uint32_t model;
        uint32_t id;
        int type;
+       int inverted;
+       int polling;
+       int execsense;
        nid_t hpnid;
        nid_t spkrnid[8];
        nid_t eapdnid;
 } hdac_hp_switch[] = {
        /* Specific OEM models */
-       { HP_V3000_SUBVENDOR,  HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL,
-           17, { 16, -1 }, 16 },
+       { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 17, { 16, -1 }, 16 },
+       /* { HP_XW4300_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 21, { 16, 17, -1 }, -1 } */
+       /*{ HP_3010_SUBVENDOR,  HDA_CODEC_ALC260, HDAC_HP_SWITCH_DEBUG,
+           0, 1, 0, 16, { 15, 18, 19, 20, 21, -1 }, -1 },*/
        { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
-            6, {  5, -1 },  5 },
+           0, 0, -1, 6, { 5, -1 }, 5 },
        { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
-            6, {  5, -1 },  5 },
+           0, 0, -1, 6, { 5, -1 }, 5 },
+       { HP_NX6325_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 6, { 5, -1 }, 5 },
+       { TOSHIBA_U200_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 6, { 5, -1 }, -1 },
        { DELL_D820_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL,
-           13, { 14, -1 }, -1 },
+           0, 0, -1, 13, { 14, -1 }, -1 },
        { DELL_I1300_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL,
-           13, { 14, -1 }, -1 },
+           0, 0, -1, 13, { 14, -1 }, -1 },
+       { DELL_OPLX745_SUBVENDOR, HDA_CODEC_AD1983, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 6, { 5, 7, -1 }, -1 },
+       { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, HDAC_HP_SWITCH_CTRL,
+           0, 0, -1, 10, { 13, -1 }, -1 },
+       { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL,
+           1, 0, -1, 26, { 27, -1 }, -1 },
+       { LG_LW20_SUBVENDOR, HDA_CODEC_ALC880, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 27, { 20, -1 }, -1 },
+       { ACER_A5050_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 20, { 21, -1 }, -1 },
+       { ACER_3681WXM_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 20, { 21, -1 }, -1 },
+       { MSI_MS1034_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 20, { 27, -1 }, -1 },
        /*
         * All models that at least come from the same vendor with
         * simmilar codec.
         */
-       { HP_ALL_SUBVENDOR,  HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL,
-           17, { 16, -1 }, 16 },
+       { HP_ALL_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 17, { 16, -1 }, 16 },
        { HP_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
-            6, {  5, -1 },  5 },
+           0, 0, -1, 6, { 5, -1 }, 5 },
+       { TOSHIBA_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 6, { 5, -1 }, -1 },
        { DELL_ALL_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL,
-           13, { 14, -1 }, -1 },
+           0, 0, -1, 13, { 14, -1 }, -1 },
+       { LENOVO_ALL_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL,
+           1, 0, -1, 26, { 27, -1 }, -1 },
+#if 0
+       { ACER_ALL_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
+           0, 0, -1, 20, { 21, -1 }, -1 },
+#endif
 };
 #define HDAC_HP_SWITCH_LEN     \
                (sizeof(hdac_hp_switch) / sizeof(hdac_hp_switch[0]))
@@ -479,7 +696,7 @@ static int  hdac_get_capabilities(struct hdac_softc *);
 static void    hdac_dma_cb(void *, bus_dma_segment_t *, int, int);
 static int     hdac_dma_alloc(struct hdac_softc *,
                                        struct hdac_dma *, bus_size_t);
-static void    hdac_dma_free(struct hdac_dma *);
+static void    hdac_dma_free(struct hdac_softc *, struct hdac_dma *);
 static int     hdac_mem_alloc(struct hdac_softc *);
 static void    hdac_mem_free(struct hdac_softc *);
 static int     hdac_irq_alloc(struct hdac_softc *);
@@ -513,6 +730,9 @@ static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *,
 static int     hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *);
 static struct  hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t);
 
+static int     hdac_rirb_flush(struct hdac_softc *sc);
+static int     hdac_unsolq_flush(struct hdac_softc *sc);
+
 #define hdac_command(a1, a2, a3)       \
                hdac_command_sendone_internal(a1, a2, a3)
 
@@ -621,7 +841,7 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo)
        struct hdac_softc *sc;
        struct hdac_widget *w;
        struct hdac_audio_ctl *ctl;
-       uint32_t id, res;
+       uint32_t val, id, res;
        int i = 0, j, forcemute;
        nid_t cad;
 
@@ -650,6 +870,10 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo)
                            HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ? 0 : 1;
        }
 
+       if (hdac_hp_switch[i].execsense != -1)
+               hdac_command(sc,
+                   HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid,
+                   hdac_hp_switch[i].execsense), cad);
        res = hdac_command(sc,
            HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad);
        HDA_BOOTVERBOSE(
@@ -657,30 +881,36 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo)
                    "HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n",
                    hdac_hp_switch[i].hpnid, res);
        );
-       res >>= 31;
+       res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res);
+       res ^= hdac_hp_switch[i].inverted;
 
        switch (hdac_hp_switch[i].type) {
        case HDAC_HP_SWITCH_CTL:
                ctl = hdac_audio_ctl_amp_get(devinfo,
                    hdac_hp_switch[i].hpnid, 0, 1);
                if (ctl != NULL) {
-                       ctl->muted = (res != 0 && forcemute == 0) ?
+                       val = (res != 0 && forcemute == 0) ?
                            HDA_AMP_MUTE_NONE : HDA_AMP_MUTE_ALL;
-                       hdac_audio_ctl_amp_set(ctl,
-                           HDA_AMP_MUTE_DEFAULT, ctl->left,
-                           ctl->right);
-               }
-               for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
-                       ctl = hdac_audio_ctl_amp_get(devinfo,
-                           hdac_hp_switch[i].spkrnid[j], 0, 1);
-                       if (ctl != NULL) {
-                               ctl->muted = (res != 0 || forcemute == 1) ?
-                                   HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE;
+                       if (val != ctl->muted) {
+                               ctl->muted = val;
                                hdac_audio_ctl_amp_set(ctl,
                                    HDA_AMP_MUTE_DEFAULT, ctl->left,
                                    ctl->right);
                        }
                }
+               for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
+                       ctl = hdac_audio_ctl_amp_get(devinfo,
+                           hdac_hp_switch[i].spkrnid[j], 0, 1);
+                       if (ctl == NULL)
+                               continue;
+                       val = (res != 0 || forcemute == 1) ?
+                           HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE;
+                       if (val == ctl->muted)
+                               continue;
+                       ctl->muted = val;
+                       hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT,
+                           ctl->left, ctl->right);
+               }
                break;
        case HDAC_HP_SWITCH_CTRL:
                if (res != 0) {
@@ -689,58 +919,93 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo)
                        if (w != NULL && w->type ==
                            HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
                                if (forcemute == 0)
-                                       w->wclass.pin.ctrl |=
+                                       val = w->wclass.pin.ctrl |
                                            HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
                                else
-                                       w->wclass.pin.ctrl &=
+                                       val = w->wclass.pin.ctrl &
                                            ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
-                               hdac_command(sc,
-                                   HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid,
-                                   w->wclass.pin.ctrl), cad);
+                               if (val != w->wclass.pin.ctrl) {
+                                       w->wclass.pin.ctrl = val;
+                                       hdac_command(sc,
+                                           HDA_CMD_SET_PIN_WIDGET_CTRL(cad,
+                                           w->nid, w->wclass.pin.ctrl), cad);
+                               }
                        }
                        for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
                                w = hdac_widget_get(devinfo,
                                    hdac_hp_switch[i].spkrnid[j]);
-                               if (w != NULL && w->type ==
-                                   HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
-                                       w->wclass.pin.ctrl &=
-                                           ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
-                                       hdac_command(sc,
-                                           HDA_CMD_SET_PIN_WIDGET_CTRL(cad,
-                                           w->nid,
-                                           w->wclass.pin.ctrl), cad);
-                               }
+                               if (w == NULL || w->type !=
+                                   HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
+                                       continue;
+                               val = w->wclass.pin.ctrl &
+                                   ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
+                               if (val == w->wclass.pin.ctrl)
+                                       continue;
+                               w->wclass.pin.ctrl = val;
+                               hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(
+                                   cad, w->nid, w->wclass.pin.ctrl), cad);
                        }
                } else {
                        /* HP out */
                        w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid);
                        if (w != NULL && w->type ==
                            HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
-                               w->wclass.pin.ctrl &=
+                               val = w->wclass.pin.ctrl &
                                    ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
-                               hdac_command(sc,
-                                   HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid,
-                                   w->wclass.pin.ctrl), cad);
+                               if (val != w->wclass.pin.ctrl) {
+                                       w->wclass.pin.ctrl = val;
+                                       hdac_command(sc,
+                                           HDA_CMD_SET_PIN_WIDGET_CTRL(cad,
+                                           w->nid, w->wclass.pin.ctrl), cad);
+                               }
                        }
                        for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
                                w = hdac_widget_get(devinfo,
                                    hdac_hp_switch[i].spkrnid[j]);
-                               if (w != NULL && w->type ==
-                                   HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
-                                       if (forcemute == 0)
-                                               w->wclass.pin.ctrl |=
-                                                   HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
-                                       else
-                                               w->wclass.pin.ctrl &=
-                                                   ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
-                                       hdac_command(sc,
-                                           HDA_CMD_SET_PIN_WIDGET_CTRL(cad,
-                                           w->nid,
-                                           w->wclass.pin.ctrl), cad);
-                               }
+                               if (w == NULL || w->type !=
+                                   HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
+                                       continue;
+                               if (forcemute == 0)
+                                       val = w->wclass.pin.ctrl |
+                                           HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
+                               else
+                                       val = w->wclass.pin.ctrl &
+                                           ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
+                               if (val == w->wclass.pin.ctrl)
+                                       continue;
+                               w->wclass.pin.ctrl = val;
+                               hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(
+                                   cad, w->nid, w->wclass.pin.ctrl), cad);
                        }
                }
                break;
+       case HDAC_HP_SWITCH_DEBUG:
+               if (hdac_hp_switch[i].execsense != -1)
+                       hdac_command(sc,
+                           HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid,
+                           hdac_hp_switch[i].execsense), cad);
+               res = hdac_command(sc,
+                   HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad);
+               device_printf(sc->dev,
+                   "[ 0] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n",
+                   hdac_hp_switch[i].hpnid, res);
+               for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
+                       w = hdac_widget_get(devinfo,
+                           hdac_hp_switch[i].spkrnid[j]);
+                       if (w == NULL || w->type !=
+                           HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
+                               continue;
+                       if (hdac_hp_switch[i].execsense != -1)
+                               hdac_command(sc,
+                                   HDA_CMD_SET_PIN_SENSE(cad, w->nid,
+                                   hdac_hp_switch[i].execsense), cad);
+                       res = hdac_command(sc,
+                           HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad);
+                       device_printf(sc->dev,
+                           "[%2d] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n",
+                           j + 1, w->nid, res);
+               }
+               break;
        default:
                break;
        }
@@ -784,12 +1049,15 @@ hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag)
        case HDAC_UNSOLTAG_EVENT_HP:
                hdac_hp_switch_handler(devinfo);
                break;
+       case HDAC_UNSOLTAG_EVENT_TEST:
+               device_printf(sc->dev, "Unsol Test!\n");
+               break;
        default:
                break;
        }
 }
 
-static void
+static int
 hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
 {
        /* XXX to be removed */
@@ -798,7 +1066,7 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
 #endif
 
        if (ch->blkcnt == 0)
-               return;
+               return (0);
 
        /* XXX to be removed */
 #ifdef HDAC_INTR_EXTRA
@@ -823,16 +1091,13 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch)
 #ifdef HDAC_INTR_EXTRA
        if (res & HDAC_SDSTS_BCIS) {
 #endif
-               ch->prevptr = ch->ptr;
-               ch->ptr += sndbuf_getblksz(ch->b);
-               ch->ptr %= sndbuf_getsize(ch->b);
-               hdac_unlock(sc);
-               chn_intr(ch->c);
-               hdac_lock(sc);
+               return (1);
        /* XXX to be removed */
 #ifdef HDAC_INTR_EXTRA
        }
 #endif
+
+       return (0);
 }
 
 /****************************************************************************
@@ -846,14 +1111,16 @@ hdac_intr_handler(void *context)
        struct hdac_softc *sc;
        uint32_t intsts;
        uint8_t rirbsts;
-       uint8_t rirbwp;
-       struct hdac_rirb *rirb_base, *rirb;
-       nid_t ucad;
-       uint32_t utag;
+       struct hdac_rirb *rirb_base;
+       uint32_t trigger = 0;
 
        sc = (struct hdac_softc *)context;
 
        hdac_lock(sc);
+       if (sc->polling != 0) {
+               hdac_unlock(sc);
+               return;
+       }
        /* Do we have anything to do? */
        intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS);
        if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) {
@@ -867,26 +1134,9 @@ hdac_intr_handler(void *context)
                rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS);
                /* Get as many responses that we can */
                while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) {
-                       HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL);
-                       rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
-                       bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
-                           BUS_DMASYNC_POSTREAD);
-                       while (sc->rirb_rp != rirbwp) {
-                               sc->rirb_rp++;
-                               sc->rirb_rp %= sc->rirb_size;
-                               rirb = &rirb_base[sc->rirb_rp];
-                               if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
-                                       ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
-                                       utag = rirb->response >> 26;
-                                       if (ucad > -1 && ucad < HDAC_CODEC_MAX &&
-                                           sc->codecs[ucad] != NULL) {
-                                               sc->unsolq[sc->unsolq_wp++] =
-                                                   (ucad << 16) |
-                                                   (utag & 0xffff);
-                                               sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
-                                       }
-                               }
-                       }
+                       HDAC_WRITE_1(&sc->mem,
+                           HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL);
+                       hdac_rirb_flush(sc);
                        rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS);
                }
                /* XXX to be removed */
@@ -895,29 +1145,29 @@ hdac_intr_handler(void *context)
                HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS);
 #endif
        }
+
+       hdac_unsolq_flush(sc);
+
        if (intsts & HDAC_INTSTS_SIS_MASK) {
-               if (intsts & (1 << sc->num_iss))
-                       hdac_stream_intr(sc, &sc->play);
-               if (intsts & (1 << 0))
-                       hdac_stream_intr(sc, &sc->rec);
+               if ((intsts & (1 << sc->num_iss)) &&
+                   hdac_stream_intr(sc, &sc->play) != 0)
+                       trigger |= 1;
+               if ((intsts & (1 << 0)) &&
+                   hdac_stream_intr(sc, &sc->rec) != 0)
+                       trigger |= 2;
                /* XXX to be removed */
 #ifdef HDAC_INTR_EXTRA
-               HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & HDAC_INTSTS_SIS_MASK);
+               HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts &
+                   HDAC_INTSTS_SIS_MASK);
 #endif
        }
 
-       if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
-               sc->unsolq_st = HDAC_UNSOLQ_BUSY;
-               while (sc->unsolq_rp != sc->unsolq_wp) {
-                       ucad = sc->unsolq[sc->unsolq_rp] >> 16;
-                       utag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
-                       sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
-                       hdac_unsolicited_handler(sc->codecs[ucad], utag);
-               }
-               sc->unsolq_st = HDAC_UNSOLQ_READY;
-       }
-
        hdac_unlock(sc);
+
+       if (trigger & 1)
+               chn_intr(sc->play.c);
+       if (trigger & 2)
+               chn_intr(sc->rec.c);
 }
 
 /****************************************************************************
@@ -942,11 +1192,17 @@ hdac_reset(struct hdac_softc *sc)
                HDAC_WRITE_4(&sc->mem, HDAC_BSDCTL(sc, i), 0x0);
 
        /*
-        * Stop Control DMA engines
+        * Stop Control DMA engines.
         */
        HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, 0x0);
        HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, 0x0);
 
+       /*
+        * Reset DMA position buffer.
+        */
+       HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, 0x0);
+       HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, 0x0);
+
        /*
         * Reset the controller. The reset must remain asserted for
         * a minimum of 100us.
@@ -1068,16 +1324,6 @@ hdac_dma_cb(void *callback_arg, bus_dma_segment_t *segs, int nseg, int error)
        }
 }
 
-static void
-hdac_dma_nocache(void *ptr)
-{
-#if defined(__i386__) || defined(__amd64__)
-       vm_offset_t va;
-
-       va = (vm_offset_t)ptr;
-       pmap_kmodify_nc(va);
-#endif
-}
 
 /****************************************************************************
  * int hdac_dma_alloc
@@ -1088,9 +1334,11 @@ hdac_dma_nocache(void *ptr)
 static int
 hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size)
 {
+       bus_size_t roundsz;
        int result;
        int lowaddr;
 
+       roundsz = roundup2(size, HDAC_DMA_ALIGNMENT);
        lowaddr = (sc->support_64bit) ? BUS_SPACE_MAXADDR :
            BUS_SPACE_MAXADDR_32BIT;
        bzero(dma, sizeof(*dma));
@@ -1105,69 +1353,90 @@ hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size)
            BUS_SPACE_MAXADDR,                  /* highaddr */
            NULL,                               /* filtfunc */
            NULL,                               /* fistfuncarg */
-           size,                               /* maxsize */
+           roundsz,                            /* maxsize */
            1,                                  /* nsegments */
-           size,                               /* maxsegsz */
+           roundsz,                            /* maxsegsz */
            0,                                  /* flags */
            &dma->dma_tag);                     /* dmat */
        if (result != 0) {
                device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n",
                    __func__, result);
-               goto fail;
+               goto hdac_dma_alloc_fail;
        }
 
        /*
         * Allocate DMA memory
         */
+#if 0 /* TODO: No uncacheable DMA support in DragonFly. */
+       result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr,
+           BUS_DMA_NOWAIT | BUS_DMA_ZERO |
+           ((sc->nocache != 0) ? BUS_DMA_NOCACHE : 0), &dma->dma_map);
+#else
        result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr,
-           BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &dma->dma_map);
+           BUS_DMA_NOWAIT | BUS_DMA_ZERO, &dma->dma_map);
+#endif
        if (result != 0) {
                device_printf(sc->dev, "%s: bus_dmamem_alloc failed (%x)\n",
                    __func__, result);
-               goto fail;
+               goto hdac_dma_alloc_fail;
        }
 
+       dma->dma_size = roundsz;
+
        /*
         * Map the memory
         */
        result = bus_dmamap_load(dma->dma_tag, dma->dma_map,
-           (void *)dma->dma_vaddr, size, hdac_dma_cb, (void *)dma,
-           BUS_DMA_NOWAIT);
+           (void *)dma->dma_vaddr, roundsz, hdac_dma_cb, (void *)dma, 0);
        if (result != 0 || dma->dma_paddr == 0) {
+               if (result == 0)
+                       result = ENOMEM;
                device_printf(sc->dev, "%s: bus_dmamem_load failed (%x)\n",
                    __func__, result);
-               goto fail;
+               goto hdac_dma_alloc_fail;
        }
-       bzero((void *)dma->dma_vaddr, size);
-       hdac_dma_nocache(dma->dma_vaddr);
+
+       HDA_BOOTVERBOSE(
+               device_printf(sc->dev, "%s: size=%ju -> roundsz=%ju\n",
+                   __func__, (uintmax_t)size, (uintmax_t)roundsz);
+       );
 
        return (0);
-fail:
-       if (dma->dma_map != NULL)
-               bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
-       if (dma->dma_tag != NULL)
-               bus_dma_tag_destroy(dma->dma_tag);
+
+hdac_dma_alloc_fail:
+       hdac_dma_free(sc, dma);
+
        return (result);
 }
 
 
 /****************************************************************************
- * void hdac_dma_free(struct hdac_dma *)
+ * void hdac_dma_free(struct hdac_softc *, struct hdac_dma *)
  *
  * Free a struct dhac_dma that has been previously allocated via the
  * hdac_dma_alloc function.
  ****************************************************************************/
 static void
-hdac_dma_free(struct hdac_dma *dma)
+hdac_dma_free(struct hdac_softc *sc, struct hdac_dma *dma)
 {
-       if (dma->dma_tag != NULL) {
+       if (dma->dma_map != NULL) {
+#if 0
                /* Flush caches */
                bus_dmamap_sync(dma->dma_tag, dma->dma_map,
                    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+#endif
                bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+       }
+       if (dma->dma_vaddr != NULL) {
                bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
+               dma->dma_vaddr = NULL;
+       }
+       dma->dma_map = NULL;
+       if (dma->dma_tag != NULL) {
                bus_dma_tag_destroy(dma->dma_tag);
+               dma->dma_tag = NULL;
        }
+       dma->dma_size = 0;
 }
 
 /****************************************************************************
@@ -1210,6 +1479,7 @@ hdac_mem_free(struct hdac_softc *sc)
        if (mem->mem_res != NULL)
                bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid,
                    mem->mem_res);
+       mem->mem_res = NULL;
 }
 
 /****************************************************************************
@@ -1230,23 +1500,22 @@ hdac_irq_alloc(struct hdac_softc *sc)
        if (irq->irq_res == NULL) {
                device_printf(sc->dev, "%s: Unable to allocate irq\n",
                    __func__);
-               goto fail;
+               goto hdac_irq_alloc_fail;
        }
        result = snd_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE,
-               hdac_intr_handler, sc, &irq->irq_handle);
+           hdac_intr_handler, sc, &irq->irq_handle);
        if (result != 0) {
                device_printf(sc->dev,
                    "%s: Unable to setup interrupt handler (%x)\n",
                    __func__, result);
-               goto fail;
+               goto hdac_irq_alloc_fail;
        }
 
        return (0);
 
-fail:
-       if (irq->irq_res != NULL)
-               bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid,
-                   irq->irq_res);
+hdac_irq_alloc_fail:
+       hdac_irq_free(sc);
+
        return (ENXIO);
 }
 
@@ -1261,11 +1530,13 @@ hdac_irq_free(struct hdac_softc *sc)
        struct hdac_irq *irq;
 
        irq = &sc->irq;
-       if (irq->irq_handle != NULL)
+       if (irq->irq_res != NULL && irq->irq_handle != NULL)
                bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle);
        if (irq->irq_res != NULL)
                bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid,
                    irq->irq_res);
+       irq->irq_handle = NULL;
+       irq->irq_res = NULL;
 }
 
 /****************************************************************************
@@ -1356,17 +1627,20 @@ hdac_rirb_init(struct hdac_softc *sc)
        sc->rirb_rp = 0;
        HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST);
 
-       /* Setup the interrupt threshold */
-       HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2);
+       if (sc->polling == 0) {
+               /* Setup the interrupt threshold */
+               HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2);
 
-       /* Enable Overrun and response received reporting */
+               /* Enable Overrun and response received reporting */
 #if 0
-       HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL,
-           HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL);
+               HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL,
+                   HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL);
 #else
-       HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL);
+               HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL);
 #endif
+       }
 
+#if 0
        /*
         * Make sure that the Host CPU cache doesn't contain any dirty
         * cache lines that falls in the rirb. If I understood correctly, it
@@ -1375,6 +1649,7 @@ hdac_rirb_init(struct hdac_softc *sc)
         */
        bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
            BUS_DMASYNC_PREREAD);
+#endif
 }
 
 /****************************************************************************
@@ -1424,15 +1699,15 @@ hdac_scan_codecs(struct hdac_softc *sc)
        for (i = 0; i < HDAC_CODEC_MAX; i++) {
                if (HDAC_STATESTS_SDIWAKE(statests, i)) {
                        /* We have found a codec. */
-                       hdac_unlock(sc);
                        codec = (struct hdac_codec *)kmalloc(sizeof(*codec),
                            M_HDAC, M_ZERO | M_NOWAIT);
-                       hdac_lock(sc);
                        if (codec == NULL) {
                                device_printf(sc->dev,
                                    "Unable to allocate memory for codec\n");
                                continue;
                        }
+                       codec->commands = NULL;
+                       codec->responses_received = 0;
                        codec->verbs_sent = 0;
                        codec->sc = sc;
                        codec->cad = i;
@@ -1476,13 +1751,6 @@ hdac_probe_codec(struct hdac_codec *codec)
        startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode);
        endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode);
 
-       if (vendorid == HDAC_INVALID ||
-           revisionid == HDAC_INVALID ||
-           subnode == HDAC_INVALID) {
-               device_printf(sc->dev, "invalid response\n");
-               return (0);
-       }
-
        HDA_BOOTVERBOSE(
                device_printf(sc->dev, "HDA_DEBUG: \tstartnode=%d endnode=%d\n",
                    startnode, endnode);
@@ -1530,10 +1798,8 @@ hdac_probe_function(struct hdac_codec *codec, nid_t nid)
        if (fctgrptype != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO)
                return (NULL);
 
-       hdac_unlock(sc);
        devinfo = (struct hdac_devinfo *)kmalloc(sizeof(*devinfo), M_HDAC,
            M_NOWAIT | M_ZERO);
-       hdac_lock(sc);
        if (devinfo == NULL) {
                device_printf(sc->dev, "%s: Unable to allocate ivar\n",
                    __func__);
@@ -1562,60 +1828,96 @@ hdac_widget_connection_parse(struct hdac_widget *w)
 {
        struct hdac_softc *sc = w->devinfo->codec->sc;
        uint32_t res;
-       int i, j, max, found, entnum, cnid;
+       int i, j, max, ents, entnum;
        nid_t cad = w->devinfo->codec->cad;
        nid_t nid = w->nid;
+       nid_t cnid, addcnid, prevcnid;
+
+       w->nconns = 0;
 
        res = hdac_command(sc,
            HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad);
 
-       w->nconns = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res);
+       ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res);
 
-       if (w->nconns < 1)
+       if (ents < 1)
                return;
 
        entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4;
-       res = 0;
-       i = 0;
-       found = 0;
        max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1;
+       prevcnid = 0;
+
+#define CONN_RMASK(e)          (1 << ((32 / (e)) - 1))
+#define CONN_NMASK(e)          (CONN_RMASK(e) - 1)
+#define CONN_RESVAL(r, e, n)   ((r) >> ((32 / (e)) * (n)))
+#define CONN_RANGE(r, e, n)    (CONN_RESVAL(r, e, n) & CONN_RMASK(e))
+#define CONN_CNID(r, e, n)     (CONN_RESVAL(r, e, n) & CONN_NMASK(e))
 
-       while (i < w->nconns) {
+       for (i = 0; i < ents; i += entnum) {
                res = hdac_command(sc,
                    HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad);
                for (j = 0; j < entnum; j++) {
-                       cnid = res;
-                       cnid >>= (32 / entnum) * j;
-                       cnid &= (1 << (32 / entnum)) - 1;
-                       if (cnid == 0)
-                               continue;
-                       if (found > max) {
+                       cnid = CONN_CNID(res, entnum, j);
+                       if (cnid == 0) {
+                               if (w->nconns < ents)
+                                       device_printf(sc->dev,
+                                           "%s: nid=%d WARNING: zero cnid "
+                                           "entnum=%d j=%d index=%d "
+                                           "entries=%d found=%d res=0x%08x\n",
+                                           __func__, nid, entnum, j, i,
+                                           ents, w->nconns, res);
+                               else
+                                       goto getconns_out;
+                       }
+                       if (cnid < w->devinfo->startnode ||
+                           cnid >= w->devinfo->endnode) {
+                               HDA_BOOTVERBOSE(
+                                       device_printf(sc->dev,
+                                           "%s: GHOST: nid=%d j=%d "
+                                           "entnum=%d index=%d res=0x%08x\n",
+                                           __func__, nid, j, entnum, i, res);
+                               );
+                       }
+                       if (CONN_RANGE(res, entnum, j) == 0)
+                               addcnid = cnid;
+                       else if (prevcnid == 0 || prevcnid >= cnid) {
                                device_printf(sc->dev,
-                                   "node %d: Adding %d: "
-                                   "Max connection reached!\n",
-                                   nid, cnid);
-                               continue;
+                                   "%s: WARNING: Invalid child range "
+                                   "nid=%d index=%d j=%d entnum=%d "
+                                   "prevcnid=%d cnid=%d res=0x%08x\n",
+                                   __func__, nid, i, j, entnum, prevcnid,
+                                   cnid, res);
+                               addcnid = cnid;
+                       } else
+                               addcnid = prevcnid + 1;
+                       while (addcnid <= cnid) {
+                               if (w->nconns > max) {
+                                       device_printf(sc->dev,
+                                           "%s: nid=%d: Adding %d: "
+                                           "Max connection reached! max=%d\n",
+                                           __func__, nid, addcnid, max + 1);
+                                       goto getconns_out;
+                               }
+                               w->conns[w->nconns++] = addcnid++;
                        }
-                       w->conns[found++] = cnid;
+                       prevcnid = cnid;
                }
-               i += entnum;
        }
 
+getconns_out:
        HDA_BOOTVERBOSE(
-               if (w->nconns != found) {
-                       device_printf(sc->dev,
-                           "HDA_DEBUG: nid=%d WARNING!!! Connection "
-                           "length=%d != found=%d\n",
-                           nid, w->nconns, found);
-               }
+               device_printf(sc->dev,
+                   "HDA_DEBUG: %s: nid=%d entries=%d found=%d\n",
+                   __func__, nid, ents, w->nconns);
        );
+       return;
 }
 
 static uint32_t
 hdac_widget_pin_getconfig(struct hdac_widget *w)
 {
        struct hdac_softc *sc;
-       uint32_t config, id;
+       uint32_t config, orig, id;
        nid_t cad, nid;
 
        sc = w->devinfo->codec->sc;
@@ -1626,10 +1928,25 @@ hdac_widget_pin_getconfig(struct hdac_widget *w)
        config = hdac_command(sc,
            HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid),
            cad);
+       orig = config;
+
        /*
         * XXX REWRITE!!!! Don't argue!
         */
-       if (id == HDA_CODEC_ALC880 &&
+       if (id == HDA_CODEC_ALC880 && sc->pci_subvendor == LG_LW20_SUBVENDOR) {
+               switch (nid) {
+               case 26:
+                       config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
+                       config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN;
+                       break;
+               case 27:
+                       config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
+                       config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT;
+                       break;
+               default:
+                       break;
+               }
+       } else if (id == HDA_CODEC_ALC880 &&
            (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR ||
            sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) {
                /*
@@ -1671,11 +1988,139 @@ hdac_widget_pin_getconfig(struct hdac_widget *w)
                default:
                        break;
                }
+       } else if (id == HDA_CODEC_ALC883 &&
+           HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor)) {
+               switch (nid) {
+               case 25:
+                       config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
+                       config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED);
+                       break;
+               case 28:
+                       config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
+                       config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED);
+                       break;
+               default:
+                       break;
+               }
+       } else if (id == HDA_CODEC_CXVENICE && sc->pci_subvendor ==
+           HP_V3000_SUBVENDOR) {
+               switch (nid) {
+               case 18:
+                       config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK;
+                       config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE;
+                       break;
+               case 20:
+                       config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
+                       config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED);
+                       break;
+               case 21:
+                       config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
+                       config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED);
+                       break;
+               default:
+                       break;
+               }
+       } else if (id == HDA_CODEC_CXWAIKIKI && sc->pci_subvendor ==
+           HP_DV5000_SUBVENDOR) {
+               switch (nid) {
+               case 20:
+               case 21:
+                       config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK;
+                       config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE;
+                       break;
+               default:
+                       break;
+               }
+       } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor ==
+           ASUS_W6F_SUBVENDOR) {
+               switch (nid) {
+               case 11:
+                       config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
+                       config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED);
+                       break;
+               case 15:
+                       config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
+                       config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK);
+                       break;
+               default:
+                       break;
+               }
+       } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor ==
+           UNIWILL_9075_SUBVENDOR) {
+               switch (nid) {
+               case 15:
+                       config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
+                       config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT |
+                           HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK);
+                       break;
+               default:
+                       break;
+               }
+       } else if (id == HDA_CODEC_AD1986A && sc->pci_subvendor ==
+           ASUS_M2NPVMX_SUBVENDOR) {
+               switch (nid) {
+               case 28:        /* LINE */
+                       config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
+                       config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN;
+                       break;
+               case 29:        /* MIC */
+                       config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
+                       config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN;
+                       break;
+               default:
+                       break;
+               }
        }
 
+       HDA_BOOTVERBOSE(
+               if (config != orig)
+                       device_printf(sc->dev,
+                           "HDA_DEBUG: Pin config nid=%u 0x%08x -> 0x%08x\n",
+                           nid, orig, config);
+       );
+
        return (config);
 }
 
+static uint32_t
+hdac_widget_pin_getcaps(struct hdac_widget *w)
+{
+       struct hdac_softc *sc;
+       uint32_t caps, orig, id;
+       nid_t cad, nid;
+
+       sc = w->devinfo->codec->sc;
+       cad = w->devinfo->codec->cad;
+       nid = w->nid;
+       id = hdac_codec_id(w->devinfo);
+
+       caps = hdac_command(sc,
+           HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad);
+       orig = caps;
+
+       HDA_BOOTVERBOSE(
+               if (caps != orig)
+                       device_printf(sc->dev,
+                           "HDA_DEBUG: Pin caps nid=%u 0x%08x -> 0x%08x\n",
+                           nid, orig, caps);
+       );
+
+       return (caps);
+}
+
 static void
 hdac_widget_pin_parse(struct hdac_widget *w)
 {
@@ -1688,15 +2133,15 @@ hdac_widget_pin_parse(struct hdac_widget *w)
        config = hdac_widget_pin_getconfig(w);
        w->wclass.pin.config = config;
 
-       pincap = hdac_command(sc,
-               HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad);
+       pincap = hdac_widget_pin_getcaps(w);
        w->wclass.pin.cap = pincap;
 
        w->wclass.pin.ctrl = hdac_command(sc,
-               HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) &
-               ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE |
-               HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE |
-               HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE);
+           HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) &
+           ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE |
+           HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE |
+           HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE |
+           HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK);
 
        if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap))
                w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE;
@@ -1908,6 +2353,149 @@ hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid)
        return (&devinfo->widget[nid - devinfo->startnode]);
 }
 
+static __inline int
+hda_poll_channel(struct hdac_chan *ch)
+{
+       uint32_t sz, delta;
+       volatile uint32_t ptr;
+
+       if (ch->active == 0)
+               return (0);
+
+       sz = ch->blksz * ch->blkcnt;
+       if (ch->dmapos != NULL)
+               ptr = *(ch->dmapos);
+       else
+               ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem,
+                   ch->off + HDAC_SDLPIB);
+       ch->ptr = ptr;
+       ptr %= sz;
+       ptr &= ~(ch->blksz - 1);
+       delta = (sz + ptr - ch->prevptr) % sz;
+
+       if (delta < ch->blksz)
+               return (0);
+
+       ch->prevptr = ptr;
+
+       return (1);
+}
+
+#define hda_chan_active(sc)    ((sc)->play.active + (sc)->rec.active)
+
+static void
+hda_poll_callback(void *arg)
+{
+       struct hdac_softc *sc = arg;
+       uint32_t trigger = 0;
+
+       if (sc == NULL)
+               return;
+
+       hdac_lock(sc);
+       if (sc->polling == 0 || hda_chan_active(sc) == 0) {
+               hdac_unlock(sc);
+               return;
+       }
+
+       trigger |= (hda_poll_channel(&sc->play) != 0) ? 1 : 0;
+       trigger |= (hda_poll_channel(&sc->rec) != 0) ? 2 : 0;
+
+       /* XXX */
+       callout_reset(&sc->poll_hda, 1/*sc->poll_ticks*/,
+           hda_poll_callback, sc);
+
+       hdac_unlock(sc);
+
+       if (trigger & 1)
+               chn_intr(sc->play.c);
+       if (trigger & 2)
+               chn_intr(sc->rec.c);
+}
+
+static int
+hdac_rirb_flush(struct hdac_softc *sc)
+{
+       struct hdac_rirb *rirb_base, *rirb;
+       struct hdac_codec *codec;
+       struct hdac_command_list *commands;
+       nid_t cad;
+       uint32_t resp;
+       uint8_t rirbwp;
+       int ret = 0;
+
+       rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr;
+       rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
+#if 0
+       bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
+           BUS_DMASYNC_POSTREAD);
+#endif
+
+       while (sc->rirb_rp != rirbwp) {
+               sc->rirb_rp++;
+               sc->rirb_rp %= sc->rirb_size;
+               rirb = &rirb_base[sc->rirb_rp];
+               cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
+               if (cad < 0 || cad >= HDAC_CODEC_MAX ||
+                   sc->codecs[cad] == NULL)
+                       continue;
+               resp = rirb->response;
+               codec = sc->codecs[cad];
+               commands = codec->commands;
+               if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
+                       sc->unsolq[sc->unsolq_wp++] = (cad << 16) |
+                           ((resp >> 26) & 0xffff);
+                       sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
+               } else if (commands != NULL && commands->num_commands > 0 &&
+                   codec->responses_received < commands->num_commands)
+                       commands->responses[codec->responses_received++] =
+                           resp;
+               ret++;
+       }
+
+       return (ret);
+}
+
+static int
+hdac_unsolq_flush(struct hdac_softc *sc)
+{
+       nid_t cad;
+       uint32_t tag;
+       int ret = 0;
+
+       if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
+               sc->unsolq_st = HDAC_UNSOLQ_BUSY;
+               while (sc->unsolq_rp != sc->unsolq_wp) {
+                       cad = sc->unsolq[sc->unsolq_rp] >> 16;
+                       tag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
+                       sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
+                       hdac_unsolicited_handler(sc->codecs[cad], tag);
+                       ret++;
+               }
+               sc->unsolq_st = HDAC_UNSOLQ_READY;
+       }
+
+       return (ret);
+}
+
+static void
+hdac_poll_callback(void *arg)
+{
+       struct hdac_softc *sc = arg;
+       if (sc == NULL)
+               return;
+
+       hdac_lock(sc);
+       if (sc->polling == 0 || sc->poll_ival == 0) {
+               hdac_unlock(sc);
+               return;
+       }
+       hdac_rirb_flush(sc);
+       hdac_unsolq_flush(sc);
+       callout_reset(&sc->poll_hdac, sc->poll_ival, hdac_poll_callback, sc);
+       hdac_unlock(sc);
+}
+
 static void
 hdac_stream_stop(struct hdac_chan *ch)
 {
@@ -1919,9 +2507,50 @@ hdac_stream_stop(struct hdac_chan *ch)
            HDAC_SDCTL_RUN);
        HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl);
 
-       ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
-       ctl &= ~(1 << (ch->off >> 5));
-       HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
+       ch->active = 0;
+
+       if (sc->polling != 0) {
+               int pollticks;
+
+               if (hda_chan_active(sc) == 0) {
+                       callout_stop(&sc->poll_hda);
+                       sc->poll_ticks = 1;
+               } else {
+                       if (sc->play.active != 0)
+                               ch = &sc->play;
+                       else
+                               ch = &sc->rec;
+                       pollticks = ((uint64_t)hz * ch->blksz) /
+                           ((uint64_t)sndbuf_getbps(ch->b) *
+                           sndbuf_getspd(ch->b));
+                       pollticks >>= 2;
+                       if (pollticks > hz)
+                               pollticks = hz;
+                       if (pollticks < 1) {
+                               HDA_BOOTVERBOSE(
+                                       device_printf(sc->dev,
+                                           "%s: pollticks=%d < 1 !\n",
+                                           __func__, pollticks);
+                               );
+                               pollticks = 1;
+                       }
+                       if (pollticks > sc->poll_ticks) {
+                               HDA_BOOTVERBOSE(
+                                       device_printf(sc->dev,
+                                           "%s: pollticks %d -> %d\n",
+                                           __func__, sc->poll_ticks,
+                                           pollticks);
+                               );
+                               sc->poll_ticks = pollticks;
+                               callout_reset(&sc->poll_hda, 1,
+                                   hda_poll_callback, sc);
+                       }
+               }
+       } else {
+               ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
+               ctl &= ~(1 << (ch->off >> 5));
+               HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
+       }
 }
 
 static void
@@ -1930,14 +2559,52 @@ hdac_stream_start(struct hdac_chan *ch)
        struct hdac_softc *sc = ch->devinfo->codec->sc;
        uint32_t ctl;
 
-       ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
-       ctl |= 1 << (ch->off >> 5);
-       HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
+       if (sc->polling != 0) {
+               int pollticks;
 
-       ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
-       ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE |
-           HDAC_SDCTL_RUN;
+               pollticks = ((uint64_t)hz * ch->blksz) /
+                   ((uint64_t)sndbuf_getbps(ch->b) * sndbuf_getspd(ch->b));
+               pollticks >>= 2;
+               if (pollticks > hz)
+                       pollticks = hz;
+               if (pollticks < 1) {
+                       HDA_BOOTVERBOSE(
+                               device_printf(sc->dev,
+                                   "%s: pollticks=%d < 1 !\n",
+                                   __func__, pollticks);
+                       );
+                       pollticks = 1;
+               }
+               if (hda_chan_active(sc) == 0 || pollticks < sc->poll_ticks) {
+                       HDA_BOOTVERBOSE(
+                               if (hda_chan_active(sc) == 0) {
+                                       device_printf(sc->dev,
+                                           "%s: pollticks=%d\n",
+                                           __func__, pollticks);
+                               } else {
+                                       device_printf(sc->dev,
+                                           "%s: pollticks %d -> %d\n",
+                                           __func__, sc->poll_ticks,
+                                           pollticks);
+                               }
+                       );
+                       sc->poll_ticks = pollticks;
+                       callout_reset(&sc->poll_hda, 1, hda_poll_callback,
+                           sc);
+               }
+               ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
+               ctl |= HDAC_SDCTL_RUN;
+       } else {
+               ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL);
+               ctl |= 1 << (ch->off >> 5);
+               HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl);
+               ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0);
+               ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE |
+                   HDAC_SDCTL_RUN;
+       } 
        HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl);
+
+       ch->active = 1;
 }
 
 static void
@@ -1989,31 +2656,42 @@ static void
 hdac_bdl_setup(struct hdac_chan *ch)
 {
        struct hdac_softc *sc = ch->devinfo->codec->sc;
-       uint64_t addr;
-       int blks, size, blocksize;
        struct hdac_bdle *bdle;
+       uint64_t addr;
+       uint32_t blksz, blkcnt;
        int i;
 
        addr = (uint64_t)sndbuf_getbufaddr(ch->b);
-       size = sndbuf_getsize(ch->b);
-       blocksize = sndbuf_getblksz(ch->b);
-       blks = size / blocksize;
-       bdle = (struct hdac_bdle*)ch->bdl_dma.dma_vaddr;
+       bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr;
+
+       if (sc->polling != 0) {
+               blksz = ch->blksz * ch->blkcnt;
+               blkcnt = 1;
+       } else {
+               blksz = ch->blksz;
+               blkcnt = ch->blkcnt;
+       }
 
-       for (i = 0; i < blks; i++, bdle++) {
+       for (i = 0; i < blkcnt; i++, bdle++) {
                bdle->addrl = (uint32_t)addr;
                bdle->addrh = (uint32_t)(addr >> 32);
-               bdle->len = blocksize;
-               bdle->ioc = 1;
-
-               addr += blocksize;
+               bdle->len = blksz;
+               bdle->ioc = 1 ^ sc->polling;
+               addr += blksz;
        }
 
-       HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, size);
-       HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blks - 1);
+       HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, blksz * blkcnt);
+       HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blkcnt - 1);
        addr = ch->bdl_dma.dma_paddr;
        HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr);
        HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32));
+       if (ch->dmapos != NULL &&
+           !(HDAC_READ_4(&sc->mem, HDAC_DPIBLBASE) & 0x00000001)) {
+               addr = sc->pos_dma.dma_paddr;
+               HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE,
+                   ((uint32_t)addr & HDAC_DPLBASE_DPLBASE_MASK) | 0x00000001);
+               HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, (uint32_t)(addr >> 32));
+       }
 }
 
 static int
@@ -2028,7 +2706,6 @@ hdac_bdl_alloc(struct hdac_chan *ch)
                device_printf(sc->dev, "can't alloc bdl\n");
                return (rc);
        }
-       hdac_dma_nocache(ch->bdl_dma.dma_vaddr);
 
        return (0);
 }
@@ -2047,7 +2724,7 @@ hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid,
                v = (1 << (15 - dir)) | (1 << 13) | (index << 8) |
                    (lmute << 7) | left;
                hdac_command(sc,
-                       HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad);
+                   HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad);
                v = (1 << (15 - dir)) | (1 << 12) | (index << 8) |
                    (rmute << 7) | right;
        } else
@@ -2143,14 +2820,12 @@ hdac_command_send_internal(struct hdac_softc *sc,
        struct hdac_codec *codec;
        int corbrp;
        uint32_t *corb;
-       uint8_t rirbwp;
        int timeout;
        int retry = 10;
-       struct hdac_rirb *rirb_base, *rirb;
-       nid_t ucad;
-       uint32_t utag;
+       struct hdac_rirb *rirb_base;
 
-       if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL)
+       if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL ||
+           commands->num_commands < 1)
                return;
 
        codec = sc->codecs[cad];
@@ -2164,8 +2839,10 @@ hdac_command_send_internal(struct hdac_softc *sc,
                if (codec->verbs_sent != commands->num_commands) {
                        /* Queue as many verbs as possible */
                        corbrp = HDAC_READ_2(&sc->mem, HDAC_CORBRP);
+#if 0
                        bus_dmamap_sync(sc->corb_dma.dma_tag,
                            sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE);
+#endif
                        while (codec->verbs_sent != commands->num_commands &&
                            ((sc->corb_wp + 1) % sc->corb_size) != corbrp) {
                                sc->corb_wp++;
@@ -2175,61 +2852,30 @@ hdac_command_send_internal(struct hdac_softc *sc,
                        }
 
                        /* Send the verbs to the codecs */
+#if 0
                        bus_dmamap_sync(sc->corb_dma.dma_tag,
                            sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE);
+#endif
                        HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp);
                }
 
-               timeout = 10;
-               do {
-                       rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
-                       bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
-                           BUS_DMASYNC_POSTREAD);
-                       if (sc->rirb_rp != rirbwp) {
-                               do {
-                                       sc->rirb_rp++;
-                                       sc->rirb_rp %= sc->rirb_size;
-                                       rirb = &rirb_base[sc->rirb_rp];
-                                       if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) {
-                                               ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex);
-                                               utag = rirb->response >> 26;
-                                               if (ucad > -1 && ucad < HDAC_CODEC_MAX &&
-                                                   sc->codecs[ucad] != NULL) {
-                                                       sc->unsolq[sc->unsolq_wp++] =
-                                                           (ucad << 16) |
-                                                           (utag & 0xffff);
-                                                       sc->unsolq_wp %= HDAC_UNSOLQ_MAX;
-                                               }
-                                       } else if (codec->responses_received < commands->num_commands)
-                                               codec->commands->responses[codec->responses_received++] =
-                                                   rirb->response;
-                               } while (sc->rirb_rp != rirbwp);
-                               break;
-                       }
+               timeout = 1000;
+               while (hdac_rirb_flush(sc) == 0 && --timeout)
                        DELAY(10);
-               } while (--timeout);
        } while ((codec->verbs_sent != commands->num_commands ||
-               codec->responses_received != commands->num_commands) &&
-               --retry);
+           codec->responses_received != commands->num_commands) && --retry);
 
        if (retry == 0)
                device_printf(sc->dev,
-                       "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n",
-                       __func__, commands->num_commands,
-                       codec->verbs_sent, codec->responses_received);
+                   "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n",
+                   __func__, commands->num_commands, codec->verbs_sent,
+                   codec->responses_received);
 
+       codec->commands = NULL;
+       codec->responses_received = 0;
        codec->verbs_sent = 0;
 
-       if (sc->unsolq_st == HDAC_UNSOLQ_READY) {
-               sc->unsolq_st = HDAC_UNSOLQ_BUSY;
-               while (sc->unsolq_rp != sc->unsolq_wp) {
-                       ucad = sc->unsolq[sc->unsolq_rp] >> 16;
-                       utag = sc->unsolq[sc->unsolq_rp++] & 0xffff;
-                       sc->unsolq_rp %= HDAC_UNSOLQ_MAX;
-                       hdac_unsolicited_handler(sc->codecs[ucad], utag);
-               }
-               sc->unsolq_st = HDAC_UNSOLQ_READY;
-       }
+       hdac_unsolq_flush(sc);
 }
 
 
@@ -2261,21 +2907,21 @@ hdac_probe(device_t dev)
        for (i = 0; i < HDAC_DEVICES_LEN; i++) {
                if (hdac_devices[i].model == model) {
                        strlcpy(desc, hdac_devices[i].desc, sizeof(desc));
-                       result = 0;
+                       result = BUS_PROBE_DEFAULT;
                        break;
                }
                if (HDA_DEV_MATCH(hdac_devices[i].model, model) &&
                    class == PCIC_MULTIMEDIA &&
                    subclass == PCIS_MULTIMEDIA_HDA) {
                        strlcpy(desc, hdac_devices[i].desc, sizeof(desc));
-                       result = 0;
+                       result = BUS_PROBE_GENERIC;
                        break;
                }
        }
        if (result == ENXIO && class == PCIC_MULTIMEDIA &&
            subclass == PCIS_MULTIMEDIA_HDA) {
                strlcpy(desc, "Generic", sizeof(desc));
-               result = 0;
+               result = BUS_PROBE_GENERIC;
        }
        if (result != ENXIO) {
                strlcat(desc, " High Definition Audio Controller",
@@ -2298,14 +2944,10 @@ hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b,
        if (dir == PCMDIR_PLAY) {
                ch = &sc->play;
                ch->off = (sc->num_iss + devinfo->function.audio.playcnt) << 5;
-               ch->dir = PCMDIR_PLAY;
-               ch->sid = ++sc->streamcnt;
                devinfo->function.audio.playcnt++;
        } else {
                ch = &sc->rec;
                ch->off = devinfo->function.audio.reccnt << 5;
-               ch->dir = PCMDIR_REC;
-               ch->sid = ++sc->streamcnt;
                devinfo->function.audio.reccnt++;
        }
        if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) {
@@ -2313,6 +2955,13 @@ hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b,
                ch->pcmrates[0] = 48000;
                ch->pcmrates[1] = 0;
        }
+       if (sc->pos_dma.dma_vaddr != NULL)
+               ch->dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr +
+                   (sc->streamcnt * 8));
+       else
+               ch->dmapos = NULL;
+       ch->sid = ++sc->streamcnt;
+       ch->dir = dir;
        ch->b = b;
        ch->c = c;
        ch->devinfo = devinfo;
@@ -2328,11 +2977,29 @@ hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b,
        if (sndbuf_alloc(ch->b, sc->chan_dmat, sc->chan_size) != 0)
                return (NULL);
 
-       hdac_dma_nocache(sndbuf_getbuf(ch->b));
+       HDAC_DMA_ATTR(sc, sndbuf_getbuf(ch->b), sndbuf_getmaxsize(ch->b),
+           PAT_UNCACHEABLE);
 
        return (ch);
 }
 
+static int
+hdac_channel_free(kobj_t obj, void *data)
+{
+       struct hdac_softc *sc;
+       struct hdac_chan *ch;
+
+       ch = (struct hdac_chan *)data;
+       sc = (ch != NULL && ch->devinfo != NULL && ch->devinfo->codec != NULL) ?
+           ch->devinfo->codec->sc : NULL;
+       if (ch != NULL && sc != NULL) {
+               HDAC_DMA_ATTR(sc, sndbuf_getbuf(ch->b),
+                   sndbuf_getmaxsize(ch->b), PAT_WRITE_BACK);
+       }
+
+       return (1);
+}
+
 static int
 hdac_channel_setformat(kobj_t obj, void *data, uint32_t format)
 {
@@ -2353,16 +3020,18 @@ static int
 hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed)
 {
        struct hdac_chan *ch = data;
-       uint32_t spd = 0;
+       uint32_t spd = 0, threshold;
        int i;
 
        for (i = 0; ch->pcmrates[i] != 0; i++) {
                spd = ch->pcmrates[i];
-               if (spd >= speed)
+               threshold = spd + ((ch->pcmrates[i + 1] != 0) ?
+                   ((ch->pcmrates[i + 1] - spd) >> 1) : 0);
+               if (speed < threshold)
                        break;
        }
 
-       if (spd == 0)
+       if (spd == 0)   /* impossible */
                ch->spd = 48000;
        else
                ch->spd = spd;
@@ -2417,11 +3086,51 @@ hdac_stream_setup(struct hdac_chan *ch)
 }
 
 static int
-hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+hdac_channel_setfragments(kobj_t obj, void *data,
+                                       uint32_t blksz, uint32_t blkcnt)
+{
+       struct hdac_chan *ch = data;
+       struct hdac_softc *sc = ch->devinfo->codec->sc;
+
+       blksz &= HDA_BLK_ALIGN;
+
+       if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN))
+               blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN;
+       if (blksz < HDA_BLK_MIN)
+               blksz = HDA_BLK_MIN;
+       if (blkcnt > HDA_BDL_MAX)
+               blkcnt = HDA_BDL_MAX;
+       if (blkcnt < HDA_BDL_MIN)
+               blkcnt = HDA_BDL_MIN;
+
+       while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) {
+               if ((blkcnt >> 1) >= HDA_BDL_MIN)
+                       blkcnt >>= 1;
+               else if ((blksz >> 1) >= HDA_BLK_MIN)
+                       blksz >>= 1;
+               else
+                       break;
+       }
+
+       if ((sndbuf_getblksz(ch->b) != blksz ||
+           sndbuf_getblkcnt(ch->b) != blkcnt) &&
+           sndbuf_resize(ch->b, blkcnt, blksz) != 0)
+               device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
+                   __func__, blksz, blkcnt);
+
+       ch->blksz = sndbuf_getblksz(ch->b);
+       ch->blkcnt = sndbuf_getblkcnt(ch->b);
+
+       return (1);
+}
+
+static int
+hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz)
 {
        struct hdac_chan *ch = data;
+       struct hdac_softc *sc = ch->devinfo->codec->sc;
 
-       sndbuf_resize(ch->b, ch->blkcnt, ch->blksz);
+       hdac_channel_setfragments(obj, data, blksz, sc->chan_blkcnt);
 
        return (ch->blksz);
 }
@@ -2461,6 +3170,9 @@ hdac_channel_trigger(kobj_t obj, void *data, int go)
        struct hdac_chan *ch = data;
        struct hdac_softc *sc = ch->devinfo->codec->sc;
 
+       if (!(go == PCMTRIG_START || go == PCMTRIG_STOP || go == PCMTRIG_ABORT))
+               return (0);
+
        hdac_lock(sc);
        switch (go) {
        case PCMTRIG_START:
@@ -2470,6 +3182,8 @@ hdac_channel_trigger(kobj_t obj, void *data, int go)
        case PCMTRIG_ABORT:
                hdac_channel_stop(sc, ch);
                break;
+       default:
+               break;
        }
        hdac_unlock(sc);
 
@@ -2481,26 +3195,22 @@ hdac_channel_getptr(kobj_t obj, void *data)
 {
        struct hdac_chan *ch = data;
        struct hdac_softc *sc = ch->devinfo->codec->sc;
-       int sz, delta;
        uint32_t ptr;
 
        hdac_lock(sc);
-       ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB);
+       if (sc->polling != 0)
+               ptr = ch->ptr;
+       else if (ch->dmapos != NULL)
+               ptr = *(ch->dmapos);
+       else
+               ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB);
        hdac_unlock(sc);
 
-       sz = sndbuf_getsize(ch->b);
-       ptr %= sz;
-
-       if (ch->dir == PCMDIR_REC) {
-               delta = ptr % sndbuf_getblksz(ch->b);
-               if (delta != 0) {
-                       ptr -= delta;
-                       if (ptr < delta)
-                               ptr = sz - delta;
-                       else
-                               ptr -= delta;
-               }
-       }
+       /*
+        * Round to available space and force 128 bytes aligment.
+        */
+       ptr %= ch->blksz * ch->blkcnt;
+       ptr &= HDA_BLK_ALIGN;
 
        return (ptr);
 }
@@ -2513,6 +3223,7 @@ hdac_channel_getcaps(kobj_t obj, void *data)
 
 static kobj_method_t hdac_channel_methods[] = {
        KOBJMETHOD(channel_init,                hdac_channel_init),
+       KOBJMETHOD(channel_free,                hdac_channel_free),
        KOBJMETHOD(channel_setformat,           hdac_channel_setformat),
        KOBJMETHOD(channel_setspeed,            hdac_channel_setspeed),
        KOBJMETHOD(channel_setblocksize,        hdac_channel_setblocksize),
@@ -2523,6 +3234,27 @@ static kobj_method_t hdac_channel_methods[] = {
 };
 CHANNEL_DECLARE(hdac_channel);
 
+static void
+hdac_jack_poll_callback(void *arg)
+{
+       struct hdac_devinfo *devinfo = arg;
+       struct hdac_softc *sc;
+
+       if (devinfo == NULL || devinfo->codec == NULL ||
+           devinfo->codec->sc == NULL)
+               return;
+       sc = devinfo->codec->sc;
+       hdac_lock(sc);
+       if (sc->poll_ival == 0) {
+               hdac_unlock(sc);
+               return;
+       }
+       hdac_hp_switch_handler(devinfo);
+       callout_reset(&sc->poll_jack, sc->poll_ival,
+           hdac_jack_poll_callback, devinfo);
+       hdac_unlock(sc);
+}
+
 static int
 hdac_audio_ctl_ossmixer_init(struct snd_mixer *m)
 {
@@ -2543,31 +3275,35 @@ hdac_audio_ctl_ossmixer_init(struct snd_mixer *m)
        cad = devinfo->codec->cad;
        for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) {
                if (!(HDA_DEV_MATCH(hdac_hp_switch[i].model,
-                   sc->pci_subvendor) &&
-                   hdac_hp_switch[i].id == id))
+                   sc->pci_subvendor) && hdac_hp_switch[i].id == id))
                        continue;
                w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid);
-               if (w != NULL && w->enable != 0
-                   && w->type ==
-                   HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
-                   HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) {
+               if (w == NULL || w->enable == 0 || w->type !=
+                   HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
+                       continue;
+               if (hdac_hp_switch[i].polling != 0)
+                       callout_reset(&sc->poll_jack, 1,
+                           hdac_jack_poll_callback, devinfo);
+               else if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap))
                 &nb