dect
/
linux-2.6
Archived
13
0
Fork 0

ALSA: virtuoso: add Xonar HDAV1.3 support

Add support for the Asus Xonar HDAV1.3 and the Xonar HDAV1.3 Deluxe.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
This commit is contained in:
Clemens Ladisch 2008-09-22 09:12:11 +02:00
parent 79c50e23d4
commit 7407a2e4b9
3 changed files with 254 additions and 2 deletions

View File

@ -2120,7 +2120,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
------------------- -------------------
Module for sound cards based on the Asus AV100/AV200 chips, Module for sound cards based on the Asus AV100/AV200 chips,
i.e., Xonar D1, DX, D2 and D2X. i.e., Xonar D1, DX, D2, D2X and HDAV1.3 (Deluxe).
This module supports autoprobe and multiple cards. This module supports autoprobe and multiple cards.

View File

@ -857,7 +857,8 @@ config SND_VIRTUOSO
select SND_OXYGEN_LIB select SND_OXYGEN_LIB
help help
Say Y here to include support for sound cards based on the Say Y here to include support for sound cards based on the
Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2 and D2X. Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X and
HDAV1.3 (Deluxe).
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called snd-virtuoso. will be called snd-virtuoso.

View File

@ -62,14 +62,66 @@
* AD0 <- 0 * AD0 <- 0
*/ */
/*
* Xonar HDAV1.3 (Deluxe)
* ----------------------
*
* CMI8788:
*
* I²C <-> PCM1796 (front)
*
* GPI 0 <- external power present
*
* GPIO 0 -> enable output to speakers
* GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
* TXD -> HDMI controller
* RXD <- HDMI controller
*
* PCM1796 front: AD1,0 <- 0,0
*
* no daughterboard
* ----------------
*
* GPIO 4 <- 1
*
* H6 daughterboard
* ----------------
*
* GPIO 4 <- 0
* GPIO 5 <- 0
*
* I²C <-> PCM1796 (surround)
* <-> PCM1796 (center/LFE)
* <-> PCM1796 (back)
*
* PCM1796 surround: AD1,0 <- 0,1
* PCM1796 center/LFE: AD1,0 <- 1,0
* PCM1796 back: AD1,0 <- 1,1
*
* unknown daughterboard
* ---------------------
*
* GPIO 4 <- 0
* GPIO 5 <- 1
*
* I²C <-> CS4362A (surround, center/LFE, back)
*
* CS4362A: AD0 <- 0
*/
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/asoundef.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h> #include <sound/tlv.h>
#include "oxygen.h" #include "oxygen.h"
#include "cm9780.h" #include "cm9780.h"
@ -98,12 +150,15 @@ enum {
MODEL_D2X, MODEL_D2X,
MODEL_D1, MODEL_D1,
MODEL_DX, MODEL_DX,
MODEL_HDAV, /* without daughterboard */
MODEL_HDAV_H6, /* with H6 daughterboard */
}; };
static struct pci_device_id xonar_ids[] __devinitdata = { static struct pci_device_id xonar_ids[] __devinitdata = {
{ OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 }, { OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 },
{ OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX }, { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
{ OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X }, { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
{ OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 }, { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
{ } { }
}; };
@ -124,6 +179,10 @@ MODULE_DEVICE_TABLE(pci, xonar_ids);
#define GPIO_DX_FRONT_PANEL 0x0002 #define GPIO_DX_FRONT_PANEL 0x0002
#define GPIO_DX_INPUT_ROUTE 0x0100 #define GPIO_DX_INPUT_ROUTE 0x0100
#define GPIO_HDAV_DB_MASK 0x0030
#define GPIO_HDAV_DB_H6 0x0000
#define GPIO_HDAV_DB_XX 0x0020
#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ADx=i, /W=0 */ #define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ADx=i, /W=0 */
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */ #define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */ #define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
@ -140,6 +199,7 @@ struct xonar_data {
u8 pcm1796_oversampling; u8 pcm1796_oversampling;
u8 cs4398_fm; u8 cs4398_fm;
u8 cs4362a_fm; u8 cs4362a_fm;
u8 hdmi_params[5];
}; };
static void xonar_gpio_changed(struct oxygen *chip); static void xonar_gpio_changed(struct oxygen *chip);
@ -185,6 +245,24 @@ static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
} }
static void hdmi_write_command(struct oxygen *chip, u8 command,
unsigned int count, const u8 *params)
{
unsigned int i;
u8 checksum;
oxygen_write_uart(chip, 0xfb);
oxygen_write_uart(chip, 0xef);
oxygen_write_uart(chip, command);
oxygen_write_uart(chip, count);
for (i = 0; i < count; ++i)
oxygen_write_uart(chip, params[i]);
checksum = 0xfb + 0xef + command + count;
for (i = 0; i < count; ++i)
checksum += params[i];
oxygen_write_uart(chip, checksum);
}
static void xonar_enable_output(struct oxygen *chip) static void xonar_enable_output(struct oxygen *chip)
{ {
struct xonar_data *data = chip->model_data; struct xonar_data *data = chip->model_data;
@ -369,6 +447,43 @@ static void xonar_d1_init(struct oxygen *chip)
snd_component_add(chip->card, "CS5361"); snd_component_add(chip->card, "CS5361");
} }
static void xonar_hdav_init(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
u8 param;
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK |
OXYGEN_2WIRE_SPEED_FAST);
data->anti_pop_delay = 100;
data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
data->ext_power_reg = OXYGEN_GPI_DATA;
data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
data->ext_power_bit = GPI_DX_EXT_POWER;
data->pcm1796_oversampling = PCM1796_OS_64;
pcm1796_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DX_INPUT_ROUTE);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE);
oxygen_reset_uart(chip);
param = 0;
hdmi_write_command(chip, 0x61, 1, &param);
param = 1;
hdmi_write_command(chip, 0x74, 1, &param);
data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
data->hdmi_params[4] = 1;
hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
xonar_common_init(chip);
snd_component_add(chip->card, "PCM1796");
snd_component_add(chip->card, "CS5381");
}
static void xonar_disable_output(struct oxygen *chip) static void xonar_disable_output(struct oxygen *chip)
{ {
struct xonar_data *data = chip->model_data; struct xonar_data *data = chip->model_data;
@ -388,6 +503,14 @@ static void xonar_d1_cleanup(struct oxygen *chip)
oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
} }
static void xonar_hdav_cleanup(struct oxygen *chip)
{
u8 param = 0;
hdmi_write_command(chip, 0x74, 1, &param);
xonar_disable_output(chip);
}
static void xonar_d2_suspend(struct oxygen *chip) static void xonar_d2_suspend(struct oxygen *chip)
{ {
xonar_d2_cleanup(chip); xonar_d2_cleanup(chip);
@ -398,6 +521,12 @@ static void xonar_d1_suspend(struct oxygen *chip)
xonar_d1_cleanup(chip); xonar_d1_cleanup(chip);
} }
static void xonar_hdav_suspend(struct oxygen *chip)
{
xonar_hdav_cleanup(chip);
msleep(2);
}
static void xonar_d2_resume(struct oxygen *chip) static void xonar_d2_resume(struct oxygen *chip)
{ {
pcm1796_init(chip); pcm1796_init(chip);
@ -410,6 +539,33 @@ static void xonar_d1_resume(struct oxygen *chip)
xonar_enable_output(chip); xonar_enable_output(chip);
} }
static void xonar_hdav_resume(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
u8 param;
oxygen_reset_uart(chip);
param = 0;
hdmi_write_command(chip, 0x61, 1, &param);
param = 1;
hdmi_write_command(chip, 0x74, 1, &param);
hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
pcm1796_init(chip);
xonar_enable_output(chip);
}
static void xonar_hdav_pcm_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware)
{
if (channel == PCM_MULTICH) {
hardware->rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_192000;
hardware->rate_min = 44100;
}
}
static void set_pcm1796_params(struct oxygen *chip, static void set_pcm1796_params(struct oxygen *chip,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
@ -460,6 +616,42 @@ static void set_cs43xx_params(struct oxygen *chip,
cs4362a_write(chip, 0x0c, data->cs4362a_fm); cs4362a_write(chip, 0x0c, data->cs4362a_fm);
} }
static void set_hdmi_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
struct xonar_data *data = chip->model_data;
data->hdmi_params[0] = 0; /* 1 = non-audio */
switch (params_rate(params)) {
case 44100:
data->hdmi_params[1] = IEC958_AES3_CON_FS_44100;
break;
case 48000:
data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
break;
default: /* 96000 */
data->hdmi_params[1] = IEC958_AES3_CON_FS_96000;
break;
case 192000:
data->hdmi_params[1] = IEC958_AES3_CON_FS_192000;
break;
}
data->hdmi_params[2] = params_channels(params) / 2 - 1;
if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
data->hdmi_params[3] = 0;
else
data->hdmi_params[3] = 0xc0;
data->hdmi_params[4] = 1; /* ? */
hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
}
static void set_hdav_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
set_pcm1796_params(chip, params);
set_hdmi_params(chip, params);
}
static void xonar_gpio_changed(struct oxygen *chip) static void xonar_gpio_changed(struct oxygen *chip)
{ {
struct xonar_data *data = chip->model_data; struct xonar_data *data = chip->model_data;
@ -479,6 +671,18 @@ static void xonar_gpio_changed(struct oxygen *chip)
} }
} }
static void xonar_hdav_uart_input(struct oxygen *chip)
{
if (chip->uart_input_count >= 2 &&
chip->uart_input[chip->uart_input_count - 2] == 'O' &&
chip->uart_input[chip->uart_input_count - 1] == 'K') {
printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:");
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
chip->uart_input, chip->uart_input_count);
chip->uart_input_count = 0;
}
}
static int gpio_bit_switch_get(struct snd_kcontrol *ctl, static int gpio_bit_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value) struct snd_ctl_elem_value *value)
{ {
@ -576,16 +780,33 @@ static int xonar_model_probe(struct oxygen *chip, unsigned long driver_data)
[MODEL_DX] = "Xonar DX", [MODEL_DX] = "Xonar DX",
[MODEL_D2] = "Xonar D2", [MODEL_D2] = "Xonar D2",
[MODEL_D2X] = "Xonar D2X", [MODEL_D2X] = "Xonar D2X",
[MODEL_HDAV] = "Xonar HDAV1.3",
[MODEL_HDAV_H6] = "Xonar HDAV1.3+H6",
}; };
static const u8 dacs[] = { static const u8 dacs[] = {
[MODEL_D1] = 2, [MODEL_D1] = 2,
[MODEL_DX] = 2, [MODEL_DX] = 2,
[MODEL_D2] = 4, [MODEL_D2] = 4,
[MODEL_D2X] = 4, [MODEL_D2X] = 4,
[MODEL_HDAV] = 1,
[MODEL_HDAV_H6] = 4,
}; };
struct xonar_data *data = chip->model_data; struct xonar_data *data = chip->model_data;
data->model = driver_data; data->model = driver_data;
if (data->model == MODEL_HDAV) {
oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_HDAV_DB_MASK);
switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
GPIO_HDAV_DB_MASK) {
case GPIO_HDAV_DB_H6:
data->model = MODEL_HDAV_H6;
break;
case GPIO_HDAV_DB_XX:
snd_printk(KERN_ERR "unknown daughterboard\n");
return -ENODEV;
}
}
data->dacs = dacs[data->model]; data->dacs = dacs[data->model];
chip->model.shortname = names[data->model]; chip->model.shortname = names[data->model];
@ -654,6 +875,35 @@ static const struct oxygen_model model_xonar_d1 = {
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
}; };
static const struct oxygen_model model_xonar_hdav = {
.longname = "Asus Virtuoso 200",
.chip = "AV200",
.owner = THIS_MODULE,
.probe = xonar_model_probe,
.init = xonar_hdav_init,
.cleanup = xonar_hdav_cleanup,
.suspend = xonar_hdav_suspend,
.resume = xonar_hdav_resume,
.pcm_hardware_filter = xonar_hdav_pcm_hardware_filter,
.set_dac_params = set_hdav_params,
.set_adc_params = set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute,
.uart_input = xonar_hdav_uart_input,
.ac97_switch = xonar_line_mic_ac97_switch,
.dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_data),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2,
.dac_channels = 8,
.dac_volume_min = 0x0f,
.dac_volume_max = 0xff,
.function_flags = OXYGEN_FUNCTION_2WIRE,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
static int __devinit xonar_probe(struct pci_dev *pci, static int __devinit xonar_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id) const struct pci_device_id *pci_id)
{ {
@ -662,6 +912,7 @@ static int __devinit xonar_probe(struct pci_dev *pci,
[MODEL_DX] = &model_xonar_d1, [MODEL_DX] = &model_xonar_d1,
[MODEL_D2] = &model_xonar_d2, [MODEL_D2] = &model_xonar_d2,
[MODEL_D2X] = &model_xonar_d2, [MODEL_D2X] = &model_xonar_d2,
[MODEL_HDAV] = &model_xonar_hdav,
}; };
static int dev; static int dev;
int err; int err;