新闻中心

EEPW首页>嵌入式系统>设计应用> AM335x(TQ335x)学习笔记——WM8960声卡驱动移植

AM335x(TQ335x)学习笔记——WM8960声卡驱动移植

作者: 时间:2016-11-28 来源:网络 收藏
经过一段时间的调试,终于调好了TQ335x的声卡驱动。TQ335x采用的Codec是WM8960,本文来总结下WM8960驱动在AM335x平台上的移植方法。Linux声卡驱动架构有OSS和ALSA两种架构,目前最常用的架构是ALSA,本文也使用ALSA架构对WM8960驱动进行移植。

ASoC是对ALSA驱动架构的进一步封装。ASoC将ALSA驱动中的各模块抽象为三部分:Platform、Codec和Machine。Platform主要是平台硬件驱动,包括SoC的IIS模块、DMA等,在本文中就是指AM335x的McASP模块及AM335x用于音频读写操作的EDMA。Codec是编解码芯片驱动,在本文中就是WM8960。Machine是用来描述单板音频系统连接关系的驱动,在本文中其作用是将WM8960与McASP绑定起来,注册声卡设备节点。由于3.17版本的内核已经带有TI维护的McASP驱动和Wolf公司维护的WM8960驱动,因此,原理上讲,我们只需要编写Machine部分,建立WM8960与McASP的连接关系即可。不幸的是Wolf对WM8960的维护不是太完善,还需要我们进一步修改。下面我们来看下WM8960在TQ335x上的移植方法。

本文引用地址://m.amcfsurvey.com/article/201611/322823.htm

1. 在DTS中添加声卡信息

Step1. 完善sound信息

在DTS有一个节点名为sound,该节点用来描述单板上声卡设备信息,修改后的内容如下:

  1. sound{
  2. compatible="ti,tq-evm-audio";
  3. ti,model="AM335x-EVM";
  4. ti,audio-codec=<&wm8960>;
  5. ti,mcasp-controller=<&mcasp1>;
  6. ti,codec-clock-rate=<24576000>;
  7. ti,audio-routing=
  8. "HeadphoneJack","HP_L",
  9. "HeadphoneJack","HP_R",
  10. "LINPUT1","LineIn";
  11. };
含义解释:

(1)compatible = "ti,tq-evm-audio" --> 指定声卡兼容的设备,与Machine驱动中的compatible匹配。

(2)ti,model = "AM335x-EVM" --> 声卡的名称,原则上讲可以随意指定,但最好具有一定的可读性,这里没有修改。

(3)ti,audio-codec = <&wm8960> --> 指定单板使用的Codec,具体的Codec信息由其指向的节点wm8960描述。

(4)ti,mcasp-controller = <&mcasp1> --> 指定单板使用的Codec连接到AM335x的McASP1上,McASP1的具体信息由其指向的节点mcasp1描述。

(5)ti,codec-clock-rate = <24576000> --> 指定Codec的MCLK时钟频率,单位是HZ。TQ335x的Codec使用24.576MHZ的有源晶振提供MCLK,故设置为24576000。

(6)ti,audio-routing --> DAPM信息描述,用来指定Codec与McASP的连接关系。此处若不设置,则需要在Machine驱动中进行设置。本文在这里做了修改。

Step2. 完善Codec信息

通过阅读TQ335x的原理图可知,WM8960的控制端口连接到了AM335x的I2C0端口上,因此,可以i2c0节点内添加如下信息(类似上篇文章中触摸设备驱动节点):

  1. wm8960:wm8960@1a{
  2. compatible="wlf,wm8960";
  3. reg=<0x1a>;
  4. };
含义解释:

(1)compatible = "wlf,wm8960" --> 指定Codec兼容设备,与Codec驱动中的compatible匹配。

(2) reg = <0x1a> --> WM8960的I2C地址是1A,故设置为0x1a。

Step3. 完善Platform信息

AM335x的Platform信息主要指McASP和EMDA设置信息。由于默认的DTS已经配置好了McASP及EDMA的大部分信息,需要我们配置的是McASP的pinmux和i2s信息。

(1) 修改pinmux信息需要具体参考TQ335x的原理图,下面是根据原理图中的引脚连接方式修改的pinmux信息,如果有啥不懂的可以留言讨论:

  1. am335x_evm_audio_pins:am335x_evm_audio_pins{
  2. pinctrl-single,pins=<
  3. 0x1A0(PIN_INPUT_PULLDOWN|MUX_MODE3)/*mcasp0_aclkr.mcasp1_aclkx*/
  4. 0x1A4(PIN_INPUT_PULLDOWN|MUX_MODE3)/*mcasp0_fsr.mcasp1_fsx*/
  5. 0x1A8(PIN_OUTPUT_PULLDOWN|MUX_MODE3)/*mcasp0_axr1.mcasp1_axr0*/
  6. 0x1AC(PIN_INPUT_PULLDOWN|MUX_MODE3)/*mcasp0_ahclkx.mcasp1_axr1*/
  7. >;
  8. };
(2) i2s的配置信息需要在mcasp1节点中修改,具体的修改如下:
  1. &mcasp1{
  2. pinctrl-names="default";
  3. pinctrl-0=<&am335x_evm_audio_pins>;
  4. status="okay";
  5. op-mode=<0>;/*MCASP_IIS_MODE*/
  6. tdm-slots=<2>;
  7. /*4serializers*/
  8. serial-dir=
  9. 1200
  10. >;
  11. tx-num-evt=<1>;
  12. rx-num-evt=<1>;
  13. };
含义:

(1)pinctrl-0 = <&am335x_evm_audio_pins> --> 指定mcasp1的pinmux信息。

(2)op-mode = <0> --> 指定McASP为I2S工作模式。

(3)tdm-slots = <2> --> 指定通道数。AM335x的手册以更广泛意义的单词slot命名,具体到I2S接口,其含义就是Channel。

(4)serial-dir --> 指定serializer的方向。AM335x的手册中提到每个McASP有16个serializer,但AM335x这款芯片的McAPS只有4个serializer,分别用于AXR0、AXR1、AXR2和ARX3。由于TQ335x中将AXR0作为发送(输出)、ARX1作为接收(输入)且没有ARX2和ARX3,故设置4个serial-dir为1、2、0、0(0表示没有使用,1表示发送,2表示接收)。
(5)tx-num-evt = <1> --> 指定发送FIFO大小,本文设置为1。

(6)rx-num-evt = <1> --> 指定接收FIFO大小,本文设置为1。

至此,就完成了DTS的全部配置,后面我会将完整的DTS文件上传到我的资源。

2. Codec驱动完善

Step1. 修改Codec驱动,使其支持DTS

由于我们在DTS中指定了Codec的compatible为"wlf,wm8960",而Linux内核自带的WM8960驱动并没有支持新式的DTS模式关联。修改方法很简单,添加i2c_driver的.driver中指定of_match_table即可,修改后的代码片段如下:

  1. staticconststructof_device_idwm8960_of_match[]={
  2. {.compatible="wlf,wm8960",},
  3. {}
  4. };
  5. MODULE_DEVICE_TABLE(of,wm8960_of_match);
  6. staticstructi2c_driverwm8960_i2c_driver={
  7. .driver={
  8. .name="wm8960",
  9. .owner=THIS_MODULE,
  10. .of_match_table=wm8960_of_match,
  11. },
  12. .probe=wm8960_i2c_probe,
  13. .remove=wm8960_i2c_remove,
  14. .id_table=wm8960_i2c_id,
  15. };
Step2. 完善WM8960的初始化信息

默认的WM8960驱动初始化信息不够完整,还需要对WM8960进行额外的初始化,修改后的代码片段如下:

  1. staticintwm8960_probe(structsnd_soc_codec*codec)
  2. {
  3. structwm8960_priv*wm8960=snd_soc_codec_get_drvdata(codec);
  4. structwm8960_data*pdata=dev_get_platdata(codec->dev);
  5. intret;
  6. wm8960->set_bias_level=wm8960_set_bias_level_out3;
  7. if(!pdata){
  8. dev_warn(codec->dev,"Noplatformdatasupplied");
  9. }else{
  10. if(pdata->capless)
  11. wm8960->set_bias_level=wm8960_set_bias_level_capless;
  12. }
  13. ret=wm8960_reset(codec);
  14. if(ret<0){
  15. dev_err(codec->dev,"Failedtoissuereset");
  16. returnret;
  17. }
  18. wm8960->set_bias_level(codec,SND_SOC_BIAS_STANDBY);
  19. /*Latchtheupdatebits*/
  20. snd_soc_update_bits(codec,WM8960_LINVOL,0x100,0x100);
  21. snd_soc_update_bits(codec,WM8960_RINVOL,0x100,0x100);
  22. snd_soc_update_bits(codec,WM8960_LADC,0x100,0x100);
  23. snd_soc_update_bits(codec,WM8960_RADC,0x100,0x100);
  24. snd_soc_update_bits(codec,WM8960_LDAC,0x100,0x100);
  25. snd_soc_update_bits(codec,WM8960_RDAC,0x100,0x100);
  26. snd_soc_update_bits(codec,WM8960_LOUT1,0x100,0x100);
  27. snd_soc_update_bits(codec,WM8960_ROUT1,0x100,0x100);
  28. snd_soc_update_bits(codec,WM8960_LOUT2,0x100,0x100);
  29. snd_soc_update_bits(codec,WM8960_ROUT2,0x100,0x100);
  30. /*otherconfiguration*/
  31. snd_soc_update_bits(codec,WM8960_POWER1,0x1ea,0x1ea);
  32. snd_soc_update_bits(codec,WM8960_POWER2,0x1f8,0x1f8);
  33. snd_soc_update_bits(codec,WM8960_POWER3,0xcc,0xcc);
  34. snd_soc_update_bits(codec,WM8960_LOUTMIX,0x100,0x100);
  35. snd_soc_update_bits(codec,WM8960_ROUTMIX,0x100,0x100);
  36. snd_soc_update_bits(codec,WM8960_POWER3,0xc,0xc);
  37. snd_soc_update_bits(codec,WM8960_LOUT1,0x7f,0x7f);
  38. snd_soc_update_bits(codec,WM8960_ROUT1,0x7f,0x7f);
  39. snd_soc_update_bits(codec,WM8960_IFACE2,0x40,0x40);
  40. snd_soc_update_bits(codec,WM8960_MONOMIX2,0x120,0x120);
  41. snd_soc_update_bits(codec,WM8960_LINPATH,0x1f8,0x138);
  42. snd_soc_update_bits(codec,WM8960_LINVOL,0x19f,0x11f);
  43. snd_soc_update_bits(codec,WM8960_RINVOL,0x19f,0x11f);
  44. snd_soc_update_bits(codec,WM8960_LOUT2,0x1ff,0x1ff);
  45. snd_soc_update_bits(codec,WM8960_ROUT2,0x1ff,0x1ff);
  46. snd_soc_update_bits(codec,WM8960_CLASSD3,0x1a,0x12);
  47. snd_soc_update_bits(codec,WM8960_CLASSD1,0xc0,0xc0);
  48. snd_soc_add_codec_controls(codec,wm8960_snd_controls,
  49. ARRAY_SIZE(wm8960_snd_controls));
  50. wm8960_add_widgets(codec);
  51. return0;
  52. }
具体的含义可以参考WM8960的芯片手册,这里我就不一一介绍了。


上一页 1 2 3 下一页

评论


技术专区

关闭