Re: high-end audio drivers [was: OSS audio drivers]

Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
From: Alexandre Ratchov
Date: Tuesday, October 23, 2007 - 11:33 pm

On Wed, Oct 24, 2007 at 12:55:39AM +0200, Jan Stary wrote:

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 <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 *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 <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_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 <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;
+};
+
+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);
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
OSS audio drivers, Jan Stary, (Tue Oct 23, 3:25 am)
Re: OSS audio drivers, Edd Barrett, (Tue Oct 23, 5:43 am)
Re: OSS audio drivers , Theo de Raadt, (Tue Oct 23, 6:32 am)
Re: OSS audio drivers, Alexandre Ratchov, (Tue Oct 23, 10:06 am)
high-end audio drivers [was: OSS audio drivers], Jan Stary, (Tue Oct 23, 3:55 pm)
Re: high-end audio drivers [was: OSS audio drivers], Alexandre Ratchov, (Tue Oct 23, 11:33 pm)