📄 omap-audio.c
字号:
/* * linux/sound/oss/omap-audio.c * * Common audio handling for the OMAP processors * * Copyright (C) 2004 Texas Instruments, Inc. * * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org> * * This package 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. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * History: * * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 * and 2420 platforms. * * 2004-11-01 Nishanth Menon - modified to support 16xx and 17xx * platform multi channel chaining. * * 2004-11-04 Nishanth Menon - Added support for power management *//***************************** INCLUDES ************************************/#include <linux/config.h>#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 <linux/delay.h>#include <linux/device.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/semaphore.h>#include "omap-audio-dma-intfc.h"#include "omap-audio.h"/***************************** MACROS ************************************/#undef DEBUG//#define DEBUG#ifdef DEBUG#define DPRINTK printk#define FN_IN printk("[omap_audio.c:[%s] start\n", __FUNCTION__)#define FN_OUT(n) printk(" end\n")#else#define DPRINTK( x... )#define FN_IN#define FN_OUT(x)#endif#define OMAP_AUDIO_NAME "omap-audio"#define AUDIO_NBFRAGS_DEFAULT 8#define AUDIO_FRAGSIZE_DEFAULT 8192/* HACK ALERT!: These values will bave to be tuned as this is a trade off b/w * Sampling Rate vs buffer size and delay we are prepared to do before giving up */#define MAX_QUEUE_FULL_RETRIES 1000000#define QUEUE_WAIT_TIME 10#if !(defined( CONFIG_ARCH_OMAP16XX))#define OMAP_AUDIO_DMA_RETRY 1#endif#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)#define SPIN_ADDR (dma_addr_t)0#define SPIN_SIZE 2048/***************************** MODULES SPECIFIC FUNCTION PROTOTYPES ********************/static int audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos);static int audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos);static int audio_mmap(struct file *file, struct vm_area_struct *vma);static unsigned int audio_poll(struct file *file, struct poll_table_struct *wait);static loff_t audio_llseek(struct file *file, loff_t offset, int origin);static int audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg);static int audio_open(struct inode *inode, struct file *file);static int audio_release(struct inode *inode, struct file *file);static int audio_probe(struct device *dev);static int audio_remove(struct device *dev);static void audio_shutdown(struct device *dev);static int audio_suspend(struct device *dev, u32 state, u32 level);static int audio_resume(struct device *dev, u32 level);static void audio_free(struct device *dev);/***************************** Data Structures **********************************//* * The function pointer set to be registered by the codec. */static audio_state_t audio_state = { 0 };/* DMA Call back function */static dma_callback_t audio_dma_callback = 0;/* File Ops structure */static struct file_operations omap_audio_fops = { .open = audio_open, .release = audio_release, .write = audio_write, .read = audio_read, .mmap = audio_mmap, .poll = audio_poll, .ioctl = audio_ioctl, .llseek = audio_llseek, .owner = THIS_MODULE};/* Driver information */static struct device_driver omap_audio_driver = { .name = OMAP_AUDIO_NAME, .bus = &platform_bus_type, .probe = audio_probe, .remove = audio_remove, .suspend = audio_suspend, .resume = audio_resume, .shutdown = audio_shutdown,};/* Device Information */static struct platform_device omap_audio_device = { .name = OMAP_AUDIO_NAME, .dev = { .driver_data = &audio_state, .release = audio_free, }, .id = 0,};/***************************** GLOBAL FUNCTIONs **********************************//* Power Management Functions for Linux Device Model *//* DEBUG PUPOSES ONLY! */#ifdef CONFIG_PM//#undef CONFIG_PM#endif#ifdef CONFIG_PM/********************************************************************************* * * audio_ldm_suspend(): Suspend operation * *********************************************************************************/static int audio_ldm_suspend(void *data){ audio_state_t *state = data; FN_IN; if (AUDIO_ACTIVE(state) && state->hw_init) { printk(KERN_ERR "Audio device Active, Cannot Suspend"); return -EPERM;#if 0 /* NOTE: * This Piece of code is commented out in hope * That one day we would need to suspend the device while * audio operations are in progress and resume the operations * once the resume is done. * This is just a sample implementation of how it could be done. * Currently NOT SUPPORTED */ audio_stream_t *is = state->input_stream; audio_stream_t *os = state->output_stream; int stopstate; if (is && is->buffers) { printk("IS Suspend\n"); stopstate = is->stopped; audio_stop_dma(is); DMA_CLEAR(is); is->dma_spinref = 0; is->stopped = stopstate; } if (os && os->buffers) { printk("OS Suspend\n"); stopstate = os->stopped; audio_stop_dma(os); DMA_CLEAR(os); os->dma_spinref = 0; os->stopped = stopstate; }#endif } FN_OUT(0); return 0;}/********************************************************************************* * * audio_ldm_resume(): Resume Operations * *********************************************************************************/static int audio_ldm_resume(void *data){ audio_state_t *state = data; FN_IN; if (AUDIO_ACTIVE(state) && state->hw_init) { /* Should never occur - since we never suspend with active state */ BUG(); return -EPERM;#if 0 /* NOTE: * This Piece of code is commented out in hope * That one day we would need to suspend the device while * audio operations are in progress and resume the operations * once the resume is done. * This is just a sample implementation of how it could be done. * Currently NOT SUPPORTED */ audio_stream_t *is = state->input_stream; audio_stream_t *os = state->output_stream; if (os && os->buffers) { printk("OS Resume\n"); audio_reset(os); audio_process_dma(os); } if (is && is->buffers) { printk("IS Resume\n"); audio_reset(is); audio_process_dma(is); }#endif } FN_OUT(0); return 0;}#endif /* End of #ifdef CONFIG_PM *//********************************************************************************* * * audio_free(): The Audio driver release function * This is a dummy function required by the platform driver * *********************************************************************************/static void audio_free(struct device *dev){ /* Nothing to Release! */}/********************************************************************************* * * audio_probe(): The Audio driver probe function * WARNING!!!! : It is expected that the codec would have registered with us by now * *********************************************************************************/static int audio_probe(struct device *dev){ int ret; FN_IN; if (!audio_state.hw_probe) { printk(KERN_ERR "Probe Function Not Registered\n"); return -ENODEV; } ret = audio_state.hw_probe(); FN_OUT(ret); return ret;}/********************************************************************************* * * audio_remove() Function to handle removal operations * *********************************************************************************/static int audio_remove(struct device *dev){ FN_IN; if (audio_state.hw_remove) { DPRINTK("Calling Hadware remove \n"); audio_state.hw_remove(); } FN_OUT(0); return 0;}/********************************************************************************* * * audio_shutdown(): Function to handle shutdown operations * *********************************************************************************/static void audio_shutdown(struct device *dev){ FN_IN; if (audio_state.hw_cleanup) { DPRINTK("Calling Hadware Cleanup \n"); audio_state.hw_cleanup(); } FN_OUT(0); return;}/********************************************************************************* * * audio_suspend(): Function to handle suspend operations * *********************************************************************************/static int audio_suspend(struct device *dev, u32 state, u32 level){ int ret = 0;#ifdef CONFIG_PM void *data = dev->driver_data; FN_IN; if (level != 3) { return 0; } if (audio_state.hw_suspend) { DPRINTK("Calling Hadware suspend(level=%d) \n", level); ret = audio_ldm_suspend(data); if (ret == 0) ret = audio_state.hw_suspend(); } if (ret) { printk(KERN_INFO "Audio Suspend Failed \n"); } else { printk(KERN_INFO "Audio Suspend Success \n"); }#endif /* CONFIG_PM */ FN_OUT(ret); return ret;}/********************************************************************************* * * audio_resume(): Function to handle resume operations * *********************************************************************************/static int audio_resume(struct device *dev, u32 level){ int ret = 0;#ifdef CONFIG_PM void *data = dev->driver_data; FN_IN; if (level != 0) { return 0; } if (audio_state.hw_resume) { DPRINTK("Calling Hadware resume(level=%d) \n", level); ret = audio_ldm_resume(data); if (ret == 0) ret = audio_state.hw_resume(); } if (ret) { printk(KERN_INFO " Audio Resume Failed \n"); } else { printk(KERN_INFO " Audio Resume Success \n"); }#endif /* CONFIG_PM */ FN_OUT(ret); return ret;}/********************************************************************************* * * audio_get_fops(): Return the fops required to get the function pointers of * OMAP Audio Driver * *********************************************************************************/struct file_operations *audio_get_fops(void){ FN_IN; FN_OUT(0); return &omap_audio_fops;}/********************************************************************************* * * audio_register_codec(): Register a Codec fn points using this function * WARNING!!!!! : Codecs should ensure that they do so! no sanity checks * during runtime is done due to obvious performance * penalties. * *********************************************************************************/int audio_register_codec(audio_state_t * codec_state){ int ret; FN_IN; /* We dont handle multiple codecs now */ if (audio_state.hw_init) { printk(KERN_ERR " Codec Already registered\n"); return -EPERM; } /* Grab the dma Callback */ audio_dma_callback = audio_get_dma_callback(); if (!audio_dma_callback) { printk(KERN_ERR "Unable to get call back function\n"); return -EPERM; } /* Sanity checks */ if (!codec_state) { printk(KERN_ERR "NULL ARGUMENT!\n"); return -EPERM; } if (!codec_state->hw_probe || !codec_state->hw_init || !codec_state->hw_shutdown || !codec_state->client_ioctl) { printk(KERN_ERR "Required Fn Entry point Missing probe=%p init=%p,down=%p,ioctl=%p!\n", codec_state->hw_probe, codec_state->hw_init, codec_state->hw_shutdown, codec_state->client_ioctl); return -EPERM; } memcpy(&audio_state, codec_state, sizeof(audio_state_t)); ret = platform_device_register(&omap_audio_device); if (ret != 0) { printk(KERN_ERR "Platform dev_register failed =%d\n", ret); ret = -ENODEV; goto register_out; } ret = driver_register(&omap_audio_driver); if (ret != 0) { printk(KERN_ERR "Device Register failed =%d\n", ret); ret = -ENODEV; platform_device_unregister(&omap_audio_device); goto register_out; } register_out: FN_OUT(ret); return ret;}/********************************************************************************* * * audio_unregister_codec(): Un-Register a Codec using this function * *********************************************************************************/int audio_unregister_codec(audio_state_t * codec_state){ FN_IN; /* We dont handle multiple codecs now */ if (!audio_state.hw_init) { printk(KERN_ERR " No Codec registered\n"); return -EPERM; } /* Security check */ if (audio_state.hw_init != codec_state->hw_init) { printk(KERN_ERR " Attempt to unregister codec which was not registered with us\n"); return -EPERM; } driver_unregister(&omap_audio_driver); platform_device_unregister(&omap_audio_device); memset(&audio_state, 0, sizeof(audio_state_t)); FN_OUT(0); return 0;}/***************************** MODULES SPECIFIC FUNCTION *************************//********************************************************************************* * * audio_write(): Exposed to write() call * *********************************************************************************/static intaudio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos){ const char *buffer0 = buffer; audio_state_t *state = file->private_data; audio_stream_t *s = state->output_stream; int chunksize, ret = 0;#ifdef OMAP_AUDIO_DMA_RETRY int ret1 = 0, try_count = 0;#endif DPRINTK("audio_write: count=%d\n", count); if (*ppos != file->f_pos) { printk("FPOS not ppos ppos=0x%x fpos =0x%x\n", (u32) * ppos, (u32) file->f_pos); return -ESPIPE; } if (s->mapped) { printk("s already mapped\n"); return -ENXIO; } if (!s->buffers && audio_setup_buf(s)) { printk("NO MEMORY\n"); return -ENOMEM; } while (count > 0) { audio_buf_t *b = &s->buffers[s->usr_head]; /* Wait for a buffer to become free */ 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; DPRINTK("write %d to %d\n", chunksize, s->usr_head); if (copy_from_user(b->data + b->offset, buffer, chunksize)) { printk(KERN_ERR "Audio: CopyFrom User failed \n"); up(&s->sem); return -EFAULT; } buffer += chunksize; count -= chunksize; b->offset += chunksize; DPRINTK("count: %d, chunksize: %d, offset: %d\n", count, chunksize, b->offset); if (b->offset < s->fragsize) { up(&s->sem); break; } /* Update pointers and send current fragment to DMA */ b->offset = 0; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; /* Add the num of frags pending */ s->pending_frags++; s->active = 1;#ifdef OMAP_AUDIO_DMA_RETRY try_count = 0; ret1 = 0; do { ret1 = audio_process_dma(s); if (ret1 == -2) { /* Queue full? */ udelay(QUEUE_WAIT_TIME); /* give a chance to empty queue */ } try_count++; } while ((try_count < MAX_QUEUE_FULL_RETRIES) && (ret1 == -2));#else audio_process_dma(s);#endif#ifdef OMAP_AUDIO_DMA_RETRY if (ret1) { printk(KERN_ERR "audio_write: Queue is Full or dma processing error (%d)!\n", ret1); break; }#endif }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -