内核版本Linux 3.9.11      编译器版本 4.5.1

Asoc框架是linux内核采用的一种音频子系统框架。他在alsa框架的基础上又封装了一层。实际上就把asoc移植看成alsa移植即可。(虽然2个我都不太懂,初学asoc驱动)
在对实际文件分析后我把alsa框架需要修改的主要为如下几类:

SMDK_xxxx.c文件      --> Machine
I2C子系统驱动         -->codec
I2S控制器驱动     

其中I2S控制器驱动和I2C子系统驱动由内核已经给出。I2S驱动主要是设置I2S相关的寄存器,便于ASOC其他部分(框架理解不是很透彻,这里可以看成PCM数据发送)发送数据。
而I2C子系统驱动则负责配置WM8960的寄存器。SMDK.c文件主要是负责将asoc各个部分通过一个结构体描述连接起来。他们都是由asoc更上层的结构进行调用的。

对于我们移植来说,主要是关心SMDK_xxxx.c这样的Machine部分的文件。如果内核没有的话,就需要自己动手写了,我这里是没有的,所以我去找了个S3c2451的WM8960的Machine文件,接下来说下修改步骤。
想要正确播放出声音,第一步就需要修改machine中的dai结构体:

下面只说下主要的成员:

Cpu_dai_name         对应i2s控制器驱动
Codec_dai_name     对应于内核提供的codec文件中的同名dai 图3 给出
Platform_name       用于数据传输的平台名称 (我猜的 I2SDMA?)
Codec_name           对应于控制codec的 i2c驱动
ops                         主要成员是.hw_params变量,当通过alsa-util测试播放的时候,应用会通过 这个函数传入一些控制参数来设置i2s和wm8960。 通过 图2 划线的2个参数就能对应到codec和i2s各自的控制函数当中。分别进行设置。参数则通过查看他们对应的函数就能得出。

                               图2

                               图3

修改后若使用默认的设置无法播放,则需要检查设置让关键的寄存器值变正确。
IIS设置主要要设置时钟源选I2SCLK,通过EPLL产生时钟,设置可以查看芯片手册中的i2s时钟路线图,然后就是bfs,rfs,psr参数,通常使用bfs=32 rfs=256,因为wm8960中默认设置还会将传入的频率/256和/32,所以为了保证频率正确就这样设置,这两个可以通过应用传参得到。而psr就需要我们通过计算得到了,比如44100hz采样率就是5 采样率是22050时就是10。这是通过公式算出来的。(psr是i2s预分频寄存器)

    N + 1 = (67.7Mhz) / (256 * 44.1Khz) = 5.99N + 1 = (67.7Mhz) / (256 * 22.05Khz) = 11

我们只需要让这些值真确写入寄存器就好了。设置通过i2s控制器驱动中的i2s_set_clkdiv函数,当然这些只是设置,并没有实际写入到寄存器,真正写入到寄存器的是config_setup函数,里面调用

set_bfs(i2s, bfs);
set_rfs(i2s, rfs);

写入寄存器,psr在最后进行设置。

if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {psr =m_psr;// i2s->rclk_srcrate / i2s->frmclk / rfs;printk("i2s %s  setup psr:%d \r\n",__func__,psr);writel(((psr ) << 8) | PSR_PSREN, i2s->addr + I2SPSR);dev_dbg(&i2s->pdev->dev,"RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n",i2s->rclk_srcrate, psr, rfs, bfs);}

可以看到我用m_psr替换掉了原始的psr计算,这是因为,我在machine中已经计算好了psr的值,传入设置好的时候,不用计算直接写入即可。
并且,i2s驱动中的set_clkdiv可能会少 rfs和psr的设置,加入即可。
(这些修改的部分是通过machine中的.hw_params函数设置的)
以下代码是I2S.c中的代码,添加了2和3两个选项(这是通过调试得到的缺少的部分)。

#define DIV_RCLK 2
#define DIV_PSR 3
static int i2s_set_clkdiv(struct snd_soc_dai *dai,int div_id, int div)
{struct i2s_dai *i2s = to_info(dai);struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;switch (div_id) {case SAMSUNG_I2S_DIV_BCLK:if ((any_active(i2s) && div && (get_bfs(i2s) != div))|| (other && other->bfs && (other->bfs != div))) {dev_err(&i2s->pdev->dev,"%s:%d Other DAI busy\n", __func__, __LINE__);return -EAGAIN;}printk(KERN_ERR"bfs:%d\r\n",div);i2s->bfs = div;break;case DIV_RCLK:if ((any_active(i2s) && div && (get_rfs(i2s) != div))|| (other && other->rfs && (other->rfs != div))) {dev_err(&i2s->pdev->dev,"%s:%d Other DAI busy\n", __func__, __LINE__);return -EAGAIN;}printk(KERN_ERR"rfs:%d\r\n",div);i2s->rfs = div;break;case DIV_PSR:{m_psr=div;}break;default:dev_err(&i2s->pdev->dev,"Invalid clock divider(%d)\n", div_id);return -EINVAL;}return 0;
}

这里还需要注意一个函数就是i2s_txctrl ,这个函数控制着i2s的输出,在函数的末尾可以看到设置IISCON和IISMOD寄存器,如果不出声音可以在此跟踪一下这两个寄存器的状态。
正确的状态不知道的话可以先写下裸机wm8960驱动以后再来验证,裸机驱动wm8960的代码网上还是比较多的。
我这里改成了con |1, 原因可以通过看手册和跟踪寄存器得出。


到此I2S部分和machine部分就修改完了。还要注意一点,如果iis设置全对,还是没有声音,这时就要考虑codec驱动的初始化问题了,以wm8960.c为例,我是在代码中的wm8960_probe函数中看到了wm8960的一些寄存器初始化函数调用,一些关键的设置并不正确,于是修改为如下:

static int wm8960_probe(struct snd_soc_codec *codec)
{struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);struct wm8960_data *pdata = dev_get_platdata(codec->dev);int ret;wm8960->set_bias_level = wm8960_set_bias_level_out3;if (!pdata) {dev_warn(codec->dev, "No platform data supplied\n");} else {if (pdata->capless)wm8960->set_bias_level = wm8960_set_bias_level_capless;}ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);if (ret < 0) {dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);return ret;}ret = wm8960_reset(codec);if (ret < 0) {dev_err(codec->dev, "Failed to issue reset\n");return ret;}wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);/* Latch the update bits *//*snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100);snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100);*///powersnd_soc_update_bits(codec, WM8960_POWER1,0x1c0,0x1c0);snd_soc_update_bits(codec, WM8960_POWER2,0x1f8 ,0x1f8);snd_soc_update_bits(codec, WM8960_POWER3,0x00c ,0x00c);// clock  CLKSEL = 0 : no PLL -> SYSCLK using MCLKsnd_soc_update_bits(codec, 0x4,0, 0x0); // no mute  oksnd_soc_update_bits(codec, 0x5,0, 0x0); // set no mute//snd_soc_update_bits(codec, 0x5, 0x08);// set mute // audio interfacesnd_soc_update_bits(codec, 0x7,0x2, 0x2);// 00 = 16bits,  10 = IIS format// volume +6db  oksnd_soc_update_bits(codec, 0x2,0x1ff, 0x60 | 0x100);// WM8960_LOUT1snd_soc_update_bits(codec, 0x3,0x1ff, 0x60 | 0x100);// WM8960_ROUT1snd_soc_update_bits(codec, 0xa,0x1ff, 0xFF | 0x100);// Left DAC volumesnd_soc_update_bits(codec, 0xb,0x1ff, 0xFF | 0x100);// Right DAC volume// mixer controlsnd_soc_update_bits(codec, 0x22,0x180, 1<<8 | 1<<7);        // Left output mixer controlsnd_soc_update_bits(codec, 0x25,0x180, 1<<8 | 1<<7);        // Right output mixer controlprintk(KERN_ERR"wm8960 init done!\r\n");snd_soc_add_codec_controls(codec, wm8960_snd_controls,ARRAY_SIZE(wm8960_snd_controls));wm8960_add_widgets(codec);return 0;
}

注释掉的是原来的,wm8960寄存器地址在wm8960.h中都有定义的,我为了省事直接把裸机代码拿过来用了,效果是一样的。主要设置了电源,时钟,音量和声音输出,这些缺1不可(测试过)。
这样修改完后声音应该能放出来了。
如果出声音了,速率不对,偏快或偏慢,那是因为EPLL时钟设置不对,IIS需要67.7M的时钟频率才能正确播放。,当然80M的频率播放也蛮有意思的,音调被改变,一样的音乐,不一样的感受。^_^

另外提一下asoc驱动移植注意。

smdk_8960.c是我手动添加的,还需要修改Makefile和Kconfig文件,才能被配置和编译。
首先需要在smdk_s5pv210.c中的device __init数值,确定iis0和i2c0设备在数组中。(我的wm8960 用的是iis0和i2c0),将wm8960作为i2c子设备添加到 i2c0中,配置内核支持i2c,asoc然后启动后确定i2c0,wm8960,asoc正确被加载(看输出),i2s0会在wm8960匹配成功后会出现如下类似输出:

soc-audio soc-audio:  wm8960-hifi <-> samsung-i2s.0 mapping ok

最终成功时会有如下输出:

ALSA device list:#0: SMDK-I2S

也就是成功注册了一个声卡设备。
在/dev目录下会出现这些设备节点:

controlC0   -->   用于声卡的控制,例如通道选择,混音,麦克风的控制等
pcmC0D0c    -->   用于录音的pcm设备
pcmC0D0p    -->   用于播放的pcm设备
timer       -->   定时器

最后通过alsa-util调用alsa-lib库 播放wav文件 如果需要播放MP3还需要libmad(MP3解码库)的支持,直接让alsa-lib使用libmad库我还没找到方法,但可以通过自己写一个程序,调用libmad和alsa-lib来播放MP3,代码网上可以搜到。

附上测试播放调试图:
WAV文件:

MP3文件:

可以看到,播放MP3和WAV的驱动输出基本是一样的,最原始的音频数据是WAV,播放MP3只是通过软解码转换成WAV文件流而已。

心得体会

对于这些带框架的驱动学习,如果遇到问题不知道出在哪里可以先去调试一下裸机代码,确定寄存器的正确配置后再来调驱动框架会有事半功倍的效果。^_^

S5PV210 WM8960 ASOC 移植相关推荐

  1. USB WIFI网卡在S5PV210上的移植和使用最全攻略(2)

    目录 一.在自己制作的rootfs中移植网卡 1.确认自己制作的rootfs可正常工作并启动 2.需要的工具集确认 3.交叉编译iwconfig 二.移植wpa_supplicant并制作镜像 1.交 ...

  2. s5pv210 linux内核移植,S5PV210(TQ210)学习笔记——内核移植与文件系统构建之二

    由于之前做过2440和6410,鉴于时间原因,这里暂时跨过其他模块的裸机驱动和uboot移植,直接进入内核移植及驱动开发方面的学习. 三 设置uboot启动参数 我的nfs根目录为/nfsroot,我 ...

  3. linux驱动编写(声卡驱动之asoc移植)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] Linux下面的声卡驱动很复杂,根本不是一篇博客能够说清楚的.所以,本片文章的目的就是让同学门快 ...

  4. S5PV210 WM8960音频驱动 学习

          最新由于项目需要,调试S5PV210开发板 Linux系统上面的音频功能,我们选择的音频编解码芯片是WM8960,连接到CPU的IIS接口,调试过   程中遇到了一系列的问题,现总结如下: ...

  5. S5PV210 Linux内核移植 - 天嵌E8

    1.获取Linux-4.19.114 内核代码 从kernel.org下载最新版本的内核: https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/ ...

  6. X210开发板(S5PV210芯片)uboot移植DM9000驱动移植

    前言 本文是介绍在uboot中如何移植DM9000的驱动,并不深入去讲解DM9000芯片的操作时序和内部寄存器,想要读懂驱动代码要仔细阅读DM9000芯片的数据手册.移植的基础是手里有DM9000芯片 ...

  7. s5pv210 linux内核移植,S5PV210 LINUX内核移植

    环境:debian 6 linux 2.6.35.7 arm-linux-gcc: LINUX只是一个内核,不能算是操作系统.操作系统还包括更多的东西,比如用户界面.我们要做的第一步是要让内核在目标版 ...

  8. s5pv210 linux内核移植,S5PV210-kernel-从三星官方的内核开始移植

    1.拿到三星官方移植过的内核 2.在Linux下解压一份在共享目录下,解压一份在Linux的源生目录下.一份编辑,一份编译 3.建立SI工程,添加kernel源代码 4.在Linux下先看makefi ...

  9. USB WIFI网卡在S5PV210上的移植和使用最全攻略(1)

    目录 一.本季文章将会带来那些干货 1.MT7601网卡驱动的移植 2.无线网卡的配置和使用 3.在自己定制的rootfs中移植USB WIFI 4.在自己定制的rootf中添加dhcp支持 二.项目 ...

最新文章

  1. spring注解注入属性
  2. JAVA enum实现简单状态机功能
  3. 整合spring cloud云架构 - SSO单点登录之OAuth2.0 登出流程(3)
  4. 手机MODEM开发(27)---VOLTE SIP 信令流程图
  5. android listpreference 自定义,android ListPreference 不显示内容 设置默认值
  6. Webpack 2 视频教程 009 - 配置 ESLint 实现代码规范自动测试 (上)
  7. SSIS连接-左外连接
  8. 技术分享 | 相较于ROS 1,ROS 2有哪些优越性?
  9. Java设计模式学习笔记
  10. 腾讯会议发布录屏工具“会记”,让云端视频协作随用随录、随享随看
  11. 颠覆你想象的150个故事(1)
  12. 微信无法下载文件如何做提示跳转到浏览器
  13. SQL server management studio 使用
  14. 微信小程序图片处理方案,解决加载缓慢,影响用户体验
  15. html5 人在星空中,《在星空中》的教学设计
  16. 团队任务分配管理软件,可以分配团队任务的团队管理软件
  17. Vue3中Element-Plus的el-upload限制只上传一个文件(最简单明了)
  18. expert个人版 sqlite_SQLiteExpert个人免费版(SQLite Expert Personal)下载 v5.3.1.357 官方32/64位版 - 比克尔下载...
  19. 一文看懂推荐系统:概要02:推荐系统的链路,从召回粗排,到精排,到重排,最终推荐展示给用户
  20. python识别excel文件格式 ——(专栏:基于python编写简单office阅卷程序②)

热门文章

  1. 歌尔2718硅咪 超小体积 S15OB383-058 S15OB381-055 S15OT421-001
  2. java cda安装_JAVA如何与R完美结合起来
  3. 微信公众号使用:微信公众平台企业号一次发布多条图文消息的方法
  4. 为葡萄酒数据集构造SVM分类器和使用随机森林回归模型预测葡萄酒质量
  5. CodeForces Gym 100989B LCS (B)
  6. R pdf大小_免费、开源、多平台支持的PDF文档处理软件 PDFsam Basic
  7. 新媒体营销的常用思维有哪些呢?
  8. ERP系统在元器件贸易企业中的应用
  9. Spark并行度的设定
  10. linux 选择 平铺 窗口,【如何配置linux 平铺式窗口管理器i3wm?】-看准网