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

📄 sa1100-audio.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Common audio handling for the SA11x0 processor * * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License. * * * This module handles the generic buffering/DMA/mmap audio interface for * codecs connected to the SA1100 chip.  All features depending on specific * hardware implementations like supported audio formats or samplerates are * relegated to separate specific modules. * * * History: * * 2000-05-21	Nicolas Pitre	Initial release. * * 2000-06-10	Erik Bunce	Add initial poll support. * * 2000-08-22	Nicolas Pitre	Removed all DMA stuff. Now using the * 				generic SA1100 DMA interface. * * 2000-11-30	Nicolas Pitre	- Validation of opened instances; * 				- Power handling at open/release time instead * 				  of driver load/unload; * * 2001-06-03	Nicolas Pitre	Made this file a separate module, based on * 				the former sa1100-uda1341.c driver. * * 2001-07-22	Nicolas Pitre	- added mmap() and realtime support * 				- corrected many details to better comply * 				  with the OSS API * * 2001-10-19	Nicolas Pitre	- brought DMA registration processing * 				  into this module for better ressource * 				  management.  This also fixes a bug * 				  with the suspend/resume logic. */#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/poll.h>#include <linux/pm.h>#include <linux/errno.h>#include <linux/sound.h>#include <linux/soundcard.h>#include <linux/sysrq.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/semaphore.h>#include <asm/dma.h>#include "sa1100-audio.h"#undef DEBUG#ifdef DEBUG#define DPRINTK( x... )  printk( ##x )#else#define DPRINTK( x... )#endif#define AUDIO_NAME		"sa1100-audio"#define AUDIO_NBFRAGS_DEFAULT	8#define AUDIO_FRAGSIZE_DEFAULT	8192#define NEXT_BUF(_s_,_b_) { \	(_s_)->_b_##_idx++; \	(_s_)->_b_##_idx %= (_s_)->nbfrags; \	(_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; }#define AUDIO_ACTIVE(state)	((state)->rd_ref || (state)->wr_ref)/* * This function frees all buffers */static void audio_clear_buf(audio_stream_t * s){	DPRINTK("audio_clear_buf\n");	/* ensure DMA won't run anymore */	s->active = 0;	s->stopped = 0;	sa1100_dma_flush_all(s->dma_ch);	if (s->buffers) {		int frag;		for (frag = 0; frag < s->nbfrags; frag++) {			if (!s->buffers[frag].master)				continue;			consistent_free(s->buffers[frag].start,					s->buffers[frag].master,					s->buffers[frag].dma_addr);		}		kfree(s->buffers);		s->buffers = NULL;	}	s->buf_idx = 0;	s->buf = NULL;}/* * This function allocates the buffer structure array and buffer data space * according to the current number of fragments and fragment size. */static int audio_setup_buf(audio_stream_t * s){	int frag;	int dmasize = 0;	char *dmabuf = NULL;	dma_addr_t dmaphys = 0;	if (s->buffers)		return -EBUSY;	s->buffers = (audio_buf_t *)		kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);	if (!s->buffers)		goto err;	memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags);	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 allocate more smaller buffers.		 */		if (!dmasize) {			dmasize = (s->nbfrags - frag) * s->fragsize;			do {			  	dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA,							  dmasize,							  &dmaphys);				if (!dmabuf)					dmasize -= s->fragsize;			} while (!dmabuf && dmasize);			if (!dmabuf)				goto err;			b->master = dmasize;			memzero(dmabuf, dmasize);		}		b->start = dmabuf;		b->dma_addr = dmaphys;		b->stream = s;		sema_init(&b->sem, 1);		DPRINTK("buf %d: start %p dma %p\n", frag, b->start,			b->dma_addr);		dmabuf += s->fragsize;		dmaphys += s->fragsize;		dmasize -= s->fragsize;	}	s->buf_idx = 0;	s->buf = &s->buffers[0];	s->bytecount = 0;	s->fragcount = 0;	return 0;err:	printk(AUDIO_NAME ": unable to allocate audio memory\n ");	audio_clear_buf(s);	return -ENOMEM;}/* * This function yanks all buffers from the DMA code's control and * resets them ready to be used again. */static void audio_reset_buf(audio_stream_t * s){	int frag;	s->active = 0;	s->stopped = 0;	sa1100_dma_flush_all(s->dma_ch);	if (s->buffers) {		for (frag = 0; frag < s->nbfrags; frag++) {			audio_buf_t *b = &s->buffers[frag];			b->size = 0;			sema_init(&b->sem, 1);		}	}	s->bytecount = 0;	s->fragcount = 0;}/* * DMA callback functions */static void audio_dmaout_done_callback(void *buf_id, int size){	audio_buf_t *b = (audio_buf_t *) buf_id;	audio_stream_t *s = b->stream;	/* Accounting */	s->bytecount += size;	s->fragcount++;	/* Recycle buffer */	if (s->mapped)		sa1100_dma_queue_buffer(s->dma_ch, buf_id,					b->dma_addr, s->fragsize);	else		up(&b->sem);	/* And any process polling on write. */	wake_up(&s->wq);}static void audio_dmain_done_callback(void *buf_id, int size){	audio_buf_t *b = (audio_buf_t *) buf_id;	audio_stream_t *s = b->stream;	/* Accounting */	s->bytecount += size;	s->fragcount++;	/* Recycle buffer */	if (s->mapped) {		sa1100_dma_queue_buffer(s->dma_ch, buf_id,					b->dma_addr, s->fragsize);	} else {		b->size = size;		up(&b->sem);	}	/* And any process polling on write. */	wake_up(&s->wq);}static int audio_sync(struct file *file){	audio_state_t *state = (audio_state_t *)file->private_data;	audio_stream_t *s = state->output_stream;	audio_buf_t *b;	DPRINTK("audio_sync\n");	if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped)		return 0;	/*	 * Send current buffer if it contains data.  Be sure to send	 * a full sample count.	 */	b = s->buf;	if (b->size &= ~3) {		down(&b->sem);		sa1100_dma_queue_buffer(s->dma_ch, (void *) b,					b->dma_addr, b->size);		b->size = 0;		NEXT_BUF(s, buf);	}	/*	 * Let's wait for the last buffer we sent i.e. the one before the	 * current buf_idx.  When we acquire the semaphore, this means either:	 * - DMA on the buffer completed or	 * - the buffer was already free thus nothing else to sync.	 */	b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags);	if (down_interruptible(&b->sem))		return -EINTR;	up(&b->sem);	return 0;}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;	DPRINTK("audio_write: count=%d\n", count);	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->buf;		/* Wait for a buffer to become free */		if (file->f_flags & O_NONBLOCK) {			ret = -EAGAIN;			if (down_trylock(&b->sem))				break;		} else {			ret = -ERESTARTSYS;			if (down_interruptible(&b->sem))				break;		}		/* Feed the current buffer */		chunksize = s->fragsize - b->size;		if (chunksize > count)			chunksize = count;		DPRINTK("write %d to %d\n", chunksize, s->buf_idx);		if (copy_from_user(b->start + b->size, buffer, chunksize)) {			up(&b->sem);			return -EFAULT;		}		b->size += chunksize;		buffer += chunksize;		count -= chunksize;		if (b->size < s->fragsize) {			up(&b->sem);			break;		}		/* Send current buffer to dma */		s->active = 1;		sa1100_dma_queue_buffer(s->dma_ch, (void *) b,					b->dma_addr, b->size);		b->size = 0;	/* indicate that the buffer has been sent */		NEXT_BUF(s, buf);	}	if ((buffer - buffer0))		ret = buffer - buffer0;	DPRINTK("audio_write: return=%d\n", ret);	return ret;}static inline void audio_check_tx_spin(audio_state_t *state){	/*	 * With some codecs like the Philips UDA1341 we must ensure	 * there is an output stream at any time while recording since	 * this is how the UDA1341 gets its clock from the SA1100.	 * So while there is no playback data to send, the output DMA	 * will spin with all zeroes.  We use the cache flush special	 * area for that.	 */	if (state->need_tx_for_rx && !state->tx_spinning) {		sa1100_dma_set_spin(state->output_stream->dma_ch,				    (dma_addr_t)FLUSH_BASE_PHYS, 2048);		state->tx_spinning = 1;	}}static void audio_prime_dma(audio_stream_t *s){	int i;	s->active = 1;	for (i = 0; i < s->nbfrags; i++) {		audio_buf_t *b = s->buf;		down(&b->sem);		sa1100_dma_queue_buffer(s->dma_ch, (void *) b,					b->dma_addr, s->fragsize);		NEXT_BUF(s, buf);	}}static int audio_read(struct file *file, char *buffer,		      size_t count, loff_t * ppos){	char *buffer0 = buffer;	audio_state_t *state = (audio_state_t *)file->private_data;	audio_stream_t *s = state->input_stream;	int chunksize, ret = 0;	DPRINTK("audio_read: count=%d\n", count);	if (ppos != &file->f_pos)		return -ESPIPE;	if (s->mapped)		return -ENXIO;	if (!s->active) {		if (!s->buffers && audio_setup_buf(s))			return -ENOMEM;		audio_check_tx_spin(state);		audio_prime_dma(s);	}	while (count > 0) {		audio_buf_t *b = s->buf;		/* Wait for a buffer to become full */		if (file->f_flags & O_NONBLOCK) {			ret = -EAGAIN;			if (down_trylock(&b->sem))				break;		} else {			ret = -ERESTARTSYS;			if (down_interruptible(&b->sem))				break;		}		/* Grab data from the current buffer */		chunksize = b->size;		if (chunksize > count)			chunksize = count;		DPRINTK("read %d from %d\n", chunksize, s->buf_idx);		if (copy_to_user(buffer,				 b->start + s->fragsize - b->size,				 chunksize)) {			up(&b->sem);			return -EFAULT;		}		b->size -= chunksize;		buffer += chunksize;		count -= chunksize;		if (b->size > 0) {			up(&b->sem);			break;		}		/* Make current buffer available for DMA again */		sa1100_dma_queue_buffer(s->dma_ch, (void *) b,					b->dma_addr, s->fragsize);		NEXT_BUF(s, buf);	}	if ((buffer - buffer0))		ret = buffer - buffer0;	DPRINTK("audio_read: return=%d\n", ret);	return ret;}static int audio_mmap(struct file *file, struct vm_area_struct *vma){	audio_state_t *state = (audio_state_t *)file->private_data;	audio_stream_t *s;	unsigned long size, vma_addr;	int i, ret;	if (vma->vm_pgoff != 0)		return -EINVAL;	if (vma->vm_flags & VM_WRITE) {		if (!state->wr_ref)			return -EINVAL;;		s = state->output_stream;	} else if (vma->vm_flags & VM_READ) {		if (!state->rd_ref)			return -EINVAL;		s = state->input_stream;	} else return -EINVAL;	if (s->mapped)		return -EINVAL;	size = vma->vm_end - vma->vm_start;	if (size != s->fragsize * s->nbfrags)		return -EINVAL;	if (!s->buffers && audio_setup_buf(s))		return -ENOMEM;	vma_addr = vma->vm_start;	for (i = 0; i < s->nbfrags; i++) {

⌨️ 快捷键说明

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