📄 es1938.c
字号:
/* * initialize the chip - used by resume callback, too */static void snd_es1938_chip_init(struct es1938 *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));}#ifdef CONFIG_PM/* * PM support */static unsigned char saved_regs[SAVED_REG_SIZE+1] = { 0x14, 0x1a, 0x1c, 0x3a, 0x3c, 0x3e, 0x36, 0x38, 0x50, 0x52, 0x60, 0x61, 0x62, 0x63, 0x64, 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x7c, 0x7d, 0xa8, 0xb4,};static int es1938_suspend(struct pci_dev *pci, pm_message_t state){ struct snd_card *card = pci_get_drvdata(pci); struct es1938 *chip = card->private_data; unsigned char *s, *d; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); /* save mixer-related registers */ for (s = saved_regs, d = chip->saved_regs; *s; s++, d++) *d = snd_es1938_reg_read(chip, *s); outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */ if (chip->irq >= 0) { synchronize_irq(chip->irq); free_irq(chip->irq, chip); chip->irq = -1; } pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, pci_choose_state(pci, state)); return 0;}static int es1938_resume(struct pci_dev *pci){ struct snd_card *card = pci_get_drvdata(pci); struct es1938 *chip = card->private_data; unsigned char *s, *d; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) { printk(KERN_ERR "es1938: pci_enable_device failed, " "disabling device\n"); snd_card_disconnect(card); return -EIO; } if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_SHARED, "ES1938", chip)) { printk(KERN_ERR "es1938: unable to grab IRQ %d, " "disabling device\n", pci->irq); snd_card_disconnect(card); return -EIO; } chip->irq = pci->irq; snd_es1938_chip_init(chip); /* restore mixer-related registers */ for (s = saved_regs, d = chip->saved_regs; *s; s++, d++) { if (*s < 0xa0) snd_es1938_mixer_write(chip, *s, *d); else snd_es1938_write(chip, *s, *d); } snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0;}#endif /* CONFIG_PM */#ifdef SUPPORT_JOYSTICKstatic int __devinit snd_es1938_create_gameport(struct es1938 *chip){ struct gameport *gp; chip->gameport = gp = gameport_allocate_port(); if (!gp) { printk(KERN_ERR "es1938: cannot allocate memory for gameport\n"); return -ENOMEM; } gameport_set_name(gp, "ES1938"); gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); gameport_set_dev_parent(gp, &chip->pci->dev); gp->io = chip->game_port; gameport_register_port(gp); return 0;}static void snd_es1938_free_gameport(struct es1938 *chip){ if (chip->gameport) { gameport_unregister_port(chip->gameport); chip->gameport = NULL; }}#elsestatic inline int snd_es1938_create_gameport(struct es1938 *chip) { return -ENOSYS; }static inline void snd_es1938_free_gameport(struct es1938 *chip) { }#endif /* SUPPORT_JOYSTICK */static int snd_es1938_free(struct es1938 *chip){ /* disable irqs */ outb(0x00, SLIO_REG(chip, IRQCONTROL)); if (chip->rmidi) snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); snd_es1938_free_gameport(chip); if (chip->irq >= 0) { synchronize_irq(chip->irq); free_irq(chip->irq, chip); } pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); return 0;}static int snd_es1938_dev_free(struct snd_device *device){ struct es1938 *chip = device->device_data; return snd_es1938_free(chip);}static int __devinit snd_es1938_create(struct snd_card *card, struct pci_dev * pci, struct es1938 ** rchip){ struct es1938 *chip; int err; static struct snd_device_ops 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, DMA_24BIT_MASK) < 0 || pci_set_consistent_dma_mask(pci, DMA_24BIT_MASK) < 0) { snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n"); pci_disable_device(pci); return -ENXIO; } chip = kzalloc(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; chip->irq = -1; 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, IRQF_SHARED, "ES1938", chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_es1938_free(chip); return -EBUSY; } chip->irq = pci->irq;#ifdef ES1938_DDEBUG snd_printk(KERN_DEBUG "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);#endif chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */ snd_es1938_chip_init(chip); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_es1938_free(chip); return err; } snd_card_set_dev(card, &pci->dev); *rchip = chip; return 0;}/* -------------------------------------------------------------------- * Interrupt handler * -------------------------------------------------------------------- */static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id){ struct es1938 *chip = dev_id; unsigned char status, audiostatus; int handled = 0; status = inb(SLIO_REG(chip, IRQCONTROL));#if 0 printk("Es1938debug - interrupt status: =0x%x\n", status);#endif /* AUDIO 1 */ if (status & 0x10) {#if 0 printk("Es1938debug - AUDIO channel 1 interrupt\n"); printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n", inw(SLDM_REG(chip, DMACOUNT))); printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n", inl(SLDM_REG(chip, DMAADDR))); printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n", inl(SLDM_REG(chip, DMASTATUS)));#endif /* clear irq */ handled = 1; audiostatus = inb(SLSB_REG(chip, STATUS)); if (chip->active & ADC1) snd_pcm_period_elapsed(chip->capture_substream); else if (chip->active & DAC1) snd_pcm_period_elapsed(chip->playback2_substream); } /* AUDIO 2 */ if (status & 0x20) {#if 0 printk("Es1938debug - AUDIO channel 2 interrupt\n"); printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n", inw(SLIO_REG(chip, AUDIO2DMACOUNT))); printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n", inl(SLIO_REG(chip, AUDIO2DMAADDR)));#endif /* clear irq */ handled = 1; snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0); if (chip->active & DAC2) snd_pcm_period_elapsed(chip->playback1_substream); } /* Hardware volume */ if (status & 0x40) { int split = snd_es1938_mixer_read(chip, 0x64) & 0x80; handled = 1; snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id); if (!split) { snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); } /* ack interrupt */ snd_es1938_mixer_write(chip, 0x66, 0x00); } /* MPU401 */ if (status & 0x80) { // the following line is evil! It switches off MIDI interrupt handling after the first interrupt received. // replacing the last 0 by 0x40 works for ESS-Solo1, but just doing nothing works as well! // andreas@flying-snail.de // snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); /* ack? */ if (chip->rmidi) { handled = 1; snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); } } return IRQ_RETVAL(handled);}#define ES1938_DMA_SIZE 64static int __devinit snd_es1938_mixer(struct es1938 *chip){ struct snd_card *card; unsigned int idx; int err; card = chip->card; strcpy(card->mixername, "ESS Solo-1"); for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) { struct snd_kcontrol *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; } return 0;} static int __devinit snd_es1938_probe(struct pci_dev *pci, const struct pci_device_id *pci_id){ static int dev; struct snd_card *card; struct es1938 *chip; struct snd_opl3 *opl3; int idx, err; if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { dev++; return -ENOENT; } card = snd_card_new(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; } card->private_data = chip; 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 (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; } } if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, chip->mpu_port, MPU401_INFO_INTEGRATED, 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); } snd_es1938_create_gameport(chip); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } pci_set_drvdata(pci, card); dev++; return 0;}static void __devexit snd_es1938_remove(struct pci_dev *pci){ snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL);}static struct pci_driver driver = { .name = "ESS ES1938 (Solo-1)", .id_table = snd_es1938_ids, .probe = snd_es1938_probe, .remove = __devexit_p(snd_es1938_remove),#ifdef CONFIG_PM .suspend = es1938_suspend, .resume = es1938_resume,#endif};static int __init alsa_card_es1938_init(void){ return pci_register_driver(&driver);}static void __exit alsa_card_es1938_exit(void){ pci_unregister_driver(&driver);}module_init(alsa_card_es1938_init)module_exit(alsa_card_es1938_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -