📄 es1938.cpp
字号:
{
es1938_t *chip = (es1938_t *) device->device_data;
return snd_es1938_free(chip);
}
/* -----------------------------------------------------------------
* Write command to Controller Registers
* -----------------------------------------------------------------*/
static void snd_es1938_write_cmd(es1938_t *chip, unsigned char cmd)
{
int i;
unsigned char v;
for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) {
if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) {
outb(cmd, SLSB_REG(chip, WRITEDATA));
return;
}
}
printk("snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
}
/* -----------------------------------------------------------------
* Read the Read Data Buffer
* -----------------------------------------------------------------*/
static int snd_es1938_get_byte(es1938_t *chip)
{
int i;
unsigned char v;
for (i = GET_LOOP_TIMEOUT; i; i--)
if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80)
return inb(SLSB_REG(chip, READDATA));
snd_printk("get_byte timeout: status 0x02%x\n", v);
return -ENODEV;
}
/* -----------------------------------------------------------------
* read value cmd register
* -----------------------------------------------------------------*/
static unsigned char snd_es1938_read(es1938_t *chip, unsigned char reg)
{
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1938_write_cmd(chip, ESS_CMD_READREG);
snd_es1938_write_cmd(chip, reg);
val = snd_es1938_get_byte(chip);
spin_unlock_irqrestore(&chip->reg_lock, flags);
#ifdef REG_DEBUG
snd_printk("Reg %02x now is %02x\n", reg, val);
#endif
return val;
}
/* -----------------------------------------------------------------
* Write value cmd register
* -----------------------------------------------------------------*/
static void snd_es1938_write(es1938_t *chip, unsigned char reg, unsigned char val)
{
unsigned long flags;
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1938_write_cmd(chip, reg);
snd_es1938_write_cmd(chip, val);
spin_unlock_irqrestore(&chip->reg_lock, flags);
#ifdef REG_DEBUG
snd_printk("Reg %02x set to %02x\n", reg, val);
#endif
}
/* -----------------------------------------------------------------
* Write to a mixer register
* -----------------------------------------------------------------*/
static void snd_es1938_mixer_write(es1938_t *chip, unsigned char reg, unsigned char val)
{
unsigned long flags;
spin_lock_irqsave(&chip->mixer_lock, flags);
outb(reg, SLSB_REG(chip, MIXERADDR));
outb(val, SLSB_REG(chip, MIXERDATA));
spin_unlock_irqrestore(&chip->mixer_lock, flags);
#ifdef REG_DEBUG
snd_printk_interrupt("Mixer reg %02x set to %02x\n", reg, val);
#endif
}
/* -----------------------------------------------------------------
* Write data to cmd register and return old value
* -----------------------------------------------------------------*/
static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
{
unsigned long flags;
unsigned char old, newval, oval;
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1938_write_cmd(chip, ESS_CMD_READREG);
snd_es1938_write_cmd(chip, reg);
old = snd_es1938_get_byte(chip);
oval = old & mask;
if (val != oval) {
snd_es1938_write_cmd(chip, reg);
newval = (old & ~mask) | (val & mask);
snd_es1938_write_cmd(chip, newval);
#ifdef REG_DEBUG
snd_printk("Reg %02x was %02x, set to %02x\n", reg, old, newval);
#endif
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
return oval;
}
static void snd_es1938_reset(es1938_t *chip)
{
int i;
outb(3, SLSB_REG(chip, RESET));
inb(SLSB_REG(chip, RESET));
outb(0, SLSB_REG(chip, RESET));
for (i = 0; i < RESET_LOOP_TIMEOUT; i++) {
if (inb(SLSB_REG(chip, STATUS)) & 0x80) {
if (inb(SLSB_REG(chip, READDATA)) == 0xaa)
goto __next;
}
}
snd_printk("ESS Solo-1 reset failed\n");
__next:
snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT);
/* Demand transfer DMA: 4 bytes per DMA request */
snd_es1938_write(chip, ESS_CMD_DMATYPE, 2);
/* Change behaviour of register A1
4x oversampling
2nd channel DAC asynchronous */
snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32);
/* enable/select DMA channel and IRQ channel */
snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50);
snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50);
snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1);
/* Set spatializer parameters to recommended values */
snd_es1938_mixer_write(chip, 0x54, 0x8f);
snd_es1938_mixer_write(chip, 0x56, 0x95);
snd_es1938_mixer_write(chip, 0x58, 0x94);
snd_es1938_mixer_write(chip, 0x5a, 0x80);
}
/*
* initialize the chip - used by resume callback, too
*/
static void snd_es1938_chip_init(es1938_t *chip)
{
/* reset chip */
snd_es1938_reset(chip);
/* configure native mode */
/* enable bus master */
pci_set_master(chip->pci);
/* disable legacy audio */
pci_write_config_word(chip->pci, SL_PCI_LEGACYCONTROL, 0x805f);
/* set DDMA base */
pci_write_config_word(chip->pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1);
/* set DMA/IRQ policy */
pci_write_config_dword(chip->pci, SL_PCI_CONFIG, 0);
/* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/
outb(0xf0, SLIO_REG(chip, IRQCONTROL));
/* reset DMA */
outb(0, SLDM_REG(chip, DMACLEAR));
}
static snd_device_ops_t dev_ops;
static int __devinit snd_es1938_create(snd_card_t * card,
struct pci_dev * pci,
es1938_t ** rchip)
{
es1938_t *chip;
int err;
dev_ops.dev_free = snd_es1938_dev_free;
*rchip = NULL;
/* enable PCI device */
if ((err = pci_enable_device(pci)) < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 24 bits */
if (pci_set_dma_mask(pci, 0x00ffffff) < 0 ||
pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) {
snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
pci_disable_device(pci);
return -ENXIO;
}
chip = (es1938_t *)kcalloc(1, sizeof(*chip), GFP_KERNEL);
if (chip == NULL) {
pci_disable_device(pci);
return -ENOMEM;
}
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
chip->card = card;
chip->pci = pci;
pGlobChip = chip;
if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
chip->io_port = pci_resource_start(pci, 0);
chip->sb_port = pci_resource_start(pci, 1);
chip->vc_port = pci_resource_start(pci, 2);
chip->mpu_port = pci_resource_start(pci, 3);
chip->game_port = pci_resource_start(pci, 4);
if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) {
snd_printk("unable to grab IRQ %d\n", pci->irq);
snd_es1938_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port);
chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */
snd_es1938_chip_init(chip);
snd_card_set_pm_callback(card, es1938_suspend, es1938_resume, chip);
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &dev_ops)) < 0) {
snd_es1938_free(chip);
return err;
}
snd_card_set_dev(card, &pci->dev);
*rchip = chip;
return 0;
}
/* -----------------------------------------------------------------
* Read from a mixer register
* -----------------------------------------------------------------*/
static int snd_es1938_mixer_read(es1938_t *chip, unsigned char reg)
{
int data;
unsigned long flags;
spin_lock_irqsave(&chip->mixer_lock, flags);
outb(reg, SLSB_REG(chip, MIXERADDR));
data = inb(SLSB_REG(chip, MIXERDATA));
spin_unlock_irqrestore(&chip->mixer_lock, flags);
#ifdef REG_DEBUG
snd_printk("Mixer reg %02x now is %02x\n", reg, data);
#endif
return data;
}
static int __devinit snd_es1938_mixer(es1938_t *chip)
{
snd_card_t *card;
unsigned int idx;
int err;
card = chip->card;
strcpy(card->mixername, "ESS Solo-1");
#if VXWORKS_UNUSED
for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) {
snd_kcontrol_t *kctl;
kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip);
switch (idx) {
case 0:
chip->master_volume = kctl;
kctl->private_free = snd_es1938_hwv_free;
break;
case 1:
chip->master_switch = kctl;
kctl->private_free = snd_es1938_hwv_free;
break;
case 2:
chip->hw_volume = kctl;
kctl->private_free = snd_es1938_hwv_free;
break;
case 3:
chip->hw_switch = kctl;
kctl->private_free = snd_es1938_hwv_free;
break;
}
if ((err = snd_ctl_add(card, kctl)) < 0)
return err;
}
#endif
return 0;
}
static int __devinit snd_es1938_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev=0;
snd_card_t *card;
es1938_t *chip;
#if ES1938_INCLUDE_OPL_FMSYNTH
opl3_t *opl3;
#endif
int idx, err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
card = snd_card_new(a_index[dev], id[dev], THIS_MODULE, 0);
if (card == NULL)
return -ENOMEM;
for (idx = 0; idx < 5; idx++) {
if (pci_resource_start(pci, idx) == 0 ||
!(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
snd_card_free(card);
return -ENODEV;
}
}
if ((err = snd_es1938_create(card, pci, &chip)) < 0) {
snd_card_free(card);
return err;
}
strcpy(card->driver, "ES1938");
strcpy(card->shortname, "ESS ES1938 (Solo-1)");
sprintf(card->longname, "%s rev %i, irq %i",
card->shortname,
chip->revision,
chip->irq);
if ((err = snd_es1938_new_pcm(chip, 0)) < 0) {
snd_card_free(card);
return err;
}
if ((err = snd_es1938_mixer(chip)) < 0) {
snd_card_free(card);
return err;
}
#if ES1938_INCLUDE_OPL_FMSYNTH
if (snd_opl3_create(card,
SLSB_REG(chip, FMLOWADDR),
SLSB_REG(chip, FMHIGHADDR),
OPL3_HW_OPL3, 1, &opl3) < 0) {
printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n",
SLSB_REG(chip, FMLOWADDR));
} else {
if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
snd_card_free(card);
return err;
}
if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
snd_card_free(card);
return err;
}
}
#endif
#if ES1938_INCLUDE_MP401_UART
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) {
printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
} else {
// this line is vital for MIDI interrupt handling on ess-solo1
// andreas@flying-snail.de
snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0x40);
}
#endif
#if ES1938_INCLUDE_GAMEPORT
snd_es1938_create_gameport(chip);
#endif
/* note: added the variable chip->pcm for use w/ vxworks */
if ((err = snd_card_register(card, chip->pcm )) < 0) {
snd_card_free(card);
return err;
}
pci_set_drvdata(pci, card);
dev++;
return 0;
}
/* sets the playback volume for left and right channels of specified substream
TODO: make a better interface */
static int snd_es1938_set_volume(snd_pcm_substream_t * substream, uint8 value )
{
es1938_t *chip = (es1938_t *)snd_pcm_substream_chip(substream);
switch (substream->number) {
case 0:
snd_es1938_mixer_write(chip,ESSSB_IREG_AUDIO2,value);
return 0;
case 1:
snd_es1938_mixer_write(chip,ESSSB_IREG_AUDIO1,value);
return 0;
}
snd_BUG();
return -EINVAL;
}
static void __devexit snd_es1938_remove(struct pci_dev *pci)
{
snd_card_free((snd_card_t *)pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
char ES1938_Name[] = "ESS ES1938 (Solo-1)";
struct pci_driver ES1938_driver = {
{ NULL }, /* list_head node */
ES1938_Name, /* char *name */
#if VXWORKS_UNUSED
NULL, /* struct module *owner */
#endif
snd_es1938_ids, /* const struct pci_device_id *id_table */
snd_es1938_probe, /* int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); New device inserted */
NULL, /* Device removed (NULL if not a hot-plug capable driver) */
vx_snd_start_driver,
SND_PCI_PM_CALLBACKS
};
int dr_mixer( unsigned char reg )
{
return snd_es1938_mixer_read( pGlobChip, reg );
}
void dw_mixer( unsigned char reg, unsigned char val )
{
snd_es1938_mixer_write( pGlobChip, reg, val );
}
int dr_1938( unsigned char reg )
{
return snd_es1938_read( pGlobChip, reg );
}
void dw_1938( unsigned char reg, unsigned char val )
{
snd_es1938_write( pGlobChip, reg, val );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -