📄 fm801.c
字号:
.write = snd_fm801_tea575x_64pcr_write, .read = snd_fm801_tea575x_64pcr_read, }};#endif/* * Mixer routines */#define FM801_SINGLE(xname, reg, shift, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \ .get = snd_fm801_get_single, .put = snd_fm801_put_single, \ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }static int snd_fm801_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ int mask = (kcontrol->private_value >> 16) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0;}static int snd_fm801_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ fm801_t *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; return 0;}static int snd_fm801_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ fm801_t *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; unsigned short val; val = (ucontrol->value.integer.value[0] & mask); if (invert) val = mask - val; return snd_fm801_update_bits(chip, reg, mask << shift, val << shift);}#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }static int snd_fm801_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ int mask = (kcontrol->private_value >> 16) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0;}static int snd_fm801_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ fm801_t *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift_left = (kcontrol->private_value >> 8) & 0x0f; int shift_right = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; spin_lock_irq(&chip->reg_lock); ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask; ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask; spin_unlock_irq(&chip->reg_lock); if (invert) { ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; } return 0;}static int snd_fm801_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ fm801_t *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift_left = (kcontrol->private_value >> 8) & 0x0f; int shift_right = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; unsigned short val1, val2; val1 = ucontrol->value.integer.value[0] & mask; val2 = ucontrol->value.integer.value[1] & mask; if (invert) { val1 = mask - val1; val2 = mask - val2; } return snd_fm801_update_bits(chip, reg, (mask << shift_left) | (mask << shift_right), (val1 << shift_left ) | (val2 << shift_right));}static int snd_fm801_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ static char *texts[5] = { "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 5; if (uinfo->value.enumerated.item > 4) uinfo->value.enumerated.item = 4; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_fm801_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ fm801_t *chip = snd_kcontrol_chip(kcontrol); unsigned short val; val = inw(FM801_REG(chip, REC_SRC)) & 7; if (val > 4) val = 4; ucontrol->value.enumerated.item[0] = val; return 0;}static int snd_fm801_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ fm801_t *chip = snd_kcontrol_chip(kcontrol); unsigned short val; if ((val = ucontrol->value.enumerated.item[0]) > 4) return -EINVAL; return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);}#define FM801_CONTROLS (sizeof(snd_fm801_controls)/sizeof(snd_kcontrol_new_t))static snd_kcontrol_new_t snd_fm801_controls[] __devinitdata = {FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1),FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1),FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1),FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Capture Source", .info = snd_fm801_info_mux, .get = snd_fm801_get_mux, .put = snd_fm801_put_mux,}};#define FM801_CONTROLS_MULTI (sizeof(snd_fm801_controls_multi)/sizeof(snd_kcontrol_new_t))static snd_kcontrol_new_t snd_fm801_controls_multi[] __devinitdata = {FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0),FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0),FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0),FM801_SINGLE("IEC958 Playback Switch", FM801_GEN_CTRL, 2, 1, 0),};static void snd_fm801_mixer_free_ac97_bus(ac97_bus_t *bus){ fm801_t *chip = bus->private_data; chip->ac97_bus = NULL;}static void snd_fm801_mixer_free_ac97(ac97_t *ac97){ fm801_t *chip = ac97->private_data; if (ac97->num == 0) { chip->ac97 = NULL; } else { chip->ac97_sec = NULL; }}static int __devinit snd_fm801_mixer(fm801_t *chip){ ac97_template_t ac97; unsigned int i; int err; static ac97_bus_ops_t 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 snd_fm801_free(fm801_t *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, (void *)chip); pci_release_regions(chip->pci); kfree(chip); return 0;}static int snd_fm801_dev_free(snd_device_t *device){ fm801_t *chip = device->device_data; return snd_fm801_free(chip);}static int __devinit snd_fm801_create(snd_card_t * card, struct pci_dev * pci, int tea575x_tuner, fm801_t ** rchip){ fm801_t *chip; unsigned char rev, id; unsigned short cmdw; unsigned long timeout; int err; static snd_device_ops_t ops = { .dev_free = snd_fm801_dev_free, }; *rchip = NULL; if ((err = pci_enable_device(pci)) < 0) return err; chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; chip->irq = -1; if ((err = pci_request_regions(pci, "FM801")) < 0) { kfree(chip); return err; } chip->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, "FM801", (void *)chip)) { snd_printk("unable to grab IRQ %d\n", chip->irq); snd_fm801_free(chip); return -EBUSY; } chip->irq = pci->irq; pci_set_master(pci); pci_read_config_byte(pci, PCI_REVISION_ID, &rev); if (rev >= 0xb1) /* FM801-AU */ chip->multichannel = 1; /* 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)); timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); udelay(5); do { if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) goto __ac97_secondary; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } while (time_after(timeout, jiffies)); snd_printk("Primary AC'97 codec not found\n"); snd_fm801_free(chip); return -EIO; __ac97_secondary: if (!chip->multichannel) /* lookup is not required */ goto __ac97_ok; for (id = 3; id > 0; id--) { /* my card has the secondary codec */ /* at address #3, so the loop is inverted */ timeout = jiffies + HZ / 20; outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1, FM801_REG(chip, AC97_CMD)); udelay(5); do { if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) { cmdw = inw(FM801_REG(chip, AC97_DATA)); if (cmdw != 0xffff && cmdw != 0) { chip->secondary = 1; chip->secondary_addr = id; goto __ac97_ok; } } set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } while (time_after(timeout, jiffies)); } /* the recovery phase, it seems that probing for non-existing codec might */ /* cause timeout problems */ timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); udelay(5); do { if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) goto __ac97_ok; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } while (time_after(timeout, jiffies)); snd_printk("Primary AC'97 codec not responding\n"); snd_fm801_free(chip); return -EIO; __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 - unmask MPU, PLAYBACK & CAPTURE */ cmdw = inw(FM801_REG(chip, IRQ_MASK)); cmdw &= ~0x0083; outw(cmdw, FM801_REG(chip, IRQ_MASK)); /* interrupt clear */ outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); 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 & 0xffff) < 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 & 0xffff) - 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; snd_card_t *card; fm801_t *chip; opl3_t *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; } 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 ((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), 1, 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; } 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);}static struct pci_driver driver = { .name = "FM801", .id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, .remove = __devexit_p(snd_card_fm801_remove),};static int __init alsa_card_fm801_init(void){ return pci_module_init(&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 + -