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. ROW_NUMBER() OVER(PARTITION BY)

    select * from (select *,ROW_NUMBER() OVER(PARTITION BY GoodsID ORDER BY IsMain desc,OrderNum) as Mai ...

  2. ADO Recordset 对象链接

    http://baike.baidu.com/link?url=4Xdc46R8M5uj-BbOGaH761N5oDEYlGQJFeR2WbPwx1iQBusAUKU3qbWcHZCMmayatj9n ...

  3. 深入理解Hystrix之文档翻译

    转载请标明出处: http://blog.csdn.net/forezp/article/details/75333088 本文出自方志朋的博客 什么是Hystrix 在分布式系统中,服务与服务之间依 ...

  4. 我也说说Emacs吧(5) - 基本编辑操作

    基本编辑操作 进入编辑模式 标准的emacs用户是遇不到这一节的,因为默认就可以编辑.但是spacemacs用户需要先学习一下强大的vi的模式切换功能了. vi的一个重要特点就是命令特别多,所以一旦学 ...

  5. 【剑指offer】08二叉树的下一个节点,C++实现

    原创博文,转载请注明出处! # 题目 父节点指向子节点的指针用实线表示,从子节点指向父节点的指针用虚线表示. # 思路 如果节点有右子节点,则右子节点的最左节点是该节点的下一个节点.例如,寻找b的下一 ...

  6. 每天一个linux命令:【转载】pwd命令

    Linux中用 pwd 命令来查看”当前工作目录“的完整路径. 简单得说,每当你在终端进行操作时,你都会有一个当前工作目录. 在不太确定当前位置时,就会使用pwd来判定当前目录在文件系统内的确切位置. ...

  7. Codeforces1070 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred)总结

    第一次打ACM比赛,和yyf两个人一起搞事情 感觉被两个学长队暴打的好惨啊 然后我一直做傻子题,yyf一直在切神仙题 然后放一波题解(部分) A. Find a Number LINK 题目大意 给你 ...

  8. Spring Cloud微服务框架介绍

    Spring Cloud为开发人员提供了一整套的快速构建分布式应用的工具,入服务注册.服务发现.熔断.负载均衡.路由等,提供了开箱即用的各种依赖以及良好的可扩展机制. 目前在Spring Cloud的 ...

  9. oralce之 10046对Hash Join分析

    前两天解决了一个优化SQL的case,SQL语句如下,big_table为150G大小,small_table很小,9000多条记录,不到1M大小,hash_area_size, sort_area_ ...

  10. 数据双向绑定页面无反应(angularjs)

    问题引入 使用 angularjs进行过一段时间的开发后,基本上都会遇到一个这样的坑:页面进行了双向数据绑定,控制层的数据也已经改变了,但是视图层的数据却没有改变. 其实造成这个问题的原因大致分为以下 ...