📄 fm801.c
字号:
};static void snd_fm801_mixer_free_ac97_bus(struct snd_ac97_bus *bus){ struct fm801 *chip = bus->private_data; chip->ac97_bus = NULL;}static void snd_fm801_mixer_free_ac97(struct snd_ac97 *ac97){ struct fm801 *chip = ac97->private_data; if (ac97->num == 0) { chip->ac97 = NULL; } else { chip->ac97_sec = NULL; }}static int __devinit snd_fm801_mixer(struct fm801 *chip){ struct snd_ac97_template ac97; unsigned int i; int err; static struct snd_ac97_bus_ops ops = { .write = snd_fm801_codec_write, .read = snd_fm801_codec_read, }; if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0) return err; chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.private_free = snd_fm801_mixer_free_ac97; if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) return err; if (chip->secondary) { ac97.num = 1; ac97.addr = chip->secondary_addr; if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0) return err; } for (i = 0; i < FM801_CONTROLS; i++) snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip)); if (chip->multichannel) { for (i = 0; i < FM801_CONTROLS_MULTI; i++) snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip)); } return 0;}/* * initialization routines */static int wait_for_codec(struct fm801 *chip, unsigned int codec_id, unsigned short reg, unsigned long waits){ unsigned long timeout = jiffies + waits; outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg, FM801_REG(chip, AC97_CMD)); udelay(5); do { if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY)) == FM801_AC97_VALID) return 0; schedule_timeout_uninterruptible(1); } while (time_after(timeout, jiffies)); return -EIO;}static int snd_fm801_chip_init(struct fm801 *chip, int resume){ int id; unsigned short cmdw; if (chip->tea575x_tuner & 0x0010) goto __ac97_ok; /* codec cold reset + AC'97 warm reset */ outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL)); inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */ udelay(100); outw(0, FM801_REG(chip, CODEC_CTRL)); if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) { snd_printk(KERN_ERR "Primary AC'97 codec not found\n"); if (! resume) return -EIO; } if (chip->multichannel) { if (chip->secondary_addr) { wait_for_codec(chip, chip->secondary_addr, AC97_VENDOR_ID1, msecs_to_jiffies(50)); } else { /* my card has the secondary codec */ /* at address #3, so the loop is inverted */ for (id = 3; id > 0; id--) { if (! wait_for_codec(chip, id, AC97_VENDOR_ID1, msecs_to_jiffies(50))) { cmdw = inw(FM801_REG(chip, AC97_DATA)); if (cmdw != 0xffff && cmdw != 0) { chip->secondary = 1; chip->secondary_addr = id; break; } } } } /* the recovery phase, it seems that probing for non-existing codec might */ /* cause timeout problems */ wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750)); } __ac97_ok: /* init volume */ outw(0x0808, FM801_REG(chip, PCM_VOL)); outw(0x9f1f, FM801_REG(chip, FM_VOL)); outw(0x8808, FM801_REG(chip, I2S_VOL)); /* I2S control - I2S mode */ outw(0x0003, FM801_REG(chip, I2S_MODE)); /* interrupt setup */ cmdw = inw(FM801_REG(chip, IRQ_MASK)); if (chip->irq < 0) cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */ else cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */ outw(cmdw, FM801_REG(chip, IRQ_MASK)); /* interrupt clear */ outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); return 0;}static int snd_fm801_free(struct fm801 *chip){ unsigned short cmdw; if (chip->irq < 0) goto __end_hw; /* interrupt setup - mask everything */ cmdw = inw(FM801_REG(chip, IRQ_MASK)); cmdw |= 0x00c3; outw(cmdw, FM801_REG(chip, IRQ_MASK)); __end_hw:#ifdef TEA575X_RADIO snd_tea575x_exit(&chip->tea);#endif if (chip->irq >= 0) free_irq(chip->irq, chip); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); return 0;}static int snd_fm801_dev_free(struct snd_device *device){ struct fm801 *chip = device->device_data; return snd_fm801_free(chip);}static int __devinit snd_fm801_create(struct snd_card *card, struct pci_dev * pci, int tea575x_tuner, struct fm801 ** rchip){ struct fm801 *chip; int err; static struct snd_device_ops ops = { .dev_free = snd_fm801_dev_free, }; *rchip = NULL; if ((err = pci_enable_device(pci)) < 0) return err; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) { pci_disable_device(pci); return -ENOMEM; } spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; chip->irq = -1; chip->tea575x_tuner = tea575x_tuner; if ((err = pci_request_regions(pci, "FM801")) < 0) { kfree(chip); pci_disable_device(pci); return err; } chip->port = pci_resource_start(pci, 0); if ((tea575x_tuner & 0x0010) == 0) { if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED, "FM801", chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq); snd_fm801_free(chip); return -EBUSY; } chip->irq = pci->irq; pci_set_master(pci); } if (pci->revision >= 0xb1) /* FM801-AU */ chip->multichannel = 1; snd_fm801_chip_init(chip, 0); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_fm801_free(chip); return err; } snd_card_set_dev(card, &pci->dev);#ifdef TEA575X_RADIO if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) { chip->tea.dev_nr = tea575x_tuner >> 16; chip->tea.card = card; chip->tea.freq_fixup = 10700; chip->tea.private_data = chip; chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1]; snd_tea575x_init(&chip->tea); }#endif *rchip = chip; return 0;}static int __devinit snd_card_fm801_probe(struct pci_dev *pci, const struct pci_device_id *pci_id){ static int dev; struct snd_card *card; struct fm801 *chip; struct snd_opl3 *opl3; int 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; if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) { snd_card_free(card); return err; } card->private_data = chip; strcpy(card->driver, "FM801"); strcpy(card->shortname, "ForteMedia FM801-"); strcat(card->shortname, chip->multichannel ? "AU" : "AS"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->port, chip->irq); if (tea575x_tuner[dev] & 0x0010) goto __fm801_tuner_only; if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) { snd_card_free(card); return err; } if ((err = snd_fm801_mixer(chip)) < 0) { snd_card_free(card); return err; } if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, FM801_REG(chip, MPU401_DATA), MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi)) < 0) { snd_card_free(card); return err; } if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0), FM801_REG(chip, OPL3_BANK1), OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) { snd_card_free(card); return err; } if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { snd_card_free(card); return err; } __fm801_tuner_only: 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_card_fm801_remove(struct pci_dev *pci){ snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL);}#ifdef CONFIG_PMstatic unsigned char saved_regs[] = { FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC, FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2, FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2, FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL,};static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state){ struct snd_card *card = pci_get_drvdata(pci); struct fm801 *chip = card->private_data; int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); snd_ac97_suspend(chip->ac97_sec); for (i = 0; i < ARRAY_SIZE(saved_regs); i++) chip->saved_regs[i] = inw(chip->port + saved_regs[i]); /* FIXME: tea575x suspend */ pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, pci_choose_state(pci, state)); return 0;}static int snd_fm801_resume(struct pci_dev *pci){ struct snd_card *card = pci_get_drvdata(pci); struct fm801 *chip = card->private_data; int i; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) { printk(KERN_ERR "fm801: pci_enable_device failed, " "disabling device\n"); snd_card_disconnect(card); return -EIO; } pci_set_master(pci); snd_fm801_chip_init(chip, 1); snd_ac97_resume(chip->ac97); snd_ac97_resume(chip->ac97_sec); for (i = 0; i < ARRAY_SIZE(saved_regs); i++) outw(chip->saved_regs[i], chip->port + saved_regs[i]); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0;}#endifstatic struct pci_driver driver = { .name = "FM801", .id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, .remove = __devexit_p(snd_card_fm801_remove),#ifdef CONFIG_PM .suspend = snd_fm801_suspend, .resume = snd_fm801_resume,#endif};static int __init alsa_card_fm801_init(void){ return pci_register_driver(&driver);}static void __exit alsa_card_fm801_exit(void){ pci_unregister_driver(&driver);}module_init(alsa_card_fm801_init)module_exit(alsa_card_fm801_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -