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

📄 ice1724.c

📁 鼎力推荐!本程序是基于嵌入式LUNUX系统开发的源程序代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *   ALSA driver for VT1724 ICEnsemble ICE1724 / VIA VT1724 (Envy24HT) *                   VIA VT1720 (Envy24PT) * *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz> *                    2002 James Stafford <jstafford@ampltd.com> *                    2003 Takashi Iwai <tiwai@suse.de> * *   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 * */      #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/info.h>#include <sound/mpu401.h>#include <sound/initval.h>#include <sound/asoundef.h>#include "ice1712.h"#include "envy24ht.h"/* lowlevel routines */#include "amp.h"#include "revo.h"#include "aureon.h"#include "vt1720_mobo.h"#include "pontis.h"#include "prodigy192.h"MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{"	       REVO_DEVICE_DESC	       AMP_AUDIO2000_DEVICE_DESC	       AUREON_DEVICE_DESC	       VT1720_MOBO_DEVICE_DESC	       PONTIS_DEVICE_DESC	       PRODIGY192_DEVICE_DESC		"{VIA,VT1720},"		"{VIA,VT1724},"		"{ICEnsemble,Generic ICE1724},"		"{ICEnsemble,Generic Envy24HT}"		"{ICEnsemble,Generic Envy24PT}}");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];module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for ICE1724 soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for ICE1724 soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable ICE1724 soundcard.");module_param_array(model, charp, NULL, 0444);MODULE_PARM_DESC(model, "Use the given board model.");#ifndef PCI_VENDOR_ID_ICE#define PCI_VENDOR_ID_ICE		0x1412#endif#ifndef PCI_DEVICE_ID_VT1724#define PCI_DEVICE_ID_VT1724		0x1724#endif/* Both VT1720 and VT1724 have the same PCI IDs */static struct pci_device_id snd_vt1724_ids[] = {	{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	{ 0, }};MODULE_DEVICE_TABLE(pci, snd_vt1724_ids);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(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;}static inline int is_pro_rate_locked(ice1712_t *ice){	return is_spdif_master(ice) || PRO_RATE_LOCKED;}/* * ac97 section */static unsigned char snd_vt1724_ac97_ready(ice1712_t *ice){	unsigned char old_cmd;	int tm;	for (tm = 0; tm < 0x10000; tm++) {		old_cmd = inb(ICEMT1724(ice, AC97_CMD));		if (old_cmd & (VT1724_AC97_WRITE | VT1724_AC97_READ))			continue;		if (!(old_cmd & VT1724_AC97_READY))			continue;		return old_cmd;	}	snd_printd(KERN_ERR "snd_vt1724_ac97_ready: timeout\n");	return old_cmd;}static int snd_vt1724_ac97_wait_bit(ice1712_t *ice, unsigned char bit){	int tm;	for (tm = 0; tm < 0x10000; tm++)		if ((inb(ICEMT1724(ice, AC97_CMD)) & bit) == 0)			return 0;	snd_printd(KERN_ERR "snd_vt1724_ac97_wait_bit: timeout\n");	return -EIO;}static void snd_vt1724_ac97_write(ac97_t *ac97,				  unsigned short reg,				  unsigned short val){	ice1712_t *ice = (ice1712_t *)ac97->private_data;	unsigned char old_cmd;	old_cmd = snd_vt1724_ac97_ready(ice);	old_cmd &= ~VT1724_AC97_ID_MASK;	old_cmd |= ac97->num;	outb(reg, ICEMT1724(ice, AC97_INDEX));	outw(val, ICEMT1724(ice, AC97_DATA));	outb(old_cmd | VT1724_AC97_WRITE, ICEMT1724(ice, AC97_CMD));	snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_WRITE);}static unsigned short snd_vt1724_ac97_read(ac97_t *ac97, unsigned short reg){	ice1712_t *ice = (ice1712_t *)ac97->private_data;	unsigned char old_cmd;	old_cmd = snd_vt1724_ac97_ready(ice);	old_cmd &= ~VT1724_AC97_ID_MASK;	old_cmd |= ac97->num;	outb(reg, ICEMT1724(ice, AC97_INDEX));	outb(old_cmd | VT1724_AC97_READ, ICEMT1724(ice, AC97_CMD));	if (snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_READ) < 0)		return ~0;	return inw(ICEMT1724(ice, AC97_DATA));}/* * GPIO operations *//* set gpio direction 0 = read, 1 = write */static void snd_vt1724_set_gpio_dir(ice1712_t *ice, unsigned int data){	outl(data, ICEREG1724(ice, GPIO_DIRECTION));	inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */}/* set the gpio mask (0 = writable) */static void snd_vt1724_set_gpio_mask(ice1712_t *ice, unsigned int data){	outw(data, ICEREG1724(ice, GPIO_WRITE_MASK));	if (! ice->vt1720) /* VT1720 supports only 16 GPIO bits */		outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22));	inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */}static void snd_vt1724_set_gpio_data(ice1712_t *ice, unsigned int data){	outw(data, ICEREG1724(ice, GPIO_DATA));	if (! ice->vt1720)		outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22));	inw(ICEREG1724(ice, GPIO_DATA)); /* dummy read for pci-posting */}static unsigned int snd_vt1724_get_gpio_data(ice1712_t *ice){	unsigned int data;	if (! ice->vt1720)		data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22));	else		data = 0;	data = (data << 16) | inw(ICEREG1724(ice, GPIO_DATA));	return data;}/* *  Interrupt handler */static irqreturn_t snd_vt1724_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(ICEREG1724(ice, IRQSTAT));		if (status == 0)			break;		handled = 1;				/*  these should probably be separated at some point, 			but as we don't currently have MPU support on the board I will leave it */		if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) {			if (ice->rmidi[0])				snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);			outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT));			status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX);		}		if (status & VT1724_IRQ_MTPCM) {			/*			 * Multi-track PCM			 * PCM assignment are:			 * Playback DMA0 (M/C) = playback_pro_substream			 * Playback DMA1 = playback_con_substream_ds[0]			 * Playback DMA2 = playback_con_substream_ds[1]			 * Playback DMA3 = playback_con_substream_ds[2]			 * Playback DMA4 (SPDIF) = playback_con_substream			 * Record DMA0 = capture_pro_substream			 * Record DMA1 = capture_con_substream			 */			unsigned char mtstat = inb(ICEMT1724(ice, IRQ));			if (mtstat & VT1724_MULTI_PDMA0) {				if (ice->playback_pro_substream)					snd_pcm_period_elapsed(ice->playback_pro_substream);			}			if (mtstat & VT1724_MULTI_RDMA0) {				if (ice->capture_pro_substream)					snd_pcm_period_elapsed(ice->capture_pro_substream);			}			if (mtstat & VT1724_MULTI_PDMA1) {				if (ice->playback_con_substream_ds[0])					snd_pcm_period_elapsed(ice->playback_con_substream_ds[0]);			}			if (mtstat & VT1724_MULTI_PDMA2) {				if (ice->playback_con_substream_ds[1])					snd_pcm_period_elapsed(ice->playback_con_substream_ds[1]);			}			if (mtstat & VT1724_MULTI_PDMA3) {				if (ice->playback_con_substream_ds[2])					snd_pcm_period_elapsed(ice->playback_con_substream_ds[2]);			}			if (mtstat & VT1724_MULTI_PDMA4) {				if (ice->playback_con_substream)					snd_pcm_period_elapsed(ice->playback_con_substream);			}			if (mtstat & VT1724_MULTI_RDMA1) {				if (ice->capture_con_substream)					snd_pcm_period_elapsed(ice->capture_con_substream);			}			/* ack anyway to avoid freeze */			outb(mtstat, ICEMT1724(ice, IRQ));			/* ought to really handle this properly */			if (mtstat & VT1724_MULTI_FIFO_ERR) {				unsigned char fstat = inb(ICEMT1724(ice, DMA_FIFO_ERR));				outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR));					outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));					/* If I don't do this, I get machine lockup due to continual interrupts */			}		}	}	return IRQ_RETVAL(handled);}/* *  PCM code - professional part (multitrack) */static unsigned int rates[] = {	8000, 9600, 11025, 12000, 16000, 22050, 24000,	32000, 44100, 48000, 64000, 88200, 96000,	176400, 192000,};static snd_pcm_hw_constraint_list_t hw_constraints_rates_96 = {	.count = ARRAY_SIZE(rates) - 2, /* up to 96000 */	.list = rates,	.mask = 0,};static snd_pcm_hw_constraint_list_t hw_constraints_rates_48 = {	.count = ARRAY_SIZE(rates) - 5, /* up to 48000 */	.list = rates,	.mask = 0,};static snd_pcm_hw_constraint_list_t hw_constraints_rates_192 = {	.count = ARRAY_SIZE(rates),	.list = rates,	.mask = 0,};struct vt1724_pcm_reg {	unsigned int addr;	/* ADDR register offset */	unsigned int size;	/* SIZE register offset */	unsigned int count;	/* COUNT register offset */	unsigned int start;	/* start & pause bit */};static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd){	ice1712_t *ice = snd_pcm_substream_chip(substream);	unsigned char what;	unsigned char old;	struct list_head *pos;	snd_pcm_substream_t *s;	what = 0;	snd_pcm_group_for_each(pos, substream) {		struct vt1724_pcm_reg *reg;		s = snd_pcm_group_substream_entry(pos);		reg = s->runtime->private_data;		what |= reg->start;		snd_pcm_trigger_done(s, substream);	}	switch (cmd) {	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		spin_lock(&ice->reg_lock);		old = inb(ICEMT1724(ice, DMA_PAUSE));		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)			old |= what;		else			old &= ~what;		outb(old, ICEMT1724(ice, DMA_PAUSE));		spin_unlock(&ice->reg_lock);		break;	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_STOP:		spin_lock(&ice->reg_lock);		old = inb(ICEMT1724(ice, DMA_CONTROL));		if (cmd == SNDRV_PCM_TRIGGER_START)			old |= what;		else			old &= ~what;		outb(old, ICEMT1724(ice, DMA_CONTROL));		spin_unlock(&ice->reg_lock);		break;	default:		return -EINVAL;	}	return 0;}/* */#define DMA_STARTS	(VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|\	VT1724_PDMA1_START|VT1724_PDMA2_START|VT1724_PDMA3_START|VT1724_PDMA4_START)#define DMA_PAUSES	(VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\	VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE)static int get_max_rate(ice1712_t *ice){	if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {		if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720)			return 192000;		else			return 96000;	} else		return 48000;}static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force){	unsigned long flags;	unsigned char val, old;	unsigned int i;	if (rate > get_max_rate(ice))		return;	spin_lock_irqsave(&ice->reg_lock, flags);	if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || 	    (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {		/* running? we cannot change the rate now... */		spin_unlock_irqrestore(&ice->reg_lock, flags);		return;	}	if (!force && is_pro_rate_locked(ice)) {		spin_unlock_irqrestore(&ice->reg_lock, flags);		return;	}	switch (rate) {	case 8000: val = 6; break;	case 9600: val = 3; break;	case 11025: val = 10; break;	case 12000: val = 2; break;	case 16000: val = 5; break;	case 22050: val = 9; break;	case 24000: val = 1; break;	case 32000: val = 4; break;	case 44100: val = 8; break;	case 48000: val = 0; break;	case 64000: val = 15; break;	case 88200: val = 11; break;	case 96000: val = 7; break;	case 176400: val = 12; break;	case 192000: val = 14; break;	default:		snd_BUG();		val = 0;		break;	}	old = inb(ICEMT1724(ice, RATE));	if (old != val)		outb(val, ICEMT1724(ice, RATE));	else if (rate == ice->cur_rate) {		spin_unlock_irqrestore(&ice->reg_lock, flags);		return;	}	ice->cur_rate = rate;	/* check MT02 */	if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {		val = old = inb(ICEMT1724(ice, I2S_FORMAT));		if (rate > 96000)			val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */		else			val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */		if (val != old) {			outb(val, ICEMT1724(ice, I2S_FORMAT));			if (ice->eeprom.subvendor == VT1724_SUBDEVICE_REVOLUTION71) {				/* FIXME: is this revo only? */				/* assert PRST# to converters; MT05 bit 7 */

⌨️ 快捷键说明

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