项目需求,在基于yocto Linux3.14.28的IMX6平台下移植wm8960声卡。硬件部分如下:

声卡驱动在内核代码的sound/soc/codec,和sound/soc/fsl目录下。原代码中在sound/soc/codec下面有wm8960.c,wm8960.h文件,但sound/soc/fsl目录下没有imx-wm8960.c,只有默认的imx-wm8962.c,尝试根据imx-wm8962.c修改新建一个wm8960的machine驱动,经过调试可以识别到声卡,但是无法输出声音,I2S测量没有波形输出。最终以失败告终,感觉wm8960和wm8962的差距还是比较大。

最终参考Linux3.14.52的代码解决,具体修改如下:

1,在http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/sound/soc/fsl?h=imx_3.14.52_1.1.0_ga中下载sound/soc/codec/wm8960.c,sound/soc/codec/wm8960.h,sound/soc/fsl/imx-wm8960.c到对应目录,并修改Makefile和Kconfig将其加入内核编译。

2,由于默认imx-wm8960.c采用sai总线,而我的硬件采用ssi总线与之连接,因此需要修改dts文件,修改arch/arm/boot/dts/imx6qdl-sabresd.dtsi如下:

        sound {compatible = "fsl,imx6q-sabresd-wm8960","fsl,imx-audio-wm8960";model = "wm8960-audio";cpu-dai = <&ssi2>;audio-codec = <&codec>;codec-master;audio-routing ="Headset Jack", "HP_L","Headset Jack", "HP_R","Ext Spk", "SPK_RP","Ext Spk", "SPK_RN","Ext Spk", "SPK_LP","Ext Spk", "SPK_LN","LINPUT1", "Main MIC"/*,"IN3R", "MICBIAS","DMIC", "MICBIAS","DMICDAT", "DMIC" */;/*mux-int-port = <2>;*/mux-int-port = <2>;mux-ext-port = <3>;hp-det-gpios = <&gpio7 8 0>;mic-det-gpios = <&gpio1 9 0>;};......&ssi2 {fsl,mode = "i2s-slave";status = "okay";};......codec: wm8960@1a {compatible = "wlf,wm8960";reg = <0x1a>;clocks = <&clks IMX6QDL_CLK_CKO>;clock-names = "mclk";DCVDD-supply = <&reg_audio>;DBVDD-supply = <&reg_audio>;AVDD-supply = <&reg_audio>;CPVDD-supply = <&reg_audio>;MICVDD-supply = <&reg_audio>;PLLVDD-supply = <&reg_audio>;SPKVDD1-supply = <&reg_audio>;SPKVDD2-supply = <&reg_audio>;amic-mono;wlf,shared-lrclk;};

3,修改之后启动可以识别到声卡,但是无法输出声音,用示波器测量I2S总线只有时钟正常,其他信号幅值偏低,这是由于在声卡驱动中没有配置imx6的audmux的原因,另外还有一个原因是在probe_last里面要增加对wm8960的寄存求配置,否则默认无输出,修改sound/soc/fsl/imx-wm8960.c如下:

//在probe函数中增加audmux配置
static int imx_wm8960_probe(struct platform_device *pdev)
{struct device_node *cpu_np, *codec_np, *gpr_np;struct platform_device *cpu_pdev;struct imx_priv *priv = &card_priv;struct i2c_client *codec_dev;struct imx_wm8960_data *data;struct platform_device *asrc_pdev = NULL;struct device_node *asrc_np;u32 width;int ret;struct device_node *np = pdev->dev.of_node;int int_port, ext_port;priv->pdev = pdev; cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0);if (!cpu_np) {dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");ret = -EINVAL;goto fail;}#if 1  //增加audmux部分if (!strstr(cpu_np->name, "ssi"))goto audmux_bypass;ret = of_property_read_u32(np, "mux-int-port", &int_port);if (ret) {dev_err(&pdev->dev, "mux-int-port missing or invalid\n");return ret;}ret = of_property_read_u32(np, "mux-ext-port", &ext_port);if (ret) {dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");return ret;}/** The port numbering in the hardware manual starts at 1, while* the audmux API expects it starts at 0.*/int_port--;ext_port--;dev_err(&pdev->dev, "audmix int-port:%d,ext-port%d\n",int_port,ext_port);ret = imx_audmux_v2_configure_port(int_port,IMX_AUDMUX_V2_PTCR_SYN |IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |IMX_AUDMUX_V2_PTCR_TFSDIR |IMX_AUDMUX_V2_PTCR_TCLKDIR,IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));if (ret) {dev_err(&pdev->dev, "audmux internal port setup failed\n");return ret;}imx_audmux_v2_configure_port(ext_port,IMX_AUDMUX_V2_PTCR_SYN,IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));if (ret) {dev_err(&pdev->dev, "audmux external port setup failed\n");return ret;}audmux_bypass:#endifcodec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);if (!codec_np) {dev_err(&pdev->dev, "phandle missing or invalid\n");ret = -EINVAL;goto fail;}cpu_pdev = of_find_device_by_node(cpu_np);if (!cpu_pdev) {dev_err(&pdev->dev, "failed to find SAI platform device\n");ret = -EINVAL;goto fail;}codec_dev = of_find_i2c_device_by_node(codec_np);if (!codec_dev || !codec_dev->dev.driver) {dev_err(&pdev->dev, "failed to find codec platform device\n");ret = -EINVAL;goto fail;}data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);if (!data) {ret = -ENOMEM;goto fail;}if (of_property_read_bool(pdev->dev.of_node, "codec-master"))data->is_codec_master = true;data->codec_clk = devm_clk_get(&codec_dev->dev, "mclk");if (IS_ERR(data->codec_clk)) {ret = PTR_ERR(data->codec_clk);dev_err(&pdev->dev, "failed to get codec clk: %d\n", ret);goto fail;}gpr_np = of_parse_phandle(pdev->dev.of_node, "gpr", 0);if (gpr_np) {data->gpr = syscon_node_to_regmap(gpr_np);if (IS_ERR(data->gpr)) {ret = PTR_ERR(data->gpr);dev_err(&pdev->dev, "failed to get gpr regmap\n");goto fail;}}of_property_read_u32_array(pdev->dev.of_node, "hp-det", data->hp_det, 2);asrc_np = of_parse_phandle(pdev->dev.of_node, "asrc-controller", 0);if (asrc_np) {asrc_pdev = of_find_device_by_node(asrc_np);priv->asrc_pdev = asrc_pdev;}data->card.dai_link = imx_wm8960_dai;imx_wm8960_dai[0].codec_of_node  = codec_np;imx_wm8960_dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev);imx_wm8960_dai[0].platform_of_node = cpu_np;if (!asrc_pdev) {data->card.num_links = 1;} else {imx_wm8960_dai[1].cpu_of_node = asrc_np;imx_wm8960_dai[1].platform_of_node = asrc_np;imx_wm8960_dai[2].codec_of_node = codec_np;imx_wm8960_dai[2].cpu_dai_name = dev_name(&cpu_pdev->dev);data->card.num_links = 3;ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",&data->asrc_rate);if (ret) {dev_err(&pdev->dev, "failed to get output rate\n");ret = -EINVAL;goto fail;}ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);if (ret) {dev_err(&pdev->dev, "failed to get output rate\n");ret = -EINVAL;goto fail;}if (width == 24)data->asrc_format = SNDRV_PCM_FORMAT_S24_LE;elsedata->asrc_format = SNDRV_PCM_FORMAT_S16_LE;}data->card.dev = &pdev->dev;ret = snd_soc_of_parse_card_name(&data->card, "model");if (ret)goto fail;data->card.dapm_widgets = imx_wm8960_dapm_widgets;data->card.num_dapm_widgets = ARRAY_SIZE(imx_wm8960_dapm_widgets);ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");if (ret)goto fail;data->card.late_probe = imx_wm8960_late_probe;platform_set_drvdata(pdev, &data->card);snd_soc_card_set_drvdata(&data->card, data);ret = devm_snd_soc_register_card(&pdev->dev, &data->card);if (ret) {dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);goto fail;}priv->snd_card = data->card.snd_card;priv->hp_set_gpio = of_get_named_gpio_flags(pdev->dev.of_node, "hp-det-gpios", 0,(enum of_gpio_flags *)&priv->hp_active_low);if (IS_ERR(ERR_PTR(priv->hp_set_gpio)))goto fail;priv->headset_kctl = snd_kctl_jack_new("Headset", 0, NULL);ret = snd_ctl_add(data->card.snd_card, priv->headset_kctl);if (ret)goto fail;ret = imx_wm8960_gpio_init(&data->card);if (gpio_is_valid(priv->hp_set_gpio)) {ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);if (ret) {dev_err(&pdev->dev, "create hp attr failed (%d)\n", ret);goto fail;}ret = driver_create_file(pdev->dev.driver, &driver_attr_micphone);if (ret) {dev_err(&pdev->dev, "create mic attr failed (%d)\n", ret);goto fail;}}
fail:if (cpu_np)of_node_put(cpu_np);if (codec_np)of_node_put(codec_np);return ret;
}//在wm8960_init函数中增加wm8960的寄存器配置
static void wm8960_init(struct snd_soc_dai *codec_dai)
{struct snd_soc_codec *codec = codec_dai->codec;struct snd_soc_card *card = codec_dai->card;struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);/** codec ADCLRC pin configured as GPIO, DACLRC pin is used as a frame* clock for ADCs and DACs*/snd_soc_update_bits(codec, WM8960_IFACE2, 1<<6, 1<<6);/** GPIO1 used as headphone detect output*/snd_soc_update_bits(codec, WM8960_ADDCTL4, 7<<4, 3<<4);/** Enable headphone jack detect*/snd_soc_update_bits(codec, WM8960_ADDCTL2, 1<<6, 1<<6);snd_soc_update_bits(codec, WM8960_ADDCTL2, 1<<5, data->hp_det[1]<<5);snd_soc_update_bits(codec, WM8960_ADDCTL4, 3<<2, data->hp_det[0]<<2);snd_soc_update_bits(codec, WM8960_ADDCTL1, 3, 3);/** route left channel to right channel in default.*/snd_soc_update_bits(codec, WM8960_ADDCTL1, 3<<2, 1<<2);snd_soc_write(codec,0x0 ,0x13f);snd_soc_write(codec,0x1 , 0x13f);snd_soc_write(codec,0x2 , 0x165);snd_soc_write(codec,0x3 , 0x165);snd_soc_write(codec, 0x4 , 0x5);snd_soc_write(codec,0x5 , 0x0);snd_soc_write(codec,0x6 ,0x0);snd_soc_write(codec,0x7 , 0x42);snd_soc_write(codec, 0x8 , 0x1c4);snd_soc_write(codec,0x9 , 0x0);snd_soc_write(codec,0xa , 0xd6);snd_soc_write(codec,0xb , 0xd6);snd_soc_write(codec,0x10 , 0x0);snd_soc_write(codec,0x11 , 0x7b);snd_soc_write(codec, 0x12 , 0x100);snd_soc_write(codec,0x13 , 0x32);snd_soc_write(codec,0x14 , 0x0);snd_soc_write(codec,0x15 , 0xc3);snd_soc_write(codec,0x16 , 0xc3);snd_soc_write(codec,0x17 , 0x1c0);snd_soc_write(codec,0x18 , 0x0);snd_soc_write(codec,0x19 , 0xfc);snd_soc_write(codec,0x1a , 0x1fb);snd_soc_write(codec,0x1b , 0x0);snd_soc_write(codec,0x1c , 0x8);snd_soc_write(codec,0x1d , 0x0);snd_soc_write(codec,0x20 , 0x108);snd_soc_write(codec,0x21 , 0x108);snd_soc_write(codec,0x22 , 0x100);snd_soc_write(codec,0x25 , 0x100);snd_soc_write(codec,0x26 , 0x0);snd_soc_write(codec,0x27 , 0x0);snd_soc_write(codec,0x28 , 0x165);snd_soc_write(codec,0x29 , 0x165);snd_soc_write(codec,0x2a , 0x40);snd_soc_write(codec,0x2b , 0x0);snd_soc_write(codec,0x2c , 0x0);snd_soc_write(codec,0x2d , 0x50);snd_soc_write(codec,0x2e , 0x50);snd_soc_write(codec,0x2f , 0x3c);snd_soc_write(codec,0x30 , 0x2);snd_soc_write(codec,0x31 , 0xf7);snd_soc_write(codec,0x33 , 0x9b);snd_soc_write(codec,0x34 , 0x37);snd_soc_write(codec,0x35 , 0x86);snd_soc_write(codec,0x36 , 0xc2);snd_soc_write(codec,0x37 , 0x27);
}

经过如上修改后,执行aplay 1.wav测试声卡正常工作。

IMX6基于yocto3.14.28移植声卡wm8960相关推荐

  1. 基于龙芯2K1000移植uboot之DDR

    基于龙芯2K1000移植uboot之DDR 移植环境 开始移植 额外说明 移植环境 CPU:LS2K1000 DDR:SCB13H8G162BF-13KI 编译环境:Ubuntu16.04+gcc-4 ...

  2. Nervos DAO锁定总额突破100亿,近日解锁的14.28亿生态基金已被全部存入

    根据Nervos Explorer显示,Nervos DAO中,CKB锁定总额已突破100亿,达105.78亿,占CKB流通量45.15%,再次创历史新高. 据悉,此次Nervos DAO中新增的14 ...

  3. 【转】基于Ubuntu 14.04 LTS编译Android4.4.2源代码

    原文网址:http://blog.csdn.net/gobitan/article/details/24367439 基于Ubuntu 14.04 LTS编译Android4.4.2源代码 Denni ...

  4. 基于STM32F4的CANOpen移植教程(超级详细)

    CANopen移植到STM32F4平台 前言 1 物品准备 2 相关软件安装 2.1 CAN上位机 2.2 对象字典生成工具objdictedit环境配置 3 将CANopen移植到STM32F407 ...

  5. 基于Qt5.14.2和mingw的Qt源码学习(三) — 元对象系统简介及moc工具是如何保存类属性和方法的

    基于Qt5.14.2和mingw的Qt源码学习(三) - 元对象系统简介及moc工具是如何保存类属性和方法的 一.什么是元对象系统 1.元对象系统目的 2.实现元对象系统的关键 3.元对象系统的其他一 ...

  6. 基于Linux的kfifo移植到STM32(支持os的互斥访问)

    基于Linux的kfifo移植到STM32(支持os的互斥访问) 声明:本文为杰杰原创,转载请说明出处 https://blog.csdn.net/jiejiemcu/article/details/ ...

  7. 《社会调查数据管理——基于Stata 14管理CGSS数据》一第2章 数据管理的流程及内容2.1 数据管理的工作流程...

    本节书摘来自异步社区<社会调查数据管理--基于Stata 14管理CGSS数据>一书中的第2章,第2.1节,作者 唐丽娜,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  8. 《社会调查数据管理——基于Stata 14管理CGSS数据》一2.2 数据管理的工作标准...

    本节书摘来自异步社区<社会调查数据管理--基于Stata 14管理CGSS数据>一书中的第2章,第2.2节,作者 唐丽娜,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  9. 《社会调查数据管理——基于Stata 14管理CGSS数据》一3.2 和统计有关的术语

    本节书摘来自异步社区<社会调查数据管理--基于Stata 14管理CGSS数据>一书中的第3章,第3.2节,作者 唐丽娜,更多章节内容可以访问云栖社区"异步社区"公众号 ...

最新文章

  1. linux sudo命令
  2. .Net Core集成Office Web Apps(一)
  3. list python 转tensor_Pytorch--Tensor, Numpy--Array,Python--List 相互之间的转换。
  4. python创建虚拟环境venv_Python 3 使用venv创建虚拟环境
  5. c语言函数大全doc,c语言函数大全.doc
  6. aspx repeater 用法_ASP.NET-----Repeater数据控件的用法总结
  7. java录制pcm文件_AudioRecord录制PCM格式的语音示例
  8. XenServer 6.5实战:Creating a Storage Repository (CIFS)
  9. Java编程:二分查找算法(非递归)
  10. php js的视频教程,【JS视频教程推荐】2021年最值得推荐的5个JavaScript视频教程
  11. select into from 和 insert into select 的用法和区别
  12. 脑子傻怎么学php,小孩子学习不好脑子不开窍怎么办?4个方法让“笨”孩子聪明起来...
  13. 怎么在云服务器上建网站_怎么在云服务器建网站?阿里云服务器搭建网站教程...
  14. 语音特征提取(语谱图Spectrogram,Fbank, MFCC, 及其delta-一阶差分)——python代码
  15. VUE使用echarts实现中国地图航线动态展示
  16. andriod studio页面跳转
  17. 实时频谱分析仪作下变频器的技术实现
  18. GitHub使用教程、注册与安装
  19. SSM开发笔记-尚硅谷-佟刚-Spring4.0.0
  20. 学习【AI测试】人工智能 (AI) 测试--开篇经验分享

热门文章

  1. 跨时钟域电路设计方法
  2. 《勋伯格和声学》读书笔记(五):小调中的七和弦及其转位,没有共同音的和弦的连接
  3. PostgreSQL 常用命令 总结 ||数据库导入导出
  4. human36m 3d姿态可视化
  5. even parity
  6. shred 粉碎文件
  7. 最新整理英文论坛大全(有评论)
  8. 技巧:Access中查询多表更新其中一表数据
  9. 毕业设计 单片音乐律动响应桌面灯
  10. Status Code 304