基于uda34x的ALSA声卡驱动之s3c24xx-pcm.c

来源:互联网 发布:商品管理系统java代码 编辑:程序博客网 时间:2024/05/30 22:45
#include <linux/module.h>#include <linux/init.h>#include <linux/io.h>#include <linux/platform_device.h>#include <linux/slab.h>#include <linux/dma-mapping.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/soc.h>#include <asm/dma.h>#include <mach/hardware.h>#include <mach/dma.h>#include <mach/audio.h>#include "s3c24xx-pcm.h"#define S3C24XX_PCM_DEBUG 0#if S3C24XX_PCM_DEBUG#define DBG(x...) printk(KERN_DEBUG "s3c24xx-pcm: " x)#else#define DBG(x...)#endif//定义了一些缓冲区信息,在函数s3c24xx_pcm_open中用于初始化结构体substream->runtime->hwstatic const struct snd_pcm_hardware s3c24xx_pcm_hardware = { .info   = SNDRV_PCM_INFO_INTERLEAVED |        SNDRV_PCM_INFO_BLOCK_TRANSFER |        SNDRV_PCM_INFO_MMAP |        SNDRV_PCM_INFO_MMAP_VALID |        SNDRV_PCM_INFO_PAUSE |        SNDRV_PCM_INFO_RESUME, .formats  = SNDRV_PCM_FMTBIT_S16_LE |        SNDRV_PCM_FMTBIT_U16_LE |        SNDRV_PCM_FMTBIT_U8 |        SNDRV_PCM_FMTBIT_S8, .channels_min  = 2, .channels_max  = 2, .buffer_bytes_max = 128*1024, .period_bytes_min = PAGE_SIZE, .period_bytes_max = PAGE_SIZE*2, .periods_min  = 2, .periods_max  = 128, .fifo_size  = 32,};//缓存管理结构体在函数s3c24xx_pcm_open中挂到runtime->private_data上//该结构体在函数s3c24xx_pcm_hw_params中被初始化//所管理的缓存区域的分配在函数s3c24xx_pcm_preallocate_dma_buffer中struct s3c24xx_runtime_data { spinlock_t lock; int state; unsigned int dma_loaded;//已插入DMA备用存储链的存储段数 unsigned int dma_limit;//缓存段的最大数字 unsigned int dma_period;//每段缓存的最大存储数据量 dma_addr_t dma_start;//缓存区的开始地址 dma_addr_t dma_pos;//第一个未被插入DMA备用缓存链的存储段地址 dma_addr_t dma_end;//缓存区的尾地址 //结构params在函数s3c24xx_pcm_hw_params中被初始化prtd->params = dma; struct s3c24xx_pcm_dma_params *params;};/* s3c24xx_pcm_enqueue * * place a dma buffer onto the queue for the dma system * to handle.*/static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream){ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; dma_addr_t pos = prtd->dma_pos; int ret; DBG("Entered %s\n", __func__); while (prtd->dma_loaded < prtd->dma_limit) {  unsigned long len = prtd->dma_period;  DBG("dma_loaded: %d\n", prtd->dma_loaded);  if ((pos + len) > prtd->dma_end) {   len  = prtd->dma_end - pos;   DBG(KERN_DEBUG "%s: corrected dma len %ld\n",          __func__, len);  }//将缓存插入DMA备用缓存链,pos必须是物理地址,它将被写入DMA的初始化目的或源寄存器  ret = s3c2410_dma_enqueue(prtd->params->channel,   substream, pos, len);  if (ret == 0) {   prtd->dma_loaded++;   pos += prtd->dma_period;   if (pos >= prtd->dma_end)    pos = prtd->dma_start;  } else   break; } prtd->dma_pos = pos;}//当一段缓存用完时该函数将在中断处理函数中被调用static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel,    void *dev_id, int size,    enum s3c2410_dma_buffresult result){ struct snd_pcm_substream *substream = dev_id; struct s3c24xx_runtime_data *prtd; DBG("Entered %s\n", __func__); if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)  return; prtd = substream->runtime->private_data; if (substream)  snd_pcm_period_elapsed(substream); spin_lock(&prtd->lock); if (prtd->state & ST_RUNNING) {  prtd->dma_loaded--;//用完一段缓存,将该缓存插入DMA备用链表尾。整个缓存区为一环形缓存区  s3c24xx_pcm_enqueue(substream); } spin_unlock(&prtd->lock);}static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params){ struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct s3c24xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; //从结构体params->intervals中获取缓冲区大小 unsigned long totbytes = params_buffer_bytes(params); int ret = 0; DBG("Entered %s\n", __func__); /* return if this is a bufferless transfer e.g.  * codec <--> BT codec or GSM modem -- lg FIXME */ if (!dma)  return 0; /* this may get called several times by oss emulation  * with different params -HW */ if (prtd->params == NULL) {  /* prepare DMA */  //结构体dma为s3c24xx_i2s_pcm_stereo_out或s3c24xx_i2s_pcm_stereo_in  //在文件s3c24xx-i2s.c中定义  //结构体params为s3c24xx_runtime_data  prtd->params = dma;  DBG("params %p, client %p, channel %d\n", prtd->params,   prtd->params->client, prtd->params->channel);      //申请一DMA通道  ret = s3c2410_dma_request(prtd->params->channel,       prtd->params->client, NULL);  if (ret < 0) {   DBG(KERN_ERR "failed to get dma channel\n");   return ret;  } }//该函数主要实现chan->callback_fn = rtn;即让chan->callback_fn指向函数s3c24xx_audio_buffdone s3c2410_dma_set_buffdone_fn(prtd->params->channel,        s3c24xx_audio_buffdone);//用已分配的缓存区substream->dma_buffer区初始化substream->runtime的一些变量 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = totbytes; spin_lock_irq(&prtd->lock); prtd->dma_loaded = 0; prtd->dma_limit = runtime->hw.periods_min; prtd->dma_period = params_period_bytes(params);//获取一段缓存的大小 prtd->dma_start = runtime->dma_addr; prtd->dma_pos = prtd->dma_start; prtd->dma_end = prtd->dma_start + totbytes; spin_unlock_irq(&prtd->lock); return 0;}static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream){ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; DBG("Entered %s\n", __func__); /* TODO - do we need to ensure DMA flushed */ snd_pcm_set_runtime_buffer(substream, NULL); if (prtd->params) {  s3c2410_dma_free(prtd->params->channel, prtd->params->client);  prtd->params = NULL; } return 0;}static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream){ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; DBG("Entered %s\n", __func__); /* return if this is a bufferless transfer e.g.  * codec <--> BT codec or GSM modem -- lg FIXME */ if (!prtd->params)  return 0; /* channel needs configuring for mem=>device, increment memory addr,  * sync to pclk, half-word transfers to the IIS-FIFO. */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {//如果为放音  s3c2410_dma_devconfig(prtd->params->channel,//配置初始化目的寄存器和相应控制寄存器    S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC |    S3C2410_DISRCC_APB, prtd->params->dma_addr);  s3c2410_dma_config(prtd->params->channel,    prtd->params->dma_size,//配置DMA控制寄存器,将配置值存于chan->dcon = dcon;    S3C2410_DCON_SYNC_PCLK |    S3C2410_DCON_HANDSHAKE); } else {//录音,  s3c2410_dma_config(prtd->params->channel,    prtd->params->dma_size,//配置初始化源寄存器和相应控制寄存器    S3C2410_DCON_HANDSHAKE |    S3C2410_DCON_SYNC_PCLK);  s3c2410_dma_devconfig(prtd->params->channel,     S3C2410_DMASRC_HW, 0x3,     prtd->params->dma_addr); } /* flush the DMA channel */ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); prtd->dma_loaded = 0; prtd->dma_pos = prtd->dma_start; /* enqueue dma buffers */ s3c24xx_pcm_enqueue(substream);//将缓存链插入DMA备用缓存链 return ret;}static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd){ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; DBG("Entered %s\n", __func__); spin_lock(&prtd->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:  prtd->state |= ST_RUNNING;//装载DMA缓存,开始DMA数据传输  s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);  s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STARTED);  break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:  prtd->state &= ~ST_RUNNING;//停止DMA传输  s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP);  break; default:  ret = -EINVAL;  break; } spin_unlock(&prtd->lock); return ret;}static snd_pcm_uframes_ts3c24xx_pcm_pointer(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; unsigned long res; dma_addr_t src, dst; DBG("Entered %s\n", __func__); spin_lock(&prtd->lock);//读取DMA的当前目的寄存器和当前源寄存器 s3c2410_dma_getposition(prtd->params->channel, &src, &dst); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)  res = dst - prtd->dma_start;//计算已传输的数据 else  res = src - prtd->dma_start; spin_unlock(&prtd->lock); DBG("Pointer %x %x\n", src, dst); /* we seem to be getting the odd error from the pcm library due  * to out-of-bounds pointers. this is maybe due to the dma engine  * not having loaded the new values for the channel before being  * callled... (todo - fix )  */ if (res >= snd_pcm_lib_buffer_bytes(substream)) {  if (res == snd_pcm_lib_buffer_bytes(substream))   res = 0; } return bytes_to_frames(substream->runtime, res);//将缓存中的数据转换成帧}static int s3c24xx_pcm_open(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd; DBG("Entered %s\n", __func__);//用s3c24xx_pcm_hardware初始化结构体substream->runtime->hw snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);//为结构体s3c24xx_runtime_data分配内存 prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL); if (prtd == NULL)  return -ENOMEM; spin_lock_init(&prtd->lock); runtime->private_data = prtd;// return 0;}static int s3c24xx_pcm_close(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; DBG("Entered %s\n", __func__); if (!prtd)  DBG("s3c24xx_pcm_close called with prtd == NULL\n"); kfree(prtd); return 0;}static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma){ struct snd_pcm_runtime *runtime = substream->runtime; DBG("Entered %s\n", __func__);//关联一些用户空间地址到设备内存. 无论何时程序在给定范围内读或写, 它实际上是在存取设备 return dma_mmap_writecombine(substream->pcm->card->dev, vma,         runtime->dma_area,         runtime->dma_addr,         runtime->dma_bytes);}//播放和录音的数据流操作函数static struct snd_pcm_ops s3c24xx_pcm_ops = { .open  = s3c24xx_pcm_open, .close  = s3c24xx_pcm_close, .ioctl  = snd_pcm_lib_ioctl, .hw_params = s3c24xx_pcm_hw_params, .hw_free = s3c24xx_pcm_hw_free, .prepare = s3c24xx_pcm_prepare, .trigger = s3c24xx_pcm_trigger, .pointer = s3c24xx_pcm_pointer, .mmap  = s3c24xx_pcm_mmap,};//为数据缓存区分配一段内存static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream){ struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; size_t size = s3c24xx_pcm_hardware.buffer_bytes_max; DBG("Entered %s\n", __func__); buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; buf->area = dma_alloc_writecombine(pcm->card->dev, size,        &buf->addr, GFP_KERNEL); if (!buf->area)  return -ENOMEM; buf->bytes = size; return 0;}static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm){ struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; int stream; DBG("Entered %s\n", __func__); for (stream = 0; stream < 2; stream++) {  substream = pcm->streams[stream].substream;  if (!substream)   continue;  buf = &substream->dma_buffer;  if (!buf->area)   continue;  dma_free_writecombine(pcm->card->dev, buf->bytes,          buf->area, buf->addr);  buf->area = NULL; }}static u64 s3c24xx_pcm_dmamask = DMA_32BIT_MASK;//分配播放和录音缓存区static int s3c24xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm){ int ret = 0; DBG("Entered %s\n", __func__); if (!card->dev->dma_mask)  card->dev->dma_mask = &s3c24xx_pcm_dmamask; if (!card->dev->coherent_dma_mask)  card->dev->coherent_dma_mask = 0xffffffff; if (dai->playback.channels_min) {  ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,   SNDRV_PCM_STREAM_PLAYBACK);  if (ret)   goto out; } if (dai->capture.channels_min) {  ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,   SNDRV_PCM_STREAM_CAPTURE);  if (ret)   goto out; } out: return ret;}//pcm实例的初始化函数和数据流操作函数struct snd_soc_platform s3c24xx_soc_platform = { .name  = "s3c24xx-audio", .pcm_ops  = &s3c24xx_pcm_ops, .pcm_new = s3c24xx_pcm_new, .pcm_free = s3c24xx_pcm_free_dma_buffers,};EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);static int __init s3c24xx_soc_platform_init(void){//将s3c24xx_soc_platform 挂到链表platform_list上,调用函数snd_soc_instantiate_card, //如果本声卡没有初始化,则从各链表上取下相应结构体,并调用各结构体中的初始化函数, return snd_soc_register_platform(&s3c24xx_soc_platform);}module_init(s3c24xx_soc_platform_init);static void __exit s3c24xx_soc_platform_exit(void){ snd_soc_unregister_platform(&s3c24xx_soc_platform);}module_exit(s3c24xx_soc_platform_exit);MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module");MODULE_LICENSE("GPL");
来源:http://chxxxyg.blog.163.com/blog/static/150281193201033105123937/