1、参数设置分析

(1)open: soc_pcm_open 依次调用cpu_dai, dma, codec_dai, machine的open或startup函数





只在dma的open函数里添加参数相关的代码





(2)SNDRV_PCM_IOCTL_HW_PARAMS: soc_pcm_hw_params 依次调用machine,codec_dai,cpu_dai,platform(dma)的hw_params函数

    

在uda1341.c, s3c2440-iis.c里实现hw_params函数(把裸板程序里面的相关代码移过来)





(s3c2440-dma.c 主要涉及数据传输,在下一节实现hw_params函数)

(3)打开声卡的时候会调用到machine部分的dma.c的snd_pcm_ops的dma_open函数)

2、open函数

(1)s3c2440_dma.c(Platform)

static int s3c2440_dma_open(struct snd_pcm_substream *substream)

{

struct snd_pcm_runtime *runtime = substream->runtime;

    int ret;

    /* 设置属性 */

snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);

snd_soc_set_runtime_hwparams(substream, &s3c2440_dma_hardware);

    }

return 0;

}

(2)snd_pcm_hardware结构体

static const struct snd_pcm_hardware s3c2440_dma_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,

};



3、hw_params函数(硬件参数)

(1)uda1341.c(codec)

static int uda1341_hw_params(struct snd_pcm_substream *substream,

struct snd_pcm_hw_params *params,

struct snd_soc_dai *dai)

{

    /* 根据params的值,设置UDA1341的寄存器 

     * 比如时钟设置,格式

     */

    /* 为了简单, 在uda1341_init_regs里就设置好时钟、格式等参数 */

    return 0;

}



Uda1341的初始化

static void uda1341_init_regs(struct snd_soc_codec *codec)

{





/* GPB 4: L3CLOCK */

/* GPB 3: L3DATA */

/* GPB 2: L3MODE */

    *gpbcon &= ~((3<<4) | (3<<6) | (3<<8));

    *gpbcon |= ((1<<4) | (1<<6) | (1<<8));



    uda1341_write_reg(codec, UDA1341_STATUS0, 0x40 | STAT0_SC_384FS | STAT0_DC_FILTER); // reset uda1341

    uda1341_write_reg(codec, UDA1341_STATUS1, STAT1_ADC_ON | STAT1_DAC_ON);





    uda1341_write_reg(codec, UDA1341_DATA00, DATA0_VOLUME(0x0)); // maximum volume

    uda1341_write_reg(codec, UDA1341_DATA01, DATA1_BASS(0)| DATA1_TREBLE(0));

    uda1341_write_reg(codec, UDA1341_DATA10, 0);  // not mute

}



驱动程序函数uda1341_soc_probe调用uda1341_init_regs函数

/* 所有寄存器的默认值 */

static const char uda1341_reg[UDA1341_REG_NUM] = {

    /* DATA0 */

    0x00, 0x40, 0x80,

    

/* Extended address registers */

0x04, 0x04, 0x04, 0x00, 0x00, 0x00,





    /* data1 */

    0x00,

    

    /* status regs */

    0x00, 0x83,

};

probe函数

static int uda1341_soc_probe(struct snd_soc_codec *codec)

{

    int ret;

    uda1341_init_regs(codec);

    return ret;

}



(2)s3c2440_iis.c(Platform)

static int s3c2440_i2s_hw_params(struct snd_pcm_substream
*substream,

struct snd_pcm_hw_params *params,

struct snd_soc_dai *dai)

{

    /* 根据params设置IIS控制器 */





    int tmp_fs;

    int i;

    int min = 0xffff;

    int pre = 0;

    unsigned int fs;

    struct clk *clk = clk_get(NULL, "pclk");



入口函数映射寄存器(出口函数取消映射)

    /*gpecon   = ioremap(0x56000040, 4);//0x56000040是物理地址,大小是4字节

    iis_regs = ioremap(0x55000000, sizeof(struct s3c2440_iis_regs));*/

    /* 配置GPIO用于IIS */

*gpecon &= ~((3<<0) | (3<<2) | (3<<4) | (3<<6) | (3<<8));

    *gpecon |= ((2<<0) | (2<<2) | (2<<4) | (2<<6) | (2<<8));

    

    

    /* bit[9] : Master clock select, 0-PCLK

     * bit[8] : 0 = Master mode

     * bit[7:6] : 10 = Transmit mode

     * bit[4] : 0-IIS compatible format

     * bit[2] : 384fs, 确定了MASTER CLOCK之后, fs = MASTER CLOCK/384

     * bit[1:0] : Serial bit clock frequency select, 32fs

     */

     寄存器映射(写入一个结构体后再映射)

static volatile struct s3c2440_iis_regs *iis_regs;

struct s3c2440_iis_regs {

    unsigned int iiscon ; 

    unsigned int iismod ; 

    unsigned int iispsr ; 

    unsigned int iisfcon; 

    unsigned int iisfifo; 

};

    iis_regs = ioremap(0x55000000, sizeof(struct s3c2440_iis_regs));//寄存器映射

if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)//每个采样值占据的位数(是8位还是16位根据params决定)

        iis_regs->iismod = (2<<6) | (0<<4) | (1<<3) | (1<<2) | (1);

    else if (params_format(params) == SNDRV_PCM_FORMAT_S8)

        iis_regs->iismod = (2<<6) | (0<<4) | (0<<3) | (1<<2) | (1);

    else

        return -EINVAL;//其他值是错误的

    struct clk *clk = clk_get(NULL, "pclk");

PCLK=clk_get_rate(clk);最后要用clk_put(clk);

    /* Master clock = PCLK/(n+1)

     * fs = Master clock / 384//fs是采样率

     * fs = PCLK / (n+1) / 384

     */

    fs = params_rate(params);//采样频率根据params得到

    for (i = 0; i <= 31; i++)

    {

        tmp_fs = clk_get_rate(clk)/384/(i+1);

        if (ABS(tmp_fs, fs) < min)

        {

            min = ABS(tmp_fs, fs);

            pre = i;

        }

    }

    iis_regs->iispsr = (pre << 5) | (pre);





    /*

     * bit15 : Transmit FIFO access mode select, 1-DMA

     * bit13 : Transmit FIFO, 1-enable

     */

    iis_regs->iisfcon = (1<<15) | (1<<13);

    

    /*

     * bit[5] : Transmit DMA service request, 1-enable

     * bit[1] : IIS prescaler, 1-enable

     */

    iis_regs->iiscon = (1<<5) | (1<<1) ;





    clk_put(clk);

    

    return 0;

}

(3)s3c2440_dma.c(Platform)

static int s3c2440_dma_hw_params(struct snd_pcm_substream *substream,

struct snd_pcm_hw_params *params)

{

struct snd_pcm_runtime *runtime = substream->runtime;

unsigned long totbytes = params_buffer_bytes(params);

    

    /* 根据params设置DMA */

snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);





    /* s3c2440_dma_new分配了很大的DMA BUFFER

     * params决定使用多大

     */

runtime->dma_bytes            = totbytes;

    playback_dma_info.buffer_size = totbytes;

    playback_dma_info.period_size = params_period_bytes(params);





    return 0;

}



4、uda1341.c

codec部分

static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {

    .probe = uda1341_soc_probe,

    /* UDA1341的寄存器不支持读操作,只支持写操作

     * 要知道某个寄存器的当前值,

     * 只能在写入时保存起来(cache)

     */

.reg_cache_size = sizeof(uda1341_reg), //存放的是寄存器的值(cache有多大,看看寄存器个数)

.reg_word_size = sizeof(u8),//每个寄存器占的数据位数

.reg_cache_default = uda1341_reg,//默认值

.reg_cache_step = 1,

.read  = uda1341_read_reg_cache,//读寄存器的函数

.write = uda1341_write_reg,  /* 写寄存器 */

};

(1)读寄存器(支持寄存器的读操作的编解码芯片可用读寄存器函数)

/*

 * The codec has no support for reading its registers except for peak level...

 */

对于uda1341智能在cache中读出来

static inline unsigned int uda1341_read_reg_cache(struct snd_soc_codec *codec,

unsigned int reg)

{

u8 *cache = codec->reg_cache;//对于uda1341智能在cache中读出来





if (reg >= UDA1341_REG_NUM)//寄存器的个数大于某个值,返回-1

return -1;

return cache[reg];

}

(2)写寄存器

static int uda1341_write_reg(struct snd_soc_codec *codec, unsigned int reg,

unsigned int value)

{

u8 *cache = codec->reg_cache;//把写寄存器的值写到cache里面去(备份下来,因为uda1341不允许读寄存器操作)





    /* 先保存 */

if (reg >= UDA1341_REG_NUM)//寄存器个数为12

return -1;

cache[reg] = value;//保存值





    /* 再写入硬件 */

    

    /* 对于EA(扩展寄存器),需要调用2次l3_write */先把EA地址值作为数据发送出去,再把ED作为数据值发送出去

    if ((reg >= UDA1341_EA000) && (reg <= UDA1341_EA110))

    { //左边的参数是地址(data0),右边的参数是数据  (uda1341_reg_addr[reg]对应的是扩展地址),最高两位或上1

        l3_write(UDA1341_DATA0_ADDR, uda1341_reg_addr[reg] | UDA1341_EXTADDR_PREFIX);

        l3_write(UDA1341_DATA0_ADDR, value | UDA1341_EXTDATA_PREFIX);

    }

    else

    {

        l3_write(uda1341_reg_addr[reg], value | uda1341_data_bit[reg]);//我们访问某个寄存器的时候,数据值要或上某一些位,才能定位到uda1341的某一类下的某个寄存器

    }





    return 0;

}

dai部分

static struct snd_soc_dai_driver uda1341_dai = {

.ops = &uda1341_dai_ops,

};

static const struct snd_soc_dai_ops uda1341_dai_ops = {

.hw_params
 = uda1341_hw_params,

};

5、uda1341硬件分析

(1)2440通过3条线连接uda1341,想写uda1341的寄存器,肯定需要地址和数据,L3MODE等于0时表示线L3DATA线上传输的是地址,为1时传输的是数据。L3CLOCK表示每一个时钟 传输1位。

(2)看芯片手册(UDA1341TS.pdf)有多少个寄存器

数据位7~2代表设备地址(表示uda1341),数据位0和1表示地址

(3)L3接口

先发出地址,再发出数据。地址里bit7~bit2用于表示uda1341,数据位0和1表示访问哪类寄存器,有3类

data0类能访问多少个寄存器

可认为第1、2、3三行代表的是寄存器,第4、5行不是寄存器,是扩展地址,发出的数据是data0这1类,并且前面两个数据是11的话,后面的3位表示扩展地址。

想访问某个扩展地址,如访问EA,先是地址,后是数据(数据是扩展寄存器的地址(高两位是1))

下面的1、2、3、4、5、6、7代表寄存器,对于data0这1类有9个寄存器

data1这1类只有1个

status这1类,先发出地址,再发出数据,如果数据的最高位是0,选第1行数据,否则选第2行数据。

所以uda1341会根据第2个周期的某些位来分辨访问哪些寄存器,共有3类12个寄存器。

ALSA声卡09_从零编写之参数设置_学习笔记的更多相关文章

  1. ALSA声卡12_从零编写之添加音量控制_学习笔记

    1.设置音量时应用程序的调用过程 (1)strace分析: amixer cset numid=1 30 (设置音量) /dev/snd/controlC0 open SNDRV_CTL_IOCTL_ ...

  2. ALSA声卡10_从零编写之数据传输_学习笔记

    1.引言 (1)应用程序使用声卡的时候,数据流程是:应用程序把数据发送给驱动,驱动把数据发送给硬件声卡,声卡把数据转换成声音数据播放出去. (2)可以使用两种方式发送数据 第一种:app发数据,等驱动 ...

  3. ALSA声卡08_从零编写之框架_学习笔记

    1.整体框架 (1)图示((DAI(全称Digital Audio Interface)接口)) 在嵌入式系统里面,声卡驱动是ASOC,是在ALSA驱动上封装的一层,包括以下三大块 (2)程序框架 m ...

  4. ALSA声卡11_从零编写之调试——学习笔记

    1.调试 (1)把程序拷贝到服务器上进行编译 (2)把程序放到内核上面去 重新配置内核,吧原来的声卡驱动程序去掉 a. 修改语法错误 11th_myalsa b. 配置内核去掉原来的声卡驱动 -> ...

  5. ALSA声卡16_编写ALSA声卡应用程序_学习笔记

    1.体验 (1)ALSA声卡使用体验:使用arecord录音,使用aplay播放,在Alsa-utils里面) 准备: cd linux-3.4.2 patch -p1 < ../linux-3 ...

  6. ALSA声卡07_分析调用过程_学习笔记

    1.编译新的strace工具分析aplay和amixer应用程序对声卡的调用过程 (1)因为旧的strace工具不能识别不能识别alsa声卡驱动程序里面的ioctrl. (2)编译过程参考http:/ ...

  7. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  8. 《从零開始学Swift》学习笔记(Day 61)——Core Foundation框架之内存管理

    原创文章,欢迎转载. 转载请注明:关东升的博客 在Swift原生数据类型.Foundation框架数据类型和Core Foundation框架数据类型之间转换过程中,尽管是大部分是能够零开销桥接,零开 ...

  9. 零基础学Java之Java学习笔记(二):Java快速入门

    提出要求: 开发一个 HelloWorld.java 程序,可以输出 "Hello,world!" 开发步骤: (1)将 Java 代码编写到扩展名为 HelloWorld.jav ...

随机推荐

  1. MoreEffectiveC++Item35 条款26: 限制某个class所能产生的对象个数

    一 允许零个或一个对象 我们知道每当即将产生一个对象,我们有一个constructor被调用,那么我们现在想组织某个对象的产生,最简单的方法就是将其构造函数声明成private(这样做同事防止了这个类 ...

  2. New Concept English three (23)

    31w 45 People become quite illogical when they try to decide what can be eaten and what cannot be ea ...

  3. request参数获取的几种方式

    经常用到Request.QueryString[""]和Request.Form[""]来获取参数值. 今天照常用,ajax方式向后台传参,先用的Request ...

  4. SpringMVC札集(04)——SpringMVC传递参数

    自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onL ...

  5. [置顶] JVM层对jar包字节码加密

    github https://github.com/sea-boat/ByteCodeEncrypt 需求 拿到的需求是要对某特定的jar包实现加密保护,jar包需要提供给外部使用,但核心逻辑部分需要 ...

  6. Golang的简明安装指南

    引言: Go language现在是大名鼎鼎,很多的开源项目都是基于go来做的,比如codis, ethereum等都用到了go lang,所以免不了要使用这个东东,本文将简明介绍安装步骤以及环境变量 ...

  7. URL中不应出现汉字

    浏览器会在跳转时,先将汉字编译成 Unicode,然后跳转,导致hash值改变两次,相当于history 中 加入了两次URL 

  8. HihoCoder 1055 : 刷油漆 树形DP第一题(对象 点)

    刷油漆 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上回说到,小Ho有着一棵灰常好玩的树玩具!这棵树玩具是由N个小球和N-1根木棍拼凑而成,这N个小球都被小Ho标上了 ...

  9. linux自学(九)之开始centos学习,安装数据库MariaDB

    上一篇:linux自学(八)之开始centos学习,安装tomcat 数据库我们不安装mysql,我网上看了好多资料发现mysql安装比较麻烦,我们这里安装同一个父亲的产品MariaDB.驱动,端口等 ...

  10. 《DSP using MATLAB》示例Example 6.12

    上代码: % x = -8:7 y = TwosComplement(x, 4) y = dec2bin(y, 4); disp(sprintf('%s', [y'; char(ones(1, 16) ...