diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2009-11-02 05:54:17 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2009-11-02 05:54:17 +0000 |
commit | 8cf0f3b7277cb8e42f8471fdda85b3869063464b (patch) | |
tree | c942881de8f43e4f7a920333365128adc26db27d /sys/dev/pci/envy.c | |
parent | a4fc96907fb0609d373c5c19f4ae315eaad555d9 (diff) |
Add support for ``M-Audio Revolution 5.1'' cards, based on envy24HT.
From Alexandr Shadchin <ShadchinAV _at_ mail.ru>
Thanks!
Diffstat (limited to 'sys/dev/pci/envy.c')
-rw-r--r-- | sys/dev/pci/envy.c | 255 |
1 files changed, 252 insertions, 3 deletions
diff --git a/sys/dev/pci/envy.c b/sys/dev/pci/envy.c index 76943ee0ddb..37079b0c769 100644 --- a/sys/dev/pci/envy.c +++ b/sys/dev/pci/envy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: envy.c,v 1.30 2009/10/24 09:13:35 ratchov Exp $ */ +/* $OpenBSD: envy.c,v 1.31 2009/11/02 05:54:16 ratchov Exp $ */ /* * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> * @@ -66,6 +66,9 @@ 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); +void envy_gpio_i2c_start_bit(struct envy_softc *, int, int); +void envy_gpio_i2c_stop_bit(struct envy_softc *, int, int); +void envy_gpio_i2c_byte_out(struct envy_softc *, int, int, int); int envy_eeprom_gpioxxx(struct envy_softc *, int); void envy_reset(struct envy_softc *); int envy_codec_read(struct envy_softc *, int, int); @@ -103,6 +106,9 @@ int envy_get_props(void *); void delta_init(struct envy_softc *); void delta_codec_write(struct envy_softc *, int, int, int); +void revo51_init(struct envy_softc *); +void revo51_codec_write(struct envy_softc *, int, int, int); + void julia_init(struct envy_softc *); void julia_codec_write(struct envy_softc *, int, int, int); @@ -124,6 +130,11 @@ void ak4358_dac_devinfo(struct envy_softc *, struct mixer_devinfo *, int); void ak4358_dac_get(struct envy_softc *, struct mixer_ctrl *, int); int ak4358_dac_set(struct envy_softc *, struct mixer_ctrl *, int); +int ak5365_adc_ndev(struct envy_softc *); +void ak5365_adc_devinfo(struct envy_softc *, struct mixer_devinfo *, int); +void ak5365_adc_get(struct envy_softc *, struct mixer_ctrl *, int); +int ak5365_adc_set(struct envy_softc *, struct mixer_ctrl *, int); + struct cfattach envy_ca = { sizeof(struct envy_softc), envymatch, envyattach, envydetach }; @@ -197,6 +208,8 @@ struct envy_codec ak4524_dac = { "ak4524 adc", ak4524_adc_ndev, ak4524_adc_devinfo, ak4524_adc_get, ak4524_adc_set }, ak4358_dac = { "ak4358 dac", ak4358_dac_ndev, ak4358_dac_devinfo, ak4358_dac_get, ak4358_dac_set +}, ak5365_adc = { + "ak5365 adc", ak5365_adc_ndev, ak5365_adc_devinfo, ak5365_adc_get, ak5365_adc_set }, unkenvy_codec = { "unknown codec", unkenvy_codec_ndev, NULL, NULL, NULL }; @@ -256,6 +269,12 @@ struct envy_card envy_cards[] = { julia_codec_write, julia_eeprom }, { + PCI_ID_CODE(0x1412, 0x3631), + "M-Audio Revolution 5.1", + 2, &ak5365_adc, 6, &ak4358_dac, + revo51_init, + revo51_codec_write + }, { 0, "unknown 1724-based card", 2, &unkenvy_codec, 8, &unkenvy_codec, @@ -318,6 +337,104 @@ delta_codec_write(struct envy_softc *sc, int dev, int addr, int data) delay(1); } +/* + * m-audio revolution 5.1 specific code + */ + +#define REVO51_GPIO_CSMASK 0x30 +#define REVO51_GPIO_CS(dev) ((dev) ? 0x10 : 0x20) +#define REVO51_MUTE 0x400000 +#define REVO51_PT2258S_SDA 0x40 +#define REVO51_PT2258S_SCL 0x80 +#define REVO51_PT2258S_ADDR 0x80 +#define REVO51_PT2258S_MUTE 6 + +void +revo51_init(struct envy_softc *sc) +{ + int i, reg; + + /* AK4358 */ + envy_codec_write(sc, 0, 0, 0); /* reset */ + delay(300); + envy_codec_write(sc, 0, 0, 0x87); /* i2s mode */ + for (i = 0; i < sc->card->noch; i++) { + sc->shadow[0][AK4358_ATT(i)] = 0xff; + } + + /* AK5365 */ + envy_codec_write(sc, 1, AK5365_RST, 0); /* reset */ + delay(300); + envy_codec_write(sc, 1, AK5365_CTRL, AK5365_CTRL_I2S); /* i2s mode */ + envy_codec_write(sc, 1, AK5365_RST , AK5365_RST_NORM); + sc->shadow[1][AK5365_ATT(0)] = 0x7f; + sc->shadow[1][AK5365_ATT(1)] = 0x7f; + + /* PT2258S */ + envy_codec_write(sc, 2, REVO51_PT2258S_MUTE, 0xc0); /* reset */ + envy_codec_write(sc, 2, REVO51_PT2258S_MUTE, 0xf9); /* mute */ + + reg = envy_gpio_getstate(sc); + reg |= REVO51_MUTE; + envy_gpio_setstate(sc, reg); +} + +void +revo51_codec_write(struct envy_softc *sc, int dev, int addr, int data) +{ + int attn, bits, mask, reg; + int xlat[6] = {0x90, 0x50, 0x10, 0x30, 0x70, 0xb0}; + + /* AK4358 & AK5365 */ + if (dev < 2) { + reg = envy_gpio_getstate(sc); + reg &= ~REVO51_GPIO_CSMASK; + reg |= REVO51_GPIO_CS(dev); + envy_gpio_setstate(sc, reg); + delay(1); + + bits = 0xa000 | (addr << 8) | data; + for (mask = 0x8000; mask != 0; mask >>= 1) { + reg &= ~(ENVY_GPIO_CLK | ENVY_GPIO_DOUT); + reg |= (bits & mask) ? ENVY_GPIO_DOUT : 0; + envy_gpio_setstate(sc, reg); + delay(1); + + reg |= ENVY_GPIO_CLK; + envy_gpio_setstate(sc, reg); + delay(1); + } + + reg |= REVO51_GPIO_CSMASK; + envy_gpio_setstate(sc, reg); + delay(1); + return; + } + + /* PT2258S */ + envy_gpio_i2c_start_bit(sc, REVO51_PT2258S_SDA, REVO51_PT2258S_SCL); + envy_gpio_i2c_byte_out(sc, REVO51_PT2258S_SDA, REVO51_PT2258S_SCL, + REVO51_PT2258S_ADDR); + + if (addr == REVO51_PT2258S_MUTE) { + envy_gpio_i2c_byte_out(sc, REVO51_PT2258S_SDA, + REVO51_PT2258S_SCL, data); + } else { + /* 1's digit */ + attn = data % 10; + attn += xlat[addr]; + envy_gpio_i2c_byte_out(sc, REVO51_PT2258S_SDA, + REVO51_PT2258S_SCL, attn); + + /* 10's digit */ + attn = data / 10; + attn += xlat[addr] - 0x10; + envy_gpio_i2c_byte_out(sc, REVO51_PT2258S_SDA, + REVO51_PT2258S_SCL, attn); + } + + envy_gpio_i2c_stop_bit(sc, REVO51_PT2258S_SDA, REVO51_PT2258S_SCL); +} /* * esi julia specific code @@ -374,7 +491,6 @@ ak4358_dac_ndev(struct envy_softc *sc) return sc->card->noch; } - void ak4358_dac_devinfo(struct envy_softc *sc, struct mixer_devinfo *dev, int idx) { @@ -535,6 +651,80 @@ ak4524_adc_set(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx) } /* + * AK 5365 ADC specific code + */ +int +ak5365_adc_ndev(struct envy_softc *sc) +{ + /* 1 source + 2 volume knobs per channel pair */ + return (sc->card->nich + 1); +} + +void +ak5365_adc_devinfo(struct envy_softc *sc, struct mixer_devinfo *dev, int idx) +{ + int ndev, i; + + ndev = sc->card->nich; + if (idx < ndev) { + dev->type = AUDIO_MIXER_VALUE; + dev->mixer_class = ENVY_MIX_CLASSIN; + dev->un.v.delta = 2; + dev->un.v.num_channels = 1; + snprintf(dev->label.name, MAX_AUDIO_DEV_LEN, + AudioNline "%d", idx); + strlcpy(dev->un.v.units.name, AudioNvolume, + MAX_AUDIO_DEV_LEN); + } else { + dev->type = AUDIO_MIXER_ENUM; + dev->mixer_class = ENVY_MIX_CLASSIN; + for (i = 0; i < 5; i++) { + dev->un.e.member[i].ord = i; + snprintf(dev->un.e.member[i].label.name, + MAX_AUDIO_DEV_LEN, AudioNline "%d", i); + } + dev->un.e.num_mem = 5; + strlcpy(dev->label.name, AudioNsource, + MAX_AUDIO_DEV_LEN); + } +} + +void +ak5365_adc_get(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx) +{ + int val, ndev; + + ndev = sc->card->nich; + if (idx < ndev) { + val = envy_codec_read(sc, 1, AK5365_ATT(idx)); + ctl->un.value.num_channels = 1; + ctl->un.value.level[0] = 2 * val; + } else { + ctl->un.ord = envy_codec_read(sc, 1, AK5365_SRC); + } +} + +int +ak5365_adc_set(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx) +{ + int val, ndev; + + ndev = sc->card->nich; + if (idx < ndev) { + if (ctl->un.value.num_channels != 1) + return EINVAL; + val = ctl->un.value.level[0] / 2; + envy_codec_write(sc, 1, AK5365_ATT(idx), val); + } else { + if (ctl->un.ord >= 5) + return EINVAL; + val = ctl->un.ord & AK5365_SRC_MASK; + envy_codec_write(sc, 1, AK5365_SRC, val); + } + return 0; +} + +/* * generic Envy24 and Envy24HT code, common to all cards */ @@ -633,6 +823,65 @@ envy_gpio_setdir(struct envy_softc *sc, int dir) } void +envy_gpio_i2c_start_bit(struct envy_softc *sc, int sda, int scl) +{ + int reg; + + reg = envy_gpio_getstate(sc); + reg |= (sda | scl); + envy_gpio_setstate(sc, reg); + delay(5); + reg &= ~sda; + envy_gpio_setstate(sc, reg); + delay(4); + reg &= ~scl; + envy_gpio_setstate(sc, reg); + delay(5); +} + +void +envy_gpio_i2c_stop_bit(struct envy_softc *sc, int sda, int scl) +{ + int reg; + + reg = envy_gpio_getstate(sc); + reg &= ~sda; + reg |= scl; + envy_gpio_setstate(sc, reg); + delay(4); + reg |= sda; + envy_gpio_setstate(sc, reg); +} + +void +envy_gpio_i2c_byte_out(struct envy_softc *sc, int sda, int scl, int val) +{ + int mask, reg; + + reg = envy_gpio_getstate(sc); + + for (mask = 0x80; mask != 0; mask >>= 1) { + reg &= ~sda; + reg |= (val & mask) ? sda : 0; + envy_gpio_setstate(sc, reg); + delay(1); + reg |= scl; + envy_gpio_setstate(sc, reg); + delay(4); + reg &= ~scl; + envy_gpio_setstate(sc, reg); + delay(5); + } + + reg |= scl; + envy_gpio_setstate(sc, reg); + delay(4); + reg &= ~scl; + envy_gpio_setstate(sc, reg); + delay(5); +} + +void envy_i2c_wait(struct envy_softc *sc) { int timeout = 50, st; @@ -751,7 +1000,7 @@ envy_reset(struct envy_softc *sc) (sc->eeprom[ENVY_EEPROM_I2S] << 16) | (sc->eeprom[ENVY_EEPROM_SPDIF] << 24)); } - + envy_gpio_setmask(sc, envy_eeprom_gpioxxx(sc, ENVY_EEPROM_GPIOMASK(sc))); envy_gpio_setdir(sc, envy_eeprom_gpioxxx(sc, ENVY_EEPROM_GPIODIR(sc))); envy_gpio_setstate(sc, envy_eeprom_gpioxxx(sc, ENVY_EEPROM_GPIOST(sc))); |