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

📄 pxa-audio.c

📁 Source files for pxa ac97 sound driver The packet contains the files that is listed below driv
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/drivers/sound/pxa-audio.c -- audio interface for the Cotula chip * *  Author:	Nicolas Pitre *  Created:	Aug 15, 2001 *  Copyright:	MontaVista Software Inc. * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 as *  published by the Free Software Foundation. */#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/poll.h>#include <linux/sound.h>#include <linux/soundcard.h>#include <asm/hardware.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/semaphore.h>#include <asm/dma.h>#include "pxa-audio.h"#define AUDIO_NBFRAGS_DEFAULT	8#define AUDIO_FRAGSIZE_DEFAULT	8192#define MAX_DMA_SIZE		4096#define DMA_DESC_SIZE		sizeof(pxa_dma_desc)/* * This function frees all buffers */#define audio_clear_buf pxa_audio_clear_bufvoid pxa_audio_clear_buf(audio_stream_t * s){	DECLARE_WAITQUEUE(wait, current);	int frag;	if (!s->buffers)		return;	/* Ensure DMA isn't running */	set_current_state(TASK_UNINTERRUPTIBLE);	add_wait_queue(&s->stop_wq, &wait);	DCSR(s->dma_ch) = DCSR_STOPIRQEN;	schedule();	remove_wait_queue(&s->stop_wq, &wait);	/* free DMA buffers */	for (frag = 0; frag < s->nbfrags; frag++) {		audio_buf_t *b = &s->buffers[frag];		if (!b->master)			continue;		consistent_free(b->data, b->master, b->dma_desc->dsadr);	}	/* free descriptor ring */	if (s->buffers->dma_desc)		consistent_free(s->buffers->dma_desc, 				s->nbfrags * s->descs_per_frag * DMA_DESC_SIZE,				s->dma_desc_phys);	/* free buffer structure array */	kfree(s->buffers);	s->buffers = NULL;}/* * This function allocates the DMA descriptor array and buffer data space * according to the current number of fragments and fragment size. */static int audio_setup_buf(audio_stream_t * s){	pxa_dma_desc *dma_desc;	dma_addr_t dma_desc_phys;	int nb_desc, frag, i, buf_size = 0;	char *dma_buf = NULL;	dma_addr_t dma_buf_phys = 0;	if (s->buffers)		return -EBUSY;	/* Our buffer structure array */	s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);	if (!s->buffers)		goto err;	memzero(s->buffers, sizeof(audio_buf_t) * s->nbfrags);	/* 	 * Our DMA descriptor array:	 * for Each fragment we have one checkpoint descriptor plus one 	 * descriptor per MAX_DMA_SIZE byte data blocks.	 */	nb_desc = (1 + (s->fragsize + MAX_DMA_SIZE - 1)/MAX_DMA_SIZE) * s->nbfrags;	dma_desc = consistent_alloc(GFP_KERNEL,				    nb_desc * DMA_DESC_SIZE,				    &dma_desc_phys);	if (!dma_desc)		goto err;	s->descs_per_frag = nb_desc / s->nbfrags;	s->buffers->dma_desc = dma_desc;	s->dma_desc_phys = dma_desc_phys;		for (i = 0; i < nb_desc - 1; i++)		dma_desc[i].ddadr = dma_desc_phys + (i + 1) * DMA_DESC_SIZE;	dma_desc[i].ddadr = dma_desc_phys;	/* Our actual DMA buffers */	for (frag = 0; frag < s->nbfrags; frag++) {		audio_buf_t *b = &s->buffers[frag];		/*		 * Let's allocate non-cached memory for DMA buffers.		 * We try to allocate all memory at once.		 * If this fails (a common reason is memory fragmentation),		 * then we'll try allocating smaller buffers.		 */		if (!buf_size) {			buf_size = (s->nbfrags - frag) * s->fragsize;			do {				dma_buf = consistent_alloc(GFP_KERNEL,							   buf_size, 							   &dma_buf_phys);				if (!dma_buf)					buf_size -= s->fragsize;			} while (!dma_buf && buf_size);			if (!dma_buf)				goto err;			b->master = buf_size;			memzero(dma_buf, buf_size);		}		/* 		 * Set up our checkpoint descriptor.  Since the count 		 * is always zero, we'll abuse the dsadr and dtadr fields		 * just in case this one is picked up by the hardware		 * while processing SOUND_DSP_GETPTR.		 */		dma_desc->dsadr = dma_buf_phys;		dma_desc->dtadr = dma_buf_phys;		dma_desc->dcmd = DCMD_ENDIRQEN;		if (s->output && !s->mapped)			dma_desc->ddadr |= DDADR_STOP;		b->dma_desc = dma_desc++;		/* set up the actual data descriptors */		for (i = 0; (i * MAX_DMA_SIZE) < s->fragsize; i++) {			dma_desc[i].dsadr = (s->output) ?				(dma_buf_phys + i*MAX_DMA_SIZE) : s->dev_addr;			dma_desc[i].dtadr = (s->output) ?				s->dev_addr : (dma_buf_phys + i*MAX_DMA_SIZE);			dma_desc[i].dcmd = s->dcmd |				((s->fragsize < MAX_DMA_SIZE) ?					s->fragsize : MAX_DMA_SIZE);		}		dma_desc += i;		/* handle buffer pointers */		b->data = dma_buf;		dma_buf += s->fragsize;		dma_buf_phys += s->fragsize;		buf_size -= s->fragsize;	}	s->usr_frag = s->dma_frag = 0;	s->bytecount = 0;	s->fragcount = 0;	sema_init(&s->sem, (s->output) ? s->nbfrags : 0);	return 0;err:	printk("pxa-audio: unable to allocate audio memory\n ");	audio_clear_buf(s);	return -ENOMEM;}/* * Our DMA interrupt handler */static void audio_dma_irq(int ch, void *dev_id, struct pt_regs *regs){	audio_stream_t *s = dev_id;	u_int dcsr;	dcsr = DCSR(ch);	DCSR(ch) = dcsr & ~DCSR_STOPIRQEN;	if (!s->buffers) {		printk("AC97 DMA: wow... received IRQ for channel %d but no buffer exists\n", ch);		return;	}	if (dcsr & DCSR_BUSERR)		printk("AC97 DMA: bus error interrupt on channel %d\n", ch);	if (dcsr & DCSR_ENDINTR) {		u_long cur_dma_desc;		u_int cur_dma_frag;		/* 		 * Find out which DMA desc is current.  Note that DDADR		 * points to the next desc, not the current one.		 */		cur_dma_desc = DDADR(ch) - s->dma_desc_phys - DMA_DESC_SIZE;		/*		 * Let the compiler nicely optimize constant divisors into		 * multiplications for the common cases which is much faster.		 * Common cases: x = 1 + (1 << y) for y = [0..3]		 */		switch (s->descs_per_frag) {		case 2:  cur_dma_frag = cur_dma_desc / (2*DMA_DESC_SIZE); break;		case 3:  cur_dma_frag = cur_dma_desc / (3*DMA_DESC_SIZE); break;		case 5:  cur_dma_frag = cur_dma_desc / (5*DMA_DESC_SIZE); break;		case 9:  cur_dma_frag = cur_dma_desc / (9*DMA_DESC_SIZE); break;		default: cur_dma_frag =			    cur_dma_desc / (s->descs_per_frag * DMA_DESC_SIZE);		}		//printk(KERN_DEBUG"cur_dma_frag = %d, s->dma_frag = %d\n",cur_dma_frag,s->dma_frag);		/* Account for possible wrap back of cur_dma_desc above */		if (cur_dma_frag >= s->nbfrags)			cur_dma_frag = s->nbfrags - 1;		while (s->dma_frag != cur_dma_frag) {			//printk(KERN_DEBUG"over!\n");			if (!s->mapped) {				/* 				 * This fragment is done - set the checkpoint				 * descriptor to STOP until it is gets				 * processed by the read or write function.				 */				s->buffers[s->dma_frag].dma_desc->ddadr |= DDADR_STOP;				up(&s->sem);			}			if (++s->dma_frag >= s->nbfrags)				s->dma_frag = 0;			/* Accounting */			s->bytecount += s->fragsize;			s->fragcount++;		}		/* ... and for polling processes */		wake_up(&s->frag_wq);	}	if ((dcsr & DCSR_STOPIRQEN) && (dcsr & DCSR_STOPSTATE))		wake_up(&s->stop_wq);}/* * Validate and sets up buffer fragments, etc. */static int audio_set_fragments(audio_stream_t *s, int val){	if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN)		return -EBUSY;	if (s->buffers)		audio_clear_buf(s);	s->nbfrags = (val >> 16) & 0x7FFF;	val &= 0xffff;	if (val < 5)		val = 5;	if (val > 15)		val = 15;	s->fragsize = 1 << val;	if (s->nbfrags < 2)		s->nbfrags = 2;	if (s->nbfrags * s->fragsize > 256 * 1024)		s->nbfrags = 256 * 1024 / s->fragsize;	if (audio_setup_buf(s))		return -ENOMEM;	return val|(s->nbfrags << 16);}/* * The fops functions */static int audio_write(struct file *file, const char *buffer,		       size_t count, loff_t * ppos){	const char *buffer0 = buffer;	audio_state_t *state = (audio_state_t *)file->private_data;	audio_stream_t *s = state->output_stream;	int chunksize, ret = 0;	if (ppos != &file->f_pos)		return -ESPIPE;	if (s->mapped)		return -ENXIO;	if (!s->buffers && audio_setup_buf(s))		return -ENOMEM;	while (count > 0) {		audio_buf_t *b = &s->buffers[s->usr_frag];		/* Grab a fragment */		if (file->f_flags & O_NONBLOCK) {			ret = -EAGAIN;			if (down_trylock(&s->sem))				break;		} else {			ret = -ERESTARTSYS;			if (down_interruptible(&s->sem))				break;		}		/* Feed the current buffer */		chunksize = s->fragsize - b->offset;		if (chunksize > count)			chunksize = count;		if (copy_from_user(b->data + b->offset, buffer, chunksize)) {			up(&s->sem);			return -EFAULT;		}		b->offset += chunksize;		buffer += chunksize;		count -= chunksize;		if (b->offset < s->fragsize) {			up(&s->sem);			break;		}		/* 		 * Activate DMA on current buffer.		 * We unlock this fragment's checkpoint descriptor and		 * kick DMA if it is idle.  Using checkpoint descriptors		 * allows for control operations without the need for 		 * stopping the DMA channel if it is already running.		 */		b->offset = 0;		b->dma_desc->ddadr &= ~DDADR_STOP;		if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {			DDADR(s->dma_ch) = b->dma_desc->ddadr;			DCSR(s->dma_ch) = DCSR_RUN;		}		/* move the index to the next fragment */		if (++s->usr_frag >= s->nbfrags)			s->usr_frag = 0;	}	if ((buffer - buffer0))		ret = buffer - buffer0;	return ret;}static int audio_read(struct file *file, char *buffer,		      size_t count, loff_t * ppos){	char *buffer0 = buffer;	audio_state_t *state = file->private_data;	audio_stream_t *s = state->input_stream;	int chunksize, ret = 0;	if (ppos != &file->f_pos)		return -ESPIPE;	if (s->mapped)		return -ENXIO;	if (!s->buffers && audio_setup_buf(s))		return -ENOMEM;	while (count > 0) {		audio_buf_t *b = &s->buffers[s->usr_frag];		/* prime DMA */		if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {			DDADR(s->dma_ch) = 				s->buffers[s->dma_frag].dma_desc->ddadr;			DCSR(s->dma_ch) = DCSR_RUN;		}		/* Wait for a buffer to become full */		if (file->f_flags & O_NONBLOCK) {			ret = -EAGAIN;			if (down_trylock(&s->sem))				break;		} else {			ret = -ERESTARTSYS;			if (down_interruptible(&s->sem))				break;		}		/* Grab data from current buffer */		chunksize = s->fragsize - b->offset;		if (chunksize > count)			chunksize = count;		if (copy_to_user(buffer, b->data + b->offset, chunksize)) {			up(&s->sem);			return -EFAULT;		}		b->offset += chunksize;		buffer += chunksize;		count -= chunksize;		if (b->offset < s->fragsize) {			up(&s->sem);			break;		}		/* 		 * Make this buffer available for DMA again.		 * We unlock this fragment's checkpoint descriptor and		 * kick DMA if it is idle.  Using checkpoint descriptors		 * allows for control operations without the need for 		 * stopping the DMA channel if it is already running.		 */		b->offset = 0;		b->dma_desc->ddadr &= ~DDADR_STOP;		/* move the index to the next fragment */		if (++s->usr_frag >= s->nbfrags)			s->usr_frag = 0;	}	if ((buffer - buffer0))

⌨️ 快捷键说明

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