add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
snd-hifier-objs := hifier.o
snd-oxygen-objs := oxygen.o
snd-virtuoso-objs := virtuoso.o
obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o

View File

@@ -0,0 +1,44 @@
#ifndef AK4396_H_INCLUDED
#define AK4396_H_INCLUDED
#define AK4396_WRITE 0x2000
#define AK4396_CONTROL_1 0
#define AK4396_CONTROL_2 1
#define AK4396_CONTROL_3 2
#define AK4396_LCH_ATT 3
#define AK4396_RCH_ATT 4
/* control 1 */
#define AK4396_RSTN 0x01
#define AK4396_DIF_MASK 0x0e
#define AK4396_DIF_16_LSB 0x00
#define AK4396_DIF_20_LSB 0x02
#define AK4396_DIF_24_MSB 0x04
#define AK4396_DIF_24_I2S 0x06
#define AK4396_DIF_24_LSB 0x08
#define AK4396_ACKS 0x80
/* control 2 */
#define AK4396_SMUTE 0x01
#define AK4396_DEM_MASK 0x06
#define AK4396_DEM_441 0x00
#define AK4396_DEM_OFF 0x02
#define AK4396_DEM_48 0x04
#define AK4396_DEM_32 0x06
#define AK4396_DFS_MASK 0x18
#define AK4396_DFS_NORMAL 0x00
#define AK4396_DFS_DOUBLE 0x08
#define AK4396_DFS_QUAD 0x10
#define AK4396_SLOW 0x20
#define AK4396_DZFM 0x40
#define AK4396_DZFE 0x80
/* control 3 */
#define AK4396_DZFB 0x04
#define AK4396_DCKB 0x10
#define AK4396_DCKS 0x20
#define AK4396_DSDM 0x40
#define AK4396_D_P_MASK 0x80
#define AK4396_PCM 0x00
#define AK4396_DSD 0x80
#endif

View File

@@ -0,0 +1,63 @@
#ifndef CM9780_H_INCLUDED
#define CM9780_H_INCLUDED
#define CM9780_JACK 0x62
#define CM9780_MIXER 0x64
#define CM9780_GPIO_SETUP 0x70
#define CM9780_GPIO_STATUS 0x72
/* jack control */
#define CM9780_RSOE 0x0001
#define CM9780_CBOE 0x0002
#define CM9780_SSOE 0x0004
#define CM9780_FROE 0x0008
#define CM9780_HP2FMICOE 0x0010
#define CM9780_CB2MICOE 0x0020
#define CM9780_FMIC2LI 0x0040
#define CM9780_FMIC2MIC 0x0080
#define CM9780_HP2LI 0x0100
#define CM9780_HP2MIC 0x0200
#define CM9780_MIC2LI 0x0400
#define CM9780_MIC2MIC 0x0800
#define CM9780_LI2LI 0x1000
#define CM9780_LI2MIC 0x2000
#define CM9780_LO2LI 0x4000
#define CM9780_LO2MIC 0x8000
/* mixer control */
#define CM9780_BSTSEL 0x0001
#define CM9780_STRO_MIC 0x0002
#define CM9780_SPDI_FREX 0x0004
#define CM9780_SPDI_SSEX 0x0008
#define CM9780_SPDI_CBEX 0x0010
#define CM9780_SPDI_RSEX 0x0020
#define CM9780_MIX2FR 0x0040
#define CM9780_MIX2SS 0x0080
#define CM9780_MIX2CB 0x0100
#define CM9780_MIX2RS 0x0200
#define CM9780_MIX2FR_EX 0x0400
#define CM9780_MIX2SS_EX 0x0800
#define CM9780_MIX2CB_EX 0x1000
#define CM9780_MIX2RS_EX 0x2000
#define CM9780_P47_IO 0x4000
#define CM9780_PCBSW 0x8000
/* GPIO setup */
#define CM9780_GPI0EN 0x0001
#define CM9780_GPI1EN 0x0002
#define CM9780_SENSE_P 0x0004
#define CM9780_LOCK_P 0x0008
#define CM9780_GPIO0P 0x0010
#define CM9780_GPIO1P 0x0020
#define CM9780_GPIO0IO 0x0100
#define CM9780_GPIO1IO 0x0200
/* GPIO status */
#define CM9780_GPO0 0x0001
#define CM9780_GPO1 0x0002
#define CM9780_GPIO0S 0x0010
#define CM9780_GPIO1S 0x0020
#define CM9780_GPII0S 0x0100
#define CM9780_GPII1S 0x0200
#endif

View File

@@ -0,0 +1,69 @@
/* register 01h */
#define CS4362A_PDN 0x01
#define CS4362A_DAC1_DIS 0x02
#define CS4362A_DAC2_DIS 0x04
#define CS4362A_DAC3_DIS 0x08
#define CS4362A_MCLKDIV 0x20
#define CS4362A_FREEZE 0x40
#define CS4362A_CPEN 0x80
/* register 02h */
#define CS4362A_DIF_MASK 0x70
#define CS4362A_DIF_LJUST 0x00
#define CS4362A_DIF_I2S 0x10
#define CS4362A_DIF_RJUST_16 0x20
#define CS4362A_DIF_RJUST_24 0x30
#define CS4362A_DIF_RJUST_20 0x40
#define CS4362A_DIF_RJUST_18 0x50
/* register 03h */
#define CS4362A_MUTEC_MASK 0x03
#define CS4362A_MUTEC_6 0x00
#define CS4362A_MUTEC_1 0x01
#define CS4362A_MUTEC_3 0x03
#define CS4362A_AMUTE 0x04
#define CS4362A_MUTEC_POL 0x08
#define CS4362A_RMP_UP 0x10
#define CS4362A_SNGLVOL 0x20
#define CS4362A_ZERO_CROSS 0x40
#define CS4362A_SOFT_RAMP 0x80
/* register 04h */
#define CS4362A_RMP_DN 0x01
#define CS4362A_DEM_MASK 0x06
#define CS4362A_DEM_NONE 0x00
#define CS4362A_DEM_44100 0x02
#define CS4362A_DEM_48000 0x04
#define CS4362A_DEM_32000 0x06
#define CS4362A_FILT_SEL 0x10
/* register 05h */
#define CS4362A_INV_A1 0x01
#define CS4362A_INV_B1 0x02
#define CS4362A_INV_A2 0x04
#define CS4362A_INV_B2 0x08
#define CS4362A_INV_A3 0x10
#define CS4362A_INV_B3 0x20
/* register 06h */
#define CS4362A_FM_MASK 0x03
#define CS4362A_FM_SINGLE 0x00
#define CS4362A_FM_DOUBLE 0x01
#define CS4362A_FM_QUAD 0x02
#define CS4362A_FM_DSD 0x03
#define CS4362A_ATAPI_MASK 0x7c
#define CS4362A_ATAPI_B_MUTE 0x00
#define CS4362A_ATAPI_B_R 0x04
#define CS4362A_ATAPI_B_L 0x08
#define CS4362A_ATAPI_B_LR 0x0c
#define CS4362A_ATAPI_A_MUTE 0x00
#define CS4362A_ATAPI_A_R 0x10
#define CS4362A_ATAPI_A_L 0x20
#define CS4362A_ATAPI_A_LR 0x30
#define CS4362A_ATAPI_MIX_LR_VOL 0x40
#define CS4362A_A_EQ_B 0x80
/* register 07h */
#define CS4362A_VOL_MASK 0x7f
#define CS4362A_MUTE 0x80
/* register 08h: like 07h */
/* registers 09h..0Bh: like 06h..08h */
/* registers 0Ch..0Eh: like 06h..08h */
/* register 12h */
#define CS4362A_REV_MASK 0x07
#define CS4362A_PART_MASK 0xf8
#define CS4362A_PART_CS4362A 0x50

View File

@@ -0,0 +1,69 @@
/* register 1 */
#define CS4398_REV_MASK 0x07
#define CS4398_PART_MASK 0xf8
#define CS4398_PART_CS4398 0x70
/* register 2 */
#define CS4398_FM_MASK 0x03
#define CS4398_FM_SINGLE 0x00
#define CS4398_FM_DOUBLE 0x01
#define CS4398_FM_QUAD 0x02
#define CS4398_FM_DSD 0x03
#define CS4398_DEM_MASK 0x0c
#define CS4398_DEM_NONE 0x00
#define CS4398_DEM_44100 0x04
#define CS4398_DEM_48000 0x08
#define CS4398_DEM_32000 0x0c
#define CS4398_DIF_MASK 0x70
#define CS4398_DIF_LJUST 0x00
#define CS4398_DIF_I2S 0x10
#define CS4398_DIF_RJUST_16 0x20
#define CS4398_DIF_RJUST_24 0x30
#define CS4398_DIF_RJUST_20 0x40
#define CS4398_DIF_RJUST_18 0x50
#define CS4398_DSD_SRC 0x80
/* register 3 */
#define CS4398_ATAPI_MASK 0x1f
#define CS4398_ATAPI_B_MUTE 0x00
#define CS4398_ATAPI_B_R 0x01
#define CS4398_ATAPI_B_L 0x02
#define CS4398_ATAPI_B_LR 0x03
#define CS4398_ATAPI_A_MUTE 0x00
#define CS4398_ATAPI_A_R 0x04
#define CS4398_ATAPI_A_L 0x08
#define CS4398_ATAPI_A_LR 0x0c
#define CS4398_ATAPI_MIX_LR_VOL 0x10
#define CS4398_INVERT_B 0x20
#define CS4398_INVERT_A 0x40
#define CS4398_VOL_B_EQ_A 0x80
/* register 4 */
#define CS4398_MUTEP_MASK 0x03
#define CS4398_MUTEP_AUTO 0x00
#define CS4398_MUTEP_LOW 0x02
#define CS4398_MUTEP_HIGH 0x03
#define CS4398_MUTE_B 0x08
#define CS4398_MUTE_A 0x10
#define CS4398_MUTEC_A_EQ_B 0x20
#define CS4398_DAMUTE 0x40
#define CS4398_PAMUTE 0x80
/* register 5 */
#define CS4398_VOL_A_MASK 0xff
/* register 6 */
#define CS4398_VOL_B_MASK 0xff
/* register 7 */
#define CS4398_DIR_DSD 0x01
#define CS4398_FILT_SEL 0x04
#define CS4398_RMP_DN 0x10
#define CS4398_RMP_UP 0x20
#define CS4398_ZERO_CROSS 0x40
#define CS4398_SOFT_RAMP 0x80
/* register 8 */
#define CS4398_MCLKDIV3 0x08
#define CS4398_MCLKDIV2 0x10
#define CS4398_FREEZE 0x20
#define CS4398_CPEN 0x40
#define CS4398_PDN 0x80
/* register 9 */
#define CS4398_DSD_PM_EN 0x01
#define CS4398_DSD_PM_MODE 0x02
#define CS4398_INVALID_DSD 0x04
#define CS4398_STATIC_DSD 0x08

View File

@@ -0,0 +1,224 @@
/*
* C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this driver; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
#include <linux/pci.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
#include "oxygen.h"
#include "ak4396.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("TempoTec HiFier driver");
MODULE_LICENSE("GPL v2");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "card index");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
static struct pci_device_id hifier_ids[] __devinitdata = {
{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
{ }
};
MODULE_DEVICE_TABLE(pci, hifier_ids);
struct hifier_data {
u8 ak4396_ctl2;
};
static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
{
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(0 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
AK4396_WRITE | (reg << 8) | value);
}
static void update_ak4396_volume(struct oxygen *chip)
{
ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
}
static void hifier_registers_init(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
update_ak4396_volume(chip);
}
static void hifier_init(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
hifier_registers_init(chip);
snd_component_add(chip->card, "AK4396");
snd_component_add(chip->card, "CS5340");
}
static void hifier_cleanup(struct oxygen *chip)
{
}
static void hifier_resume(struct oxygen *chip)
{
hifier_registers_init(chip);
}
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
struct hifier_data *data = chip->model_data;
u8 value;
value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
if (params_rate(params) <= 54000)
value |= AK4396_DFS_NORMAL;
else if (params_rate(params) <= 108000)
value |= AK4396_DFS_DOUBLE;
else
value |= AK4396_DFS_QUAD;
data->ak4396_ctl2 = value;
msleep(1); /* wait for the new MCLK to become stable */
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB);
ak4396_write(chip, AK4396_CONTROL_2, value);
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
}
static void update_ak4396_mute(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
u8 value;
value = data->ak4396_ctl2 & ~AK4396_SMUTE;
if (chip->dac_mute)
value |= AK4396_SMUTE;
data->ak4396_ctl2 = value;
ak4396_write(chip, AK4396_CONTROL_2, value);
}
static void set_cs5340_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
}
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static int hifier_control_filter(struct snd_kcontrol_new *template)
{
if (!strcmp(template->name, "Stereo Upmixing"))
return 1; /* stereo only - we don't need upmixing */
return 0;
}
static const struct oxygen_model model_hifier = {
.shortname = "C-Media CMI8787",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
.init = hifier_init,
.control_filter = hifier_control_filter,
.cleanup = hifier_cleanup,
.resume = hifier_resume,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_cs5340_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
.dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct hifier_data),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_1,
.dac_channels = 2,
.dac_volume_min = 0,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
static int __devinit get_hifier_model(struct oxygen *chip,
const struct pci_device_id *id)
{
chip->model = model_hifier;
return 0;
}
static int __devinit hifier_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
++dev;
return -ENOENT;
}
err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
hifier_ids, get_hifier_model);
if (err >= 0)
++dev;
return err;
}
static struct pci_driver hifier_driver = {
.name = "CMI8787HiFier",
.id_table = hifier_ids,
.probe = hifier_probe,
.remove = __devexit_p(oxygen_pci_remove),
#ifdef CONFIG_PM
.suspend = oxygen_pci_suspend,
.resume = oxygen_pci_resume,
#endif
};
static int __init alsa_card_hifier_init(void)
{
return pci_register_driver(&hifier_driver);
}
static void __exit alsa_card_hifier_exit(void)
{
pci_unregister_driver(&hifier_driver);
}
module_init(alsa_card_hifier_init)
module_exit(alsa_card_hifier_exit)

View File

@@ -0,0 +1,451 @@
/*
* C-Media CMI8788 driver for C-Media's reference design and similar models
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this driver; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* SPI 0 -> 1st AK4396 (front)
* SPI 1 -> 2nd AK4396 (surround)
* SPI 2 -> 3rd AK4396 (center/LFE)
* SPI 3 -> WM8785
* SPI 4 -> 4th AK4396 (back)
*
* GPIO 0 -> DFS0 of AK5385
* GPIO 1 -> DFS1 of AK5385
* GPIO 8 -> enable headphone amplifier on HT-Omega models
*/
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <sound/ac97_codec.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "oxygen.h"
#include "ak4396.h"
#include "wm8785.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver");
MODULE_LICENSE("GPL v2");
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "card index");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
enum {
MODEL_CMEDIA_REF, /* C-Media's reference design */
MODEL_MERIDIAN, /* AuzenTech X-Meridian */
MODEL_CLARO, /* HT-Omega Claro */
MODEL_CLARO_HALO, /* HT-Omega Claro halo */
};
static struct pci_device_id oxygen_ids[] __devinitdata = {
{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
{ }
};
MODULE_DEVICE_TABLE(pci, oxygen_ids);
#define GPIO_AK5385_DFS_MASK 0x0003
#define GPIO_AK5385_DFS_NORMAL 0x0000
#define GPIO_AK5385_DFS_DOUBLE 0x0001
#define GPIO_AK5385_DFS_QUAD 0x0002
#define GPIO_CLARO_HP 0x0100
struct generic_data {
u8 ak4396_ctl2;
u16 saved_wm8785_registers[2];
};
static void ak4396_write(struct oxygen *chip, unsigned int codec,
u8 reg, u8 value)
{
/* maps ALSA channel pair number to SPI output */
static const u8 codec_spi_map[4] = {
0, 1, 2, 4
};
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
AK4396_WRITE | (reg << 8) | value);
}
static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
{
struct generic_data *data = chip->model_data;
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(3 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value);
if (reg < ARRAY_SIZE(data->saved_wm8785_registers))
data->saved_wm8785_registers[reg] = value;
}
static void update_ak4396_volume(struct oxygen *chip)
{
unsigned int i;
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i,
AK4396_LCH_ATT, chip->dac_volume[i * 2]);
ak4396_write(chip, i,
AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
}
}
static void ak4396_registers_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
unsigned int i;
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, i,
AK4396_CONTROL_2, data->ak4396_ctl2);
ak4396_write(chip, i,
AK4396_CONTROL_3, AK4396_PCM);
}
update_ak4396_volume(chip);
}
static void ak4396_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
ak4396_registers_init(chip);
snd_component_add(chip->card, "AK4396");
}
static void ak5385_init(struct oxygen *chip)
{
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK);
snd_component_add(chip->card, "AK5385");
}
static void wm8785_registers_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
wm8785_write(chip, WM8785_R7, 0);
wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]);
wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]);
}
static void wm8785_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE |
WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
data->saved_wm8785_registers[1] = WM8785_WL_24;
wm8785_registers_init(chip);
snd_component_add(chip->card, "WM8785");
}
static void generic_init(struct oxygen *chip)
{
ak4396_init(chip);
wm8785_init(chip);
}
static void meridian_init(struct oxygen *chip)
{
ak4396_init(chip);
ak5385_init(chip);
}
static void claro_enable_hp(struct oxygen *chip)
{
msleep(300);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_HP);
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
}
static void claro_init(struct oxygen *chip)
{
ak4396_init(chip);
wm8785_init(chip);
claro_enable_hp(chip);
}
static void claro_halo_init(struct oxygen *chip)
{
ak4396_init(chip);
ak5385_init(chip);
claro_enable_hp(chip);
}
static void generic_cleanup(struct oxygen *chip)
{
}
static void claro_disable_hp(struct oxygen *chip)
{
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
}
static void claro_cleanup(struct oxygen *chip)
{
claro_disable_hp(chip);
}
static void claro_suspend(struct oxygen *chip)
{
claro_disable_hp(chip);
}
static void generic_resume(struct oxygen *chip)
{
ak4396_registers_init(chip);
wm8785_registers_init(chip);
}
static void meridian_resume(struct oxygen *chip)
{
ak4396_registers_init(chip);
}
static void claro_resume(struct oxygen *chip)
{
ak4396_registers_init(chip);
claro_enable_hp(chip);
}
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
struct generic_data *data = chip->model_data;
unsigned int i;
u8 value;
value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
if (params_rate(params) <= 54000)
value |= AK4396_DFS_NORMAL;
else if (params_rate(params) <= 108000)
value |= AK4396_DFS_DOUBLE;
else
value |= AK4396_DFS_QUAD;
data->ak4396_ctl2 = value;
msleep(1); /* wait for the new MCLK to become stable */
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB);
ak4396_write(chip, i,
AK4396_CONTROL_2, value);
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
}
}
static void update_ak4396_mute(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
unsigned int i;
u8 value;
value = data->ak4396_ctl2 & ~AK4396_SMUTE;
if (chip->dac_mute)
value |= AK4396_SMUTE;
data->ak4396_ctl2 = value;
for (i = 0; i < 4; ++i)
ak4396_write(chip, i, AK4396_CONTROL_2, value);
}
static void set_wm8785_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
unsigned int value;
wm8785_write(chip, WM8785_R7, 0);
value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
if (params_rate(params) <= 48000)
value |= WM8785_OSR_SINGLE;
else if (params_rate(params) <= 96000)
value |= WM8785_OSR_DOUBLE;
else
value |= WM8785_OSR_QUAD;
wm8785_write(chip, WM8785_R0, value);
if (snd_pcm_format_width(params_format(params)) <= 16)
value = WM8785_WL_16;
else
value = WM8785_WL_24;
wm8785_write(chip, WM8785_R1, value);
}
static void set_ak5385_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
unsigned int value;
if (params_rate(params) <= 54000)
value = GPIO_AK5385_DFS_NORMAL;
else if (params_rate(params) <= 108000)
value = GPIO_AK5385_DFS_DOUBLE;
else
value = GPIO_AK5385_DFS_QUAD;
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
value, GPIO_AK5385_DFS_MASK);
}
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static const struct oxygen_model model_generic = {
.shortname = "C-Media CMI8788",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
.init = generic_init,
.cleanup = generic_cleanup,
.resume = generic_resume,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
.dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
PLAYBACK_2_TO_AC97_1 |
CAPTURE_0_FROM_I2S_1 |
CAPTURE_1_FROM_SPDIF |
CAPTURE_2_FROM_AC97_1,
.dac_channels = 8,
.dac_volume_min = 0,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
static int __devinit get_oxygen_model(struct oxygen *chip,
const struct pci_device_id *id)
{
chip->model = model_generic;
switch (id->driver_data) {
case MODEL_MERIDIAN:
chip->model.init = meridian_init;
chip->model.resume = meridian_resume;
chip->model.set_adc_params = set_ak5385_params;
chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF;
break;
case MODEL_CLARO:
chip->model.init = claro_init;
chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume;
break;
case MODEL_CLARO_HALO:
chip->model.init = claro_halo_init;
chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume;
chip->model.set_adc_params = set_ak5385_params;
chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF;
break;
}
if (id->driver_data == MODEL_MERIDIAN ||
id->driver_data == MODEL_CLARO_HALO) {
chip->model.misc_flags = OXYGEN_MISC_MIDI;
chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
}
return 0;
}
static int __devinit generic_oxygen_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
++dev;
return -ENOENT;
}
err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
oxygen_ids, get_oxygen_model);
if (err >= 0)
++dev;
return err;
}
static struct pci_driver oxygen_driver = {
.name = "CMI8788",
.id_table = oxygen_ids,
.probe = generic_oxygen_probe,
.remove = __devexit_p(oxygen_pci_remove),
#ifdef CONFIG_PM
.suspend = oxygen_pci_suspend,
.resume = oxygen_pci_resume,
#endif
};
static int __init alsa_card_oxygen_init(void)
{
return pci_register_driver(&oxygen_driver);
}
static void __exit alsa_card_oxygen_exit(void)
{
pci_unregister_driver(&oxygen_driver);
}
module_init(alsa_card_oxygen_init)
module_exit(alsa_card_oxygen_exit)

View File

@@ -0,0 +1,246 @@
#ifndef OXYGEN_H_INCLUDED
#define OXYGEN_H_INCLUDED
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include "oxygen_regs.h"
/* 1 << PCM_x == OXYGEN_CHANNEL_x */
#define PCM_A 0
#define PCM_B 1
#define PCM_C 2
#define PCM_SPDIF 3
#define PCM_MULTICH 4
#define PCM_AC97 5
#define PCM_COUNT 6
#define OXYGEN_IO_SIZE 0x100
#define OXYGEN_EEPROM_ID 0x434d /* "CM" */
/* model-specific configuration of outputs/inputs */
#define PLAYBACK_0_TO_I2S 0x0001
/* PLAYBACK_0_TO_AC97_0 not implemented */
#define PLAYBACK_1_TO_SPDIF 0x0004
#define PLAYBACK_2_TO_AC97_1 0x0008
#define CAPTURE_0_FROM_I2S_1 0x0010
#define CAPTURE_0_FROM_I2S_2 0x0020
/* CAPTURE_0_FROM_AC97_0 not implemented */
#define CAPTURE_1_FROM_SPDIF 0x0080
#define CAPTURE_2_FROM_I2S_2 0x0100
#define CAPTURE_2_FROM_AC97_1 0x0200
/* CAPTURE_3_FROM_I2S_3 not implemented */
#define MIDI_OUTPUT 0x0800
#define MIDI_INPUT 0x1000
enum {
CONTROL_SPDIF_PCM,
CONTROL_SPDIF_INPUT_BITS,
CONTROL_MIC_CAPTURE_SWITCH,
CONTROL_LINE_CAPTURE_SWITCH,
CONTROL_CD_CAPTURE_SWITCH,
CONTROL_AUX_CAPTURE_SWITCH,
CONTROL_COUNT
};
#define OXYGEN_PCI_SUBID(sv, sd) \
.vendor = PCI_VENDOR_ID_CMEDIA, \
.device = 0x8788, \
.subvendor = sv, \
.subdevice = sd
#define BROKEN_EEPROM_DRIVER_DATA ((unsigned long)-1)
#define OXYGEN_PCI_SUBID_BROKEN_EEPROM \
OXYGEN_PCI_SUBID(PCI_VENDOR_ID_CMEDIA, 0x8788), \
.driver_data = BROKEN_EEPROM_DRIVER_DATA
struct pci_dev;
struct pci_device_id;
struct snd_card;
struct snd_pcm_substream;
struct snd_pcm_hardware;
struct snd_pcm_hw_params;
struct snd_kcontrol_new;
struct snd_rawmidi;
struct oxygen;
struct oxygen_model {
const char *shortname;
const char *longname;
const char *chip;
void (*init)(struct oxygen *chip);
int (*control_filter)(struct snd_kcontrol_new *template);
int (*mixer_init)(struct oxygen *chip);
void (*cleanup)(struct oxygen *chip);
void (*suspend)(struct oxygen *chip);
void (*resume)(struct oxygen *chip);
void (*pcm_hardware_filter)(unsigned int channel,
struct snd_pcm_hardware *hardware);
void (*set_dac_params)(struct oxygen *chip,
struct snd_pcm_hw_params *params);
void (*set_adc_params)(struct oxygen *chip,
struct snd_pcm_hw_params *params);
void (*update_dac_volume)(struct oxygen *chip);
void (*update_dac_mute)(struct oxygen *chip);
void (*gpio_changed)(struct oxygen *chip);
void (*uart_input)(struct oxygen *chip);
void (*ac97_switch)(struct oxygen *chip,
unsigned int reg, unsigned int mute);
const unsigned int *dac_tlv;
unsigned long private_data;
size_t model_data_size;
unsigned int device_config;
u8 dac_channels;
u8 dac_volume_min;
u8 dac_volume_max;
u8 misc_flags;
u8 function_flags;
u16 dac_i2s_format;
u16 adc_i2s_format;
};
struct oxygen {
unsigned long addr;
spinlock_t reg_lock;
struct mutex mutex;
struct snd_card *card;
struct pci_dev *pci;
struct snd_rawmidi *midi;
int irq;
void *model_data;
unsigned int interrupt_mask;
u8 dac_volume[8];
u8 dac_mute;
u8 pcm_active;
u8 pcm_running;
u8 dac_routing;
u8 spdif_playback_enable;
u8 revision;
u8 has_ac97_0;
u8 has_ac97_1;
u32 spdif_bits;
u32 spdif_pcm_bits;
struct snd_pcm_substream *streams[PCM_COUNT];
struct snd_kcontrol *controls[CONTROL_COUNT];
struct work_struct spdif_input_bits_work;
struct work_struct gpio_work;
wait_queue_head_t ac97_waitqueue;
union {
u8 _8[OXYGEN_IO_SIZE];
__le16 _16[OXYGEN_IO_SIZE / 2];
__le32 _32[OXYGEN_IO_SIZE / 4];
} saved_registers;
u16 saved_ac97_registers[2][0x40];
unsigned int uart_input_count;
u8 uart_input[32];
struct oxygen_model model;
};
/* oxygen_lib.c */
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
struct module *owner,
const struct pci_device_id *ids,
int (*get_model)(struct oxygen *chip,
const struct pci_device_id *id
)
);
void oxygen_pci_remove(struct pci_dev *pci);
#ifdef CONFIG_PM
int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
int oxygen_pci_resume(struct pci_dev *pci);
#endif
/* oxygen_mixer.c */
int oxygen_mixer_init(struct oxygen *chip);
void oxygen_update_dac_routing(struct oxygen *chip);
void oxygen_update_spdif_source(struct oxygen *chip);
/* oxygen_pcm.c */
int oxygen_pcm_init(struct oxygen *chip);
/* oxygen_io.c */
u8 oxygen_read8(struct oxygen *chip, unsigned int reg);
u16 oxygen_read16(struct oxygen *chip, unsigned int reg);
u32 oxygen_read32(struct oxygen *chip, unsigned int reg);
void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value);
void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value);
void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value);
void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
u8 value, u8 mask);
void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
u16 value, u16 mask);
void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
u32 value, u32 mask);
u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
unsigned int index);
void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
unsigned int index, u16 data);
void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
unsigned int index, u16 data, u16 mask);
void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
void oxygen_reset_uart(struct oxygen *chip);
void oxygen_write_uart(struct oxygen *chip, u8 data);
u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index);
void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value);
static inline void oxygen_set_bits8(struct oxygen *chip,
unsigned int reg, u8 value)
{
oxygen_write8_masked(chip, reg, value, value);
}
static inline void oxygen_set_bits16(struct oxygen *chip,
unsigned int reg, u16 value)
{
oxygen_write16_masked(chip, reg, value, value);
}
static inline void oxygen_set_bits32(struct oxygen *chip,
unsigned int reg, u32 value)
{
oxygen_write32_masked(chip, reg, value, value);
}
static inline void oxygen_clear_bits8(struct oxygen *chip,
unsigned int reg, u8 value)
{
oxygen_write8_masked(chip, reg, 0, value);
}
static inline void oxygen_clear_bits16(struct oxygen *chip,
unsigned int reg, u16 value)
{
oxygen_write16_masked(chip, reg, 0, value);
}
static inline void oxygen_clear_bits32(struct oxygen *chip,
unsigned int reg, u32 value)
{
oxygen_write32_masked(chip, reg, 0, value);
}
static inline void oxygen_ac97_set_bits(struct oxygen *chip, unsigned int codec,
unsigned int index, u16 value)
{
oxygen_write_ac97_masked(chip, codec, index, value, value);
}
static inline void oxygen_ac97_clear_bits(struct oxygen *chip,
unsigned int codec,
unsigned int index, u16 value)
{
oxygen_write_ac97_masked(chip, codec, index, 0, value);
}
#endif

View File

@@ -0,0 +1,278 @@
/*
* C-Media CMI8788 driver - helper functions
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this driver; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
#include <linux/sched.h>
#include <sound/core.h>
#include <sound/mpu401.h>
#include <asm/io.h>
#include "oxygen.h"
u8 oxygen_read8(struct oxygen *chip, unsigned int reg)
{
return inb(chip->addr + reg);
}
EXPORT_SYMBOL(oxygen_read8);
u16 oxygen_read16(struct oxygen *chip, unsigned int reg)
{
return inw(chip->addr + reg);
}
EXPORT_SYMBOL(oxygen_read16);
u32 oxygen_read32(struct oxygen *chip, unsigned int reg)
{
return inl(chip->addr + reg);
}
EXPORT_SYMBOL(oxygen_read32);
void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value)
{
outb(value, chip->addr + reg);
chip->saved_registers._8[reg] = value;
}
EXPORT_SYMBOL(oxygen_write8);
void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value)
{
outw(value, chip->addr + reg);
chip->saved_registers._16[reg / 2] = cpu_to_le16(value);
}
EXPORT_SYMBOL(oxygen_write16);
void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value)
{
outl(value, chip->addr + reg);
chip->saved_registers._32[reg / 4] = cpu_to_le32(value);
}
EXPORT_SYMBOL(oxygen_write32);
void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
u8 value, u8 mask)
{
u8 tmp = inb(chip->addr + reg);
tmp &= ~mask;
tmp |= value & mask;
outb(tmp, chip->addr + reg);
chip->saved_registers._8[reg] = tmp;
}
EXPORT_SYMBOL(oxygen_write8_masked);
void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
u16 value, u16 mask)
{
u16 tmp = inw(chip->addr + reg);
tmp &= ~mask;
tmp |= value & mask;
outw(tmp, chip->addr + reg);
chip->saved_registers._16[reg / 2] = cpu_to_le16(tmp);
}
EXPORT_SYMBOL(oxygen_write16_masked);
void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
u32 value, u32 mask)
{
u32 tmp = inl(chip->addr + reg);
tmp &= ~mask;
tmp |= value & mask;
outl(tmp, chip->addr + reg);
chip->saved_registers._32[reg / 4] = cpu_to_le32(tmp);
}
EXPORT_SYMBOL(oxygen_write32_masked);
static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask)
{
u8 status = 0;
/*
* Reading the status register also clears the bits, so we have to save
* the read bits in status.
*/
wait_event_timeout(chip->ac97_waitqueue,
({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
status & mask; }),
msecs_to_jiffies(1) + 1);
/*
* Check even after a timeout because this function should not require
* the AC'97 interrupt to be enabled.
*/
status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
return status & mask ? 0 : -EIO;
}
/*
* About 10% of AC'97 register reads or writes fail to complete, but even those
* where the controller indicates completion aren't guaranteed to have actually
* happened.
*
* It's hard to assign blame to either the controller or the codec because both
* were made by C-Media ...
*/
void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
unsigned int index, u16 data)
{
unsigned int count, succeeded;
u32 reg;
reg = data;
reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT;
reg |= OXYGEN_AC97_REG_DIR_WRITE;
reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
succeeded = 0;
for (count = 5; count > 0; --count) {
udelay(5);
oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
/* require two "completed" writes, just to be sure */
if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 &&
++succeeded >= 2) {
chip->saved_ac97_registers[codec][index / 2] = data;
return;
}
}
snd_printk(KERN_ERR "AC'97 write timeout\n");
}
EXPORT_SYMBOL(oxygen_write_ac97);
u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
unsigned int index)
{
unsigned int count;
unsigned int last_read = UINT_MAX;
u32 reg;
reg = index << OXYGEN_AC97_REG_ADDR_SHIFT;
reg |= OXYGEN_AC97_REG_DIR_READ;
reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
for (count = 5; count > 0; --count) {
udelay(5);
oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
udelay(10);
if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) {
u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS);
/* we require two consecutive reads of the same value */
if (value == last_read)
return value;
last_read = value;
/*
* Invert the register value bits to make sure that two
* consecutive unsuccessful reads do not return the same
* value.
*/
reg ^= 0xffff;
}
}
snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec);
return 0;
}
EXPORT_SYMBOL(oxygen_read_ac97);
void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
unsigned int index, u16 data, u16 mask)
{
u16 value = oxygen_read_ac97(chip, codec, index);
value &= ~mask;
value |= data & mask;
oxygen_write_ac97(chip, codec, index, value);
}
EXPORT_SYMBOL(oxygen_write_ac97_masked);
void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
{
unsigned int count;
/* should not need more than 7.68 us (24 * 320 ns) */
count = 10;
while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
&& count > 0) {
udelay(1);
--count;
}
oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
if (control & OXYGEN_SPI_DATA_LENGTH_3)
oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
}
EXPORT_SYMBOL(oxygen_write_spi);
void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
{
/* should not need more than about 300 us */
msleep(1);
oxygen_write8(chip, OXYGEN_2WIRE_MAP, map);
oxygen_write8(chip, OXYGEN_2WIRE_DATA, data);
oxygen_write8(chip, OXYGEN_2WIRE_CONTROL,
device | OXYGEN_2WIRE_DIR_WRITE);
}
EXPORT_SYMBOL(oxygen_write_i2c);
static void _write_uart(struct oxygen *chip, unsigned int port, u8 data)
{
if (oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_TX_FULL)
msleep(1);
oxygen_write8(chip, OXYGEN_MPU401 + port, data);
}
void oxygen_reset_uart(struct oxygen *chip)
{
_write_uart(chip, 1, MPU401_RESET);
msleep(1); /* wait for ACK */
_write_uart(chip, 1, MPU401_ENTER_UART);
}
EXPORT_SYMBOL(oxygen_reset_uart);
void oxygen_write_uart(struct oxygen *chip, u8 data)
{
_write_uart(chip, 0, data);
}
EXPORT_SYMBOL(oxygen_write_uart);
u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index)
{
unsigned int timeout;
oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
index | OXYGEN_EEPROM_DIR_READ);
for (timeout = 0; timeout < 100; ++timeout) {
udelay(1);
if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
& OXYGEN_EEPROM_BUSY))
break;
}
return oxygen_read16(chip, OXYGEN_EEPROM_DATA);
}
void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value)
{
unsigned int timeout;
oxygen_write16(chip, OXYGEN_EEPROM_DATA, value);
oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
index | OXYGEN_EEPROM_DIR_WRITE);
for (timeout = 0; timeout < 10; ++timeout) {
msleep(1);
if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
& OXYGEN_EEPROM_BUSY))
return;
}
snd_printk(KERN_ERR "EEPROM write timeout\n");
}

View File

@@ -0,0 +1,752 @@
/*
* C-Media CMI8788 driver - main driver module
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this driver; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <sound/ac97_codec.h>
#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/mpu401.h>
#include <sound/pcm.h>
#include "oxygen.h"
#include "cm9780.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 helper library");
MODULE_LICENSE("GPL v2");
#define DRIVER "oxygen"
static inline int oxygen_uart_input_ready(struct oxygen *chip)
{
return !(oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_RX_EMPTY);
}
static void oxygen_read_uart(struct oxygen *chip)
{
if (unlikely(!oxygen_uart_input_ready(chip))) {
/* no data, but read it anyway to clear the interrupt */
oxygen_read8(chip, OXYGEN_MPU401);
return;
}
do {
u8 data = oxygen_read8(chip, OXYGEN_MPU401);
if (data == MPU401_ACK)
continue;
if (chip->uart_input_count >= ARRAY_SIZE(chip->uart_input))
chip->uart_input_count = 0;
chip->uart_input[chip->uart_input_count++] = data;
} while (oxygen_uart_input_ready(chip));
if (chip->model.uart_input)
chip->model.uart_input(chip);
}
static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
{
struct oxygen *chip = dev_id;
unsigned int status, clear, elapsed_streams, i;
status = oxygen_read16(chip, OXYGEN_INTERRUPT_STATUS);
if (!status)
return IRQ_NONE;
spin_lock(&chip->reg_lock);
clear = status & (OXYGEN_CHANNEL_A |
OXYGEN_CHANNEL_B |
OXYGEN_CHANNEL_C |
OXYGEN_CHANNEL_SPDIF |
OXYGEN_CHANNEL_MULTICH |
OXYGEN_CHANNEL_AC97 |
OXYGEN_INT_SPDIF_IN_DETECT |
OXYGEN_INT_GPIO |
OXYGEN_INT_AC97);
if (clear) {
if (clear & OXYGEN_INT_SPDIF_IN_DETECT)
chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
chip->interrupt_mask & ~clear);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
chip->interrupt_mask);
}
elapsed_streams = status & chip->pcm_running;
spin_unlock(&chip->reg_lock);
for (i = 0; i < PCM_COUNT; ++i)
if ((elapsed_streams & (1 << i)) && chip->streams[i])
snd_pcm_period_elapsed(chip->streams[i]);
if (status & OXYGEN_INT_SPDIF_IN_DETECT) {
spin_lock(&chip->reg_lock);
i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT |
OXYGEN_SPDIF_RATE_INT)) {
/* write the interrupt bit(s) to clear */
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i);
schedule_work(&chip->spdif_input_bits_work);
}
spin_unlock(&chip->reg_lock);
}
if (status & OXYGEN_INT_GPIO)
schedule_work(&chip->gpio_work);
if (status & OXYGEN_INT_MIDI) {
if (chip->midi)
snd_mpu401_uart_interrupt(0, chip->midi->private_data);
else
oxygen_read_uart(chip);
}
if (status & OXYGEN_INT_AC97)
wake_up(&chip->ac97_waitqueue);
return IRQ_HANDLED;
}
static void oxygen_spdif_input_bits_changed(struct work_struct *work)
{
struct oxygen *chip = container_of(work, struct oxygen,
spdif_input_bits_work);
u32 reg;
/*
* This function gets called when there is new activity on the SPDIF
* input, or when we lose lock on the input signal, or when the rate
* changes.
*/
msleep(1);
spin_lock_irq(&chip->reg_lock);
reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
OXYGEN_SPDIF_LOCK_STATUS))
== OXYGEN_SPDIF_SENSE_STATUS) {
/*
* If we detect activity on the SPDIF input but cannot lock to
* a signal, the clock bit is likely to be wrong.
*/
reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK;
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
spin_unlock_irq(&chip->reg_lock);
msleep(1);
spin_lock_irq(&chip->reg_lock);
reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
OXYGEN_SPDIF_LOCK_STATUS))
== OXYGEN_SPDIF_SENSE_STATUS) {
/* nothing detected with either clock; give up */
if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK)
== OXYGEN_SPDIF_IN_CLOCK_192) {
/*
* Reset clock to <= 96 kHz because this is
* more likely to be received next time.
*/
reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK;
reg |= OXYGEN_SPDIF_IN_CLOCK_96;
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
}
}
}
spin_unlock_irq(&chip->reg_lock);
if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) {
spin_lock_irq(&chip->reg_lock);
chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
chip->interrupt_mask);
spin_unlock_irq(&chip->reg_lock);
/*
* We don't actually know that any channel status bits have
* changed, but let's send a notification just to be sure.
*/
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&chip->controls[CONTROL_SPDIF_INPUT_BITS]->id);
}
}
static void oxygen_gpio_changed(struct work_struct *work)
{
struct oxygen *chip = container_of(work, struct oxygen, gpio_work);
if (chip->model.gpio_changed)
chip->model.gpio_changed(chip);
}
#ifdef CONFIG_PROC_FS
static void oxygen_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct oxygen *chip = entry->private_data;
int i, j;
snd_iprintf(buffer, "CMI8788\n\n");
for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; ++j)
snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j));
snd_iprintf(buffer, "\n");
}
if (mutex_lock_interruptible(&chip->mutex) < 0)
return;
if (chip->has_ac97_0) {
snd_iprintf(buffer, "\nAC97\n");
for (i = 0; i < 0x80; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; j += 2)
snd_iprintf(buffer, " %04x",
oxygen_read_ac97(chip, 0, i + j));
snd_iprintf(buffer, "\n");
}
}
if (chip->has_ac97_1) {
snd_iprintf(buffer, "\nAC97 2\n");
for (i = 0; i < 0x80; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; j += 2)
snd_iprintf(buffer, " %04x",
oxygen_read_ac97(chip, 1, i + j));
snd_iprintf(buffer, "\n");
}
}
mutex_unlock(&chip->mutex);
}
static void oxygen_proc_init(struct oxygen *chip)
{
struct snd_info_entry *entry;
if (!snd_card_proc_new(chip->card, "cmi8788", &entry))
snd_info_set_text_ops(entry, chip, oxygen_proc_read);
}
#else
#define oxygen_proc_init(chip)
#endif
static const struct pci_device_id *
oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
{
u16 subdevice;
/*
* Make sure the EEPROM pins are available, i.e., not used for SPI.
* (This function is called before we initialize or use SPI.)
*/
oxygen_clear_bits8(chip, OXYGEN_FUNCTION,
OXYGEN_FUNCTION_ENABLE_SPI_4_5);
/*
* Read the subsystem device ID directly from the EEPROM, because the
* chip didn't if the first EEPROM word was overwritten.
*/
subdevice = oxygen_read_eeprom(chip, 2);
/* use default ID if EEPROM is missing */
if (subdevice == 0xffff)
subdevice = 0x8788;
/*
* We use only the subsystem device ID for searching because it is
* unique even without the subsystem vendor ID, which may have been
* overwritten in the EEPROM.
*/
for (; ids->vendor; ++ids)
if (ids->subdevice == subdevice &&
ids->driver_data != BROKEN_EEPROM_DRIVER_DATA)
return ids;
return NULL;
}
static void oxygen_restore_eeprom(struct oxygen *chip,
const struct pci_device_id *id)
{
if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) {
/*
* This function gets called only when a known card model has
* been detected, i.e., we know there is a valid subsystem
* product ID at index 2 in the EEPROM. Therefore, we have
* been able to deduce the correct subsystem vendor ID, and
* this is enough information to restore the original EEPROM
* contents.
*/
oxygen_write_eeprom(chip, 1, id->subvendor);
oxygen_write_eeprom(chip, 0, OXYGEN_EEPROM_ID);
oxygen_set_bits8(chip, OXYGEN_MISC,
OXYGEN_MISC_WRITE_PCI_SUBID);
pci_write_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID,
id->subvendor);
pci_write_config_word(chip->pci, PCI_SUBSYSTEM_ID,
id->subdevice);
oxygen_clear_bits8(chip, OXYGEN_MISC,
OXYGEN_MISC_WRITE_PCI_SUBID);
snd_printk(KERN_INFO "EEPROM ID restored\n");
}
}
static void oxygen_init(struct oxygen *chip)
{
unsigned int i;
chip->dac_routing = 1;
for (i = 0; i < 8; ++i)
chip->dac_volume[i] = chip->model.dac_volume_min;
chip->dac_mute = 1;
chip->spdif_playback_enable = 1;
chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
chip->spdif_pcm_bits = chip->spdif_bits;
if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2)
chip->revision = 2;
else
chip->revision = 1;
if (chip->revision == 1)
oxygen_set_bits8(chip, OXYGEN_MISC,
OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
i = oxygen_read16(chip, OXYGEN_AC97_CONTROL);
chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0;
chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0;
oxygen_write8_masked(chip, OXYGEN_FUNCTION,
OXYGEN_FUNCTION_RESET_CODEC |
chip->model.function_flags,
OXYGEN_FUNCTION_RESET_CODEC |
OXYGEN_FUNCTION_2WIRE_SPI_MASK |
OXYGEN_FUNCTION_ENABLE_SPI_4_5);
oxygen_write8(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0);
oxygen_write8(chip, OXYGEN_PLAY_CHANNELS,
OXYGEN_PLAY_CHANNELS_2 |
OXYGEN_DMA_A_BURST_8 |
OXYGEN_DMA_MULTICH_BURST_8);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
oxygen_write8_masked(chip, OXYGEN_MISC,
chip->model.misc_flags,
OXYGEN_MISC_WRITE_PCI_SUBID |
OXYGEN_MISC_REC_C_FROM_SPDIF |
OXYGEN_MISC_REC_B_FROM_AC97 |
OXYGEN_MISC_REC_A_FROM_MULTICH |
OXYGEN_MISC_MIDI);
oxygen_write8(chip, OXYGEN_REC_FORMAT,
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) |
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) |
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_C_SHIFT));
oxygen_write8(chip, OXYGEN_PLAY_FORMAT,
(OXYGEN_FORMAT_16 << OXYGEN_SPDIF_FORMAT_SHIFT) |
(OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
OXYGEN_RATE_48000 | chip->model.dac_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
else
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
CAPTURE_2_FROM_I2S_2))
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
else
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE |
OXYGEN_SPDIF_LOOPBACK);
if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_SENSE_MASK |
OXYGEN_SPDIF_LOCK_MASK |
OXYGEN_SPDIF_RATE_MASK |
OXYGEN_SPDIF_LOCK_PAR |
OXYGEN_SPDIF_IN_CLOCK_96,
OXYGEN_SPDIF_SENSE_MASK |
OXYGEN_SPDIF_LOCK_MASK |
OXYGEN_SPDIF_RATE_MASK |
OXYGEN_SPDIF_SENSE_PAR |
OXYGEN_SPDIF_LOCK_PAR |
OXYGEN_SPDIF_IN_CLOCK_MASK);
else
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_SENSE_MASK |
OXYGEN_SPDIF_LOCK_MASK |
OXYGEN_SPDIF_RATE_MASK);
oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK |
OXYGEN_2WIRE_SPEED_STANDARD);
oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0);
oxygen_write16(chip, OXYGEN_PLAY_ROUTING,
OXYGEN_PLAY_MULTICH_I2S_DAC |
OXYGEN_PLAY_SPDIF_SPDIF |
(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
(1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
(3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT));
oxygen_write8(chip, OXYGEN_REC_ROUTING,
OXYGEN_REC_A_ROUTE_I2S_ADC_1 |
OXYGEN_REC_B_ROUTE_I2S_ADC_2 |
OXYGEN_REC_C_ROUTE_SPDIF);
oxygen_write8(chip, OXYGEN_ADC_MONITOR, 0);
oxygen_write8(chip, OXYGEN_A_MONITOR_ROUTING,
(0 << OXYGEN_A_MONITOR_ROUTE_0_SHIFT) |
(1 << OXYGEN_A_MONITOR_ROUTE_1_SHIFT) |
(2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) |
(3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT));
if (chip->has_ac97_0 | chip->has_ac97_1)
oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
OXYGEN_AC97_INT_READ_DONE |
OXYGEN_AC97_INT_WRITE_DONE);
else
oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, 0);
oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0);
oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0);
if (!(chip->has_ac97_0 | chip->has_ac97_1))
oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
OXYGEN_AC97_CLOCK_DISABLE);
if (!chip->has_ac97_0) {
oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
OXYGEN_AC97_NO_CODEC_0);
} else {
oxygen_write_ac97(chip, 0, AC97_RESET, 0);
msleep(1);
oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_SETUP,
CM9780_GPIO0IO | CM9780_GPIO1IO);
oxygen_ac97_set_bits(chip, 0, CM9780_MIXER,
CM9780_BSTSEL | CM9780_STRO_MIC |
CM9780_MIX2FR | CM9780_PCBSW);
oxygen_ac97_set_bits(chip, 0, CM9780_JACK,
CM9780_RSOE | CM9780_CBOE |
CM9780_SSOE | CM9780_FROE |
CM9780_MIC2MIC | CM9780_LI2LI);
oxygen_write_ac97(chip, 0, AC97_MASTER, 0x0000);
oxygen_write_ac97(chip, 0, AC97_PC_BEEP, 0x8000);
oxygen_write_ac97(chip, 0, AC97_MIC, 0x8808);
oxygen_write_ac97(chip, 0, AC97_LINE, 0x0808);
oxygen_write_ac97(chip, 0, AC97_CD, 0x8808);
oxygen_write_ac97(chip, 0, AC97_VIDEO, 0x8808);
oxygen_write_ac97(chip, 0, AC97_AUX, 0x8808);
oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000);
oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080);
oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080);
oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS,
CM9780_GPO0);
/* power down unused ADCs and DACs */
oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN,
AC97_PD_PR0 | AC97_PD_PR1);
oxygen_ac97_set_bits(chip, 0, AC97_EXTENDED_STATUS,
AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK);
}
if (chip->has_ac97_1) {
oxygen_set_bits32(chip, OXYGEN_AC97_OUT_CONFIG,
OXYGEN_AC97_CODEC1_SLOT3 |
OXYGEN_AC97_CODEC1_SLOT4);
oxygen_write_ac97(chip, 1, AC97_RESET, 0);
msleep(1);
oxygen_write_ac97(chip, 1, AC97_MASTER, 0x0000);
oxygen_write_ac97(chip, 1, AC97_HEADPHONE, 0x8000);
oxygen_write_ac97(chip, 1, AC97_PC_BEEP, 0x8000);
oxygen_write_ac97(chip, 1, AC97_MIC, 0x8808);
oxygen_write_ac97(chip, 1, AC97_LINE, 0x8808);
oxygen_write_ac97(chip, 1, AC97_CD, 0x8808);
oxygen_write_ac97(chip, 1, AC97_VIDEO, 0x8808);
oxygen_write_ac97(chip, 1, AC97_AUX, 0x8808);
oxygen_write_ac97(chip, 1, AC97_PCM, 0x0808);
oxygen_write_ac97(chip, 1, AC97_REC_SEL, 0x0000);
oxygen_write_ac97(chip, 1, AC97_REC_GAIN, 0x0000);
oxygen_ac97_set_bits(chip, 1, 0x6a, 0x0040);
}
}
static void oxygen_card_free(struct snd_card *card)
{
struct oxygen *chip = card->private_data;
spin_lock_irq(&chip->reg_lock);
chip->interrupt_mask = 0;
chip->pcm_running = 0;
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
spin_unlock_irq(&chip->reg_lock);
if (chip->irq >= 0)
free_irq(chip->irq, chip);
flush_scheduled_work();
chip->model.cleanup(chip);
kfree(chip->model_data);
mutex_destroy(&chip->mutex);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
}
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
struct module *owner,
const struct pci_device_id *ids,
int (*get_model)(struct oxygen *chip,
const struct pci_device_id *id
)
)
{
struct snd_card *card;
struct oxygen *chip;
const struct pci_device_id *pci_id;
int err;
err = snd_card_create(index, id, owner, sizeof(*chip), &card);
if (err < 0)
return err;
chip = card->private_data;
chip->card = card;
chip->pci = pci;
chip->irq = -1;
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->mutex);
INIT_WORK(&chip->spdif_input_bits_work,
oxygen_spdif_input_bits_changed);
INIT_WORK(&chip->gpio_work, oxygen_gpio_changed);
init_waitqueue_head(&chip->ac97_waitqueue);
err = pci_enable_device(pci);
if (err < 0)
goto err_card;
err = pci_request_regions(pci, DRIVER);
if (err < 0) {
snd_printk(KERN_ERR "cannot reserve PCI resources\n");
goto err_pci_enable;
}
if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) {
snd_printk(KERN_ERR "invalid PCI I/O range\n");
err = -ENXIO;
goto err_pci_regions;
}
chip->addr = pci_resource_start(pci, 0);
pci_id = oxygen_search_pci_id(chip, ids);
if (!pci_id) {
err = -ENODEV;
goto err_pci_regions;
}
oxygen_restore_eeprom(chip, pci_id);
err = get_model(chip, pci_id);
if (err < 0)
goto err_pci_regions;
if (chip->model.model_data_size) {
chip->model_data = kzalloc(chip->model.model_data_size,
GFP_KERNEL);
if (!chip->model_data) {
err = -ENOMEM;
goto err_pci_regions;
}
}
pci_set_master(pci);
snd_card_set_dev(card, &pci->dev);
card->private_free = oxygen_card_free;
oxygen_init(chip);
chip->model.init(chip);
err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
DRIVER, chip);
if (err < 0) {
snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
goto err_card;
}
chip->irq = pci->irq;
strcpy(card->driver, chip->model.chip);
strcpy(card->shortname, chip->model.shortname);
sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
chip->model.longname, chip->revision, chip->addr, chip->irq);
strcpy(card->mixername, chip->model.chip);
snd_component_add(card, chip->model.chip);
err = oxygen_pcm_init(chip);
if (err < 0)
goto err_card;
err = oxygen_mixer_init(chip);
if (err < 0)
goto err_card;
if (chip->model.device_config & (MIDI_OUTPUT | MIDI_INPUT)) {
unsigned int info_flags = MPU401_INFO_INTEGRATED;
if (chip->model.device_config & MIDI_OUTPUT)
info_flags |= MPU401_INFO_OUTPUT;
if (chip->model.device_config & MIDI_INPUT)
info_flags |= MPU401_INFO_INPUT;
err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
chip->addr + OXYGEN_MPU401,
info_flags, 0, 0,
&chip->midi);
if (err < 0)
goto err_card;
}
oxygen_proc_init(chip);
spin_lock_irq(&chip->reg_lock);
if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
if (chip->has_ac97_0 | chip->has_ac97_1)
chip->interrupt_mask |= OXYGEN_INT_AC97;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
spin_unlock_irq(&chip->reg_lock);
err = snd_card_register(card);
if (err < 0)
goto err_card;
pci_set_drvdata(pci, card);
return 0;
err_pci_regions:
pci_release_regions(pci);
err_pci_enable:
pci_disable_device(pci);
err_card:
snd_card_free(card);
return err;
}
EXPORT_SYMBOL(oxygen_pci_probe);
void oxygen_pci_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
EXPORT_SYMBOL(oxygen_pci_remove);
#ifdef CONFIG_PM
int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
struct oxygen *chip = card->private_data;
unsigned int i, saved_interrupt_mask;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
for (i = 0; i < PCM_COUNT; ++i)
if (chip->streams[i])
snd_pcm_suspend(chip->streams[i]);
if (chip->model.suspend)
chip->model.suspend(chip);
spin_lock_irq(&chip->reg_lock);
saved_interrupt_mask = chip->interrupt_mask;
chip->interrupt_mask = 0;
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
spin_unlock_irq(&chip->reg_lock);
synchronize_irq(chip->irq);
flush_scheduled_work();
chip->interrupt_mask = saved_interrupt_mask;
pci_disable_device(pci);
pci_save_state(pci);
pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
EXPORT_SYMBOL(oxygen_pci_suspend);
static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = {
0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff,
0x00300000, 0x00000fe4, 0x0ff7001f, 0x00000000
};
static const u32 ac97_registers_to_restore[2][0x40 / 32] = {
{ 0x18284fa2, 0x03060000 },
{ 0x00007fa6, 0x00200000 }
};
static inline int is_bit_set(const u32 *bitmap, unsigned int bit)
{
return bitmap[bit / 32] & (1 << (bit & 31));
}
static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec)
{
unsigned int i;
oxygen_write_ac97(chip, codec, AC97_RESET, 0);
msleep(1);
for (i = 1; i < 0x40; ++i)
if (is_bit_set(ac97_registers_to_restore[codec], i))
oxygen_write_ac97(chip, codec, i * 2,
chip->saved_ac97_registers[codec][i]);
}
int oxygen_pci_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct oxygen *chip = card->private_data;
unsigned int i;
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
if (pci_enable_device(pci) < 0) {
snd_printk(KERN_ERR "cannot reenable device");
snd_card_disconnect(card);
return -EIO;
}
pci_set_master(pci);
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
for (i = 0; i < OXYGEN_IO_SIZE; ++i)
if (is_bit_set(registers_to_restore, i))
oxygen_write8(chip, i, chip->saved_registers._8[i]);
if (chip->has_ac97_0)
oxygen_restore_ac97(chip, 0);
if (chip->has_ac97_1)
oxygen_restore_ac97(chip, 1);
if (chip->model.resume)
chip->model.resume(chip);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
EXPORT_SYMBOL(oxygen_pci_resume);
#endif /* CONFIG_PM */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,752 @@
/*
* C-Media CMI8788 driver - PCM code
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this driver; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/pci.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "oxygen.h"
/* most DMA channels have a 16-bit counter for 32-bit words */
#define BUFFER_BYTES_MAX ((1 << 16) * 4)
/* the multichannel DMA channel has a 24-bit counter */
#define BUFFER_BYTES_MAX_MULTICH ((1 << 24) * 4)
#define PERIOD_BYTES_MIN 64
#define DEFAULT_BUFFER_BYTES (BUFFER_BYTES_MAX / 2)
#define DEFAULT_BUFFER_BYTES_MULTICH (1024 * 1024)
static const struct snd_pcm_hardware oxygen_stereo_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 |
SNDRV_PCM_RATE_192000,
.rate_min = 32000,
.rate_max = 192000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = BUFFER_BYTES_MAX / 2,
.periods_min = 2,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
};
static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 |
SNDRV_PCM_RATE_192000,
.rate_min = 32000,
.rate_max = 192000,
.channels_min = 2,
.channels_max = 8,
.buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH,
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = BUFFER_BYTES_MAX_MULTICH / 2,
.periods_min = 2,
.periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
};
static const struct snd_pcm_hardware oxygen_ac97_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = BUFFER_BYTES_MAX / 2,
.periods_min = 2,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
};
static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
[PCM_A] = &oxygen_stereo_hardware,
[PCM_B] = &oxygen_stereo_hardware,
[PCM_C] = &oxygen_stereo_hardware,
[PCM_SPDIF] = &oxygen_stereo_hardware,
[PCM_MULTICH] = &oxygen_multichannel_hardware,
[PCM_AC97] = &oxygen_ac97_hardware,
};
static inline unsigned int
oxygen_substream_channel(struct snd_pcm_substream *substream)
{
return (unsigned int)(uintptr_t)substream->runtime->private_data;
}
static int oxygen_open(struct snd_pcm_substream *substream,
unsigned int channel)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
runtime->private_data = (void *)(uintptr_t)channel;
if (channel == PCM_B && chip->has_ac97_1 &&
(chip->model.device_config & CAPTURE_2_FROM_AC97_1))
runtime->hw = oxygen_ac97_hardware;
else
runtime->hw = *oxygen_hardware[channel];
switch (channel) {
case PCM_C:
runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_64000);
runtime->hw.rate_min = 44100;
break;
case PCM_MULTICH:
runtime->hw.channels_max = chip->model.dac_channels;
break;
}
if (chip->model.pcm_hardware_filter)
chip->model.pcm_hardware_filter(channel, &runtime->hw);
err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
if (err < 0)
return err;
err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
if (err < 0)
return err;
if (runtime->hw.formats & SNDRV_PCM_FMTBIT_S32_LE) {
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if (err < 0)
return err;
}
if (runtime->hw.channels_max > 2) {
err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
2);
if (err < 0)
return err;
}
if (channel == PCM_MULTICH) {
err = snd_pcm_hw_constraint_minmax
(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0, 8192000);
if (err < 0)
return err;
}
snd_pcm_set_sync(substream);
chip->streams[channel] = substream;
mutex_lock(&chip->mutex);
chip->pcm_active |= 1 << channel;
if (channel == PCM_SPDIF) {
chip->spdif_pcm_bits = chip->spdif_bits;
chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &=
~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO,
&chip->controls[CONTROL_SPDIF_PCM]->id);
}
mutex_unlock(&chip->mutex);
return 0;
}
static int oxygen_rec_a_open(struct snd_pcm_substream *substream)
{
return oxygen_open(substream, PCM_A);
}
static int oxygen_rec_b_open(struct snd_pcm_substream *substream)
{
return oxygen_open(substream, PCM_B);
}
static int oxygen_rec_c_open(struct snd_pcm_substream *substream)
{
return oxygen_open(substream, PCM_C);
}
static int oxygen_spdif_open(struct snd_pcm_substream *substream)
{
return oxygen_open(substream, PCM_SPDIF);
}
static int oxygen_multich_open(struct snd_pcm_substream *substream)
{
return oxygen_open(substream, PCM_MULTICH);
}
static int oxygen_ac97_open(struct snd_pcm_substream *substream)
{
return oxygen_open(substream, PCM_AC97);
}
static int oxygen_close(struct snd_pcm_substream *substream)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
unsigned int channel = oxygen_substream_channel(substream);
mutex_lock(&chip->mutex);
chip->pcm_active &= ~(1 << channel);
if (channel == PCM_SPDIF) {
chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |=
SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO,
&chip->controls[CONTROL_SPDIF_PCM]->id);
}
if (channel == PCM_SPDIF || channel == PCM_MULTICH)
oxygen_update_spdif_source(chip);
mutex_unlock(&chip->mutex);
chip->streams[channel] = NULL;
return 0;
}
static unsigned int oxygen_format(struct snd_pcm_hw_params *hw_params)
{
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
return OXYGEN_FORMAT_24;
else
return OXYGEN_FORMAT_16;
}
static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
{
switch (params_rate(hw_params)) {
case 32000:
return OXYGEN_RATE_32000;
case 44100:
return OXYGEN_RATE_44100;
default: /* 48000 */
return OXYGEN_RATE_48000;
case 64000:
return OXYGEN_RATE_64000;
case 88200:
return OXYGEN_RATE_88200;
case 96000:
return OXYGEN_RATE_96000;
case 176400:
return OXYGEN_RATE_176400;
case 192000:
return OXYGEN_RATE_192000;
}
}
static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params)
{
if (params_rate(hw_params) <= 96000)
return OXYGEN_I2S_MCLK_256;
else
return OXYGEN_I2S_MCLK_128;
}
static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
{
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
return OXYGEN_I2S_BITS_24;
else
return OXYGEN_I2S_BITS_16;
}
static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params)
{
switch (params_channels(hw_params)) {
default: /* 2 */
return OXYGEN_PLAY_CHANNELS_2;
case 4:
return OXYGEN_PLAY_CHANNELS_4;
case 6:
return OXYGEN_PLAY_CHANNELS_6;
case 8:
return OXYGEN_PLAY_CHANNELS_8;
}
}
static const unsigned int channel_base_registers[PCM_COUNT] = {
[PCM_A] = OXYGEN_DMA_A_ADDRESS,
[PCM_B] = OXYGEN_DMA_B_ADDRESS,
[PCM_C] = OXYGEN_DMA_C_ADDRESS,
[PCM_SPDIF] = OXYGEN_DMA_SPDIF_ADDRESS,
[PCM_MULTICH] = OXYGEN_DMA_MULTICH_ADDRESS,
[PCM_AC97] = OXYGEN_DMA_AC97_ADDRESS,
};
static int oxygen_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
unsigned int channel = oxygen_substream_channel(substream);
int err;
err = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (err < 0)
return err;
oxygen_write32(chip, channel_base_registers[channel],
(u32)substream->runtime->dma_addr);
if (channel == PCM_MULTICH) {
oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT,
params_buffer_bytes(hw_params) / 4 - 1);
oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT,
params_period_bytes(hw_params) / 4 - 1);
} else {
oxygen_write16(chip, channel_base_registers[channel] + 4,
params_buffer_bytes(hw_params) / 4 - 1);
oxygen_write16(chip, channel_base_registers[channel] + 6,
params_period_bytes(hw_params) / 4 - 1);
}
return 0;
}
static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
int err;
err = oxygen_hw_params(substream, hw_params);
if (err < 0)
return err;
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT,
OXYGEN_REC_FORMAT_A_MASK);
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
oxygen_rate(hw_params) |
oxygen_i2s_mclk(hw_params) |
chip->model.adc_i2s_format |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
OXYGEN_I2S_MCLK_MASK |
OXYGEN_I2S_BITS_MASK);
spin_unlock_irq(&chip->reg_lock);
mutex_lock(&chip->mutex);
chip->model.set_adc_params(chip, hw_params);
mutex_unlock(&chip->mutex);
return 0;
}
static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
int is_ac97;
int err;
err = oxygen_hw_params(substream, hw_params);
if (err < 0)
return err;
is_ac97 = chip->has_ac97_1 &&
(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT,
OXYGEN_REC_FORMAT_B_MASK);
if (!is_ac97)
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
oxygen_rate(hw_params) |
oxygen_i2s_mclk(hw_params) |
chip->model.adc_i2s_format |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
OXYGEN_I2S_MCLK_MASK |
OXYGEN_I2S_BITS_MASK);
spin_unlock_irq(&chip->reg_lock);
if (!is_ac97) {
mutex_lock(&chip->mutex);
chip->model.set_adc_params(chip, hw_params);
mutex_unlock(&chip->mutex);
}
return 0;
}
static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
int err;
err = oxygen_hw_params(substream, hw_params);
if (err < 0)
return err;
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT,
OXYGEN_REC_FORMAT_C_MASK);
spin_unlock_irq(&chip->reg_lock);
return 0;
}
static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
int err;
err = oxygen_hw_params(substream, hw_params);
if (err < 0)
return err;
spin_lock_irq(&chip->reg_lock);
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE);
oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
oxygen_format(hw_params) << OXYGEN_SPDIF_FORMAT_SHIFT,
OXYGEN_SPDIF_FORMAT_MASK);
oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT,
OXYGEN_SPDIF_OUT_RATE_MASK);
oxygen_update_spdif_source(chip);
spin_unlock_irq(&chip->reg_lock);
return 0;
}
static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
int err;
err = oxygen_hw_params(substream, hw_params);
if (err < 0)
return err;
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
oxygen_play_channels(hw_params),
OXYGEN_PLAY_CHANNELS_MASK);
oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT,
OXYGEN_MULTICH_FORMAT_MASK);
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
oxygen_rate(hw_params) |
chip->model.dac_i2s_format |
oxygen_i2s_mclk(hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
OXYGEN_I2S_MCLK_MASK |
OXYGEN_I2S_BITS_MASK);
oxygen_update_dac_routing(chip);
oxygen_update_spdif_source(chip);
spin_unlock_irq(&chip->reg_lock);
mutex_lock(&chip->mutex);
chip->model.set_dac_params(chip, hw_params);
mutex_unlock(&chip->mutex);
return 0;
}
static int oxygen_hw_free(struct snd_pcm_substream *substream)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
unsigned int channel = oxygen_substream_channel(substream);
unsigned int channel_mask = 1 << channel;
spin_lock_irq(&chip->reg_lock);
chip->interrupt_mask &= ~channel_mask;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
spin_unlock_irq(&chip->reg_lock);
return snd_pcm_lib_free_pages(substream);
}
static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
spin_lock_irq(&chip->reg_lock);
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE);
spin_unlock_irq(&chip->reg_lock);
return oxygen_hw_free(substream);
}
static int oxygen_prepare(struct snd_pcm_substream *substream)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
unsigned int channel = oxygen_substream_channel(substream);
unsigned int channel_mask = 1 << channel;
spin_lock_irq(&chip->reg_lock);
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
chip->interrupt_mask |= channel_mask;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
spin_unlock_irq(&chip->reg_lock);
return 0;
}
static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
unsigned int mask = 0;
int pausing;
switch (cmd) {
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_SUSPEND:
pausing = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
pausing = 1;
break;
default:
return -EINVAL;
}
snd_pcm_group_for_each_entry(s, substream) {
if (snd_pcm_substream_chip(s) == chip) {
mask |= 1 << oxygen_substream_channel(s);
snd_pcm_trigger_done(s, substream);
}
}
spin_lock(&chip->reg_lock);
if (!pausing) {
if (cmd == SNDRV_PCM_TRIGGER_START)
chip->pcm_running |= mask;
else
chip->pcm_running &= ~mask;
oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running);
} else {
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask);
else
oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask);
}
spin_unlock(&chip->reg_lock);
return 0;
}
static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
{
struct oxygen *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int channel = oxygen_substream_channel(substream);
u32 curr_addr;
/* no spinlock, this read should be atomic */
curr_addr = oxygen_read32(chip, channel_base_registers[channel]);
return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr);
}
static struct snd_pcm_ops oxygen_rec_a_ops = {
.open = oxygen_rec_a_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_rec_a_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
.trigger = oxygen_trigger,
.pointer = oxygen_pointer,
};
static struct snd_pcm_ops oxygen_rec_b_ops = {
.open = oxygen_rec_b_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_rec_b_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
.trigger = oxygen_trigger,
.pointer = oxygen_pointer,
};
static struct snd_pcm_ops oxygen_rec_c_ops = {
.open = oxygen_rec_c_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_rec_c_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
.trigger = oxygen_trigger,
.pointer = oxygen_pointer,
};
static struct snd_pcm_ops oxygen_spdif_ops = {
.open = oxygen_spdif_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_spdif_hw_params,
.hw_free = oxygen_spdif_hw_free,
.prepare = oxygen_prepare,
.trigger = oxygen_trigger,
.pointer = oxygen_pointer,
};
static struct snd_pcm_ops oxygen_multich_ops = {
.open = oxygen_multich_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_multich_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
.trigger = oxygen_trigger,
.pointer = oxygen_pointer,
};
static struct snd_pcm_ops oxygen_ac97_ops = {
.open = oxygen_ac97_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = oxygen_hw_params,
.hw_free = oxygen_hw_free,
.prepare = oxygen_prepare,
.trigger = oxygen_trigger,
.pointer = oxygen_pointer,
};
static void oxygen_pcm_free(struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
int oxygen_pcm_init(struct oxygen *chip)
{
struct snd_pcm *pcm;
int outs, ins;
int err;
outs = !!(chip->model.device_config & PLAYBACK_0_TO_I2S);
ins = !!(chip->model.device_config & (CAPTURE_0_FROM_I2S_1 |
CAPTURE_0_FROM_I2S_2));
if (outs | ins) {
err = snd_pcm_new(chip->card, "Multichannel",
0, outs, ins, &pcm);
if (err < 0)
return err;
if (outs)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&oxygen_multich_ops);
if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&oxygen_rec_a_ops);
else if (chip->model.device_config & CAPTURE_0_FROM_I2S_2)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&oxygen_rec_b_ops);
pcm->private_data = chip;
pcm->private_free = oxygen_pcm_free;
strcpy(pcm->name, "Multichannel");
if (outs)
snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
DEFAULT_BUFFER_BYTES_MULTICH,
BUFFER_BYTES_MAX_MULTICH);
if (ins)
snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
DEFAULT_BUFFER_BYTES,
BUFFER_BYTES_MAX);
}
outs = !!(chip->model.device_config & PLAYBACK_1_TO_SPDIF);
ins = !!(chip->model.device_config & CAPTURE_1_FROM_SPDIF);
if (outs | ins) {
err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
if (err < 0)
return err;
if (outs)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&oxygen_spdif_ops);
if (ins)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&oxygen_rec_c_ops);
pcm->private_data = chip;
pcm->private_free = oxygen_pcm_free;
strcpy(pcm->name, "Digital");
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
DEFAULT_BUFFER_BYTES,
BUFFER_BYTES_MAX);
}
if (chip->has_ac97_1) {
outs = !!(chip->model.device_config & PLAYBACK_2_TO_AC97_1);
ins = !!(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
} else {
outs = 0;
ins = !!(chip->model.device_config & CAPTURE_2_FROM_I2S_2);
}
if (outs | ins) {
err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
2, outs, ins, &pcm);
if (err < 0)
return err;
if (outs) {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&oxygen_ac97_ops);
oxygen_write8_masked(chip, OXYGEN_REC_ROUTING,
OXYGEN_REC_B_ROUTE_AC97_1,
OXYGEN_REC_B_ROUTE_MASK);
}
if (ins)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&oxygen_rec_b_ops);
pcm->private_data = chip;
pcm->private_free = oxygen_pcm_free;
strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
DEFAULT_BUFFER_BYTES,
BUFFER_BYTES_MAX);
}
return 0;
}

View File

@@ -0,0 +1,453 @@
#ifndef OXYGEN_REGS_H_INCLUDED
#define OXYGEN_REGS_H_INCLUDED
/* recording channel A */
#define OXYGEN_DMA_A_ADDRESS 0x00 /* 32-bit base address */
#define OXYGEN_DMA_A_COUNT 0x04 /* buffer counter (dwords) */
#define OXYGEN_DMA_A_TCOUNT 0x06 /* interrupt counter (dwords) */
/* recording channel B */
#define OXYGEN_DMA_B_ADDRESS 0x08
#define OXYGEN_DMA_B_COUNT 0x0c
#define OXYGEN_DMA_B_TCOUNT 0x0e
/* recording channel C */
#define OXYGEN_DMA_C_ADDRESS 0x10
#define OXYGEN_DMA_C_COUNT 0x14
#define OXYGEN_DMA_C_TCOUNT 0x16
/* SPDIF playback channel */
#define OXYGEN_DMA_SPDIF_ADDRESS 0x18
#define OXYGEN_DMA_SPDIF_COUNT 0x1c
#define OXYGEN_DMA_SPDIF_TCOUNT 0x1e
/* multichannel playback channel */
#define OXYGEN_DMA_MULTICH_ADDRESS 0x20
#define OXYGEN_DMA_MULTICH_COUNT 0x24 /* 24 bits */
#define OXYGEN_DMA_MULTICH_TCOUNT 0x28 /* 24 bits */
/* AC'97 (front panel) playback channel */
#define OXYGEN_DMA_AC97_ADDRESS 0x30
#define OXYGEN_DMA_AC97_COUNT 0x34
#define OXYGEN_DMA_AC97_TCOUNT 0x36
/* all registers 0x00..0x36 return current position on read */
#define OXYGEN_DMA_STATUS 0x40 /* 1 = running, 0 = stop */
#define OXYGEN_CHANNEL_A 0x01
#define OXYGEN_CHANNEL_B 0x02
#define OXYGEN_CHANNEL_C 0x04
#define OXYGEN_CHANNEL_SPDIF 0x08
#define OXYGEN_CHANNEL_MULTICH 0x10
#define OXYGEN_CHANNEL_AC97 0x20
#define OXYGEN_DMA_PAUSE 0x41 /* 1 = pause */
/* OXYGEN_CHANNEL_* */
#define OXYGEN_DMA_RESET 0x42
/* OXYGEN_CHANNEL_* */
#define OXYGEN_PLAY_CHANNELS 0x43
#define OXYGEN_PLAY_CHANNELS_MASK 0x03
#define OXYGEN_PLAY_CHANNELS_2 0x00
#define OXYGEN_PLAY_CHANNELS_4 0x01
#define OXYGEN_PLAY_CHANNELS_6 0x02
#define OXYGEN_PLAY_CHANNELS_8 0x03
#define OXYGEN_DMA_A_BURST_MASK 0x04
#define OXYGEN_DMA_A_BURST_8 0x00 /* dwords */
#define OXYGEN_DMA_A_BURST_16 0x04
#define OXYGEN_DMA_MULTICH_BURST_MASK 0x08
#define OXYGEN_DMA_MULTICH_BURST_8 0x00
#define OXYGEN_DMA_MULTICH_BURST_16 0x08
#define OXYGEN_INTERRUPT_MASK 0x44
/* OXYGEN_CHANNEL_* */
#define OXYGEN_INT_SPDIF_IN_DETECT 0x0100
#define OXYGEN_INT_MCU 0x0200
#define OXYGEN_INT_2WIRE 0x0400
#define OXYGEN_INT_GPIO 0x0800
#define OXYGEN_INT_MCB 0x2000
#define OXYGEN_INT_AC97 0x4000
#define OXYGEN_INTERRUPT_STATUS 0x46
/* OXYGEN_CHANNEL_* amd OXYGEN_INT_* */
#define OXYGEN_INT_MIDI 0x1000
#define OXYGEN_MISC 0x48
#define OXYGEN_MISC_WRITE_PCI_SUBID 0x01
#define OXYGEN_MISC_LATENCY_3F 0x02
#define OXYGEN_MISC_REC_C_FROM_SPDIF 0x04
#define OXYGEN_MISC_REC_B_FROM_AC97 0x08
#define OXYGEN_MISC_REC_A_FROM_MULTICH 0x10
#define OXYGEN_MISC_PCI_MEM_W_1_CLOCK 0x20
#define OXYGEN_MISC_MIDI 0x40
#define OXYGEN_MISC_CRYSTAL_MASK 0x80
#define OXYGEN_MISC_CRYSTAL_24576 0x00
#define OXYGEN_MISC_CRYSTAL_27 0x80 /* MHz */
#define OXYGEN_REC_FORMAT 0x4a
#define OXYGEN_REC_FORMAT_A_MASK 0x03
#define OXYGEN_REC_FORMAT_A_SHIFT 0
#define OXYGEN_REC_FORMAT_B_MASK 0x0c
#define OXYGEN_REC_FORMAT_B_SHIFT 2
#define OXYGEN_REC_FORMAT_C_MASK 0x30
#define OXYGEN_REC_FORMAT_C_SHIFT 4
#define OXYGEN_FORMAT_16 0x00
#define OXYGEN_FORMAT_24 0x01
#define OXYGEN_FORMAT_32 0x02
#define OXYGEN_PLAY_FORMAT 0x4b
#define OXYGEN_SPDIF_FORMAT_MASK 0x03
#define OXYGEN_SPDIF_FORMAT_SHIFT 0
#define OXYGEN_MULTICH_FORMAT_MASK 0x0c
#define OXYGEN_MULTICH_FORMAT_SHIFT 2
/* OXYGEN_FORMAT_* */
#define OXYGEN_REC_CHANNELS 0x4c
#define OXYGEN_REC_CHANNELS_MASK 0x07
#define OXYGEN_REC_CHANNELS_2_2_2 0x00 /* DMA A, B, C */
#define OXYGEN_REC_CHANNELS_4_2_2 0x01
#define OXYGEN_REC_CHANNELS_6_0_2 0x02
#define OXYGEN_REC_CHANNELS_6_2_0 0x03
#define OXYGEN_REC_CHANNELS_8_0_0 0x04
#define OXYGEN_FUNCTION 0x50
#define OXYGEN_FUNCTION_CLOCK_MASK 0x01
#define OXYGEN_FUNCTION_CLOCK_PLL 0x00
#define OXYGEN_FUNCTION_CLOCK_CRYSTAL 0x01
#define OXYGEN_FUNCTION_RESET_CODEC 0x02
#define OXYGEN_FUNCTION_RESET_POL 0x04
#define OXYGEN_FUNCTION_PWDN 0x08
#define OXYGEN_FUNCTION_PWDN_EN 0x10
#define OXYGEN_FUNCTION_PWDN_POL 0x20
#define OXYGEN_FUNCTION_2WIRE_SPI_MASK 0x40
#define OXYGEN_FUNCTION_SPI 0x00
#define OXYGEN_FUNCTION_2WIRE 0x40
#define OXYGEN_FUNCTION_ENABLE_SPI_4_5 0x80 /* 0 = EEPROM */
#define OXYGEN_I2S_MULTICH_FORMAT 0x60
#define OXYGEN_I2S_RATE_MASK 0x0007 /* LRCK */
#define OXYGEN_RATE_32000 0x0000
#define OXYGEN_RATE_44100 0x0001
#define OXYGEN_RATE_48000 0x0002
#define OXYGEN_RATE_64000 0x0003
#define OXYGEN_RATE_88200 0x0004
#define OXYGEN_RATE_96000 0x0005
#define OXYGEN_RATE_176400 0x0006
#define OXYGEN_RATE_192000 0x0007
#define OXYGEN_I2S_FORMAT_MASK 0x0008
#define OXYGEN_I2S_FORMAT_I2S 0x0000
#define OXYGEN_I2S_FORMAT_LJUST 0x0008
#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */
#define OXYGEN_I2S_MCLK_128 0x0000
#define OXYGEN_I2S_MCLK_256 0x0010
#define OXYGEN_I2S_MCLK_512 0x0020
#define OXYGEN_I2S_BITS_MASK 0x00c0
#define OXYGEN_I2S_BITS_16 0x0000
#define OXYGEN_I2S_BITS_20 0x0040
#define OXYGEN_I2S_BITS_24 0x0080
#define OXYGEN_I2S_BITS_32 0x00c0
#define OXYGEN_I2S_MASTER 0x0100
#define OXYGEN_I2S_BCLK_MASK 0x0600 /* BCLK/LRCK */
#define OXYGEN_I2S_BCLK_64 0x0000
#define OXYGEN_I2S_BCLK_128 0x0200
#define OXYGEN_I2S_BCLK_256 0x0400
#define OXYGEN_I2S_MUTE_MCLK 0x0800
#define OXYGEN_I2S_A_FORMAT 0x62
#define OXYGEN_I2S_B_FORMAT 0x64
#define OXYGEN_I2S_C_FORMAT 0x66
/* like OXYGEN_I2S_MULTICH_FORMAT */
#define OXYGEN_SPDIF_CONTROL 0x70
#define OXYGEN_SPDIF_OUT_ENABLE 0x00000002
#define OXYGEN_SPDIF_LOOPBACK 0x00000004 /* in to out */
#define OXYGEN_SPDIF_SENSE_MASK 0x00000008
#define OXYGEN_SPDIF_LOCK_MASK 0x00000010
#define OXYGEN_SPDIF_RATE_MASK 0x00000020
#define OXYGEN_SPDIF_SPDVALID 0x00000040
#define OXYGEN_SPDIF_SENSE_PAR 0x00000200
#define OXYGEN_SPDIF_LOCK_PAR 0x00000400
#define OXYGEN_SPDIF_SENSE_STATUS 0x00000800
#define OXYGEN_SPDIF_LOCK_STATUS 0x00001000
#define OXYGEN_SPDIF_SENSE_INT 0x00002000 /* r/wc */
#define OXYGEN_SPDIF_LOCK_INT 0x00004000 /* r/wc */
#define OXYGEN_SPDIF_RATE_INT 0x00008000 /* r/wc */
#define OXYGEN_SPDIF_IN_CLOCK_MASK 0x00010000
#define OXYGEN_SPDIF_IN_CLOCK_96 0x00000000 /* <= 96 kHz */
#define OXYGEN_SPDIF_IN_CLOCK_192 0x00010000 /* > 96 kHz */
#define OXYGEN_SPDIF_OUT_RATE_MASK 0x07000000
#define OXYGEN_SPDIF_OUT_RATE_SHIFT 24
/* OXYGEN_RATE_* << OXYGEN_SPDIF_OUT_RATE_SHIFT */
#define OXYGEN_SPDIF_OUTPUT_BITS 0x74
#define OXYGEN_SPDIF_NONAUDIO 0x00000002
#define OXYGEN_SPDIF_C 0x00000004
#define OXYGEN_SPDIF_PREEMPHASIS 0x00000008
#define OXYGEN_SPDIF_CATEGORY_MASK 0x000007f0
#define OXYGEN_SPDIF_CATEGORY_SHIFT 4
#define OXYGEN_SPDIF_ORIGINAL 0x00000800
#define OXYGEN_SPDIF_CS_RATE_MASK 0x0000f000
#define OXYGEN_SPDIF_CS_RATE_SHIFT 12
#define OXYGEN_SPDIF_V 0x00010000 /* 0 = valid */
#define OXYGEN_SPDIF_INPUT_BITS 0x78
/* 32 bits, IEC958_AES_* */
#define OXYGEN_EEPROM_CONTROL 0x80
#define OXYGEN_EEPROM_ADDRESS_MASK 0x7f
#define OXYGEN_EEPROM_DIR_MASK 0x80
#define OXYGEN_EEPROM_DIR_READ 0x00
#define OXYGEN_EEPROM_DIR_WRITE 0x80
#define OXYGEN_EEPROM_STATUS 0x81
#define OXYGEN_EEPROM_VALID 0x40
#define OXYGEN_EEPROM_BUSY 0x80
#define OXYGEN_EEPROM_DATA 0x82 /* 16 bits */
#define OXYGEN_2WIRE_CONTROL 0x90
#define OXYGEN_2WIRE_DIR_MASK 0x01
#define OXYGEN_2WIRE_DIR_WRITE 0x00
#define OXYGEN_2WIRE_DIR_READ 0x01
#define OXYGEN_2WIRE_ADDRESS_MASK 0xfe /* slave device address */
#define OXYGEN_2WIRE_ADDRESS_SHIFT 1
#define OXYGEN_2WIRE_MAP 0x91 /* address, 8 bits */
#define OXYGEN_2WIRE_DATA 0x92 /* data, 16 bits */
#define OXYGEN_2WIRE_BUS_STATUS 0x94
#define OXYGEN_2WIRE_BUSY 0x0001
#define OXYGEN_2WIRE_LENGTH_MASK 0x0002
#define OXYGEN_2WIRE_LENGTH_8 0x0000
#define OXYGEN_2WIRE_LENGTH_16 0x0002
#define OXYGEN_2WIRE_MANUAL_READ 0x0004 /* 0 = auto read */
#define OXYGEN_2WIRE_WRITE_MAP_ONLY 0x0008
#define OXYGEN_2WIRE_SLAVE_AD_MASK 0x0030 /* AD0, AD1 */
#define OXYGEN_2WIRE_INTERRUPT_MASK 0x0040 /* 0 = int. if not responding */
#define OXYGEN_2WIRE_SLAVE_NO_RESPONSE 0x0080
#define OXYGEN_2WIRE_SPEED_MASK 0x0100
#define OXYGEN_2WIRE_SPEED_STANDARD 0x0000
#define OXYGEN_2WIRE_SPEED_FAST 0x0100
#define OXYGEN_2WIRE_CLOCK_SYNC 0x0200
#define OXYGEN_2WIRE_BUS_RESET 0x0400
#define OXYGEN_SPI_CONTROL 0x98
#define OXYGEN_SPI_BUSY 0x01 /* read */
#define OXYGEN_SPI_TRIGGER 0x01 /* write */
#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02
#define OXYGEN_SPI_DATA_LENGTH_2 0x00
#define OXYGEN_SPI_DATA_LENGTH_3 0x02
#define OXYGEN_SPI_CLOCK_MASK 0xc0
#define OXYGEN_SPI_CLOCK_160 0x00 /* ns */
#define OXYGEN_SPI_CLOCK_320 0x40
#define OXYGEN_SPI_CLOCK_640 0x80
#define OXYGEN_SPI_CLOCK_1280 0xc0
#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */
#define OXYGEN_SPI_CODEC_SHIFT 4
#define OXYGEN_SPI_CEN_MASK 0x80
#define OXYGEN_SPI_CEN_LATCH_CLOCK_LO 0x00
#define OXYGEN_SPI_CEN_LATCH_CLOCK_HI 0x80
#define OXYGEN_SPI_DATA1 0x99
#define OXYGEN_SPI_DATA2 0x9a
#define OXYGEN_SPI_DATA3 0x9b
#define OXYGEN_MPU401 0xa0
#define OXYGEN_MPU401_CONTROL 0xa2
#define OXYGEN_MPU401_LOOPBACK 0x01 /* TXD to RXD */
#define OXYGEN_GPI_DATA 0xa4
/* bits 0..5 = pin XGPI0..XGPI5 */
#define OXYGEN_GPI_INTERRUPT_MASK 0xa5
/* bits 0..5, 1 = enable */
#define OXYGEN_GPIO_DATA 0xa6
/* bits 0..9 */
#define OXYGEN_GPIO_CONTROL 0xa8
/* bits 0..9, 0 = input, 1 = output */
#define OXYGEN_GPIO1_XSLAVE_RDY 0x8000
#define OXYGEN_GPIO_INTERRUPT_MASK 0xaa
/* bits 0..9, 1 = enable */
#define OXYGEN_DEVICE_SENSE 0xac
#define OXYGEN_HEAD_PHONE_DETECT 0x01
#define OXYGEN_HEAD_PHONE_MASK 0x06
#define OXYGEN_HEAD_PHONE_PASSIVE_SPK 0x00
#define OXYGEN_HEAD_PHONE_HP 0x02
#define OXYGEN_HEAD_PHONE_ACTIVE_SPK 0x04
#define OXYGEN_MCU_2WIRE_DATA 0xb0
#define OXYGEN_MCU_2WIRE_MAP 0xb2
#define OXYGEN_MCU_2WIRE_STATUS 0xb3
#define OXYGEN_MCU_2WIRE_BUSY 0x01
#define OXYGEN_MCU_2WIRE_LENGTH_MASK 0x06
#define OXYGEN_MCU_2WIRE_LENGTH_1 0x00
#define OXYGEN_MCU_2WIRE_LENGTH_2 0x02
#define OXYGEN_MCU_2WIRE_LENGTH_3 0x04
#define OXYGEN_MCU_2WIRE_WRITE 0x08 /* r/wc */
#define OXYGEN_MCU_2WIRE_READ 0x10 /* r/wc */
#define OXYGEN_MCU_2WIRE_DRV_XACT_FAIL 0x20 /* r/wc */
#define OXYGEN_MCU_2WIRE_RESET 0x40
#define OXYGEN_MCU_2WIRE_CONTROL 0xb4
#define OXYGEN_MCU_2WIRE_DRV_ACK 0x01
#define OXYGEN_MCU_2WIRE_DRV_XACT 0x02
#define OXYGEN_MCU_2WIRE_INT_MASK 0x04
#define OXYGEN_MCU_2WIRE_SYNC_MASK 0x08
#define OXYGEN_MCU_2WIRE_SYNC_RDY_PIN 0x00
#define OXYGEN_MCU_2WIRE_SYNC_DATA 0x08
#define OXYGEN_MCU_2WIRE_ADDRESS_MASK 0x30
#define OXYGEN_MCU_2WIRE_ADDRESS_10 0x00
#define OXYGEN_MCU_2WIRE_ADDRESS_12 0x10
#define OXYGEN_MCU_2WIRE_ADDRESS_14 0x20
#define OXYGEN_MCU_2WIRE_ADDRESS_16 0x30
#define OXYGEN_MCU_2WIRE_INT_POL 0x40
#define OXYGEN_MCU_2WIRE_SYNC_ENABLE 0x80
#define OXYGEN_PLAY_ROUTING 0xc0
#define OXYGEN_PLAY_MUTE01 0x0001
#define OXYGEN_PLAY_MUTE23 0x0002
#define OXYGEN_PLAY_MUTE45 0x0004
#define OXYGEN_PLAY_MUTE67 0x0008
#define OXYGEN_PLAY_MULTICH_MASK 0x0010
#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000
#define OXYGEN_PLAY_MULTICH_AC97 0x0010
#define OXYGEN_PLAY_SPDIF_MASK 0x00e0
#define OXYGEN_PLAY_SPDIF_SPDIF 0x0000
#define OXYGEN_PLAY_SPDIF_MULTICH_01 0x0020
#define OXYGEN_PLAY_SPDIF_MULTICH_23 0x0040
#define OXYGEN_PLAY_SPDIF_MULTICH_45 0x0060
#define OXYGEN_PLAY_SPDIF_MULTICH_67 0x0080
#define OXYGEN_PLAY_SPDIF_REC_A 0x00a0
#define OXYGEN_PLAY_SPDIF_REC_B 0x00c0
#define OXYGEN_PLAY_SPDIF_I2S_ADC_3 0x00e0
#define OXYGEN_PLAY_DAC0_SOURCE_MASK 0x0300
#define OXYGEN_PLAY_DAC0_SOURCE_SHIFT 8
#define OXYGEN_PLAY_DAC1_SOURCE_MASK 0x0c00
#define OXYGEN_PLAY_DAC1_SOURCE_SHIFT 10
#define OXYGEN_PLAY_DAC2_SOURCE_MASK 0x3000
#define OXYGEN_PLAY_DAC2_SOURCE_SHIFT 12
#define OXYGEN_PLAY_DAC3_SOURCE_MASK 0xc000
#define OXYGEN_PLAY_DAC3_SOURCE_SHIFT 14
#define OXYGEN_REC_ROUTING 0xc2
#define OXYGEN_MUTE_I2S_ADC_1 0x01
#define OXYGEN_MUTE_I2S_ADC_2 0x02
#define OXYGEN_MUTE_I2S_ADC_3 0x04
#define OXYGEN_REC_A_ROUTE_MASK 0x08
#define OXYGEN_REC_A_ROUTE_I2S_ADC_1 0x00
#define OXYGEN_REC_A_ROUTE_AC97_0 0x08
#define OXYGEN_REC_B_ROUTE_MASK 0x10
#define OXYGEN_REC_B_ROUTE_I2S_ADC_2 0x00
#define OXYGEN_REC_B_ROUTE_AC97_1 0x10
#define OXYGEN_REC_C_ROUTE_MASK 0x20
#define OXYGEN_REC_C_ROUTE_SPDIF 0x00
#define OXYGEN_REC_C_ROUTE_I2S_ADC_3 0x20
#define OXYGEN_ADC_MONITOR 0xc3
#define OXYGEN_ADC_MONITOR_A 0x01
#define OXYGEN_ADC_MONITOR_A_HALF_VOL 0x02
#define OXYGEN_ADC_MONITOR_B 0x04
#define OXYGEN_ADC_MONITOR_B_HALF_VOL 0x08
#define OXYGEN_ADC_MONITOR_C 0x10
#define OXYGEN_ADC_MONITOR_C_HALF_VOL 0x20
#define OXYGEN_A_MONITOR_ROUTING 0xc4
#define OXYGEN_A_MONITOR_ROUTE_0_MASK 0x03
#define OXYGEN_A_MONITOR_ROUTE_0_SHIFT 0
#define OXYGEN_A_MONITOR_ROUTE_1_MASK 0x0c
#define OXYGEN_A_MONITOR_ROUTE_1_SHIFT 2
#define OXYGEN_A_MONITOR_ROUTE_2_MASK 0x30
#define OXYGEN_A_MONITOR_ROUTE_2_SHIFT 4
#define OXYGEN_A_MONITOR_ROUTE_3_MASK 0xc0
#define OXYGEN_A_MONITOR_ROUTE_3_SHIFT 6
#define OXYGEN_AC97_CONTROL 0xd0
#define OXYGEN_AC97_COLD_RESET 0x0001
#define OXYGEN_AC97_SUSPENDED 0x0002 /* read */
#define OXYGEN_AC97_RESUME 0x0002 /* write */
#define OXYGEN_AC97_CLOCK_DISABLE 0x0004
#define OXYGEN_AC97_NO_CODEC_0 0x0008
#define OXYGEN_AC97_CODEC_0 0x0010
#define OXYGEN_AC97_CODEC_1 0x0020
#define OXYGEN_AC97_INTERRUPT_MASK 0xd2
#define OXYGEN_AC97_INT_READ_DONE 0x01
#define OXYGEN_AC97_INT_WRITE_DONE 0x02
#define OXYGEN_AC97_INT_CODEC_0 0x10
#define OXYGEN_AC97_INT_CODEC_1 0x20
#define OXYGEN_AC97_INTERRUPT_STATUS 0xd3
/* OXYGEN_AC97_INT_* */
#define OXYGEN_AC97_OUT_CONFIG 0xd4
#define OXYGEN_AC97_CODEC1_SLOT3 0x00000001
#define OXYGEN_AC97_CODEC1_SLOT3_VSR 0x00000002
#define OXYGEN_AC97_CODEC1_SLOT4 0x00000010
#define OXYGEN_AC97_CODEC1_SLOT4_VSR 0x00000020
#define OXYGEN_AC97_CODEC0_FRONTL 0x00000100
#define OXYGEN_AC97_CODEC0_FRONTR 0x00000200
#define OXYGEN_AC97_CODEC0_SIDEL 0x00000400
#define OXYGEN_AC97_CODEC0_SIDER 0x00000800
#define OXYGEN_AC97_CODEC0_CENTER 0x00001000
#define OXYGEN_AC97_CODEC0_BASE 0x00002000
#define OXYGEN_AC97_CODEC0_REARL 0x00004000
#define OXYGEN_AC97_CODEC0_REARR 0x00008000
#define OXYGEN_AC97_IN_CONFIG 0xd8
#define OXYGEN_AC97_CODEC1_LINEL 0x00000001
#define OXYGEN_AC97_CODEC1_LINEL_VSR 0x00000002
#define OXYGEN_AC97_CODEC1_LINEL_16 0x00000000
#define OXYGEN_AC97_CODEC1_LINEL_18 0x00000004
#define OXYGEN_AC97_CODEC1_LINEL_20 0x00000008
#define OXYGEN_AC97_CODEC1_LINER 0x00000010
#define OXYGEN_AC97_CODEC1_LINER_VSR 0x00000020
#define OXYGEN_AC97_CODEC1_LINER_16 0x00000000
#define OXYGEN_AC97_CODEC1_LINER_18 0x00000040
#define OXYGEN_AC97_CODEC1_LINER_20 0x00000080
#define OXYGEN_AC97_CODEC0_LINEL 0x00000100
#define OXYGEN_AC97_CODEC0_LINER 0x00000200
#define OXYGEN_AC97_REGS 0xdc
#define OXYGEN_AC97_REG_DATA_MASK 0x0000ffff
#define OXYGEN_AC97_REG_ADDR_MASK 0x007f0000
#define OXYGEN_AC97_REG_ADDR_SHIFT 16
#define OXYGEN_AC97_REG_DIR_MASK 0x00800000
#define OXYGEN_AC97_REG_DIR_WRITE 0x00000000
#define OXYGEN_AC97_REG_DIR_READ 0x00800000
#define OXYGEN_AC97_REG_CODEC_MASK 0x01000000
#define OXYGEN_AC97_REG_CODEC_SHIFT 24
#define OXYGEN_TEST 0xe0
#define OXYGEN_TEST_RAM_SUCCEEDED 0x01
#define OXYGEN_TEST_PLAYBACK_RAM 0x02
#define OXYGEN_TEST_RECORD_RAM 0x04
#define OXYGEN_TEST_PLL 0x08
#define OXYGEN_TEST_2WIRE_LOOPBACK 0x10
#define OXYGEN_DMA_FLUSH 0xe1
/* OXYGEN_CHANNEL_* */
#define OXYGEN_CODEC_VERSION 0xe4
#define OXYGEN_XCID_MASK 0x07
#define OXYGEN_REVISION 0xe6
#define OXYGEN_REVISION_XPKGID_MASK 0x0007
#define OXYGEN_REVISION_MASK 0xfff8
#define OXYGEN_REVISION_2 0x0008 /* bit flag */
#define OXYGEN_REVISION_8787 0x0014 /* 8 bits */
#define OXYGEN_OFFSIN_48K 0xe8
#define OXYGEN_OFFSBASE_48K 0xe9
#define OXYGEN_OFFSBASE_MASK 0x0fff
#define OXYGEN_OFFSIN_44K 0xec
#define OXYGEN_OFFSBASE_44K 0xed
#endif

View File

@@ -0,0 +1,58 @@
#ifndef PCM1796_H_INCLUDED
#define PCM1796_H_INCLUDED
/* register 16 */
#define PCM1796_ATL_MASK 0xff
/* register 17 */
#define PCM1796_ATR_MASK 0xff
/* register 18 */
#define PCM1796_MUTE 0x01
#define PCM1796_DME 0x02
#define PCM1796_DMF_MASK 0x0c
#define PCM1796_DMF_DISABLED 0x00
#define PCM1796_DMF_48 0x04
#define PCM1796_DMF_441 0x08
#define PCM1796_DMF_32 0x0c
#define PCM1796_FMT_MASK 0x70
#define PCM1796_FMT_16_RJUST 0x00
#define PCM1796_FMT_20_RJUST 0x10
#define PCM1796_FMT_24_RJUST 0x20
#define PCM1796_FMT_24_LJUST 0x30
#define PCM1796_FMT_16_I2S 0x40
#define PCM1796_FMT_24_I2S 0x50
#define PCM1796_ATLD 0x80
/* register 19 */
#define PCM1796_INZD 0x01
#define PCM1796_FLT_MASK 0x02
#define PCM1796_FLT_SHARP 0x00
#define PCM1796_FLT_SLOW 0x02
#define PCM1796_DFMS 0x04
#define PCM1796_OPE 0x10
#define PCM1796_ATS_MASK 0x60
#define PCM1796_ATS_1 0x00
#define PCM1796_ATS_2 0x20
#define PCM1796_ATS_4 0x40
#define PCM1796_ATS_8 0x60
#define PCM1796_REV 0x80
/* register 20 */
#define PCM1796_OS_MASK 0x03
#define PCM1796_OS_64 0x00
#define PCM1796_OS_32 0x01
#define PCM1796_OS_128 0x02
#define PCM1796_CHSL_MASK 0x04
#define PCM1796_CHSL_LEFT 0x00
#define PCM1796_CHSL_RIGHT 0x04
#define PCM1796_MONO 0x08
#define PCM1796_DFTH 0x10
#define PCM1796_DSD 0x20
#define PCM1796_SRST 0x40
/* register 21 */
#define PCM1796_PCMZ 0x01
#define PCM1796_DZ_MASK 0x06
/* register 22 */
#define PCM1796_ZFGL 0x01
#define PCM1796_ZFGR 0x02
/* register 23 */
#define PCM1796_ID_MASK 0x1f
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,45 @@
#ifndef WM8785_H_INCLUDED
#define WM8785_H_INCLUDED
#define WM8785_R0 0
#define WM8785_R1 1
#define WM8785_R2 2
#define WM8785_R7 7
/* R0 */
#define WM8785_MCR_MASK 0x007
#define WM8785_MCR_SLAVE 0x000
#define WM8785_MCR_MASTER_128 0x001
#define WM8785_MCR_MASTER_192 0x002
#define WM8785_MCR_MASTER_256 0x003
#define WM8785_MCR_MASTER_384 0x004
#define WM8785_MCR_MASTER_512 0x005
#define WM8785_MCR_MASTER_768 0x006
#define WM8785_OSR_MASK 0x018
#define WM8785_OSR_SINGLE 0x000
#define WM8785_OSR_DOUBLE 0x008
#define WM8785_OSR_QUAD 0x010
#define WM8785_FORMAT_MASK 0x060
#define WM8785_FORMAT_RJUST 0x000
#define WM8785_FORMAT_LJUST 0x020
#define WM8785_FORMAT_I2S 0x040
#define WM8785_FORMAT_DSP 0x060
/* R1 */
#define WM8785_WL_MASK 0x003
#define WM8785_WL_16 0x000
#define WM8785_WL_20 0x001
#define WM8785_WL_24 0x002
#define WM8785_WL_32 0x003
#define WM8785_LRP 0x004
#define WM8785_BCLKINV 0x008
#define WM8785_LRSWAP 0x010
#define WM8785_DEVNO_MASK 0x0e0
/* R2 */
#define WM8785_HPFR 0x001
#define WM8785_HPFL 0x002
#define WM8785_SDODIS 0x004
#define WM8785_PWRDNR 0x008
#define WM8785_PWRDNL 0x010
#define WM8785_TDM_MASK 0x1c0
#endif