summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2007-10-28 13:42:32 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2007-10-28 13:42:32 +0000
commit03bb5db05c1ac954b7bd5b880eb9cbce07bd2ce7 (patch)
tree0f91773591909e9693f1a8952e4684acc98ee84c /sys/dev
parent0ee5cadf5664eec621172ca0c9600f21bb95db75 (diff)
add new envy(4) driver for ICE1712 (aka VIA Envy24) audio devices. The
device uses 32bit samples, up to 96kHz sample rate, 12 input channels and 10 output channels. Currently AK4524 codecs are supported, so M-Audio Delta cards should work. Playback and capture work, but the mixer is still incomplete. ok jakemsr@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/envy.c957
-rw-r--r--sys/dev/pci/envyreg.h178
-rw-r--r--sys/dev/pci/envyvar.h88
-rw-r--r--sys/dev/pci/files.pci7
4 files changed, 1229 insertions, 1 deletions
diff --git a/sys/dev/pci/envy.c b/sys/dev/pci/envy.c
new file mode 100644
index 00000000000..9c68145566b
--- /dev/null
+++ b/sys/dev/pci/envy.c
@@ -0,0 +1,957 @@
+/*
+ * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/malloc.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/envyvar.h>
+#include <dev/pci/envyreg.h>
+#include <dev/audio_if.h>
+#include <machine/bus.h>
+
+#ifdef ENVY_DEBUG
+#define DPRINTF(...) do { if (envydebug) printf(__VA_ARGS__); } while(0)
+#define DPRINTFN(n, ...) do { if (envydebug > (n)) printf(__VA_ARGS__); } while(0)
+int envydebug = 1;
+#else
+#define DPRINTF(...) do {} while(0)
+#define DPRINTFN(n, ...) do {} while(0)
+#endif
+#define DEVNAME(sc) ((sc)->dev.dv_xname)
+
+int envymatch(struct device *, void *, void *);
+void envyattach(struct device *, struct device *, void *);
+int envydetach(struct device *, int);
+
+int envy_ccs_read(struct envy_softc *, int);
+void envy_ccs_write(struct envy_softc *, int, int);
+int envy_cci_read(struct envy_softc *, int);
+void envy_cci_write(struct envy_softc *, int, int);
+void envy_i2c_wait(struct envy_softc *);
+int envy_i2c_read(struct envy_softc *, int, int);
+void envy_i2c_write(struct envy_softc *, int, int, int);
+int envy_gpio_read(struct envy_softc *);
+void envy_gpio_write(struct envy_softc *, int);
+void envy_eeprom_read(struct envy_softc *, unsigned char *);
+void envy_reset(struct envy_softc *);
+int envy_ak_read(struct envy_softc *, int, int);
+void envy_ak_write(struct envy_softc *, int, int, int);
+int envy_intr(void *);
+
+int envy_lineout_getsrc(struct envy_softc *, int);
+void envy_lineout_setsrc(struct envy_softc *, int, int);
+int envy_spdout_getsrc(struct envy_softc *, int);
+void envy_spdout_setsrc(struct envy_softc *, int, int);
+void envy_mon_getvol(struct envy_softc *, int, int *, int *);
+void envy_mon_setvol(struct envy_softc *, int, int, int);
+
+int envy_open(void *, int);
+void envy_close(void *);
+void *envy_allocm(void *, int, size_t, int, int);
+void envy_freem(void *, void *, int);
+int envy_query_encoding(void *, struct audio_encoding *);
+int envy_set_params(void *, int, int, struct audio_params *,
+ struct audio_params *);
+int envy_round_blocksize(void *, int);
+size_t envy_round_buffersize(void *, int, size_t);
+int envy_trigger_output(void *, void *, void *, int,
+ void (*)(void *), void *, struct audio_params *);
+int envy_trigger_input(void *, void *, void *, int,
+ void (*)(void *), void *, struct audio_params *);
+int envy_halt_output(void *);
+int envy_halt_input(void *);
+int envy_getdev(void *, struct audio_device *);
+int envy_query_devinfo(void *, struct mixer_devinfo *);
+int envy_get_port(void *, struct mixer_ctrl *);
+int envy_set_port(void *, struct mixer_ctrl *);
+int envy_get_props(void *);
+
+struct cfattach envy_ca = {
+ sizeof(struct envy_softc), envymatch, envyattach, envydetach
+};
+
+struct cfdriver envy_cd = {
+ NULL, "envy", DV_DULL
+};
+
+struct audio_hw_if envy_hw_if = {
+ envy_open, /* open */
+ envy_close, /* close */
+ NULL, /* drain */
+ envy_query_encoding, /* query_encoding */
+ envy_set_params, /* set_params */
+ envy_round_blocksize, /* round_blocksize */
+ NULL, /* commit_settings */
+ NULL, /* init_output */
+ NULL, /* init_input */
+ NULL, /* start_output */
+ NULL, /* start_input */
+ envy_halt_output, /* halt_output */
+ envy_halt_input, /* halt_input */
+ NULL, /* speaker_ctl */
+ envy_getdev, /* getdev */
+ NULL, /* setfd */
+ envy_set_port, /* set_port */
+ envy_get_port, /* get_port */
+ envy_query_devinfo, /* query_devinfo */
+ envy_allocm, /* malloc */
+ envy_freem, /* free */
+ envy_round_buffersize, /* round_buffersize */
+ NULL, /* mappage */
+ envy_get_props, /* get_props */
+ envy_trigger_output, /* trigger_output */
+ envy_trigger_input, /* trigger_input */
+};
+
+/*
+ * correspondence between rates (in frames per second)
+ * and values of rate register
+ */
+struct {
+ int rate, reg;
+} envy_rates[] = {
+ { 8000, 0x6}, { 9600, 0x3}, {11025, 0xa}, {12000, 2}, {16000, 5},
+ {22050, 0x9}, {24000, 0x1}, {32000, 0x4}, {44100, 8}, {48000, 0},
+ {64000, 0xf}, {88200, 0xb}, {96000, 0x7}, {-1, -1}
+};
+
+int
+envy_ccs_read(struct envy_softc *sc, int reg)
+{
+ return bus_space_read_1(sc->ccs_iot, sc->ccs_ioh, reg);
+}
+
+void
+envy_ccs_write(struct envy_softc *sc, int reg, int val)
+{
+ bus_space_write_1(sc->ccs_iot, sc->ccs_ioh, reg, val);
+}
+
+int
+envy_cci_read(struct envy_softc *sc, int index)
+{
+ int val;
+ envy_ccs_write(sc, ENVY_CCI_INDEX, index);
+ val = envy_ccs_read(sc, ENVY_CCI_DATA);
+ return val;
+}
+
+void
+envy_cci_write(struct envy_softc *sc, int index, int data)
+{
+ envy_ccs_write(sc, ENVY_CCI_INDEX, index);
+ envy_ccs_write(sc, ENVY_CCI_DATA, data);
+}
+
+void
+envy_i2c_wait(struct envy_softc *sc)
+{
+ int timeout = 50, st;
+
+ for (;;) {
+ st = envy_ccs_read(sc, ENVY_I2C_CTL);
+ if (!(st & ENVY_I2C_CTL_BUSY))
+ break;
+ if (timeout == 0) {
+ printf("%s: i2c busy timeout\n", DEVNAME(sc));
+ break;
+ }
+ delay(50);
+ timeout--;
+ }
+}
+
+int
+envy_i2c_read(struct envy_softc *sc, int dev, int addr)
+{
+ envy_i2c_wait(sc);
+ envy_ccs_write(sc, ENVY_I2C_ADDR, addr);
+ envy_i2c_wait(sc);
+ envy_ccs_write(sc, ENVY_I2C_DEV, dev << 1);
+ envy_i2c_wait(sc);
+ return envy_ccs_read(sc, ENVY_I2C_DATA);
+}
+
+void
+envy_i2c_write(struct envy_softc *sc, int dev, int addr, int data)
+{
+ if (dev == 0x50) {
+ printf("%s: writing on eeprom is evil...\n", DEVNAME(sc));
+ return;
+ }
+ envy_i2c_wait(sc);
+ envy_ccs_write(sc, ENVY_I2C_ADDR, addr);
+ envy_i2c_wait(sc);
+ envy_ccs_write(sc, ENVY_I2C_DATA, data);
+ envy_i2c_wait(sc);
+ envy_ccs_write(sc, ENVY_I2C_DEV, (dev << 1) | 1);
+}
+
+void
+envy_eeprom_read(struct envy_softc *sc, unsigned char *eeprom)
+{
+ int i;
+
+ for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) {
+ eeprom[i] = envy_i2c_read(sc, ENVY_I2C_DEV_EEPROM, i);
+ }
+#ifdef ENVY_DEBUG
+ printf("%s: eeprom: ", DEVNAME(sc));
+ for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) {
+ printf(" %02x", (unsigned)eeprom[i]);
+ }
+ printf("\n");
+#endif
+}
+
+int
+envy_ak_read(struct envy_softc *sc, int dev, int addr) {
+ return sc->ak[dev].reg[addr];
+}
+
+void
+envy_ak_write(struct envy_softc *sc, int dev, int addr, int data)
+{
+ int bits, i, reg;
+
+ sc->ak[dev].reg[addr] = data;
+
+ reg = envy_cci_read(sc, ENVY_GPIO_DATA);
+ reg &= ~ENVY_GPIO_CSMASK;
+ reg |= ENVY_GPIO_CS(dev);
+ envy_cci_write(sc, ENVY_GPIO_DATA, reg);
+ delay(1);
+
+ bits = 0xa000 | (addr << 8) | data;
+ for (i = 0; i < 16; i++) {
+ reg &= ~(ENVY_GPIO_CLK | ENVY_GPIO_DOUT);
+ reg |= (bits & 0x8000) ? ENVY_GPIO_DOUT : 0;
+ envy_cci_write(sc, ENVY_GPIO_DATA, reg);
+ delay(1);
+
+ reg |= ENVY_GPIO_CLK;
+ envy_cci_write(sc, ENVY_GPIO_DATA, reg);
+ delay(1);
+ bits <<= 1;
+ }
+
+ reg |= ENVY_GPIO_CSMASK;
+ envy_cci_write(sc, ENVY_GPIO_DATA, reg);
+ delay(1);
+}
+
+void
+envy_reset(struct envy_softc *sc)
+{
+ char eeprom[ENVY_EEPROM_MAXSZ];
+ int dev;
+
+ /*
+ * full reset
+ */
+ envy_ccs_write(sc, ENVY_CTL, ENVY_CTL_RESET | ENVY_CTL_NATIVE);
+ delay(200);
+ envy_ccs_write(sc, ENVY_CTL, ENVY_CTL_NATIVE);
+ delay(200);
+
+ /*
+ * read config from eprom and write it to registers
+ */
+ envy_eeprom_read(sc, eeprom);
+ pci_conf_write(sc->pci_pc, sc->pci_tag, ENVY_CONF,
+ eeprom[ENVY_EEPROM_CONF] |
+ (eeprom[ENVY_EEPROM_ACLINK] << 8) |
+ (eeprom[ENVY_EEPROM_I2S] << 16) |
+ (eeprom[ENVY_EEPROM_SPDIF] << 24));
+ envy_cci_write(sc, ENVY_GPIO_MASK, eeprom[ENVY_EEPROM_GPIOMASK]);
+ envy_cci_write(sc, ENVY_GPIO_DIR, eeprom[ENVY_EEPROM_GPIODIR]);
+ envy_cci_write(sc, ENVY_GPIO_DATA, eeprom[ENVY_EEPROM_GPIOST]);
+
+ DPRINTF("%s: gpio_mask = %02x\n", DEVNAME(sc),
+ envy_cci_read(sc, ENVY_GPIO_MASK));
+ DPRINTF("%s: gpio_dir = %02x\n", DEVNAME(sc),
+ envy_cci_read(sc, ENVY_GPIO_DIR));
+ DPRINTF("%s: gpio_state = %02x\n", DEVNAME(sc),
+ envy_cci_read(sc, ENVY_GPIO_DATA));
+
+ /*
+ * reset ak4524 codecs
+ */
+ for (dev = 0; dev < 4; dev++) {
+ envy_ak_write(sc, dev, AK_RST, 0x0);
+ delay(300);
+ envy_ak_write(sc, dev, AK_RST, AK_RST_AD | AK_RST_DA);
+ envy_ak_write(sc, dev, AK_FMT, AK_FMT_IIS24);
+ }
+
+ /*
+ * clear all interrupts and unmask used ones
+ */
+ envy_ccs_write(sc, ENVY_CCS_INTSTAT, 0xff);
+ envy_ccs_write(sc, ENVY_CCS_INTMASK, ~ENVY_CCS_INT_MT);
+}
+
+int
+envy_intr(void *self)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ int st;
+
+ st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR);
+ if (!(st & (ENVY_MT_INTR_PACK | ENVY_MT_INTR_RACK))) {
+ return 0;
+ }
+ if (st & ENVY_MT_INTR_PACK) {
+ st = ENVY_MT_INTR_PACK;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
+ sc->ointr(sc->oarg);
+ }
+ if (st & ENVY_MT_INTR_RACK) {
+ st = ENVY_MT_INTR_RACK;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
+ sc->iintr(sc->iarg);
+ }
+ return 1;
+}
+
+int
+envy_lineout_getsrc(struct envy_softc *sc, int out) {
+ int reg, shift, src;
+
+ reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_OUTSRC);
+ DPRINTF("%s: outsrc=%x\n", DEVNAME(sc), reg);
+ shift = (out & 1) ? (out & ~1) + 8 : out;
+ src = (reg >> shift) & 3;
+ if (src == ENVY_MT_OUTSRC_DMA) {
+ return ENVY_MIX_OUTSRC_DMA;
+ } else if (src == ENVY_MT_OUTSRC_MON) {
+ return ENVY_MIX_OUTSRC_MON;
+ }
+ reg = bus_space_read_4(sc->mt_iot, sc->mt_ioh, ENVY_MT_INSEL);
+ DPRINTF("%s: insel=%x\n", DEVNAME(sc), reg);
+ reg = (reg >> (out * 4)) & 0xf;
+ if (src == ENVY_MT_OUTSRC_LINE)
+ return ENVY_MIX_OUTSRC_LINEIN + (reg & 7);
+ else
+ return ENVY_MIX_OUTSRC_SPDIN + (reg >> 3);
+}
+
+void
+envy_lineout_setsrc(struct envy_softc *sc, int out, int src) {
+ int reg, shift, mask, sel;
+
+ if (src < ENVY_MIX_OUTSRC_DMA) {
+ /*
+ * linein and spdin are used as output source so we
+ * must select the input source channel number
+ */
+ if (src < ENVY_MIX_OUTSRC_SPDIN)
+ sel = src - ENVY_MIX_OUTSRC_LINEIN;
+ else
+ sel = (src - ENVY_MIX_OUTSRC_SPDIN) << 3;
+
+ shift = out * ENVY_MT_INSEL_BITS;
+ mask = ENVY_MT_INSEL_MASK << shift;
+ reg = bus_space_read_4(sc->mt_iot, sc->mt_ioh, ENVY_MT_INSEL);
+ reg = (reg & ~mask) | (sel << shift);
+ bus_space_write_4(sc->mt_iot, sc->mt_ioh, ENVY_MT_INSEL, reg);
+ DPRINTF("%s: insel <- %x\n", DEVNAME(sc), reg);
+ }
+
+ /*
+ * set the lineout route register
+ */
+ if (src < ENVY_MIX_OUTSRC_SPDIN) {
+ sel = ENVY_MT_OUTSRC_LINE;
+ } else if (src < ENVY_MIX_OUTSRC_DMA) {
+ sel = ENVY_MT_OUTSRC_SPD;
+ } else if (src == ENVY_MIX_OUTSRC_DMA) {
+ sel = ENVY_MT_OUTSRC_DMA;
+ } else {
+ sel = ENVY_MT_OUTSRC_MON;
+ }
+ shift = (out & 1) ? (out & ~1) + 8 : out;
+ mask = ENVY_MT_INSEL_MASK << shift;
+ reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_OUTSRC);
+ reg = (reg & ~mask) | (sel << shift);
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_OUTSRC, reg);
+ DPRINTF("%s: outsrc <- %x\n", DEVNAME(sc), reg);
+}
+
+
+int
+envy_spdout_getsrc(struct envy_softc *sc, int out) {
+ int reg, src, sel;
+
+ reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_SPDROUTE);
+ DPRINTF("%s: spdroute=%x\n", DEVNAME(sc), reg);
+ src = (out == 0) ? reg : reg >> 2;
+ src &= ENVY_MT_SPDSRC_MASK;
+ if (src == ENVY_MT_SPDSRC_DMA) {
+ return ENVY_MIX_OUTSRC_DMA;
+ } else if (src == ENVY_MT_SPDSRC_MON) {
+ return ENVY_MIX_OUTSRC_MON;
+ }
+
+ sel = (out == 0) ? reg >> 8 : reg >> 12;
+ sel &= ENVY_MT_SPDSEL_MASK;
+ if (src == ENVY_MT_SPDSRC_LINE)
+ return ENVY_MIX_OUTSRC_LINEIN + (sel & 7);
+ else
+ return ENVY_MIX_OUTSRC_SPDIN + (sel >> 3);
+}
+
+void
+envy_spdout_setsrc(struct envy_softc *sc, int out, int src) {
+ int reg, shift, mask, sel;
+
+ reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_SPDROUTE);
+ if (src < ENVY_MIX_OUTSRC_DMA) {
+ /*
+ * linein and spdin are used as output source so we
+ * must select the input source channel number
+ */
+ if (src < ENVY_MIX_OUTSRC_SPDIN)
+ sel = src - ENVY_MIX_OUTSRC_LINEIN;
+ else
+ sel = (src - ENVY_MIX_OUTSRC_SPDIN) << 3;
+
+ shift = 8 + out * ENVY_MT_SPDSEL_BITS;
+ mask = ENVY_MT_SPDSEL_MASK << shift;
+ reg = (reg & ~mask) | (sel << shift);
+ }
+
+ /*
+ * set the lineout route register
+ */
+ if (src < ENVY_MIX_OUTSRC_SPDIN) {
+ sel = ENVY_MT_OUTSRC_LINE;
+ } else if (src < ENVY_MIX_OUTSRC_DMA) {
+ sel = ENVY_MT_OUTSRC_SPD;
+ } else if (src == ENVY_MIX_OUTSRC_DMA) {
+ sel = ENVY_MT_OUTSRC_DMA;
+ } else {
+ sel = ENVY_MT_OUTSRC_MON;
+ }
+ shift = out * 2;
+ mask = ENVY_MT_SPDSRC_MASK << shift;
+ reg = (reg & ~mask) | (sel << shift);
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_SPDROUTE, reg);
+ DPRINTF("%s: spdroute <- %x\n", DEVNAME(sc), reg);
+}
+
+void
+envy_mon_getvol(struct envy_softc *sc, int idx, int *l, int *r) {
+ int reg;
+
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_MONIDX, idx);
+ reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_MONDATA);
+ *l = 0x7f - ((reg) & 0x7f);
+ *r = 0x7f - ((reg >> 8) & 0x7f);
+}
+
+void
+envy_mon_setvol(struct envy_softc *sc, int idx, int l, int r) {
+ int reg;
+
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_MONIDX, idx);
+ reg = (0x7f - l) | ((0x7f - r) << 8);
+ DPRINTF("%s: mon=%d <- %d,%d\n", DEVNAME(sc), reg, l, r);
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_MONDATA, reg);
+}
+
+int
+envymatch(struct device *parent, void *match, void *aux) {
+ struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+
+ if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ICENSEMBLE &&
+ PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ICENSEMBLE_ICE1712) {
+ return 1;
+ }
+ return 0;
+}
+
+void
+envyattach(struct device *parent, struct device *self, void *aux)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+ pci_intr_handle_t ih;
+ const char *intrstr;
+
+ sc->pci_tag = pa->pa_tag;
+ sc->pci_pc = pa->pa_pc;
+ sc->pci_dmat = pa->pa_dmat;
+ sc->pci_ih = NULL;
+ sc->ibuf.addr = sc->obuf.addr = NULL;
+ sc->ccs_iosz = 0;
+ sc->mt_iosz = 0;
+
+ if (pci_mapreg_map(pa, ENVY_CTL_BAR, PCI_MAPREG_TYPE_IO, 0,
+ &sc->ccs_iot, &sc->ccs_ioh, NULL, &sc->ccs_iosz, 0)) {
+ printf(": failed to map ctl i/o space\n");
+ sc->ccs_iosz = 0;
+ return;
+ }
+ if (pci_mapreg_map(pa, ENVY_MT_BAR, PCI_MAPREG_TYPE_IO, 0,
+ &sc->mt_iot, &sc->mt_ioh, NULL, &sc->mt_iosz, 0)) {
+ printf(": failed to map mt i/o space\n");
+ sc->mt_iosz = 0;
+ return;
+ }
+ if (pci_intr_map(pa, &ih)) {
+ printf(": can't map interrupt\n");
+ }
+ intrstr = pci_intr_string(sc->pci_pc, ih);
+ sc->pci_ih = pci_intr_establish(sc->pci_pc, ih, IPL_AUDIO,
+ envy_intr, sc, sc->dev.dv_xname);
+ if (sc->pci_ih == NULL) {
+ printf(": can't establish interrupt");
+ if (intrstr)
+ printf(" at %s", intrstr);
+ printf("\n");
+ return;
+ }
+ printf(": %s\n", intrstr);
+ envy_reset(sc);
+ sc->audio = audio_attach_mi(&envy_hw_if, sc, &sc->dev);
+}
+
+int
+envydetach(struct device *self, int flags)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+
+ if (sc->pci_ih != NULL) {
+ pci_intr_disestablish(sc->pci_pc, sc->pci_ih);
+ sc->pci_ih = NULL;
+ }
+ if (sc->ccs_iosz) {
+ bus_space_unmap(sc->ccs_iot, sc->ccs_ioh, sc->ccs_iosz);
+ }
+ if (sc->mt_iosz) {
+ bus_space_unmap(sc->ccs_iot, sc->mt_ioh, sc->mt_iosz);
+ }
+ return 0;
+}
+
+int
+envy_open(void *self, int flags)
+{
+ return 0;
+}
+
+void
+envy_close(void *self)
+{
+}
+
+void *
+envy_allocm(void *self, int dir, size_t size, int type, int flags)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ int err, rsegs, basereg, wait;
+ struct envy_buf *buf;
+
+ if (dir == AUMODE_RECORD) {
+ buf = &sc->ibuf;
+ basereg = ENVY_MT_RADDR;
+ } else {
+ buf = &sc->obuf;
+ basereg = ENVY_MT_PADDR;
+ }
+ if (buf->addr != NULL) {
+ DPRINTF("%s: multiple alloc, dir = %d\n", DEVNAME(sc), dir);
+ return NULL;
+ }
+ buf->size = size;
+ wait = (flags & M_NOWAIT) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK;
+
+#define ENVY_ALIGN 4
+#define ENVY_BOUNDARY 0
+
+ err = bus_dmamem_alloc(sc->pci_dmat, buf->size, ENVY_ALIGN,
+ ENVY_BOUNDARY, &buf->seg, 1, &rsegs, wait);
+ if (err) {
+ DPRINTF("%s: dmamem_alloc: failed %d\n", DEVNAME(sc), err);
+ goto err_ret;
+ }
+
+ err = bus_dmamem_map(sc->pci_dmat, &buf->seg, rsegs, buf->size,
+ &buf->addr, wait | BUS_DMA_COHERENT);
+ if (err) {
+ DPRINTF("%s: dmamem_map: failed %d\n", DEVNAME(sc), err);
+ goto err_free;
+ }
+
+ err = bus_dmamap_create(sc->pci_dmat, buf->size, 1, buf->size, 0,
+ wait, &buf->map);
+ if (err) {
+ DPRINTF("%s: dmamap_create: failed %d\n", DEVNAME(sc), err);
+ goto err_unmap;
+ }
+
+ err = bus_dmamap_load(sc->pci_dmat, buf->map, buf->addr,
+ buf->size, NULL, wait);
+ if (err) {
+ DPRINTF("%s: dmamap_load: failed %d\n", DEVNAME(sc), err);
+ goto err_destroy;
+ }
+ bus_space_write_4(sc->mt_iot, sc->mt_ioh, basereg, buf->seg.ds_addr);
+ DPRINTF("%s: allocated %d bytes dir=%d, ka=%p, da=%p\n",
+ DEVNAME(sc), buf->size, dir, buf->addr, buf->seg.ds_addr);
+ return buf->addr;
+
+ err_destroy:
+ bus_dmamap_destroy(sc->pci_dmat, buf->map);
+ err_unmap:
+ bus_dmamem_unmap(sc->pci_dmat, buf->addr, buf->size);
+ err_free:
+ bus_dmamem_free(sc->pci_dmat, &buf->seg, 1);
+ err_ret:
+ return NULL;
+}
+
+void
+envy_freem(void *self, void *addr, int type)
+{
+ struct envy_buf *buf;
+ struct envy_softc *sc = (struct envy_softc *)self;
+ int dir;
+
+ if (sc->ibuf.addr == addr) {
+ buf = &sc->ibuf;
+ dir = AUMODE_RECORD;
+ } else if (sc->obuf.addr == addr) {
+ buf = &sc->obuf;
+ dir = AUMODE_PLAY;
+ } else {
+ DPRINTF("%s: no buf to free\n", DEVNAME(sc));
+ return;
+ }
+ bus_dmamap_destroy(sc->pci_dmat, buf->map);
+ bus_dmamem_unmap(sc->pci_dmat, buf->addr, buf->size);
+ bus_dmamem_free(sc->pci_dmat, &buf->seg, 1);
+ buf->addr = NULL;
+ DPRINTF("%s: freed buffer (mode=%d)\n", DEVNAME(sc), dir);
+}
+
+int
+envy_query_encoding(void *self, struct audio_encoding *enc)
+{
+ if (enc->index == 0) {
+ strlcpy(enc->name, AudioEslinear_le, sizeof(enc->name));
+ enc->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ enc->precision = 32;
+ enc->flags = 0;
+ return 0;
+ }
+ return EINVAL;
+}
+
+int
+envy_set_params(void *self, int setmode, int usemode,
+ struct audio_params *p, struct audio_params *r)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ int i, rate, reg;
+
+ if (setmode == 0) {
+ DPRINTF("%s: no params to set\n", DEVNAME(sc));
+ return 0;
+ }
+ if (setmode == (AUMODE_PLAY | AUMODE_RECORD) &&
+ p->sample_rate != r->sample_rate) {
+ DPRINTF("%s: play/rec rates mismatch\n", DEVNAME(sc));
+ return EINVAL;
+ }
+ rate = (setmode & AUMODE_PLAY) ? p->sample_rate : r->sample_rate;
+ for (i = 0; envy_rates[i].rate < rate; i++) {
+ if (envy_rates[i].rate == -1) {
+ i--;
+ DPRINTF("%s: rate: %d -> %d\n", DEVNAME(sc), rate, i);
+ break;
+ }
+ }
+ reg = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_RATE);
+ reg &= ~ENVY_MT_RATEMASK;
+ reg |= envy_rates[i].reg;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_RATE, reg);
+ if (setmode & AUMODE_PLAY) {
+ p->encoding = AUDIO_ENCODING_SLINEAR;
+ p->precision = 32;
+ p->channels = ENVY_PCHANS;
+ }
+ if (setmode & AUMODE_RECORD) {
+ r->encoding = AUDIO_ENCODING_SLINEAR;
+ r->precision = 32;
+ r->channels = ENVY_RCHANS;
+ }
+ return 0;
+}
+
+int
+envy_round_blocksize(void *self, int blksz)
+{
+ /*
+ * XXX: sizes depend on the mode but we don't have
+ * access to the mode here; So we use the greatest
+ * common divisor of input and output blocksizes, until
+ * upper layer is fixed
+ */
+#define ENVY_GCD (6 * 5 * 4)
+ return (blksz / ENVY_GCD) * ENVY_GCD;
+}
+
+size_t
+envy_round_buffersize(void *self, int dir, size_t bufsz)
+{
+ /*
+ * XXX: same remark as above
+ */
+ return (bufsz / ENVY_GCD) * ENVY_GCD;
+}
+
+int
+envy_trigger_output(void *self, void *start, void *end, int blksz,
+ void (*intr)(void *), void *arg, struct audio_params *param)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ size_t bufsz;
+ int st;
+
+ bufsz = end - start;
+ if (bufsz % (ENVY_PCHANS * 4) != 0) {
+ DPRINTF("%s: %d: bad output bufsz\n", DEVNAME(sc), bufsz);
+ return EINVAL;
+ }
+ if (blksz % (ENVY_PCHANS * 4) != 0) {
+ DPRINTF("%s: %d: bad output blksz\n", DEVNAME(sc), blksz);
+ return EINVAL;
+ }
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh,
+ ENVY_MT_PBUFSZ, bufsz / 4 - 1);
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh,
+ ENVY_MT_PBLKSZ, blksz / 4 - 1);
+
+ sc->ointr = intr;
+ sc->oarg = arg;
+
+ st = ENVY_MT_INTR_PACK;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
+
+ st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL);
+ st |= ENVY_MT_CTL_PSTART;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st);
+ return 0;
+}
+
+int
+envy_trigger_input(void *self, void *start, void *end, int blksz,
+ void (*intr)(void *), void *arg, struct audio_params *param)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ size_t bufsz;
+ int st;
+
+ bufsz = end - start;
+ if (bufsz % (ENVY_RCHANS * 4) != 0) {
+ DPRINTF("%s: %d: bad input bufsz\n", DEVNAME(sc), bufsz);
+ return EINVAL;
+ }
+ if (blksz % (ENVY_RCHANS * 4) != 0) {
+ DPRINTF("%s: %d: bad input blksz\n", DEVNAME(sc), blksz);
+ return EINVAL;
+ }
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh,
+ ENVY_MT_RBUFSZ, bufsz / 4 - 1);
+ bus_space_write_2(sc->mt_iot, sc->mt_ioh,
+ ENVY_MT_RBLKSZ, blksz / 4 - 1);
+
+ sc->iintr = intr;
+ sc->iarg = arg;
+
+ st = ENVY_MT_INTR_RACK;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
+
+ st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL);
+ st |= ENVY_MT_CTL_RSTART;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st);
+ return 0;
+}
+
+int
+envy_halt_output(void *self)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ int st;
+
+ st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL);
+ st &= ~ENVY_MT_CTL_PSTART;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, 0);
+ return 0;
+}
+
+int
+envy_halt_input(void *self)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ int st;
+
+ st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL);
+ st &= ~ENVY_MT_CTL_RSTART;
+ bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, 0);
+ return 0;
+}
+
+int
+envy_getdev(void *self, struct audio_device *dev)
+{
+ strlcpy(dev->name, "Envy24", MAX_AUDIO_DEV_LEN);
+ strlcpy(dev->version, "-", MAX_AUDIO_DEV_LEN); /* XXX eeprom version */
+ strlcpy(dev->config, "envy", MAX_AUDIO_DEV_LEN);
+ return 0;
+}
+
+int
+envy_query_devinfo(void *self, struct mixer_devinfo *dev)
+{
+ int i, n, out;
+ char *classes[] = {
+ AudioCinputs, AudioCoutputs, "source", AudioCmonitor
+ };
+ /* XXX: define AudioCsource */
+
+ dev->prev = dev->next = AUDIO_MIXER_LAST;
+ if (dev->index < ENVY_MIX_OUTSRC) {
+ dev->mixer_class = dev->index - ENVY_MIX_CLASSIN;
+ strlcpy(dev->label.name,
+ classes[dev->index - ENVY_MIX_CLASSIN], MAX_AUDIO_DEV_LEN);
+ return 0;
+ }
+ if (dev->index < ENVY_MIX_MONITOR) {
+ n = 0;
+ out = dev->index - ENVY_MIX_OUTSRC;
+ dev->type = AUDIO_MIXER_ENUM;
+ dev->mixer_class = ENVY_MIX_CLASSMIX;
+ for (i = 0; i < 10; i++) {
+ dev->un.e.member[n].ord = n;
+ snprintf(dev->un.e.member[n++].label.name,
+ MAX_AUDIO_DEV_LEN, "in%d", i);
+ }
+ dev->un.e.member[n].ord = n;
+ snprintf(dev->un.e.member[n++].label.name,
+ MAX_AUDIO_DEV_LEN, "play%d", out);
+ if (out < 2) {
+ dev->un.e.member[n].ord = n;
+ snprintf(dev->un.e.member[n++].label.name,
+ MAX_AUDIO_DEV_LEN, "mon%d", out);
+ }
+ snprintf(dev->label.name, MAX_AUDIO_DEV_LEN, "out%d", out);
+ dev->un.s.num_mem = n;
+ return 0;
+ }
+ if (dev->index < ENVY_MIX_INVAL) {
+ out = dev->index - ENVY_MIX_MONITOR;
+ dev->type = AUDIO_MIXER_VALUE;
+ dev->mixer_class = ENVY_MIX_CLASSMON;
+ dev->un.v.delta = 2;
+ dev->un.v.num_channels = 2;
+ snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
+ "%s%d", out < 10 ? "play" : "rec", out % 10);
+ strlcpy(dev->un.v.units.name, AudioNvolume, MAX_AUDIO_DEV_LEN);
+ return 0;
+ }
+ return ENXIO;
+}
+
+int
+envy_get_port(void *self, struct mixer_ctrl *ctl)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ int out, l, r;
+
+ if (ctl->dev < ENVY_MIX_OUTSRC) {
+ return EINVAL;
+ }
+ if (ctl->dev < ENVY_MIX_OUTSRC + 8) {
+ out = ctl->dev - ENVY_MIX_OUTSRC;
+ ctl->un.ord = envy_lineout_getsrc(sc, out);
+ return 0;
+ }
+ if (ctl->dev < ENVY_MIX_MONITOR) {
+ out = ctl->dev - (ENVY_MIX_OUTSRC + 8);
+ ctl->un.ord = envy_spdout_getsrc(sc, out);
+ return 0;
+ }
+ if (ctl->dev < ENVY_MIX_INVAL) {
+ out = ctl->dev - ENVY_MIX_MONITOR;
+ envy_mon_getvol(sc, out, &l, &r);
+ ctl->un.value.num_channels = 2;
+ ctl->un.value.level[0] = 2 * l;
+ ctl->un.value.level[1] = 2 * r;
+ return 0;
+ }
+ return ENXIO;
+}
+
+int
+envy_set_port(void *self, struct mixer_ctrl *ctl)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+ int out, maxsrc, l, r;
+
+ if (ctl->dev < ENVY_MIX_OUTSRC) {
+ return EINVAL;
+ }
+ if (ctl->dev < ENVY_MIX_OUTSRC + 8) {
+ out = ctl->dev - ENVY_MIX_OUTSRC;
+ maxsrc = (out < 2 || out >= 8) ? 12 : 11;
+ if (ctl->un.ord < 0 || ctl->un.ord >= maxsrc)
+ return EINVAL;
+ envy_lineout_setsrc(sc, out, ctl->un.ord);
+ return 0;
+ }
+ if (ctl->dev < ENVY_MIX_MONITOR) {
+ out = ctl->dev - (ENVY_MIX_OUTSRC + 8);
+ if (ctl->un.ord < 0 || ctl->un.ord >= 12)
+ return EINVAL;
+ envy_spdout_setsrc(sc, out, ctl->un.ord);
+ return 0;
+ }
+ if (ctl->dev < ENVY_MIX_INVAL) {
+ out = ctl->dev - ENVY_MIX_MONITOR;
+ if (ctl->un.value.num_channels != 2) {
+ return EINVAL;
+ }
+ l = ctl->un.value.level[0] / 2;
+ r = ctl->un.value.level[1] / 2;
+ envy_mon_setvol(sc, out, l, r);
+ return 0;
+ }
+ return ENXIO;
+}
+
+int
+envy_get_props(void *self)
+{
+ return AUDIO_PROP_FULLDUPLEX;
+}
diff --git a/sys/dev/pci/envyreg.h b/sys/dev/pci/envyreg.h
new file mode 100644
index 00000000000..4283d2822f8
--- /dev/null
+++ b/sys/dev/pci/envyreg.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef SYS_DEV_PCI_ENVYREG_H
+#define SYS_DEV_PCI_ENVYREG_H
+
+/*
+ * BARs at PCI config space
+ */
+#define ENVY_CTL_BAR 0x10
+#define ENVY_MT_BAR 0x1c
+#define ENVY_CONF 0x60
+
+/*
+ * CCS "control" register
+ */
+#define ENVY_CTL 0x00
+#define ENVY_CTL_RESET 0x80
+#define ENVY_CTL_NATIVE 0x01
+#define ENVY_CCS_INTMASK 0x01
+#define ENVY_CCS_INT_MT 0x10
+#define ENVY_CCS_INT_MIDI1 0x80
+#define ENVY_CCS_INT_TMR 0x80
+#define ENVY_CCS_INT_MIDI0 0x80
+#define ENVY_CCS_INTSTAT 0x02
+
+/*
+ * CCS registers to access indirect registers (CCI)
+ */
+#define ENVY_CCI_INDEX 0x3
+#define ENVY_CCI_DATA 0x4
+
+/*
+ * CCS regisers to access iic bus
+ */
+#define ENVY_I2C_DEV 0x10
+#define ENVY_I2C_DEV_SHIFT 0x01
+#define ENVY_I2C_DEV_WRITE 0x01
+#define ENVY_I2C_DEV_EEPROM 0x50
+#define ENVY_I2C_ADDR 0x11
+#define ENVY_I2C_DATA 0x12
+#define ENVY_I2C_CTL 0x13
+#define ENVY_I2C_CTL_BUSY 0x1
+
+/*
+ * CCI registers to access GPIO pins
+ */
+#define ENVY_GPIO_DATA 0x20
+#define ENVY_GPIO_MASK 0x21
+#define ENVY_GPIO_DIR 0x22
+
+/*
+ * GPIO pin numbers
+ */
+#define ENVY_GPIO_CLK 0x2
+#define ENVY_GPIO_DOUT 0x8
+#define ENVY_GPIO_CSMASK 0x70
+#define ENVY_GPIO_CS(dev) ((dev) << 4)
+
+/*
+ * EEPROM bytes signification
+ */
+#define ENVY_EEPROM_CONF 6
+#define ENVY_EEPROM_ACLINK 7
+#define ENVY_EEPROM_I2S 8
+#define ENVY_EEPROM_SPDIF 9
+#define ENVY_EEPROM_GPIOMASK 10
+#define ENVY_EEPROM_GPIOST 11
+#define ENVY_EEPROM_GPIODIR 12
+
+#define ENVY_EEPROM_MAXSZ 32
+
+/*
+ * MT registers for play/record params
+ */
+#define ENVY_MT_INTR 0
+#define ENVY_MT_INTR_PACK 0x01
+#define ENVY_MT_INTR_RACK 0x02
+#define ENVY_MT_INTR_PMASK 0x40
+#define ENVY_MT_INTR_RMASK 0x80
+#define ENVY_MT_RATE 1
+#define ENVY_MT_RATEMASK 0x0f
+#define ENVY_MT_PADDR 0x10
+#define ENVY_MT_PBUFSZ 0x14
+#define ENVY_MT_PBLKSZ 0x16
+#define ENVY_MT_CTL 0x18
+#define ENVY_MT_CTL_PSTART 0x01
+#define ENVY_MT_CTL_PPAUSE 0x02
+#define ENVY_MT_CTL_RSTART 0x04
+#define ENVY_MT_CTL_RPAUSE 0x08
+#define ENVY_MT_RADDR 0x20
+#define ENVY_MT_RBUFSZ 0x24
+#define ENVY_MT_RBLKSZ 0x26
+
+/*
+ * MT registers for monitor gains
+ */
+#define ENVY_MT_MONDATA 0x38
+#define ENVY_MT_MONVAL_BITS 7
+#define ENVY_MT_MONVAL_MASK ((1 << ENVY_MT_MONVAL_BITS) - 1)
+#define ENVY_MT_MONIDX 0x3a
+
+/*
+ * MT registers to access the digital mixer
+ */
+#define ENVY_MT_OUTSRC 0x30
+#define ENVY_MT_OUTSRC_DMA 0x00
+#define ENVY_MT_OUTSRC_MON 0x01
+#define ENVY_MT_OUTSRC_LINE 0x02
+#define ENVY_MT_OUTSRC_SPD 0x03
+#define ENVY_MT_OUTSRC_MASK 0x04
+#define ENVY_MT_SPDROUTE 0x32
+#define ENVY_MT_SPDSRC_DMA 0x00
+#define ENVY_MT_SPDSRC_MON 0x01
+#define ENVY_MT_SPDSRC_LINE 0x02
+#define ENVY_MT_SPDSRC_SPD 0x03
+#define ENVY_MT_SPDSRC_MASK 0x04
+#define ENVY_MT_SPDSEL_BITS 0x4
+#define ENVY_MT_SPDSEL_MASK ((1 << ENVY_MT_SPDSEL_BITS) - 1)
+#define ENVY_MT_INSEL 0x34
+#define ENVY_MT_INSEL_BITS 0x4
+#define ENVY_MT_INSEL_MASK ((1 << ENVY_MT_INSEL_BITS) - 1)
+
+/*
+ * AK4524 control registers
+ */
+#define AK_PWR 0x00
+#define AK_PWR_DA 0x01
+#define AK_PWR_AD 0x02
+#define AK_PWR_VREF 0x04
+#define AK_RST 0x01
+#define AK_RST_DA 0x01
+#define AK_RST_AD 0x02
+#define AK_FMT 0x02
+#define AK_FMT_NORM 0
+#define AK_FMT_DBL 0x01
+#define AK_FMT_QUAD 0x02
+#define AK_FMT_QAUDFILT 0x04
+#define AK_FMT_256 0
+#define AK_FMT_512 0x04
+#define AK_FMT_1024 0x08
+#define AK_FMT_384 0x10
+#define AK_FMT_768 0x14
+#define AK_FMT_LSB16 0
+#define AK_FMT_LSB20 0x20
+#define AK_FMT_MSB24 0x40
+#define AK_FMT_IIS24 0x60
+#define AK_FMT_LSB24 0x80
+#define AK_DEEMVOL 0x03
+#define AK_MUTE 0x80
+#define AK_ADC_GAIN0 0x04
+#define AK_ADC_GAIN1 0x05
+#define AK_DAC_GAIN0 0x06
+#define AK_DAC_GAIN1 0x07
+
+/*
+ * default formats
+ */
+#define ENVY_RFRAME_SIZE (4 * 12)
+#define ENVY_PFRAME_SIZE (4 * 10)
+#define ENVY_RBUF_SIZE (ENVY_RFRAME_SIZE * 0x1000)
+#define ENVY_PBUF_SIZE (ENVY_PFRAME_SIZE * 0x1000)
+#define ENVY_RCHANS 12
+#define ENVY_PCHANS 10
+
+#endif /* !defined(SYS_DEV_PCI_ENVYREG_H) */
diff --git a/sys/dev/pci/envyvar.h b/sys/dev/pci/envyvar.h
new file mode 100644
index 00000000000..4143cecad3c
--- /dev/null
+++ b/sys/dev/pci/envyvar.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SYS_DEV_PCI_ENVYVAR_H
+#define SYS_DEV_PCI_ENVYVAR_H
+
+#include <sys/types.h>
+#include <sys/device.h>
+#include <dev/audio_if.h>
+
+struct envy_buf {
+ bus_dma_segment_t seg;
+ bus_dmamap_t map;
+ caddr_t addr;
+ size_t size;
+};
+
+/*
+ * ak4524 codecs
+ */
+struct envy_ak {
+ unsigned char reg[8]; /* shadow for ak4524 registers */
+};
+
+/*
+ * the envy24 have multiple components with their respective mixers
+ * depending on the configuration of the sound card, so we use
+ * "submixers" for them
+ */
+struct envy_submix {
+ int ndevs;
+ void (*devinfo)(struct mixer_devinfo *, int);
+ int (*set)(struct mixer_devinfo *, int);
+ int (*get)(struct mixer_devinfo *, int);
+};
+
+#define ENVY_SUBMIX_NMAX 0x10
+
+struct envy_softc {
+ struct device dev;
+ struct device *audio;
+ struct envy_buf ibuf, obuf;
+ pcitag_t pci_tag;
+ pci_chipset_tag_t pci_pc;
+ pci_intr_handle_t *pci_ih;
+ bus_dma_tag_t pci_dmat;
+ bus_space_tag_t ccs_iot;
+ bus_space_handle_t ccs_ioh;
+ bus_size_t ccs_iosz;
+ bus_space_tag_t mt_iot;
+ bus_space_handle_t mt_ioh;
+ bus_size_t mt_iosz;
+ struct envy_ak ak[4];
+ struct envy_submix submix[ENVY_SUBMIX_NMAX];
+ int nsubmix;
+ void (*iintr)(void *);
+ void *iarg;
+ void (*ointr)(void *);
+ void *oarg;
+};
+
+#define ENVY_MIX_CLASSIN 0
+#define ENVY_MIX_CLASSOUT 1
+#define ENVY_MIX_CLASSMIX 2
+#define ENVY_MIX_CLASSMON 3
+#define ENVY_MIX_OUTSRC 4
+#define ENVY_MIX_MONITOR 14
+#define ENVY_MIX_INVAL 34
+
+#define ENVY_MIX_OUTSRC_LINEIN 0
+#define ENVY_MIX_OUTSRC_SPDIN 8
+#define ENVY_MIX_OUTSRC_DMA 10
+#define ENVY_MIX_OUTSRC_MON 11
+
+#endif /* !defined(SYS_DEV_PCI_ENVYVAR_H) */
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 9af8017417c..e1413ae838c 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.245 2007/09/12 12:59:55 mglocker Exp $
+# $OpenBSD: files.pci,v 1.246 2007/10/28 13:42:31 ratchov Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -131,6 +131,11 @@ attach azalia at pci
file dev/pci/azalia.c azalia
file dev/pci/azalia_codec.c azalia
+# VIA Envy24 (aka ICE1712)
+device envy: audio, auconv, mulaw
+attach envy at pci
+file dev/pci/envy.c envy
+
# Creative Labs EMU10k1 (SBLive! series and PCI512)
device emu: audio, auconv, mulaw, ac97
attach emu at pci