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

最近在调试音频,虽然AC97 但是其实思路还是一样的。转篇别人写的文章记录下

 

原文地址:

 

http://chxxxyg.blog.163.com/blog/static/150281193201033105123937/

 

 

#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->hw
static 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_t
s3c24xx_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");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值