summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2009-11-02 05:54:17 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2009-11-02 05:54:17 +0000
commit8cf0f3b7277cb8e42f8471fdda85b3869063464b (patch)
treec942881de8f43e4f7a920333365128adc26db27d /sys
parenta4fc96907fb0609d373c5c19f4ae315eaad555d9 (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')
-rw-r--r--sys/dev/pci/envy.c255
-rw-r--r--sys/dev/pci/envyreg.h14
2 files changed, 265 insertions, 4 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)));
diff --git a/sys/dev/pci/envyreg.h b/sys/dev/pci/envyreg.h
index 27a056a9d0e..02ce615cc7a 100644
--- a/sys/dev/pci/envyreg.h
+++ b/sys/dev/pci/envyreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: envyreg.h,v 1.10 2009/10/11 12:59:29 ratchov Exp $ */
+/* $OpenBSD: envyreg.h,v 1.11 2009/11/02 05:54:16 ratchov Exp $ */
/*
* Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org>
*
@@ -199,6 +199,18 @@
#define AK4358_ATT_EN 0x80
/*
+ * AK5365 control registers
+ */
+#define AK5365_RST 0x00
+#define AK5365_RST_NORM 0x01
+#define AK5365_SRC 0x01
+#define AK5365_SRC_MASK 0x07
+#define AK5365_CTRL 0x02
+#define AK5365_CTRL_MUTE 0x01
+#define AK5365_CTRL_I2S 0x08
+#define AK5365_ATT(chan) (0x4 + (chan))
+
+/*
* default formats
*/
#define ENVY_RCHANS 12