diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2009-04-25 12:10:20 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2009-04-25 12:10:20 +0000 |
commit | 83305b4d12f114d43bae3b6b8a415ba6c0c03fd1 (patch) | |
tree | 3ecb71b36dd188f5c7171bd739b89df252b317f6 /sys/dev/pci/envy.c | |
parent | d1631f649cf84ed52ca5a0ffcc5d44bcdcaaf19d (diff) |
start splitting the driver in two parts: one specific to the
generic Envy24 chip common to all cards and another part specific
to the cards. This will ease adding support for other card models.
ok jakemsr@
Diffstat (limited to 'sys/dev/pci/envy.c')
-rw-r--r-- | sys/dev/pci/envy.c | 338 |
1 files changed, 229 insertions, 109 deletions
diff --git a/sys/dev/pci/envy.c b/sys/dev/pci/envy.c index c17d1d69fff..bf7f8fb70f7 100644 --- a/sys/dev/pci/envy.c +++ b/sys/dev/pci/envy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: envy.c,v 1.15 2009/03/29 21:53:52 sthen Exp $ */ +/* $OpenBSD: envy.c,v 1.16 2009/04/25 12:10:19 ratchov Exp $ */ /* * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> * @@ -15,6 +15,19 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* + * TODO: + * + * - add ndac, nadc, nspdin, nspdout, to struct envy_card + * + * - split mixer in 3 parts: envy-specific, codec-specific, and card-specific + * (gpio knobs) and put codec/card specific parts into envy_card structure + * + * - use eeprom version rather isht flag + * + * - rename s/ak/spi/g + */ + #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> @@ -49,9 +62,13 @@ void envy_cci_write(struct envy_softc *, int, int); void envy_i2c_wait(struct envy_softc *); int envy_i2c_read(struct envy_softc *, int, int); void envy_i2c_write(struct envy_softc *, int, int, int); -int envy_gpio_read(struct envy_softc *); -void envy_gpio_write(struct envy_softc *, int); -void envy_eeprom_read(struct envy_softc *, unsigned char *); +int envy_gpio_getstate(struct envy_softc *); +void envy_gpio_setstate(struct envy_softc *, int); +int envy_gpio_getmask(struct envy_softc *); +void envy_gpio_setmask(struct envy_softc *, int); +int envy_gpio_getdir(struct envy_softc *); +void envy_gpio_setdir(struct envy_softc *, int); +int envy_eeprom_gpioxxx(struct envy_softc *, int); void envy_reset(struct envy_softc *); int envy_ak_read(struct envy_softc *, int, int); void envy_ak_write(struct envy_softc *, int, int, int); @@ -85,6 +102,12 @@ int envy_get_port(void *, struct mixer_ctrl *); int envy_set_port(void *, struct mixer_ctrl *); int envy_get_props(void *); +void delta_init(struct envy_softc *); +void delta_ak_write(struct envy_softc *, int, int, int); + +void unkenvy_init(struct envy_softc *); +void unkenvy_ak_write(struct envy_softc *, int, int, int); + struct cfattach envy_ca = { sizeof(struct envy_softc), envymatch, envyattach, envydetach }; @@ -123,6 +146,10 @@ struct audio_hw_if envy_hw_if = { NULL }; +struct pci_matchid envy_matchids[] = { + { PCI_VENDOR_ICENSEMBLE, PCI_PRODUCT_ICENSEMBLE_ICE1712 }, +}; + /* * correspondence between rates (in frames per second) * and values of rate register @@ -135,6 +162,96 @@ struct { {64000, 0xf}, {88200, 0xb}, {96000, 0x7}, {-1, -1} }; +/* + * array with vendor/product sub-IDs to card info + */ +struct envy_card envy_cards[] = { + { + PCI_ID_CODE(0x1412, 0xd63b), + "delta", + 4, 4, + delta_init, + delta_ak_write, + NULL + }, { + 0, + "unkenvy", + 4, 4, + unkenvy_init, + unkenvy_ak_write + } +}; + +/* + * m-audio delta specific code + */ + +void +delta_init(struct envy_softc *sc) +{ + int dev; + + for (dev = 0; dev < 4 /* XXX */; dev++) { + envy_ak_write(sc, dev, AK_RST, 0x0); + delay(300); + envy_ak_write(sc, dev, AK_RST, AK_RST_AD | AK_RST_DA); + envy_ak_write(sc, dev, AK_FMT, AK_FMT_IIS24); + sc->ak[dev].reg[AK_DEEMVOL] = AK_DEEM_OFF; + sc->ak[dev].reg[AK_ADC_GAIN0] = 0x7f; + sc->ak[dev].reg[AK_ADC_GAIN1] = 0x7f; + sc->ak[dev].reg[AK_DAC_GAIN0] = 0x7f; + sc->ak[dev].reg[AK_DAC_GAIN1] = 0x7f; + } +} + +void +delta_ak_write(struct envy_softc *sc, int dev, int addr, int data) +{ + int bits, i, reg; + + reg = envy_gpio_getstate(sc); + reg &= ~ENVY_GPIO_CSMASK; + reg |= ENVY_GPIO_CS(dev); + envy_gpio_setstate(sc, reg); + delay(1); + + bits = 0xa000 | (addr << 8) | data; + for (i = 0; i < 16; i++) { + reg &= ~(ENVY_GPIO_CLK | ENVY_GPIO_DOUT); + reg |= (bits & 0x8000) ? ENVY_GPIO_DOUT : 0; + envy_gpio_setstate(sc, reg); + delay(1); + + reg |= ENVY_GPIO_CLK; + envy_gpio_setstate(sc, reg); + delay(1); + bits <<= 1; + } + + reg |= ENVY_GPIO_CSMASK; + envy_gpio_setstate(sc, reg); + delay(1); +} + +/* + * unknown card, ignore codecs setup and hope it works with the power on + * settings + */ + +void +unkenvy_init(struct envy_softc *sc) +{ +} + +void +unkenvy_ak_write(struct envy_softc *sc, int dev, int addr, int data) +{ +} + +/* + * generic Envy24 and Envy24HT code, common to all cards + */ + int envy_ccs_read(struct envy_softc *sc, int reg) { @@ -163,6 +280,42 @@ envy_cci_write(struct envy_softc *sc, int index, int data) envy_ccs_write(sc, ENVY_CCI_DATA, data); } +int +envy_gpio_getstate(struct envy_softc *sc) +{ + return envy_cci_read(sc, ENVY_CCI_GPIODATA); +} + +void +envy_gpio_setstate(struct envy_softc *sc, int reg) +{ + envy_cci_write(sc, ENVY_CCI_GPIODATA, reg); +} + +int +envy_gpio_getmask(struct envy_softc *sc) +{ + return envy_cci_read(sc, ENVY_CCI_GPIOMASK); +} + +void +envy_gpio_setmask(struct envy_softc *sc, int mask) +{ + envy_cci_write(sc, ENVY_CCI_GPIOMASK, mask); +} + +int +envy_gpio_getdir(struct envy_softc *sc) +{ + return envy_cci_read(sc, ENVY_CCI_GPIODIR); +} + +void +envy_gpio_setdir(struct envy_softc *sc, int dir) +{ + envy_cci_write(sc, ENVY_CCI_GPIODIR, dir); +} + void envy_i2c_wait(struct envy_softc *sc) { @@ -207,23 +360,6 @@ envy_i2c_write(struct envy_softc *sc, int dev, int addr, int data) envy_ccs_write(sc, ENVY_I2C_DEV, (dev << 1) | 1); } -void -envy_eeprom_read(struct envy_softc *sc, unsigned char *eeprom) -{ - int i; - - for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) { - eeprom[i] = envy_i2c_read(sc, ENVY_I2C_DEV_EEPROM, i); - } -#ifdef ENVY_DEBUG - printf("%s: eeprom: ", DEVNAME(sc)); - for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) { - printf(" %02x", (unsigned)eeprom[i]); - } - printf("\n"); -#endif -} - int envy_ak_read(struct envy_softc *sc, int dev, int addr) { return sc->ak[dev].reg[addr]; @@ -232,40 +368,15 @@ envy_ak_read(struct envy_softc *sc, int dev, int addr) { void envy_ak_write(struct envy_softc *sc, int dev, int addr, int data) { - int bits, i, reg; - DPRINTFN(2, "envy_ak_write: %d, %d, 0x%x\n", dev, addr, data); sc->ak[dev].reg[addr] = data; - - reg = envy_cci_read(sc, ENVY_GPIO_DATA); - reg &= ~ENVY_GPIO_CSMASK; - reg |= ENVY_GPIO_CS(dev); - envy_cci_write(sc, ENVY_GPIO_DATA, reg); - delay(1); - - bits = 0xa000 | (addr << 8) | data; - for (i = 0; i < 16; i++) { - reg &= ~(ENVY_GPIO_CLK | ENVY_GPIO_DOUT); - reg |= (bits & 0x8000) ? ENVY_GPIO_DOUT : 0; - envy_cci_write(sc, ENVY_GPIO_DATA, reg); - delay(1); - - reg |= ENVY_GPIO_CLK; - envy_cci_write(sc, ENVY_GPIO_DATA, reg); - delay(1); - bits <<= 1; - } - - reg |= ENVY_GPIO_CSMASK; - envy_cci_write(sc, ENVY_GPIO_DATA, reg); - delay(1); + sc->card->ak_write(sc, dev, addr, data); } void envy_reset(struct envy_softc *sc) { - char eeprom[ENVY_EEPROM_MAXSZ]; - int dev; + int i; /* * full reset @@ -276,45 +387,48 @@ envy_reset(struct envy_softc *sc) delay(200); /* - * read config from eprom and write it to registers + * read eeprom using i2c device or from a static array + */ + if (sc->card->eeprom == NULL) { + for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) { + sc->eeprom[i] = envy_i2c_read(sc, ENVY_I2C_DEV_EEPROM, i); + } +#ifdef ENVY_DEBUG + printf("%s: eeprom: ", DEVNAME(sc)); + for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) { + printf(" %02x", (unsigned)sc->eeprom[i]); + } + printf("\n"); +#endif + } else + memcpy(sc->eeprom, sc->card->eeprom, ENVY_EEPROM_MAXSZ); + + /* + * write eeprom values to corresponding 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]); + sc->eeprom[ENVY_EEPROM_CONF] | + (sc->eeprom[ENVY_EEPROM_ACLINK] << 8) | + (sc->eeprom[ENVY_EEPROM_I2S] << 16) | + (sc->eeprom[ENVY_EEPROM_SPDIF] << 24)); + + envy_gpio_setmask(sc, sc->eeprom[ENVY_EEPROM_GPIOMASK]); + envy_gpio_setdir(sc, sc->eeprom[ENVY_EEPROM_GPIODIR]); + envy_gpio_setstate(sc, sc->eeprom[ENVY_EEPROM_GPIOST]); DPRINTF("%s: gpio_mask = %02x\n", DEVNAME(sc), - envy_cci_read(sc, ENVY_GPIO_MASK)); + envy_gpio_getmask(sc)); DPRINTF("%s: gpio_dir = %02x\n", DEVNAME(sc), - envy_cci_read(sc, ENVY_GPIO_DIR)); + envy_gpio_getdir(sc)); DPRINTF("%s: gpio_state = %02x\n", DEVNAME(sc), - envy_cci_read(sc, ENVY_GPIO_DATA)); - - /* - * reset ak4524 codecs - */ - for (dev = 0; dev < 4; dev++) { - envy_ak_write(sc, dev, AK_RST, 0x0); - delay(300); - envy_ak_write(sc, dev, AK_RST, AK_RST_AD | AK_RST_DA); - envy_ak_write(sc, dev, AK_FMT, AK_FMT_IIS24); - sc->ak[dev].reg[AK_DEEMVOL] = AK_DEEM_OFF; - sc->ak[dev].reg[AK_ADC_GAIN0] = 0x7f; - sc->ak[dev].reg[AK_ADC_GAIN1] = 0x7f; - sc->ak[dev].reg[AK_DAC_GAIN0] = 0x7f; - sc->ak[dev].reg[AK_DAC_GAIN1] = 0x7f; - } + envy_gpio_getstate(sc)); /* * clear all interrupts and unmask used ones */ envy_ccs_write(sc, ENVY_CCS_INTSTAT, 0xff); envy_ccs_write(sc, ENVY_CCS_INTMASK, ~ENVY_CCS_INT_MT); + sc->card->init(sc); } int @@ -487,13 +601,8 @@ envy_mon_setvol(struct envy_softc *sc, int idx, int ch, int val) { 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; + return pci_matchbyid((struct pci_attach_args *)aux, envy_matchids, + sizeof(envy_matchids) / sizeof(envy_matchids[0])); } void @@ -503,6 +612,7 @@ envyattach(struct device *parent, struct device *self, void *aux) struct pci_attach_args *pa = (struct pci_attach_args *)aux; pci_intr_handle_t ih; const char *intrstr; + int subid; sc->pci_tag = pa->pa_tag; sc->pci_pc = pa->pa_pc; @@ -513,13 +623,13 @@ envyattach(struct device *parent, struct device *self, void *aux) sc->mt_iosz = 0; if (pci_mapreg_map(pa, ENVY_CTL_BAR, PCI_MAPREG_TYPE_IO, 0, - &sc->ccs_iot, &sc->ccs_ioh, NULL, &sc->ccs_iosz, 0)) { + &sc->ccs_iot, &sc->ccs_ioh, NULL, &sc->ccs_iosz, 0)) { printf(": can't map ctl i/o space\n"); sc->ccs_iosz = 0; return; } - if (pci_mapreg_map(pa, ENVY_MT_BAR, PCI_MAPREG_TYPE_IO, 0, - &sc->mt_iot, &sc->mt_ioh, NULL, &sc->mt_iosz, 0)) { + 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(": can't map mt i/o space\n"); sc->mt_iosz = 0; return; @@ -538,6 +648,16 @@ envyattach(struct device *parent, struct device *self, void *aux) return; } printf(": %s\n", intrstr); + subid = pci_conf_read(sc->pci_pc, sc->pci_tag, PCI_SUBVEND_0); + sc->card = envy_cards; + while (sc->card->subid != subid) { + if (sc->card->subid == 0) { + printf("%s: unknown card\n", DEVNAME(sc)); + break; + } + sc->card++; + } + DPRINTF("%s: type=%s\n", DEVNAME(sc), sc->card->name); envy_reset(sc); sc->audio = audio_attach_mi(&envy_hw_if, sc, &sc->dev); } @@ -623,8 +743,8 @@ envy_allocm(void *self, int dir, size_t size, int type, int flags) 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); + DPRINTF("%s: allocated %ld bytes dir=%d, ka=%p, da=%p\n", + DEVNAME(sc), buf->size, dir, buf->addr, (void *)buf->seg.ds_addr); return buf->addr; err_destroy: @@ -718,23 +838,17 @@ envy_set_params(void *self, int setmode, int usemode, 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; +#define ENVY_MUL (6 * 5 * 4) + blksz -= blksz % ENVY_MUL; + if (blksz == 0) + blksz = ENVY_MUL; + return blksz; } size_t envy_round_buffersize(void *self, int dir, size_t bufsz) { - /* - * XXX: same remark as above - */ - return (bufsz / ENVY_GCD) * ENVY_GCD; + return bufsz; } int @@ -746,14 +860,16 @@ envy_trigger_output(void *self, void *start, void *end, int blksz, int st; bufsz = end - start; - if (bufsz % (ENVY_PCHANS * 4) != 0) { - DPRINTF("%s: %d: bad output bufsz\n", DEVNAME(sc), bufsz); +#ifdef ENVY_DEBUG + if (blksz % ENVY_PFRAME_SIZE != 0) { + printf("%s: %d: bad output blksz\n", DEVNAME(sc), blksz); return EINVAL; } - if (blksz % (ENVY_PCHANS * 4) != 0) { - DPRINTF("%s: %d: bad output blksz\n", DEVNAME(sc), blksz); + if (bufsz % blksz) { + printf("%s: %ld: bad output bufsz\n", DEVNAME(sc), bufsz); return EINVAL; } +#endif 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, @@ -780,14 +896,16 @@ envy_trigger_input(void *self, void *start, void *end, int blksz, int st; bufsz = end - start; - if (bufsz % (ENVY_RCHANS * 4) != 0) { - DPRINTF("%s: %d: bad input bufsz\n", DEVNAME(sc), bufsz); +#ifdef ENVY_DEBUG + if (blksz % ENVY_RFRAME_SIZE != 0) { + printf("%s: %d: bad input blksz\n", DEVNAME(sc), blksz); return EINVAL; } - if (blksz % (ENVY_RCHANS * 4) != 0) { - DPRINTF("%s: %d: bad input blksz\n", DEVNAME(sc), blksz); + if (bufsz % blksz != 0) { + printf("%s: %ld: bad input bufsz\n", DEVNAME(sc), bufsz); return EINVAL; } +#endif 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, @@ -832,9 +950,11 @@ envy_halt_input(void *self) int envy_getdev(void *self, struct audio_device *dev) { + struct envy_softc *sc = (struct envy_softc *)self; + 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); + strlcpy(dev->version, "-", MAX_AUDIO_DEV_LEN); + strlcpy(dev->config, sc->card->name, MAX_AUDIO_DEV_LEN); return 0; } |