On Wed, Oct 24, 2007 at 12:55:39AM +0200, Jan Stary wrote:
> > > What is the relation of OpenBSD's audio drivers to the OSS project?
> > > What, if anything, does opensourcing (GPL, I know) their code mean for
> > > our audio drivers? In particular, does that mean (future) support for
> > > the high-end soundcards such as M-Audio Delta?
> > 
> > There's work in progress on adding support for Delta cards (1010,
> > 1010LT, 66, 44), and required features to make them usable (32bit
> > encodings, 12 channel capture, higher sample rate, etc...)
> 
> Where can I get in touch with this work and possibly test it?
> Is anything commited -> available in curent?
> 

it's not in cvs yet. Below's a diff you can test. It probably only
works on delta-1010 and delta-1010LT cards and it's enabled on i386
only. The diff adds support for 32bit samples and 10 channels.
Neither capture nor mixer are implemented yet. Feel free to contact
me privately if you have questions on that.

Anyway if you have any delta card, i'm interested in seeing your
card's eeprom contents (in dmesg), the kernel should be compiled on
i386 with these options:

        option  ENVY_DEBUG

        envy* at pci?
        audio* at envy?

Also, let me know if you notice regression with other audio
drivers.

cheers,

-- Alexandre

Index: arch/i386/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/i386/conf/GENERIC,v
retrieving revision 1.583
diff -u -p -r1.583 GENERIC
--- arch/i386/conf/GENERIC      14 Oct 2007 17:39:46 -0000      1.583
+++ arch/i386/conf/GENERIC      24 Oct 2007 05:54:38 -0000
@@ -628,6 +628,7 @@ maestro* at pci?                    # ESS Maestro PCI
 esa*   at pci?                         # ESS Maestro3 PCI
 yds*   at pci? flags 0x0000            # Yamaha YMF Audio
 emu*   at pci?                         # SB Live!
+#envy* at pci?                         # VIA Envy24 (aka ICE1712)
 sb0    at isa? port 0x220 irq 5 drq 1  # SoundBlaster
 sb*    at isapnp?
 ess*   at isapnp?                      # ESS Tech ES188[78], ES888
Index: dev/pci/envy.c
===================================================================
RCS file: dev/pci/envy.c
diff -N dev/pci/envy.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/pci/envy.c      24 Oct 2007 05:54:38 -0000
@@ -0,0 +1,699 @@
+/*
+ * Copyright (c) 2007 Alexandre Ratchov <[EMAIL PROTECTED]>
+ *
+ * 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 *sc, int reg);
+void envy_ccs_write(struct envy_softc *sc, int reg, int val);
+int  envy_cci_read(struct envy_softc *sc, int index);
+void envy_cci_write(struct envy_softc *sc, int index, int data);
+void envy_i2c_wait(struct envy_softc *sc);
+int  envy_i2c_read(struct envy_softc *sc, int dev, int addr);
+void envy_i2c_write(struct envy_softc *sc, int dev, int addr, int data);
+int  envy_gpio_read(struct envy_softc *sc);
+void envy_gpio_write(struct envy_softc *sc, int data);
+void envy_eeprom_read(struct envy_softc *sc, unsigned char *);
+void envy_reset(struct envy_softc *sc);
+void envy_ak_write(struct envy_softc *sc, int dev, int addr, int data);
+int  envy_intr(void *);
+
+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)->ctl_iot, (sc)->ctl_ioh, reg);
+}
+
+void
+envy_ccs_write(struct envy_softc *sc, int reg, int val)
+{
+       bus_space_write_1((sc)->ctl_iot, (sc)->ctl_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);
+       }
+       printf("%s: eeprom[] = (", DEVNAME(sc));
+       for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) {
+               printf(" %02x", (unsigned)eeprom[i]);
+       }
+       printf(" )\n");
+}
+
+void
+envy_ak_write(struct envy_softc *sc, int dev, int addr, int data)
+{
+       int bits, i, reg;
+
+       bits  = 0xa000 | (addr << 8) | 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);
+
+       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, 0x01, 0x00);     /* reset */
+               delay(300);
+               envy_ak_write(sc, dev, 0x01, 0x03);     /* normal operation */
+               envy_ak_write(sc, dev, 0x02, 0x60);     /* data format: i2s 
data */
+       }
+
+       /*
+        * clear and unmask interrupts
+        */ 
+       envy_ccs_write(sc, ENVY_CCS_ISTAT, 0xff);
+       envy_ccs_write(sc, ENVY_CCS_IMASK, 0x00);
+}
+
+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) {
+               sc->ointr(sc->oarg);
+               st = ENVY_MT_INTR_PACK;
+               bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
+       }
+       if (st & ENVY_MT_INTR_RACK) {
+               sc->iintr(sc->iarg);
+               st = ENVY_MT_INTR_RACK;
+               bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
+       }
+       return 1;
+}
+
+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->ctl_iosz = 0;
+       sc->mt_iosz = 0;
+
+       if (pci_mapreg_map(pa, ENVY_CTL_BAR, PCI_MAPREG_TYPE_IO, 0, 
+                          &sc->ctl_iot, &sc->ctl_ioh, NULL, &sc->ctl_iosz, 0)) 
{
+               printf(": failed to map ctl i/o space\n");
+               sc->ctl_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->ctl_iosz) {
+               bus_space_unmap(sc->ctl_iot, sc->ctl_ioh, sc->ctl_iosz);
+       }
+       if (sc->mt_iosz) {
+               bus_space_unmap(sc->ctl_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_OCHANS;
+       }
+       if (setmode & AUMODE_RECORD) {
+               r->encoding = AUDIO_ENCODING_SLINEAR;
+               r->precision = 32;
+               r->channels = ENVY_ICHANS;
+       }
+       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: sizes depend on the blocksize... since we don't
+        * have it, just round as blocksize.
+        * upper layer is fixed
+        */
+       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_OCHANS * 4) != 0) {
+               DPRINTF("%s: %d: bad output bufsz\n", DEVNAME(sc), bufsz);
+               return EINVAL;
+       }
+       if (blksz % (ENVY_OCHANS * 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_ICHANS * 4) != 0) {
+               DPRINTF("%s: %d: bad input bufsz\n", DEVNAME(sc), bufsz);
+               return EINVAL;
+       }
+       if (blksz % (ENVY_ICHANS * 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 *info)
+{
+       /*
+        * XXX: no mixer yet
+        */
+       return ENXIO;
+}
+
+int
+envy_get_port(void *self, struct mixer_ctrl *cp)
+{
+       return EINVAL;
+}
+
+int
+envy_set_port(void *self, struct mixer_ctrl *cp)
+{
+       return EINVAL;
+}
+
+int
+envy_get_props(void *self)
+{
+       return AUDIO_PROP_FULLDUPLEX;
+}
+
Index: dev/pci/envyreg.h
===================================================================
RCS file: dev/pci/envyreg.h
diff -N dev/pci/envyreg.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/pci/envyreg.h   24 Oct 2007 05:54:39 -0000
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2007 Alexandre Ratchov <[EMAIL PROTECTED]>
+ *
+ * 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_IMASK         0x01
+#define ENVY_CCS_ISTAT         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 register to access the digital mixer
+ */
+#define ENVY_MT_OUTSRC         0x30    /* output source */
+#define ENVY_MT_SPDROUTE       0x32    /* spd route */
+#define ENVY_MT_INSEL          0x34    /* input select */
+
+/*
+ * default formats
+ */
+#define ENVY_IFRAME_SIZE       (4 * 12)
+#define ENVY_OFRAME_SIZE       (4 * 10)
+#define ENVY_IBUF_SIZE         (ENVY_IFRAME_SIZE * 0x1000)
+#define ENVY_OBUF_SIZE         (ENVY_OFRAME_SIZE * 0x1000)
+#define ENVY_ICHANS            12
+#define ENVY_OCHANS            10
+
+#endif /* !defined(SYS_DEV_PCI_ENVYREG_H) */
Index: dev/pci/envyvar.h
===================================================================
RCS file: dev/pci/envyvar.h
diff -N dev/pci/envyvar.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/pci/envyvar.h   24 Oct 2007 05:54:39 -0000
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2007 Alexandre Ratchov <[EMAIL PROTECTED]>
+ *
+ * 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;
+};
+
+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         ctl_iot;
+       bus_space_handle_t      ctl_ioh;
+       bus_size_t              ctl_iosz;
+       bus_space_tag_t         mt_iot;
+       bus_space_handle_t      mt_ioh;
+       bus_size_t              mt_iosz;
+       void (*iintr)(void *);
+       void *iarg;
+       void (*ointr)(void *);
+       void *oarg;
+};
+
+#endif /* !defined(SYS_DEV_PCI_ENVYVAR_H) */
Index: dev/pci/files.pci
===================================================================
RCS file: /cvs/src/sys/dev/pci/files.pci,v
retrieving revision 1.245
diff -u -p -r1.245 files.pci
--- dev/pci/files.pci   12 Sep 2007 12:59:55 -0000      1.245
+++ dev/pci/files.pci   24 Oct 2007 05:54:39 -0000
@@ -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
Index: dev/audio.c
===================================================================
RCS file: /cvs/src/sys/dev/audio.c,v
retrieving revision 1.83
diff -u -p -r1.83 audio.c
--- dev/audio.c 23 Oct 2007 17:43:41 -0000      1.83
+++ dev/audio.c 24 Oct 2007 05:54:42 -0000
@@ -129,7 +129,7 @@ int audio_check_params(struct audio_para
 
 void   audio_set_blksize(struct audio_softc *, int, int);
 void   audio_calc_blksize(struct audio_softc *, int);
-void   audio_fill_silence(struct audio_params *, u_char *, int);
+void   audio_fill_silence(struct audio_params *, u_char *, u_char *, int);
 int    audio_silence_copyout(struct audio_softc *, int, struct uio *);
 
 void   audio_init_ringbuffer(struct audio_ringbuffer *);
@@ -1062,10 +1062,10 @@ audio_drain(struct audio_softc *sc)
                cc = cb->blksize - (inp - cb->start) % cb->blksize;
                if (sc->sc_pparams.sw_code) {
                        int ncc = cc / sc->sc_pparams.factor;
-                       audio_fill_silence(&sc->sc_pparams, inp, ncc);
+                       audio_fill_silence(&sc->sc_pparams, cb->start, inp, 
ncc);
                        sc->sc_pparams.sw_code(sc->hw_hdl, inp, ncc);
                } else
-                       audio_fill_silence(&sc->sc_pparams, inp, cc);
+                       audio_fill_silence(&sc->sc_pparams, cb->start, inp, cc);
                inp += cc;
                if (inp >= cb->end)
                        inp = cb->start;
@@ -1338,17 +1338,38 @@ audio_calc_blksize(struct audio_softc *s
 }
 
 void
-audio_fill_silence(struct audio_params *params, u_char *p, int n)
+audio_fill_silence(struct audio_params *params, u_char *start, u_char *p, int 
n)
 {
-       u_char auzero0, auzero1 = 0; /* initialize to please gcc */
-       int nfill = 1;
+       size_t rounderr;
+       int i, samplesz, nsamples;
+       u_char auzero[4] = {0, 0, 0, 0};
+
+       /*
+        * p may point the middle of a sample; round it to the
+        * beginning of the sample, so we overwrite partially written
+        * ones.
+        */
+       samplesz = params->precision / 8;
+       rounderr = (p - start) % samplesz;
+       p -= rounderr;
+       n += rounderr;
+       nsamples = n / samplesz;
 
        switch (params->encoding) {
+       case AUDIO_ENCODING_SLINEAR_LE:
+       case AUDIO_ENCODING_SLINEAR_BE:
+               break;
        case AUDIO_ENCODING_ULAW:
-               auzero0 = 0x7f;
+               auzero[0] = 0x7f;
                break;
        case AUDIO_ENCODING_ALAW:
-               auzero0 = 0x55;
+               auzero[0] = 0x55;
+               break;
+       case AUDIO_ENCODING_ULINEAR_LE:
+               auzero[samplesz - 1] = 0x80; 
+               break;
+       case AUDIO_ENCODING_ULINEAR_BE:
+               auzero[0] = 0x80;
                break;
        case AUDIO_ENCODING_MPEG_L1_STREAM:
        case AUDIO_ENCODING_MPEG_L1_PACKETS:
@@ -1357,38 +1378,14 @@ audio_fill_silence(struct audio_params *
        case AUDIO_ENCODING_MPEG_L2_PACKETS:
        case AUDIO_ENCODING_MPEG_L2_SYSTEM:
        case AUDIO_ENCODING_ADPCM: /* is this right XXX */
-       case AUDIO_ENCODING_SLINEAR_LE:
-       case AUDIO_ENCODING_SLINEAR_BE:
-               auzero0 = 0;    /* fortunately this works for both 8 and 16 
bits */
-               break;
-       case AUDIO_ENCODING_ULINEAR_LE:
-       case AUDIO_ENCODING_ULINEAR_BE:
-               if (params->precision == 16) {
-                       nfill = 2;
-                       if (params->encoding == AUDIO_ENCODING_ULINEAR_LE) {
-                               auzero0 = 0;
-                               auzero1 = 0x80;
-                       } else {
-                               auzero0 = 0x80;
-                               auzero1 = 0;
-                       }
-               } else
-                       auzero0 = 0x80;
                break;
        default:
                DPRINTF(("audio: bad encoding %d\n", params->encoding));
-               auzero0 = 0;
                break;
        }
-       if (nfill == 1) {
-               while (--n >= 0)
-                       *p++ = auzero0; /* XXX memset */
-       } else /* nfill must be 2 */ {
-               while (n > 1) {
-                       *p++ = auzero0;
-                       *p++ = auzero1;
-                       n -= 2;
-               }
+       while (--nsamples >= 0) {
+               for (i = 0; i < samplesz; i++) 
+                       *p++ = auzero[i];
        }
 }
 
@@ -1399,7 +1396,7 @@ audio_silence_copyout(struct audio_softc
        int k;
        u_char zerobuf[128];
 
-       audio_fill_silence(&sc->sc_rparams, zerobuf, sizeof zerobuf);
+       audio_fill_silence(&sc->sc_rparams, zerobuf, zerobuf, sizeof zerobuf);
 
        error = 0;
        while (n > 0 && uio->uio_resid > 0 && !error) {
@@ -1564,10 +1561,10 @@ audio_write(dev_t dev, struct uio *uio, 
                        DPRINTFN(1, ("audio_write: fill %d\n", cc));
                        if (sc->sc_pparams.sw_code) {
                                int ncc = cc / sc->sc_pparams.factor;
-                               audio_fill_silence(&sc->sc_pparams, einp, ncc);
+                               audio_fill_silence(&sc->sc_pparams, cb->start, 
einp, ncc);
                                sc->sc_pparams.sw_code(sc->hw_hdl, einp, ncc);
                        } else
-                               audio_fill_silence(&sc->sc_pparams, einp, cc);
+                               audio_fill_silence(&sc->sc_pparams, cb->start, 
einp, cc);
                }
        }
        return (error);
@@ -1855,7 +1852,7 @@ audio_mmap(dev_t dev, off_t off, int pro
        if (!cb->mmapped) {
                cb->mmapped = 1;
                if (cb == &sc->sc_pr) {
-                       audio_fill_silence(&sc->sc_pparams, cb->start, 
cb->bufsize);
+                       audio_fill_silence(&sc->sc_pparams, cb->start, 
cb->start, cb->bufsize);
                        s = splaudio();
                        if (!sc->sc_pbus && !sc->sc_pr.pause)
                                (void)audiostartp(sc);
@@ -1959,10 +1956,10 @@ audio_pint_silence(struct audio_softc *s
 
                        if (sc->sc_pparams.sw_code) {
                                int ncc = cc / sc->sc_pparams.factor;
-                               audio_fill_silence(&sc->sc_pparams, inp, ncc);
+                               audio_fill_silence(&sc->sc_pparams, cb->start, 
inp, ncc);
                                sc->sc_pparams.sw_code(sc->hw_hdl, inp, ncc);
                        } else
-                               audio_fill_silence(&sc->sc_pparams, inp, cc);
+                               audio_fill_silence(&sc->sc_pparams, cb->start, 
inp, cc);
 
                } else {
                        DPRINTFN(5, ("audio_pint_silence: already silent cc=%d 
inp=%p\n", cc, inp));
@@ -1976,10 +1973,10 @@ audio_pint_silence(struct audio_softc *s
 
                if (sc->sc_pparams.sw_code) {
                        int ncc = cc / sc->sc_pparams.factor;
-                       audio_fill_silence(&sc->sc_pparams, inp, ncc);
+                       audio_fill_silence(&sc->sc_pparams, cb->start, inp, 
ncc);
                        sc->sc_pparams.sw_code(sc->hw_hdl, inp, ncc);
                } else
-                       audio_fill_silence(&sc->sc_pparams, inp, cc);
+                       audio_fill_silence(&sc->sc_pparams, cb->start, inp, cc);
 
        }
 }
@@ -2226,7 +2223,8 @@ audio_check_params(struct audio_params *
        case AUDIO_ENCODING_SLINEAR_BE:
        case AUDIO_ENCODING_ULINEAR_LE:
        case AUDIO_ENCODING_ULINEAR_BE:
-               if (p->precision != 8 && p->precision != 16)
+               if (p->precision != 8 && p->precision != 16 && 
+                   p->precision != 32)
                        return (EINVAL);
                break;
        case AUDIO_ENCODING_MPEG_L1_STREAM:
@@ -2240,7 +2238,8 @@ audio_check_params(struct audio_params *
                return (EINVAL);
        }
 
-       if (p->channels < 1 || p->channels > 8) /* sanity check # of channels */
+       /* sanity check # of channels */
+       if (p->channels < 1 || p->channels > 12)
                return (EINVAL);
 
        return (0);

Reply via email to