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);