summaryrefslogtreecommitdiff
path: root/sys/dev/pci/envy.c
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2009-04-25 12:10:20 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2009-04-25 12:10:20 +0000
commit83305b4d12f114d43bae3b6b8a415ba6c0c03fd1 (patch)
tree3ecb71b36dd188f5c7171bd739b89df252b317f6 /sys/dev/pci/envy.c
parentd1631f649cf84ed52ca5a0ffcc5d44bcdcaaf19d (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.c338
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;
}