⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fm801.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
		.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 + -