#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/