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

📄 via82xx_modem.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *   ALSA modem driver for VIA VT82xx (South Bridge) * *   VT82C686A/B/C, VT8233A/C, VT8235 * *	Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz> *	                   Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com> *                    2002 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 * *//* * Changes: * * Sep. 2,  2004  Sasha Khapyorsky <sashak@alsa-project.org> *      Modified from original audio driver 'via82xx.c' to support AC97 *      modems. */#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/pcm.h>#include <sound/pcm_params.h>#include <sound/info.h>#include <sound/ac97_codec.h>#include <sound/initval.h>#if 0#define POINTER_DEBUG#endifMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");MODULE_DESCRIPTION("VIA VT82xx modem");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C modem,pci}}");static int index = -2; /* Exclude the first card */static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */static int ac97_clock = 48000;module_param(index, int, 0444);MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge.");module_param(ac97_clock, int, 0444);MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");/* just for backward compatibility */static int enable;module_param(enable, bool, 0444);/* *  Direct registers */#define VIAREG(via, x) ((via)->port + VIA_REG_##x)#define VIADEV_REG(viadev, x) ((viadev)->port + VIA_REG_##x)/* common offsets */#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */#define   VIA_REG_STAT_PAUSED		0x40	/* RO */#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */#define   VIA_REG_STAT_EOL		0x02	/* RWC */#define   VIA_REG_STAT_FLAG		0x01	/* RWC */#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */#define   VIA_REG_CTRL_START		0x80	/* WO */#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */#define   VIA_REG_CTRL_AUTOSTART	0x20#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */#define   VIA_REG_CTRL_INT_STOP		0x04		#define   VIA_REG_CTRL_INT_EOL		0x02#define   VIA_REG_CTRL_INT_FLAG		0x01#define   VIA_REG_CTRL_RESET		0x01	/* RW - probably reset? undocumented */#define   VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)#define VIA_REG_OFFSET_TYPE		0x02	/* byte - channel type (686 only) */#define   VIA_REG_TYPE_AUTOSTART	0x80	/* RW - autostart at EOL */#define   VIA_REG_TYPE_16BIT		0x20	/* RW */#define   VIA_REG_TYPE_STEREO		0x10	/* RW */#define   VIA_REG_TYPE_INT_LLINE	0x00#define   VIA_REG_TYPE_INT_LSAMPLE	0x04#define   VIA_REG_TYPE_INT_LESSONE	0x08#define   VIA_REG_TYPE_INT_MASK		0x0c#define   VIA_REG_TYPE_INT_EOL		0x02#define   VIA_REG_TYPE_INT_FLAG		0x01#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */#define VIA_REG_OFFSET_STOP_IDX		0x08	/* dword - stop index, channel type, sample rate */#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */#define VIA_REG_OFFSET_CURR_INDEX	0x0f	/* byte - channel current index (for via8233 only) */#define DEFINE_VIA_REGSET(name,val) \enum {\	VIA_REG_##name##_STATUS		= (val),\	VIA_REG_##name##_CONTROL	= (val) + 0x01,\	VIA_REG_##name##_TYPE		= (val) + 0x02,\	VIA_REG_##name##_TABLE_PTR	= (val) + 0x04,\	VIA_REG_##name##_CURR_PTR	= (val) + 0x04,\	VIA_REG_##name##_STOP_IDX	= (val) + 0x08,\	VIA_REG_##name##_CURR_COUNT	= (val) + 0x0c,\}/* modem block */DEFINE_VIA_REGSET(MO, 0x40);DEFINE_VIA_REGSET(MI, 0x50);/* AC'97 */#define VIA_REG_AC97			0x80	/* dword */#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)#define   VIA_REG_AC97_CODEC_ID_SHIFT	30#define   VIA_REG_AC97_CODEC_ID_PRIMARY	0x00#define   VIA_REG_AC97_CODEC_ID_SECONDARY 0x01#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)#define   VIA_REG_AC97_BUSY		(1<<24)#define   VIA_REG_AC97_READ		(1<<23)#define   VIA_REG_AC97_CMD_SHIFT	16#define   VIA_REG_AC97_CMD_MASK		0x7e#define   VIA_REG_AC97_DATA_SHIFT	0#define   VIA_REG_AC97_DATA_MASK	0xffff#define VIA_REG_SGD_SHADOW		0x84	/* dword */#define   VIA_REG_SGD_STAT_PB_FLAG	(1<<0)#define   VIA_REG_SGD_STAT_CP_FLAG	(1<<1)#define   VIA_REG_SGD_STAT_FM_FLAG	(1<<2)#define   VIA_REG_SGD_STAT_PB_EOL	(1<<4)#define   VIA_REG_SGD_STAT_CP_EOL	(1<<5)#define   VIA_REG_SGD_STAT_FM_EOL	(1<<6)#define   VIA_REG_SGD_STAT_PB_STOP	(1<<8)#define   VIA_REG_SGD_STAT_CP_STOP	(1<<9)#define   VIA_REG_SGD_STAT_FM_STOP	(1<<10)#define   VIA_REG_SGD_STAT_PB_ACTIVE	(1<<12)#define   VIA_REG_SGD_STAT_CP_ACTIVE	(1<<13)#define   VIA_REG_SGD_STAT_FM_ACTIVE	(1<<14)#define   VIA_REG_SGD_STAT_MR_FLAG      (1<<16)#define   VIA_REG_SGD_STAT_MW_FLAG      (1<<17)#define   VIA_REG_SGD_STAT_MR_EOL       (1<<20)#define   VIA_REG_SGD_STAT_MW_EOL       (1<<21)#define   VIA_REG_SGD_STAT_MR_STOP      (1<<24)#define   VIA_REG_SGD_STAT_MW_STOP      (1<<25)#define   VIA_REG_SGD_STAT_MR_ACTIVE    (1<<28)#define   VIA_REG_SGD_STAT_MW_ACTIVE    (1<<29)#define VIA_REG_GPI_STATUS		0x88#define VIA_REG_GPI_INTR		0x8c#define VIA_TBL_BIT_FLAG	0x40000000#define VIA_TBL_BIT_EOL		0x80000000/* pci space */#define VIA_ACLINK_STAT		0x40#define  VIA_ACLINK_C11_READY	0x20#define  VIA_ACLINK_C10_READY	0x10#define  VIA_ACLINK_C01_READY	0x04 /* secondary codec ready */#define  VIA_ACLINK_LOWPOWER	0x02 /* low-power state */#define  VIA_ACLINK_C00_READY	0x01 /* primary codec ready */#define VIA_ACLINK_CTRL		0x41#define  VIA_ACLINK_CTRL_ENABLE	0x80 /* 0: disable, 1: enable */#define  VIA_ACLINK_CTRL_RESET	0x40 /* 0: assert, 1: de-assert */#define  VIA_ACLINK_CTRL_SYNC	0x20 /* 0: release SYNC, 1: force SYNC hi */#define  VIA_ACLINK_CTRL_SDO	0x10 /* 0: release SDO, 1: force SDO hi */#define  VIA_ACLINK_CTRL_VRA	0x08 /* 0: disable VRA, 1: enable VRA */#define  VIA_ACLINK_CTRL_PCM	0x04 /* 0: disable PCM, 1: enable PCM */#define  VIA_ACLINK_CTRL_FM	0x02 /* via686 only */#define  VIA_ACLINK_CTRL_SB	0x01 /* via686 only */#define  VIA_ACLINK_CTRL_INIT	(VIA_ACLINK_CTRL_ENABLE|\				 VIA_ACLINK_CTRL_RESET|\				 VIA_ACLINK_CTRL_PCM)#define VIA_FUNC_ENABLE		0x42#define  VIA_FUNC_MIDI_PNP	0x80 /* FIXME: it's 0x40 in the datasheet! */#define  VIA_FUNC_MIDI_IRQMASK	0x40 /* FIXME: not documented! */#define  VIA_FUNC_RX2C_WRITE	0x20#define  VIA_FUNC_SB_FIFO_EMPTY	0x10#define  VIA_FUNC_ENABLE_GAME	0x08#define  VIA_FUNC_ENABLE_FM	0x04#define  VIA_FUNC_ENABLE_MIDI	0x02#define  VIA_FUNC_ENABLE_SB	0x01#define VIA_PNP_CONTROL		0x43#define VIA_MC97_CTRL		0x44#define  VIA_MC97_CTRL_ENABLE   0x80#define  VIA_MC97_CTRL_SECONDARY 0x40#define  VIA_MC97_CTRL_INIT     (VIA_MC97_CTRL_ENABLE|\                                 VIA_MC97_CTRL_SECONDARY)/* * pcm stream */struct snd_via_sg_table {	unsigned int offset;	unsigned int size;} ;#define VIA_TABLE_SIZE	255struct viadev {	unsigned int reg_offset;	unsigned long port;	int direction;	/* playback = 0, capture = 1 */        struct snd_pcm_substream *substream;	int running;	unsigned int tbl_entries; /* # descriptors */	struct snd_dma_buffer table;	struct snd_via_sg_table *idx_table;	/* for recovery from the unexpected pointer */	unsigned int lastpos;	unsigned int bufsize;	unsigned int bufsize2;};enum { TYPE_CARD_VIA82XX_MODEM = 1 };#define VIA_MAX_MODEM_DEVS	2struct via82xx_modem {	int irq;	unsigned long port;	unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */	struct pci_dev *pci;	struct snd_card *card;	unsigned int num_devs;	unsigned int playback_devno, capture_devno;	struct viadev devs[VIA_MAX_MODEM_DEVS];	struct snd_pcm *pcms[2];	struct snd_ac97_bus *ac97_bus;	struct snd_ac97 *ac97;	unsigned int ac97_clock;	unsigned int ac97_secondary;	/* secondary AC'97 codec is present */	spinlock_t reg_lock;	struct snd_info_entry *proc_entry;};static struct pci_device_id snd_via82xx_modem_ids[] = {	{ 0x1106, 0x3068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA82XX_MODEM, },	{ 0, }};MODULE_DEVICE_TABLE(pci, snd_via82xx_modem_ids);/* *//* * allocate and initialize the descriptor buffers * periods = number of periods * fragsize = period size in bytes */static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substream,			   struct pci_dev *pci,			   unsigned int periods, unsigned int fragsize){	unsigned int i, idx, ofs, rest;	struct via82xx_modem *chip = snd_pcm_substream_chip(substream);	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);	if (dev->table.area == NULL) {		/* the start of each lists must be aligned to 8 bytes,		 * but the kernel pages are much bigger, so we don't care		 */		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),					PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),					&dev->table) < 0)			return -ENOMEM;	}	if (! dev->idx_table) {		dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);		if (! dev->idx_table)			return -ENOMEM;	}	/* fill the entries */	idx = 0;	ofs = 0;	for (i = 0; i < periods; i++) {		rest = fragsize;		/* fill descriptors for a period.		 * a period can be split to several descriptors if it's		 * over page boundary.		 */		do {			unsigned int r;			unsigned int flag;			if (idx >= VIA_TABLE_SIZE) {				snd_printk(KERN_ERR "via82xx: too much table size!\n");				return -EINVAL;			}			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));			r = PAGE_SIZE - (ofs % PAGE_SIZE);			if (rest < r)				r = rest;			rest -= r;			if (! rest) {				if (i == periods - 1)					flag = VIA_TBL_BIT_EOL; /* buffer boundary */				else					flag = VIA_TBL_BIT_FLAG; /* period boundary */			} else				flag = 0; /* period continues to the next */			// printk("via: tbl %d: at %d  size %d (rest %d)\n", idx, ofs, r, rest);			((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);			dev->idx_table[idx].offset = ofs;			dev->idx_table[idx].size = r;			ofs += r;			idx++;		} while (rest > 0);	}	dev->tbl_entries = idx;	dev->bufsize = periods * fragsize;	dev->bufsize2 = dev->bufsize / 2;	return 0;}static int clean_via_table(struct viadev *dev, struct snd_pcm_substream *substream,			   struct pci_dev *pci){	if (dev->table.area) {		snd_dma_free_pages(&dev->table);		dev->table.area = NULL;	}	kfree(dev->idx_table);	dev->idx_table = NULL;	return 0;}/* *  Basic I/O */static inline unsigned int snd_via82xx_codec_xread(struct via82xx_modem *chip){	return inl(VIAREG(chip, AC97));} static inline void snd_via82xx_codec_xwrite(struct via82xx_modem *chip, unsigned int val){	outl(val, VIAREG(chip, AC97));} static int snd_via82xx_codec_ready(struct via82xx_modem *chip, int secondary){	unsigned int timeout = 1000;	/* 1ms */	unsigned int val;		while (timeout-- > 0) {		udelay(1);		if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY))			return val & 0xffff;	}	snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n",		   secondary, snd_via82xx_codec_xread(chip));	return -EIO;} static int snd_via82xx_codec_valid(struct via82xx_modem *chip, int secondary){	unsigned int timeout = 1000;	/* 1ms */	unsigned int val, val1;	unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :					 VIA_REG_AC97_SECONDARY_VALID;		while (timeout-- > 0) {		val = snd_via82xx_codec_xread(chip);		val1 = val & (VIA_REG_AC97_BUSY | stat);		if (val1 == stat)			return val & 0xffff;		udelay(1);	}	return -EIO;} static void snd_via82xx_codec_wait(struct snd_ac97 *ac97){	struct via82xx_modem *chip = ac97->private_data;	int err;	err = snd_via82xx_codec_ready(chip, ac97->num);	/* here we need to wait fairly for long time.. */	msleep(500);}static void snd_via82xx_codec_write(struct snd_ac97 *ac97,				    unsigned short reg,				    unsigned short val)

⌨️ 快捷键说明

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