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

📄 ice1712.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *   ALSA driver for ICEnsemble ICE1712 (Envy24) * *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz> * *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU General Public License as published by *   the Free Software Foundation; either version 2 of the License, or *   (at your option) any later version. * *   This program is distributed in the hope that it will be useful, *   but WITHOUT ANY WARRANTY; without even the implied warranty of *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *   GNU General Public License for more details. * *   You should have received a copy of the GNU General Public License *   along with this program; if not, write to the Free Software *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA * */      /*  NOTES:  - spdif nonaudio consumer mode does not work (at least with my    Sony STR-DB830)*//* * Changes: * *  2002.09.09	Takashi Iwai <tiwai@suse.de> *	split the code to several files.  each low-level routine *	is stored in the local file and called from registration *	function from card_info struct. * *  2002.11.26	James Stafford <jstafford@ampltd.com> *	Added support for VT1724 (Envy24HT) *	I have left out support for 176.4 and 192 KHz for the moment.  *  I also haven't done anything with the internal S/PDIF transmitter or the MPU-401 * *  2003.02.20  Taksahi Iwai <tiwai@suse.de> *	Split vt1724 part to an independent driver. *	The GPIO is accessed through the callback functions now. * * 2004.03.31 Doug McLain <nostar@comcast.net> *    Added support for Event Electronics EZ8 card to hoontech.c. */#include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/cs8427.h>#include <sound/info.h>#include <sound/mpu401.h>#include <sound/initval.h>#include <sound/asoundef.h>#include "ice1712.h"/* lowlevel routines */#include "delta.h"#include "ews.h"#include "hoontech.h"MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{"	       HOONTECH_DEVICE_DESC	       DELTA_DEVICE_DESC	       EWS_DEVICE_DESC	       "{ICEnsemble,Generic ICE1712},"	       "{ICEnsemble,Generic Envy24}}");static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;		/* Enable this card */static char *model[SNDRV_CARDS];static int omni[SNDRV_CARDS];	/* Delta44 & 66 Omni I/O support */static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transciever reset timeout value in msec */module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for ICE1712 soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for ICE1712 soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable ICE1712 soundcard.");module_param_array(omni, bool, NULL, 0444);MODULE_PARM_DESC(omni, "Enable Midiman M-Audio Delta Omni I/O support.");module_param_array(cs8427_timeout, int, NULL, 0444);MODULE_PARM_DESC(cs8427_timeout, "Define reset timeout for cs8427 chip in msec resolution.");module_param_array(model, charp, NULL, 0444);MODULE_PARM_DESC(model, "Use the given board model.");static struct pci_device_id snd_ice1712_ids[] = {	{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },   /* ICE1712 */	{ 0, }};MODULE_DEVICE_TABLE(pci, snd_ice1712_ids);static int snd_ice1712_build_pro_mixer(ice1712_t *ice);static int snd_ice1712_build_controls(ice1712_t *ice);static int PRO_RATE_LOCKED;static int PRO_RATE_RESET = 1;static unsigned int PRO_RATE_DEFAULT = 44100;/* *  Basic I/O */ /* check whether the clock mode is spdif-in */static inline int is_spdif_master(ice1712_t *ice){	return (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER) ? 1 : 0;}static inline int is_pro_rate_locked(ice1712_t *ice){	return is_spdif_master(ice) || PRO_RATE_LOCKED;}static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data){	outb((channel << 4) | addr, ICEDS(ice, INDEX));	outl(data, ICEDS(ice, DATA));}static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr){	outb((channel << 4) | addr, ICEDS(ice, INDEX));	return inl(ICEDS(ice, DATA));}static void snd_ice1712_ac97_write(ac97_t *ac97,				   unsigned short reg,				   unsigned short val){	ice1712_t *ice = (ice1712_t *)ac97->private_data;	int tm;	unsigned char old_cmd = 0;	for (tm = 0; tm < 0x10000; tm++) {		old_cmd = inb(ICEREG(ice, AC97_CMD));		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))			continue;		if (!(old_cmd & ICE1712_AC97_READY))			continue;		break;	}	outb(reg, ICEREG(ice, AC97_INDEX));	outw(val, ICEREG(ice, AC97_DATA));	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);	outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD));	for (tm = 0; tm < 0x10000; tm++)		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)			break;}static unsigned short snd_ice1712_ac97_read(ac97_t *ac97,					    unsigned short reg){	ice1712_t *ice = (ice1712_t *)ac97->private_data;	int tm;	unsigned char old_cmd = 0;	for (tm = 0; tm < 0x10000; tm++) {		old_cmd = inb(ICEREG(ice, AC97_CMD));		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))			continue;		if (!(old_cmd & ICE1712_AC97_READY))			continue;		break;	}	outb(reg, ICEREG(ice, AC97_INDEX));	outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD));	for (tm = 0; tm < 0x10000; tm++)		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)			break;	if (tm >= 0x10000)		/* timeout */		return ~0;	return inw(ICEREG(ice, AC97_DATA));}/* * pro ac97 section */static void snd_ice1712_pro_ac97_write(ac97_t *ac97,				       unsigned short reg,				       unsigned short val){	ice1712_t *ice = (ice1712_t *)ac97->private_data;	int tm;	unsigned char old_cmd = 0;	for (tm = 0; tm < 0x10000; tm++) {		old_cmd = inb(ICEMT(ice, AC97_CMD));		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))			continue;		if (!(old_cmd & ICE1712_AC97_READY))			continue;		break;	}	outb(reg, ICEMT(ice, AC97_INDEX));	outw(val, ICEMT(ice, AC97_DATA));	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);	outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD));	for (tm = 0; tm < 0x10000; tm++)		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)			break;}static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97,						unsigned short reg){	ice1712_t *ice = (ice1712_t *)ac97->private_data;	int tm;	unsigned char old_cmd = 0;	for (tm = 0; tm < 0x10000; tm++) {		old_cmd = inb(ICEMT(ice, AC97_CMD));		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))			continue;		if (!(old_cmd & ICE1712_AC97_READY))			continue;		break;	}	outb(reg, ICEMT(ice, AC97_INDEX));	outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD));	for (tm = 0; tm < 0x10000; tm++)		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)			break;	if (tm >= 0x10000)		/* timeout */		return ~0;	return inw(ICEMT(ice, AC97_DATA));}/* * consumer ac97 digital mix */static int snd_ice1712_digmix_route_ac97_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;	uinfo->count = 1;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = 1;	return 0;}static int snd_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){	ice1712_t *ice = snd_kcontrol_chip(kcontrol);		ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;	return 0;}static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){	ice1712_t *ice = snd_kcontrol_chip(kcontrol);	unsigned char val, nval;		spin_lock_irq(&ice->reg_lock);	val = inb(ICEMT(ice, MONITOR_ROUTECTRL));	nval = val & ~ICE1712_ROUTE_AC97;	if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;	outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));	spin_unlock_irq(&ice->reg_lock);	return val != nval;}static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Digital Mixer To AC97",	.info = snd_ice1712_digmix_route_ac97_info,	.get = snd_ice1712_digmix_route_ac97_get,	.put = snd_ice1712_digmix_route_ac97_put,};/* * gpio operations */static void snd_ice1712_set_gpio_dir(ice1712_t *ice, unsigned int data){	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, data);	inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */}static void snd_ice1712_set_gpio_mask(ice1712_t *ice, unsigned int data){	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data);	inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */}static unsigned int snd_ice1712_get_gpio_data(ice1712_t *ice){	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);}static void snd_ice1712_set_gpio_data(ice1712_t *ice, unsigned int val){	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, val);	inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */}/* * * CS8427 interface * *//* * change the input clock selection * spdif_clock = 1 - IEC958 input, 0 - Envy24 */static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock){	unsigned char reg[2] = { 0x80 | 4, 0 };   /* CS8427 auto increment | register number 4 + data */	unsigned char val, nval;	int res = 0;		snd_i2c_lock(ice->i2c);	if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {		snd_i2c_unlock(ice->i2c);		return -EIO;	}	if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) {		snd_i2c_unlock(ice->i2c);		return -EIO;	}	nval = val & 0xf0;	if (spdif_clock)		nval |= 0x01;	else		nval |= 0x04;	if (val != nval) {		reg[1] = nval;		if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) {			res = -EIO;		} else {			res++;		}	}	snd_i2c_unlock(ice->i2c);	return res;}/* * spdif callbacks */static void open_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream){	snd_cs8427_iec958_active(ice->cs8427, 1);}static void close_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream){	snd_cs8427_iec958_active(ice->cs8427, 0);}static void setup_cs8427(ice1712_t *ice, int rate){	snd_cs8427_iec958_pcm(ice->cs8427, rate);}/* * create and initialize callbacks for cs8427 interface */int __devinit snd_ice1712_init_cs8427(ice1712_t *ice, int addr){	int err;	if ((err = snd_cs8427_create(ice->i2c, addr,				     (ice->cs8427_timeout * HZ) / 1000,				     &ice->cs8427)) < 0) {		snd_printk(KERN_ERR "CS8427 initialization failed\n");		return err;	}	ice->spdif.ops.open = open_cs8427;	ice->spdif.ops.close = close_cs8427;	ice->spdif.ops.setup_rate = setup_cs8427;	return 0;}/* *  Interrupt handler */static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs){	ice1712_t *ice = dev_id;	unsigned char status;	int handled = 0;	while (1) {		status = inb(ICEREG(ice, IRQSTAT));		if (status == 0)			break;		handled = 1;		if (status & ICE1712_IRQ_MPU1) {			if (ice->rmidi[0])				snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);			outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT));			status &= ~ICE1712_IRQ_MPU1;		}		if (status & ICE1712_IRQ_TIMER)			outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT));		if (status & ICE1712_IRQ_MPU2) {			if (ice->rmidi[1])				snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs);			outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT));			status &= ~ICE1712_IRQ_MPU2;		}		if (status & ICE1712_IRQ_PROPCM) {			unsigned char mtstat = inb(ICEMT(ice, IRQ));			if (mtstat & ICE1712_MULTI_PBKSTATUS) {				if (ice->playback_pro_substream)					snd_pcm_period_elapsed(ice->playback_pro_substream);				outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ));			}			if (mtstat & ICE1712_MULTI_CAPSTATUS) {				if (ice->capture_pro_substream)					snd_pcm_period_elapsed(ice->capture_pro_substream);				outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ));			}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -