summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornetchild <netchild@FreeBSD.org>2006-06-17 14:36:44 +0000
committernetchild <netchild@FreeBSD.org>2006-06-17 14:36:44 +0000
commit89b30c059ce7057890d61d0e541e2133ba533930 (patch)
tree91e403cf29a159cb8202f6a7c5108d0bb041236a
parent3a6d7ed2cf9128621b9e35fd816b275b2dcef3e9 (diff)
downloadFreeBSD-src-89b30c059ce7057890d61d0e541e2133ba533930.zip
FreeBSD-src-89b30c059ce7057890d61d0e541e2133ba533930.tar.gz
dd the envy24 driver as is to the tree. It's not connected to the build
yet. More commits to follow. I got no response from the author, but since the driver is BSD licensed I don't think he will complain. :-) I got it from http://people.freebsd.org/~lofi/envy24.tar.gz Written by: Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
-rw-r--r--sys/dev/sound/pci/ak452x.c247
-rw-r--r--sys/dev/sound/pci/ak452x.h91
-rw-r--r--sys/dev/sound/pci/envy24.c2445
-rw-r--r--sys/dev/sound/pci/envy24.h482
-rw-r--r--sys/dev/sound/pci/envy24ht.c2445
-rw-r--r--sys/dev/sound/pci/envy24ht.h482
-rw-r--r--sys/dev/sound/pci/spicds.c247
-rw-r--r--sys/dev/sound/pci/spicds.h91
-rw-r--r--sys/modules/sound/driver/ak452x/Makefile8
-rw-r--r--sys/modules/sound/driver/envy24/Makefile8
10 files changed, 6546 insertions, 0 deletions
diff --git a/sys/dev/sound/pci/ak452x.c b/sys/dev/sound/pci/ak452x.c
new file mode 100644
index 0000000..2ca3501
--- /dev/null
+++ b/sys/dev/sound/pci/ak452x.c
@@ -0,0 +1,247 @@
+/*
+ * 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$
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+#include <dev/sound/pci/ak452x.h>
+
+MALLOC_DEFINE(M_AK452X, "ak452x", "ak452x codec");
+
+#define AK452X_NAMELEN 16
+struct ak452x_info {
+ device_t dev;
+ ak452x_ctrl ctrl;
+ void *devinfo;
+ int num; /* number of this device */
+ unsigned int type; /* codec type */
+ unsigned int cif; /* Controll data Interface Format (0/1) */
+ unsigned int format; /* data format and master clock frequency */
+ unsigned int dvc; /* De-emphasis and Volume Control */
+ unsigned int left, right;
+ char name[AK452X_NAMELEN];
+ void *lock;
+};
+
+static void
+ak452x_wrbit(struct ak452x_info *codec, int bit)
+{
+ unsigned int cs, cdti;
+ if (codec->cif)
+ cs = 1;
+ else
+ cs = 0;
+ if (bit)
+ cdti = 1;
+ else
+ cdti = 0;
+ codec->ctrl(codec->devinfo, cs, 0, cdti);
+ DELAY(1);
+ codec->ctrl(codec->devinfo, cs, 1, cdti);
+ DELAY(1);
+
+ return;
+}
+
+static void
+ak452x_wrcd(struct ak452x_info *codec, int reg, u_int8_t val)
+{
+ int mask;
+
+#if(0)
+ device_printf(codec->dev, "ak452x_wrcd(codec, 0x%02x, 0x%02x)\n", reg, val);
+#endif
+ /* start */
+ if (codec->cif)
+ codec->ctrl(codec->devinfo, 1, 1, 0);
+ else
+ codec->ctrl(codec->devinfo, 0, 1, 0);
+ DELAY(1);
+ /* chip address */
+ ak452x_wrbit(codec, 1);
+ ak452x_wrbit(codec, 0);
+ /* write */
+ ak452x_wrbit(codec, 1);
+ /* register address */
+ for (mask = 0x10; mask != 0; mask >>= 1)
+ ak452x_wrbit(codec, reg & mask);
+ /* data */
+ for (mask = 0x80; mask != 0; mask >>= 1)
+ ak452x_wrbit(codec, val & mask);
+ /* stop */
+ DELAY(1);
+ if (codec->cif) {
+ codec->ctrl(codec->devinfo, 0, 1, 0);
+ DELAY(1);
+ codec->ctrl(codec->devinfo, 1, 1, 0);
+ }
+ else {
+ codec->ctrl(codec->devinfo, 1, 1, 0);
+ }
+
+ return;
+}
+
+struct ak452x_info *
+ak452x_create(device_t dev, void *devinfo, int num, ak452x_ctrl ctrl)
+{
+ struct ak452x_info *codec;
+
+#if(0)
+ device_printf(dev, "ak452x_create(dev, devinfo, %d, ctrl)\n", num);
+#endif
+ codec = (struct ak452x_info *)malloc(sizeof *codec, M_AK452X, M_NOWAIT);
+ if (codec == NULL)
+ return NULL;
+
+ snprintf(codec->name, AK452X_NAMELEN, "%s:ak452x%d", device_get_nameunit(dev), num);
+ codec->lock = snd_mtxcreate(codec->name);
+ codec->dev = dev;
+ codec->ctrl = ctrl;
+ codec->devinfo = devinfo;
+ codec->num = num;
+ codec->type = AK452X_TYPE_4524;
+ codec->cif = 0;
+ codec->format = AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X;
+ codec->dvc = AK452X_DVC_DEMOFF | AK452X_DVC_ZTM1024 | AK452X_DVC_ZCE;
+
+ return codec;
+}
+
+void
+ak452x_destroy(struct ak452x_info *codec)
+{
+ snd_mtxfree(codec->lock);
+ free(codec, M_AK452X);
+}
+
+void
+ak452x_settype(struct ak452x_info *codec, unsigned int type)
+{
+ snd_mtxlock(codec->lock);
+ codec->type = type;
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_setcif(struct ak452x_info *codec, unsigned int cif)
+{
+ snd_mtxlock(codec->lock);
+ codec->cif = cif;
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_setformat(struct ak452x_info *codec, unsigned int format)
+{
+ snd_mtxlock(codec->lock);
+ codec->format = format;
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_setdvc(struct ak452x_info *codec, unsigned int dvc)
+{
+ snd_mtxlock(codec->lock);
+ codec->type = dvc;
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_init(struct ak452x_info *codec)
+{
+#if(0)
+ device_printf(codec->dev, "ak452x_init(codec)\n");
+#endif
+ snd_mtxlock(codec->lock);
+ /* power off */
+ ak452x_wrcd(codec, AK4524_POWER, 0);
+ /* set parameter */
+ ak452x_wrcd(codec, AK4524_FORMAT, codec->format);
+ ak452x_wrcd(codec, AK4524_DVC, codec->dvc);
+ /* power on */
+ ak452x_wrcd(codec, AK4524_POWER, AK452X_POWER_PWDA | AK452X_POWER_PWAD | AK452X_POWER_PWVR);
+ /* free reset register */
+ ak452x_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD);
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_reinit(struct ak452x_info *codec)
+{
+ snd_mtxlock(codec->lock);
+ /* reset */
+ ak452x_wrcd(codec, AK4524_RESET, 0);
+ /* set parameter */
+ ak452x_wrcd(codec, AK4524_FORMAT, codec->format);
+ ak452x_wrcd(codec, AK4524_DVC, codec->dvc);
+ /* free reset register */
+ ak452x_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD);
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_set(struct ak452x_info *codec, int dir, unsigned int left, unsigned int right)
+{
+#if(0)
+ device_printf(codec->dev, "ak452x_set(codec, %d, %d, %d)\n", dir, left, right);
+#endif
+ snd_mtxlock(codec->lock);
+ if (left >= 100)
+ left = 127;
+ else
+ left = left * 127 / 100;
+ if (right >= 100)
+ right = 127;
+ else
+ right = right * 127 / 100;
+ if (dir == PCMDIR_REC && codec->type == AK452X_TYPE_4524) {
+#if(0)
+ device_printf(codec->dev, "ak452x_set(): AK4524(REC) %d/%d\n", left, right);
+#endif
+ ak452x_wrcd(codec, AK4524_LIPGA, left);
+ ak452x_wrcd(codec, AK4524_RIPGA, right);
+ }
+ if (dir == PCMDIR_PLAY && codec->type == AK452X_TYPE_4524) {
+#if(0)
+ device_printf(codec->dev, "ak452x_set(): AK4524(PLAY) %d/%d\n", left, right);
+#endif
+ ak452x_wrcd(codec, AK4524_LOATT, left);
+ ak452x_wrcd(codec, AK4524_ROATT, right);
+ }
+ if (dir == PCMDIR_PLAY && codec->type == AK452X_TYPE_4528) {
+#if(0)
+ device_printf(codec->dev, "ak452x_set(): AK4528(PLAY) %d/%d\n", left, right);
+#endif
+ ak452x_wrcd(codec, AK4528_LOATT, left);
+ ak452x_wrcd(codec, AK4528_ROATT, right);
+ }
+ snd_mtxunlock(codec->lock);
+}
+
+MODULE_DEPEND(snd_ak452x, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+MODULE_VERSION(snd_ak452x, 1);
diff --git a/sys/dev/sound/pci/ak452x.h b/sys/dev/sound/pci/ak452x.h
new file mode 100644
index 0000000..1df631f
--- /dev/null
+++ b/sys/dev/sound/pci/ak452x.h
@@ -0,0 +1,91 @@
+/*
+ * 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$
+ */
+
+/* supported CODECs */
+#define AK452X_TYPE_4524 0
+#define AK452X_TYPE_4528 1
+
+/* AK4524/AK4528 control registers */
+#define AK4524_POWER 0x00
+#define AK4528_POWER 0x00
+#define AK452X_POWER_PWDA 0x01
+#define AK452X_POWER_PWAD 0x02
+#define AK452X_POWER_PWVR 0x04
+#define AK4524_RESET 0x01
+#define AK4528_RESET 0x01
+#define AK452X_RESET_RSDA 0x01
+#define AK452X_RESET_RSAD 0x02
+#define AK4524_FORMAT 0x02
+#define AK4528_FORMAT 0x02
+#define AK452X_FORMAT_1X 0x00
+#define AK452X_FORMAT_2X 0x01
+#define AK452X_FORMAT_4X1 0x02
+#define AK452X_FORMAT_4X2 0x03
+#define AK452X_FORMAT_256FSN 0x00
+#define AK452X_FORMAT_512FSN 0x04
+#define AK452X_FORMAT_1024FSN 0x08
+#define AK452X_FORMAT_384FSN 0x10
+#define AK452X_FORMAT_768FSN 0x14
+#define AK452X_FORMAT_OM24IL16 0x00
+#define AK452X_FORMAT_OM24IL20 0x20
+#define AK452X_FORMAT_OM24IM24 0x40
+#define AK452X_FORMAT_I2S 0x60
+#define AK452X_FORMAT_OM24IL24 0x80
+#define AK4524_DVC 0x03
+#define AK452X_DVC_DEM441 0x00
+#define AK452X_DVC_DEMOFF 0x01
+#define AK452X_DVC_DEM48 0x02
+#define AK452X_DVC_DEM32 0x03
+#define AK452X_DVC_ZTM256 0x00
+#define AK452X_DVC_ZTM512 0x04
+#define AK452X_DVC_ZTM1024 0x08
+#define AK452X_DVC_ZTM2048 0x0c
+#define AK452X_DVC_ZCE 0x10
+#define AK452X_DVC_HPFL 0x04
+#define AK452X_DVC_HPFR 0x08
+#define AK452X_DVC_SMUTE 0x80
+#define AK4524_LIPGA 0x04
+#define AK4524_RIPGA 0x05
+#define AK4524_LOATT 0x06
+#define AK4524_ROATT 0x07
+#define AK4528_LOATT 0x04
+#define AK4528_ROATT 0x05
+
+struct ak452x_info;
+
+typedef void (*ak452x_ctrl)(void *, unsigned int, unsigned int, unsigned int);
+
+struct ak452x_info *ak452x_create(device_t dev, void *devinfo, int num, ak452x_ctrl);
+void ak452x_destroy(struct ak452x_info *codec);
+void ak452x_settype(struct ak452x_info *codec, unsigned int type);
+void ak452x_setcif(struct ak452x_info *codec, unsigned int cif);
+void ak452x_setformat(struct ak452x_info *codec, unsigned int format);
+void ak452x_setdvc(struct ak452x_info *codec, unsigned int dvc);
+void ak452x_init(struct ak452x_info *codec);
+void ak452x_reinit(struct ak452x_info *codec);
+void ak452x_set(struct ak452x_info *codec, int dir, unsigned int left, unsigned int right);
diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c
new file mode 100644
index 0000000..c23efc3
--- /dev/null
+++ b/sys/dev/sound/pci/envy24.c
@@ -0,0 +1,2445 @@
+/*
+ * 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$
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+#include <dev/sound/pci/ak452x.h>
+#include <dev/sound/pci/envy24.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#include "mixer_if.h"
+
+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
+
+typedef volatile u_int32_t 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 free;
+ struct codec_entry *codec;
+};
+
+/* device private data */
+struct sc_info {
+ device_t dev;
+ void *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,
+ 0,
+ &delta_codec,
+ },
+ {
+ "Envy24 audio(Generic)",
+ 0, 0,
+ 0x0f, 0x00, 0x01, 0x03,
+ 0xff, 0x00, 0x00,
+ 0,
+ &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;
+}
+
+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;
+}
+
+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 = malloc(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)
+ free(cfg, M_ENVY24);
+ return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* AC'97 codec access routines */
+
+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;
+}
+
+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;
+}
+
+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);
+
+/* -------------------------------------------------------------------- */
+
+/* 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;
+}
+
+static u_int32_t
+envy24_gpiogetmask(struct sc_info *sc)
+{
+ return envy24_rdci(sc, ENVY24_CCI_GPIOMASK);
+}
+
+static void
+envy24_gpiosetmask(struct sc_info *sc, u_int32_t mask)
+{
+ envy24_wrci(sc, ENVY24_CCI_GPIOMASK, mask);
+ return;
+}
+
+static u_int32_t
+envy24_gpiogetdir(struct sc_info *sc)
+{
+ return envy24_rdci(sc, ENVY24_CCI_GPIOCTL);
+}
+
+static void
+envy24_gpiosetdir(struct sc_info *sc, u_int32_t dir)
+{
+ envy24_wrci(sc, ENVY24_CCI_GPIOCTL, dir);
+ return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* M-Audio Delta series AK4524 access interface routine */
+
+struct envy24_delta_ak4524_codec {
+ struct ak452x_info *info;
+ struct sc_info *parent;
+ int dir;
+ int num;
+ int cs, cclk, cdti;
+};
+
+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 = malloc(sizeof(*buff), M_ENVY24, M_NOWAIT);
+ if (buff == NULL)
+ return NULL;
+
+ if (dir == PCMDIR_PLAY && sc->adc[num] != NULL)
+ buff->info = ((struct envy24_delta_ak4524_codec *)sc->adc[num])->info;
+ else if (dir == PCMDIR_REC && sc->dac[num] != NULL)
+ buff->info = ((struct envy24_delta_ak4524_codec *)sc->dac[num])->info;
+ else
+ buff->info = ak452x_create(dev, buff, num, envy24_delta_ak4524_ctl);
+ if (buff->info == NULL) {
+ free(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->adc[ptr->num] == NULL)
+ ak452x_destroy(ptr->info);
+ }
+ else {
+ if (ptr->parent->dac[ptr->num] == NULL)
+ ak452x_destroy(ptr->info);
+ }
+
+ free(codec, M_ENVY24);
+}
+
+static void
+envy24_delta_ak4524_init(void *codec)
+{
+ u_int32_t gpiomask, gpiodir;
+ 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);
+ */
+ 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;
+ ptr->cdti = ENVY24_GPIO_AK4524_CDTI;
+ ak452x_settype(ptr->info, AK452X_TYPE_4524);
+ ak452x_setcif(ptr->info, ENVY24_DELTA_AK4524_CIF);
+ ak452x_setformat(ptr->info,
+ AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X);
+ ak452x_setdvc(ptr->info, 0);
+ ak452x_init(ptr->info);
+}
+
+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
+
+ ak452x_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
+
+ ak452x_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
+ 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;
+}
+
+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;
+}
+
+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);
+ }
+
+ 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] = data[src];
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = 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] = (u_int32_t)data[src] << 16;
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = (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] = ((u_int32_t)data[src] ^ 0x80) << 24;
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = ((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];
+ data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1];
+ dst += 2;
+ dst %= dsize;
+ src++;
+ src %= ssize;
+ }
+
+ return;
+}
+
+static void
+envy24_r16sl(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) / 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];
+ data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1];
+ 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 = malloc(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];
+ sndbuf_setup(ch->buffer, ch->data, ch->size);
+ /* 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) {
+ free(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 (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_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;
+
+#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;
+ 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;
+ int i;
+
+#if(0)
+ 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(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;
+ }
+ 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);
+ }
+ 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)
+ chn_intr(ch->channel);
+ }
+ 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)
+ chn_intr(ch->channel);
+ }
+ 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");
+#endif
+ 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));
+ }
+}
+
+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");
+#endif
+ 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));
+ }
+}
+
+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", sc->adcn, sc->dacn);
+ printf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n",
+ sc->cfg->subvendor, sc->cfg->subdevice);
+ printf(" XIN2 Clock Source: ");
+ switch (sc->cfg->scfg & PCIM_SCFG_XIN2) {
+ case 0x00:
+ printf("22.5792MHz(44.1kHz*512)\n");
+ break;
+ case 0x40:
+ printf("16.9344MHz(44.1kHz*384)\n");
+ break;
+ case 0x80:
+ printf("from external clock synthesizer chip\n");
+ break;
+ default:
+ printf("illeagal system setting\n");
+ }
+ printf(" MPU-401 UART(s) #: ");
+ if (sc->cfg->scfg & PCIM_SCFG_MPU)
+ printf("2\n");
+ else
+ printf("1\n");
+ printf(" AC'97 codec: ");
+ if (sc->cfg->scfg & PCIM_SCFG_AC97)
+ printf("not exist\n");
+ else
+ printf("exist\n");
+ printf(" ADC #: ");
+ printf("%d\n", sc->adcn);
+ printf(" DAC #: ");
+ printf("%d\n", sc->dacn);
+ printf(" Multi-track converter type: ");
+ if ((sc->cfg->acl & PCIM_ACL_MTC) == 0) {
+ printf("AC'97(SDATA_OUT:");
+ if (sc->cfg->acl & PCIM_ACL_OMODE)
+ printf("packed");
+ else
+ printf("split");
+ printf("|SDATA_IN:");
+ if (sc->cfg->acl & PCIM_ACL_IMODE)
+ printf("packed");
+ else
+ printf("split");
+ printf(")\n");
+ }
+ else {
+ printf("I2S(");
+ if (sc->cfg->i2s & PCIM_I2S_VOL)
+ printf("with volume, ");
+ if (sc->cfg->i2s & PCIM_I2S_96KHZ)
+ printf("96KHz support, ");
+ switch (sc->cfg->i2s & PCIM_I2S_RES) {
+ case PCIM_I2S_16BIT:
+ printf("16bit resolution, ");
+ break;
+ case PCIM_I2S_18BIT:
+ printf("18bit resolution, ");
+ break;
+ case PCIM_I2S_20BIT:
+ printf("20bit resolution, ");
+ break;
+ case PCIM_I2S_24BIT:
+ printf("24bit resolution, ");
+ break;
+ }
+ printf("ID#0x%x)\n", sc->cfg->i2s & PCIM_I2S_ID);
+ }
+ printf(" S/PDIF(IN/OUT): ");
+ if (sc->cfg->spdif & PCIM_SPDIF_IN)
+ printf("1/");
+ else
+ printf("0/");
+ if (sc->cfg->spdif & PCIM_SPDIF_OUT)
+ printf("1 ");
+ else
+ printf("0 ");
+ if (sc->cfg->spdif & (PCIM_SPDIF_IN | PCIM_SPDIF_OUT))
+ printf("ID# 0x%02x\n", (sc->cfg->spdif & PCIM_SPDIF_ID) >> 2);
+ printf(" 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];
+ char name[ENVY24_NAMELEN];
+ int err = 0;
+ int i;
+
+#if(0)
+ device_printf(dev, "envy24_pci_attach()\n");
+#endif
+ /* get sc_info data area */
+ if ((sc = malloc(sizeof(*sc), M_ENVY24, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ bzero(sc, sizeof(*sc));
+ snprintf(name, ENVY24_NAMELEN, "%s:envy24", device_get_nameunit(dev));
+ sc->lock = snd_mtxcreate(name);
+ 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 */
+ if (err = envy24_alloc_resource(sc)) {
+ device_printf(dev, "unable to allocate system resources\n");
+ goto bad;
+ }
+
+ /* initialize card */
+ if (err = envy24_init(sc)) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ /* set multi track mixer */
+ mixer_init(dev, &envy24mixer_class, sc);
+
+ /* set channel information */
+ if (err = pcm_register(dev, sc, 5, 2 + sc->adc))
+ 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 */
+ snprintf(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);
+ 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);
+ free(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);
+ free(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, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+MODULE_DEPEND(snd_envy24, snd_ak452x, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+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
index 0000000..8830145
--- /dev/null
+++ b/sys/dev/sound/pci/envy24.h
@@ -0,0 +1,482 @@
+/*
+ * 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$
+ */
+
+
+/* -------------------------------------------------------------------- */
+
+/* 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
+
+/* end of file */
diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c
new file mode 100644
index 0000000..c23efc3
--- /dev/null
+++ b/sys/dev/sound/pci/envy24ht.c
@@ -0,0 +1,2445 @@
+/*
+ * 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$
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+#include <dev/sound/pci/ak452x.h>
+#include <dev/sound/pci/envy24.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#include "mixer_if.h"
+
+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
+
+typedef volatile u_int32_t 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 free;
+ struct codec_entry *codec;
+};
+
+/* device private data */
+struct sc_info {
+ device_t dev;
+ void *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,
+ 0,
+ &delta_codec,
+ },
+ {
+ "Envy24 audio(Generic)",
+ 0, 0,
+ 0x0f, 0x00, 0x01, 0x03,
+ 0xff, 0x00, 0x00,
+ 0,
+ &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;
+}
+
+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;
+}
+
+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 = malloc(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)
+ free(cfg, M_ENVY24);
+ return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* AC'97 codec access routines */
+
+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;
+}
+
+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;
+}
+
+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);
+
+/* -------------------------------------------------------------------- */
+
+/* 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;
+}
+
+static u_int32_t
+envy24_gpiogetmask(struct sc_info *sc)
+{
+ return envy24_rdci(sc, ENVY24_CCI_GPIOMASK);
+}
+
+static void
+envy24_gpiosetmask(struct sc_info *sc, u_int32_t mask)
+{
+ envy24_wrci(sc, ENVY24_CCI_GPIOMASK, mask);
+ return;
+}
+
+static u_int32_t
+envy24_gpiogetdir(struct sc_info *sc)
+{
+ return envy24_rdci(sc, ENVY24_CCI_GPIOCTL);
+}
+
+static void
+envy24_gpiosetdir(struct sc_info *sc, u_int32_t dir)
+{
+ envy24_wrci(sc, ENVY24_CCI_GPIOCTL, dir);
+ return;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* M-Audio Delta series AK4524 access interface routine */
+
+struct envy24_delta_ak4524_codec {
+ struct ak452x_info *info;
+ struct sc_info *parent;
+ int dir;
+ int num;
+ int cs, cclk, cdti;
+};
+
+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 = malloc(sizeof(*buff), M_ENVY24, M_NOWAIT);
+ if (buff == NULL)
+ return NULL;
+
+ if (dir == PCMDIR_PLAY && sc->adc[num] != NULL)
+ buff->info = ((struct envy24_delta_ak4524_codec *)sc->adc[num])->info;
+ else if (dir == PCMDIR_REC && sc->dac[num] != NULL)
+ buff->info = ((struct envy24_delta_ak4524_codec *)sc->dac[num])->info;
+ else
+ buff->info = ak452x_create(dev, buff, num, envy24_delta_ak4524_ctl);
+ if (buff->info == NULL) {
+ free(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->adc[ptr->num] == NULL)
+ ak452x_destroy(ptr->info);
+ }
+ else {
+ if (ptr->parent->dac[ptr->num] == NULL)
+ ak452x_destroy(ptr->info);
+ }
+
+ free(codec, M_ENVY24);
+}
+
+static void
+envy24_delta_ak4524_init(void *codec)
+{
+ u_int32_t gpiomask, gpiodir;
+ 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);
+ */
+ 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;
+ ptr->cdti = ENVY24_GPIO_AK4524_CDTI;
+ ak452x_settype(ptr->info, AK452X_TYPE_4524);
+ ak452x_setcif(ptr->info, ENVY24_DELTA_AK4524_CIF);
+ ak452x_setformat(ptr->info,
+ AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X);
+ ak452x_setdvc(ptr->info, 0);
+ ak452x_init(ptr->info);
+}
+
+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
+
+ ak452x_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
+
+ ak452x_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
+ 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;
+}
+
+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;
+}
+
+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);
+ }
+
+ 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] = data[src];
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = 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] = (u_int32_t)data[src] << 16;
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = (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] = ((u_int32_t)data[src] ^ 0x80) << 24;
+ dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1] = ((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];
+ data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1];
+ dst += 2;
+ dst %= dsize;
+ src++;
+ src %= ssize;
+ }
+
+ return;
+}
+
+static void
+envy24_r16sl(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) / 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];
+ data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1];
+ 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 = malloc(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];
+ sndbuf_setup(ch->buffer, ch->data, ch->size);
+ /* 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) {
+ free(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 (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_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;
+
+#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;
+ 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;
+ int i;
+
+#if(0)
+ 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(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;
+ }
+ 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);
+ }
+ 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)
+ chn_intr(ch->channel);
+ }
+ 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)
+ chn_intr(ch->channel);
+ }
+ 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");
+#endif
+ 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));
+ }
+}
+
+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");
+#endif
+ 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));
+ }
+}
+
+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", sc->adcn, sc->dacn);
+ printf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n",
+ sc->cfg->subvendor, sc->cfg->subdevice);
+ printf(" XIN2 Clock Source: ");
+ switch (sc->cfg->scfg & PCIM_SCFG_XIN2) {
+ case 0x00:
+ printf("22.5792MHz(44.1kHz*512)\n");
+ break;
+ case 0x40:
+ printf("16.9344MHz(44.1kHz*384)\n");
+ break;
+ case 0x80:
+ printf("from external clock synthesizer chip\n");
+ break;
+ default:
+ printf("illeagal system setting\n");
+ }
+ printf(" MPU-401 UART(s) #: ");
+ if (sc->cfg->scfg & PCIM_SCFG_MPU)
+ printf("2\n");
+ else
+ printf("1\n");
+ printf(" AC'97 codec: ");
+ if (sc->cfg->scfg & PCIM_SCFG_AC97)
+ printf("not exist\n");
+ else
+ printf("exist\n");
+ printf(" ADC #: ");
+ printf("%d\n", sc->adcn);
+ printf(" DAC #: ");
+ printf("%d\n", sc->dacn);
+ printf(" Multi-track converter type: ");
+ if ((sc->cfg->acl & PCIM_ACL_MTC) == 0) {
+ printf("AC'97(SDATA_OUT:");
+ if (sc->cfg->acl & PCIM_ACL_OMODE)
+ printf("packed");
+ else
+ printf("split");
+ printf("|SDATA_IN:");
+ if (sc->cfg->acl & PCIM_ACL_IMODE)
+ printf("packed");
+ else
+ printf("split");
+ printf(")\n");
+ }
+ else {
+ printf("I2S(");
+ if (sc->cfg->i2s & PCIM_I2S_VOL)
+ printf("with volume, ");
+ if (sc->cfg->i2s & PCIM_I2S_96KHZ)
+ printf("96KHz support, ");
+ switch (sc->cfg->i2s & PCIM_I2S_RES) {
+ case PCIM_I2S_16BIT:
+ printf("16bit resolution, ");
+ break;
+ case PCIM_I2S_18BIT:
+ printf("18bit resolution, ");
+ break;
+ case PCIM_I2S_20BIT:
+ printf("20bit resolution, ");
+ break;
+ case PCIM_I2S_24BIT:
+ printf("24bit resolution, ");
+ break;
+ }
+ printf("ID#0x%x)\n", sc->cfg->i2s & PCIM_I2S_ID);
+ }
+ printf(" S/PDIF(IN/OUT): ");
+ if (sc->cfg->spdif & PCIM_SPDIF_IN)
+ printf("1/");
+ else
+ printf("0/");
+ if (sc->cfg->spdif & PCIM_SPDIF_OUT)
+ printf("1 ");
+ else
+ printf("0 ");
+ if (sc->cfg->spdif & (PCIM_SPDIF_IN | PCIM_SPDIF_OUT))
+ printf("ID# 0x%02x\n", (sc->cfg->spdif & PCIM_SPDIF_ID) >> 2);
+ printf(" 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];
+ char name[ENVY24_NAMELEN];
+ int err = 0;
+ int i;
+
+#if(0)
+ device_printf(dev, "envy24_pci_attach()\n");
+#endif
+ /* get sc_info data area */
+ if ((sc = malloc(sizeof(*sc), M_ENVY24, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ bzero(sc, sizeof(*sc));
+ snprintf(name, ENVY24_NAMELEN, "%s:envy24", device_get_nameunit(dev));
+ sc->lock = snd_mtxcreate(name);
+ 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 */
+ if (err = envy24_alloc_resource(sc)) {
+ device_printf(dev, "unable to allocate system resources\n");
+ goto bad;
+ }
+
+ /* initialize card */
+ if (err = envy24_init(sc)) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ /* set multi track mixer */
+ mixer_init(dev, &envy24mixer_class, sc);
+
+ /* set channel information */
+ if (err = pcm_register(dev, sc, 5, 2 + sc->adc))
+ 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 */
+ snprintf(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);
+ 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);
+ free(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);
+ free(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, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+MODULE_DEPEND(snd_envy24, snd_ak452x, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+MODULE_VERSION(snd_envy24, 1);
diff --git a/sys/dev/sound/pci/envy24ht.h b/sys/dev/sound/pci/envy24ht.h
new file mode 100644
index 0000000..8830145
--- /dev/null
+++ b/sys/dev/sound/pci/envy24ht.h
@@ -0,0 +1,482 @@
+/*
+ * 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$
+ */
+
+
+/* -------------------------------------------------------------------- */
+
+/* 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
+
+/* end of file */
diff --git a/sys/dev/sound/pci/spicds.c b/sys/dev/sound/pci/spicds.c
new file mode 100644
index 0000000..2ca3501
--- /dev/null
+++ b/sys/dev/sound/pci/spicds.c
@@ -0,0 +1,247 @@
+/*
+ * 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$
+ */
+
+#include <dev/sound/pcm/sound.h>
+
+#include <dev/sound/pci/ak452x.h>
+
+MALLOC_DEFINE(M_AK452X, "ak452x", "ak452x codec");
+
+#define AK452X_NAMELEN 16
+struct ak452x_info {
+ device_t dev;
+ ak452x_ctrl ctrl;
+ void *devinfo;
+ int num; /* number of this device */
+ unsigned int type; /* codec type */
+ unsigned int cif; /* Controll data Interface Format (0/1) */
+ unsigned int format; /* data format and master clock frequency */
+ unsigned int dvc; /* De-emphasis and Volume Control */
+ unsigned int left, right;
+ char name[AK452X_NAMELEN];
+ void *lock;
+};
+
+static void
+ak452x_wrbit(struct ak452x_info *codec, int bit)
+{
+ unsigned int cs, cdti;
+ if (codec->cif)
+ cs = 1;
+ else
+ cs = 0;
+ if (bit)
+ cdti = 1;
+ else
+ cdti = 0;
+ codec->ctrl(codec->devinfo, cs, 0, cdti);
+ DELAY(1);
+ codec->ctrl(codec->devinfo, cs, 1, cdti);
+ DELAY(1);
+
+ return;
+}
+
+static void
+ak452x_wrcd(struct ak452x_info *codec, int reg, u_int8_t val)
+{
+ int mask;
+
+#if(0)
+ device_printf(codec->dev, "ak452x_wrcd(codec, 0x%02x, 0x%02x)\n", reg, val);
+#endif
+ /* start */
+ if (codec->cif)
+ codec->ctrl(codec->devinfo, 1, 1, 0);
+ else
+ codec->ctrl(codec->devinfo, 0, 1, 0);
+ DELAY(1);
+ /* chip address */
+ ak452x_wrbit(codec, 1);
+ ak452x_wrbit(codec, 0);
+ /* write */
+ ak452x_wrbit(codec, 1);
+ /* register address */
+ for (mask = 0x10; mask != 0; mask >>= 1)
+ ak452x_wrbit(codec, reg & mask);
+ /* data */
+ for (mask = 0x80; mask != 0; mask >>= 1)
+ ak452x_wrbit(codec, val & mask);
+ /* stop */
+ DELAY(1);
+ if (codec->cif) {
+ codec->ctrl(codec->devinfo, 0, 1, 0);
+ DELAY(1);
+ codec->ctrl(codec->devinfo, 1, 1, 0);
+ }
+ else {
+ codec->ctrl(codec->devinfo, 1, 1, 0);
+ }
+
+ return;
+}
+
+struct ak452x_info *
+ak452x_create(device_t dev, void *devinfo, int num, ak452x_ctrl ctrl)
+{
+ struct ak452x_info *codec;
+
+#if(0)
+ device_printf(dev, "ak452x_create(dev, devinfo, %d, ctrl)\n", num);
+#endif
+ codec = (struct ak452x_info *)malloc(sizeof *codec, M_AK452X, M_NOWAIT);
+ if (codec == NULL)
+ return NULL;
+
+ snprintf(codec->name, AK452X_NAMELEN, "%s:ak452x%d", device_get_nameunit(dev), num);
+ codec->lock = snd_mtxcreate(codec->name);
+ codec->dev = dev;
+ codec->ctrl = ctrl;
+ codec->devinfo = devinfo;
+ codec->num = num;
+ codec->type = AK452X_TYPE_4524;
+ codec->cif = 0;
+ codec->format = AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X;
+ codec->dvc = AK452X_DVC_DEMOFF | AK452X_DVC_ZTM1024 | AK452X_DVC_ZCE;
+
+ return codec;
+}
+
+void
+ak452x_destroy(struct ak452x_info *codec)
+{
+ snd_mtxfree(codec->lock);
+ free(codec, M_AK452X);
+}
+
+void
+ak452x_settype(struct ak452x_info *codec, unsigned int type)
+{
+ snd_mtxlock(codec->lock);
+ codec->type = type;
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_setcif(struct ak452x_info *codec, unsigned int cif)
+{
+ snd_mtxlock(codec->lock);
+ codec->cif = cif;
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_setformat(struct ak452x_info *codec, unsigned int format)
+{
+ snd_mtxlock(codec->lock);
+ codec->format = format;
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_setdvc(struct ak452x_info *codec, unsigned int dvc)
+{
+ snd_mtxlock(codec->lock);
+ codec->type = dvc;
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_init(struct ak452x_info *codec)
+{
+#if(0)
+ device_printf(codec->dev, "ak452x_init(codec)\n");
+#endif
+ snd_mtxlock(codec->lock);
+ /* power off */
+ ak452x_wrcd(codec, AK4524_POWER, 0);
+ /* set parameter */
+ ak452x_wrcd(codec, AK4524_FORMAT, codec->format);
+ ak452x_wrcd(codec, AK4524_DVC, codec->dvc);
+ /* power on */
+ ak452x_wrcd(codec, AK4524_POWER, AK452X_POWER_PWDA | AK452X_POWER_PWAD | AK452X_POWER_PWVR);
+ /* free reset register */
+ ak452x_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD);
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_reinit(struct ak452x_info *codec)
+{
+ snd_mtxlock(codec->lock);
+ /* reset */
+ ak452x_wrcd(codec, AK4524_RESET, 0);
+ /* set parameter */
+ ak452x_wrcd(codec, AK4524_FORMAT, codec->format);
+ ak452x_wrcd(codec, AK4524_DVC, codec->dvc);
+ /* free reset register */
+ ak452x_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD);
+ snd_mtxunlock(codec->lock);
+}
+
+void
+ak452x_set(struct ak452x_info *codec, int dir, unsigned int left, unsigned int right)
+{
+#if(0)
+ device_printf(codec->dev, "ak452x_set(codec, %d, %d, %d)\n", dir, left, right);
+#endif
+ snd_mtxlock(codec->lock);
+ if (left >= 100)
+ left = 127;
+ else
+ left = left * 127 / 100;
+ if (right >= 100)
+ right = 127;
+ else
+ right = right * 127 / 100;
+ if (dir == PCMDIR_REC && codec->type == AK452X_TYPE_4524) {
+#if(0)
+ device_printf(codec->dev, "ak452x_set(): AK4524(REC) %d/%d\n", left, right);
+#endif
+ ak452x_wrcd(codec, AK4524_LIPGA, left);
+ ak452x_wrcd(codec, AK4524_RIPGA, right);
+ }
+ if (dir == PCMDIR_PLAY && codec->type == AK452X_TYPE_4524) {
+#if(0)
+ device_printf(codec->dev, "ak452x_set(): AK4524(PLAY) %d/%d\n", left, right);
+#endif
+ ak452x_wrcd(codec, AK4524_LOATT, left);
+ ak452x_wrcd(codec, AK4524_ROATT, right);
+ }
+ if (dir == PCMDIR_PLAY && codec->type == AK452X_TYPE_4528) {
+#if(0)
+ device_printf(codec->dev, "ak452x_set(): AK4528(PLAY) %d/%d\n", left, right);
+#endif
+ ak452x_wrcd(codec, AK4528_LOATT, left);
+ ak452x_wrcd(codec, AK4528_ROATT, right);
+ }
+ snd_mtxunlock(codec->lock);
+}
+
+MODULE_DEPEND(snd_ak452x, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+MODULE_VERSION(snd_ak452x, 1);
diff --git a/sys/dev/sound/pci/spicds.h b/sys/dev/sound/pci/spicds.h
new file mode 100644
index 0000000..1df631f
--- /dev/null
+++ b/sys/dev/sound/pci/spicds.h
@@ -0,0 +1,91 @@
+/*
+ * 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$
+ */
+
+/* supported CODECs */
+#define AK452X_TYPE_4524 0
+#define AK452X_TYPE_4528 1
+
+/* AK4524/AK4528 control registers */
+#define AK4524_POWER 0x00
+#define AK4528_POWER 0x00
+#define AK452X_POWER_PWDA 0x01
+#define AK452X_POWER_PWAD 0x02
+#define AK452X_POWER_PWVR 0x04
+#define AK4524_RESET 0x01
+#define AK4528_RESET 0x01
+#define AK452X_RESET_RSDA 0x01
+#define AK452X_RESET_RSAD 0x02
+#define AK4524_FORMAT 0x02
+#define AK4528_FORMAT 0x02
+#define AK452X_FORMAT_1X 0x00
+#define AK452X_FORMAT_2X 0x01
+#define AK452X_FORMAT_4X1 0x02
+#define AK452X_FORMAT_4X2 0x03
+#define AK452X_FORMAT_256FSN 0x00
+#define AK452X_FORMAT_512FSN 0x04
+#define AK452X_FORMAT_1024FSN 0x08
+#define AK452X_FORMAT_384FSN 0x10
+#define AK452X_FORMAT_768FSN 0x14
+#define AK452X_FORMAT_OM24IL16 0x00
+#define AK452X_FORMAT_OM24IL20 0x20
+#define AK452X_FORMAT_OM24IM24 0x40
+#define AK452X_FORMAT_I2S 0x60
+#define AK452X_FORMAT_OM24IL24 0x80
+#define AK4524_DVC 0x03
+#define AK452X_DVC_DEM441 0x00
+#define AK452X_DVC_DEMOFF 0x01
+#define AK452X_DVC_DEM48 0x02
+#define AK452X_DVC_DEM32 0x03
+#define AK452X_DVC_ZTM256 0x00
+#define AK452X_DVC_ZTM512 0x04
+#define AK452X_DVC_ZTM1024 0x08
+#define AK452X_DVC_ZTM2048 0x0c
+#define AK452X_DVC_ZCE 0x10
+#define AK452X_DVC_HPFL 0x04
+#define AK452X_DVC_HPFR 0x08
+#define AK452X_DVC_SMUTE 0x80
+#define AK4524_LIPGA 0x04
+#define AK4524_RIPGA 0x05
+#define AK4524_LOATT 0x06
+#define AK4524_ROATT 0x07
+#define AK4528_LOATT 0x04
+#define AK4528_ROATT 0x05
+
+struct ak452x_info;
+
+typedef void (*ak452x_ctrl)(void *, unsigned int, unsigned int, unsigned int);
+
+struct ak452x_info *ak452x_create(device_t dev, void *devinfo, int num, ak452x_ctrl);
+void ak452x_destroy(struct ak452x_info *codec);
+void ak452x_settype(struct ak452x_info *codec, unsigned int type);
+void ak452x_setcif(struct ak452x_info *codec, unsigned int cif);
+void ak452x_setformat(struct ak452x_info *codec, unsigned int format);
+void ak452x_setdvc(struct ak452x_info *codec, unsigned int dvc);
+void ak452x_init(struct ak452x_info *codec);
+void ak452x_reinit(struct ak452x_info *codec);
+void ak452x_set(struct ak452x_info *codec, int dir, unsigned int left, unsigned int right);
diff --git a/sys/modules/sound/driver/ak452x/Makefile b/sys/modules/sound/driver/ak452x/Makefile
new file mode 100644
index 0000000..50a0e67
--- /dev/null
+++ b/sys/modules/sound/driver/ak452x/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../dev/sound/pci
+KMOD = snd_ak452x
+SRCS = device_if.h bus_if.h isa_if.h pci_if.h
+SRCS += ak452x.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/sound/driver/envy24/Makefile b/sys/modules/sound/driver/envy24/Makefile
new file mode 100644
index 0000000..37f9d48
--- /dev/null
+++ b/sys/modules/sound/driver/envy24/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../dev/sound/pci
+KMOD = snd_envy24
+SRCS = device_if.h bus_if.h isa_if.h pci_if.h
+SRCS += envy24.c
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud