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

📄 via82xx.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	}	/* 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;	dev->fragsize = fragsize;	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 *chip){	return inl(VIAREG(chip, AC97));} static inline void snd_via82xx_codec_xwrite(struct via82xx *chip, unsigned int val){	outl(val, VIAREG(chip, AC97));} static int snd_via82xx_codec_ready(struct via82xx *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 *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 *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){	struct via82xx *chip = ac97->private_data;	unsigned int xval;	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;	xval |= reg << VIA_REG_AC97_CMD_SHIFT;	xval |= val << VIA_REG_AC97_DATA_SHIFT;	snd_via82xx_codec_xwrite(chip, xval);	snd_via82xx_codec_ready(chip, ac97->num);}static unsigned short snd_via82xx_codec_read(struct snd_ac97 *ac97, unsigned short reg){	struct via82xx *chip = ac97->private_data;	unsigned int xval, val = 0xffff;	int again = 0;	xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT;	xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;	xval |= VIA_REG_AC97_READ;	xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;      	while (1) {      		if (again++ > 3) {			snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n",				   ac97->num, snd_via82xx_codec_xread(chip));		      	return 0xffff;		}		snd_via82xx_codec_xwrite(chip, xval);		udelay (20);		if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) {			udelay(25);			val = snd_via82xx_codec_xread(chip);			break;		}	}	return val & 0xffff;}static void snd_via82xx_channel_reset(struct via82xx *chip, struct viadev *viadev){	outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET,	     VIADEV_REG(viadev, OFFSET_CONTROL));	inb(VIADEV_REG(viadev, OFFSET_CONTROL));	udelay(50);	/* disable interrupts */	outb(0x00, VIADEV_REG(viadev, OFFSET_CONTROL));	/* clear interrupts */	outb(0x03, VIADEV_REG(viadev, OFFSET_STATUS));	outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */	// outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR));	viadev->lastpos = 0;	viadev->hwptr_done = 0;}/* *  Interrupt handler *  Used for 686 and 8233A */static irqreturn_t snd_via686_interrupt(int irq, void *dev_id){	struct via82xx *chip = dev_id;	unsigned int status;	unsigned int i;	status = inl(VIAREG(chip, SGD_SHADOW));	if (! (status & chip->intr_mask)) {		if (chip->rmidi)			/* check mpu401 interrupt */			return snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);		return IRQ_NONE;	}	/* check status for each stream */	spin_lock(&chip->reg_lock);	for (i = 0; i < chip->num_devs; i++) {		struct viadev *viadev = &chip->devs[i];		unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS));		if (! (c_status & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED)))			continue;		if (viadev->substream && viadev->running) {			/*			 * Update hwptr_done based on 'period elapsed'			 * interrupts. We'll use it, when the chip returns 0 			 * for OFFSET_CURR_COUNT.			 */			if (c_status & VIA_REG_STAT_EOL)				viadev->hwptr_done = 0;			else				viadev->hwptr_done += viadev->fragsize;			viadev->in_interrupt = c_status;			spin_unlock(&chip->reg_lock);			snd_pcm_period_elapsed(viadev->substream);			spin_lock(&chip->reg_lock);			viadev->in_interrupt = 0;		}		outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */	}	spin_unlock(&chip->reg_lock);	return IRQ_HANDLED;}/* *  Interrupt handler */static irqreturn_t snd_via8233_interrupt(int irq, void *dev_id){	struct via82xx *chip = dev_id;	unsigned int status;	unsigned int i;	int irqreturn = 0;	/* check status for each stream */	spin_lock(&chip->reg_lock);	status = inl(VIAREG(chip, SGD_SHADOW));	for (i = 0; i < chip->num_devs; i++) {		struct viadev *viadev = &chip->devs[i];		struct snd_pcm_substream *substream;		unsigned char c_status, shadow_status;		shadow_status = (status >> viadev->shadow_shift) &			(VIA8233_SHADOW_STAT_ACTIVE|VIA_REG_STAT_EOL|			 VIA_REG_STAT_FLAG);		c_status = shadow_status & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG);		if (!c_status)			continue;		substream = viadev->substream;		if (substream && viadev->running) {			/*			 * Update hwptr_done based on 'period elapsed'			 * interrupts. We'll use it, when the chip returns 0 			 * for OFFSET_CURR_COUNT.			 */			if (c_status & VIA_REG_STAT_EOL)				viadev->hwptr_done = 0;			else				viadev->hwptr_done += viadev->fragsize;			viadev->in_interrupt = c_status;			if (shadow_status & VIA8233_SHADOW_STAT_ACTIVE)				viadev->in_interrupt |= VIA_REG_STAT_ACTIVE;			spin_unlock(&chip->reg_lock);			snd_pcm_period_elapsed(substream);			spin_lock(&chip->reg_lock);			viadev->in_interrupt = 0;		}		outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */		irqreturn = 1;	}	spin_unlock(&chip->reg_lock);	return IRQ_RETVAL(irqreturn);}/* *  PCM callbacks *//* * trigger callback */static int snd_via82xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd){	struct via82xx *chip = snd_pcm_substream_chip(substream);	struct viadev *viadev = substream->runtime->private_data;	unsigned char val;	if (chip->chip_type != TYPE_VIA686)		val = VIA_REG_CTRL_INT;	else		val = 0;	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_RESUME:		val |= VIA_REG_CTRL_START;		viadev->running = 1;		break;	case SNDRV_PCM_TRIGGER_STOP:	case SNDRV_PCM_TRIGGER_SUSPEND:		val = VIA_REG_CTRL_TERMINATE;		viadev->running = 0;		break;	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		val |= VIA_REG_CTRL_PAUSE;		viadev->running = 0;		break;	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		viadev->running = 1;		break;	default:		return -EINVAL;	}	outb(val, VIADEV_REG(viadev, OFFSET_CONTROL));	if (cmd == SNDRV_PCM_TRIGGER_STOP)		snd_via82xx_channel_reset(chip, viadev);	return 0;}/* * pointer callbacks *//* * calculate the linear position at the given sg-buffer index and the rest count */#define check_invalid_pos(viadev,pos) \	((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 ||\				     viadev->lastpos < viadev->bufsize2))static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int idx,					   unsigned int count){	unsigned int size, base, res;	size = viadev->idx_table[idx].size;	base = viadev->idx_table[idx].offset;	res = base + size - count;	if (res >= viadev->bufsize)		res -= viadev->bufsize;	/* check the validity of the calculated position */	if (size < count) {		snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n",			   (int)size, (int)count);		res = viadev->lastpos;	} else {		if (! count) {			/* Some mobos report count = 0 on the DMA boundary,			 * i.e. count = size indeed.			 * Let's check whether this step is above the expected size.			 */			int delta = res - viadev->lastpos;			if (delta < 0)				delta += viadev->bufsize;			if ((unsigned int)delta > viadev->fragsize)				res = base;		}		if (check_invalid_pos(viadev, res)) {#ifdef POINTER_DEBUG			printk(KERN_DEBUG "fail: idx = %i/%i, lastpos = 0x%x, "			       "bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, "			       "count = 0x%x\n", idx, viadev->tbl_entries,			       viadev->lastpos, viadev->bufsize2,			       viadev->idx_table[idx].offset,			       viadev->idx_table[idx].size, count);#endif			/* count register returns full size when end of buffer is reached */			res = base + size;			if (check_invalid_pos(viadev, res)) {				snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), "					   "using last valid pointer\n");				res = viadev->lastpos;			}		}	}	return res;}/* * get the current pointer on via686 */static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substream){	struct via82xx *chip = snd_pcm_substream_chip(substream);	struct viadev *viadev = substream->runtime->private_data;	unsigned int idx, ptr, count, res;	snd_assert(viadev->tbl_entries, return 0);	if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))		return 0;	spin_lock(&chip->reg_lock);	count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff;	/* The via686a does not have the current index register,	 * so we need to calculate the index from CURR_PTR.	 */	ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR));	if (ptr <= (unsigned int)viadev->table.addr)		idx = 0;	else /* CURR_PTR holds the address + 8 */		idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries;	res = calc_linear_pos(viadev, idx, count);	viadev->lastpos = res; /* remember the last position */	spin_unlock(&chip->reg_lock);	return bytes_to_frames(substream->runtime, res);}/* * get the current pointer on via823x */static snd_pcm_uframes_t snd_via8233_pcm_pointer(struct snd_pcm_substream *substream){	struct via82xx *chip = snd_pcm_substream_chip(substream);	struct viadev *viadev = substream->runtime->private_data;	unsigned int idx, count, res;	int status;		snd_assert(viadev->tbl_entries, return 0);	spin_lock(&chip->reg_lock);	count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT));	status = viadev->in_interrupt;	if (!status)		status = inb(VIADEV_REG(viadev, OFFSET_STATUS));	/* An apparent bug in the 8251 is worked around by sending a 	 * REG_CTRL_START. */	if (chip->revision == VIA_REV_8251 && (status & VIA_REG_STAT_EOL))		snd_via82xx_pcm_trigger(substream, SNDRV_PCM_TRIGGER_START);	if (!(status & VIA_REG_STAT_ACTIVE)) {		res = 0;		goto unlock;	}	if (count & 0xffffff) {

⌨️ 快捷键说明

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