当前位置:   article > 正文

imx6q 音频芯片驱动开发--相关设备树以及代码中的对应设置_imx6q 声卡 时钟

imx6q 声卡 时钟

首先就不介绍asoc框架了,网上好的资料很多,推荐DroidPhone大神的,写的很全,知识面基本都介绍到了,不过由于写的比较早,没有使用设备树,最近也正好在调相关驱动,写点东西记录一下。

https://blog.csdn.net/droidphone/category_1118446.html

源码我也上传了 有需要的可以下载

https://download.csdn.net/download/qq_17270067/13721398

 

 

mpu:imx6q

linux-kernel:4.1.15

 

1、machine驱动

使用的是设备树中的sound节点

  1. sound {
  2. compatible = "fsl,imx-audio-tas2505";
  3. model = "tas2505-audio";
  4. cpu-dai = <&ssi1>;
  5. audio-codec = <&codec>;
  6. audio-routing =
  7. "Speaker Driver", "DAC Channel" ,
  8. "Speaker", "Speaker Driver" ;
  9. mux-int-port = <1>;
  10. mux-ext-port = <3>;
  11. };

cpu-dai:使用的platform节点

audio-codec:使用的codec节点

audio-routing:音频设备连接路径,每两个为一组,第一个为sink,第二个为source。

mux-int-port :使用了内部的哪个ssi。内部ssi接口ssi1,ssi2,ssi3, 分别对应 mux-int-port = <1>,mux-int-port = <2>,mux-int-port = <7>

mux-ext-port:使用了哪个外部AUD接口,有 AUD3,AUD4,AUD5,AUD6,分别对应mux-ext-port = <3>,mux-ext-port = <4>,mux-ext-port = <5>,mux-ext-port = <6>

 

在修改完了使用的AUDX接口之后,我们也需要将audmux的pinctrl修改成对应的接口

  1. pinctrl_audmux: audmuxgrp {
  2. fsl,pins = <
  3. MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0
  4. MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0
  5. MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0
  6. MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0
  7. >;
  8. };
  9. &audmux {
  10. pinctrl-names = "default";
  11. pinctrl-0 = <&pinctrl_audmux>;
  12. status = "okay";
  13. };

 

machine驱动文件中的probe函数如下:

  1. static int imx_tas2505_probe ( struct platform_device* pdev )
  2. {
  3. struct device_node* cpu_np, *codec_np,*gpr_np;
  4. struct device_node* np = pdev->dev.of_node;
  5. struct platform_device* cpu_pdev;
  6. struct imx_priv* priv = &card_priv;
  7. struct i2c_client* codec_dev;
  8. struct device_node* asrc_np;
  9. struct imx_tas2505_data* data = NULL;
  10. struct platform_device* asrc_pdev = NULL;
  11. int int_port, ext_port;
  12. int ret;
  13. //读取设备树中的mux-int-port属性的值
  14. ret = of_property_read_u32 ( np, "mux-int-port", &int_port );
  15. if ( ret )
  16. {
  17. dev_err ( &pdev->dev, "mux-int-port missing or invalid\n" );
  18. return ret;
  19. }
  20. //读取设备树中的mux-ext-port属性的值
  21. ret = of_property_read_u32 ( np, "mux-ext-port", &ext_port );
  22. if ( ret )
  23. {
  24. dev_err ( &pdev->dev, "mux-ext-port missing or invalid\n" );
  25. return ret;
  26. }
  27. /*
  28. * The port numbering in the hardware manual starts at 1, while
  29. * the audmux API expects it starts at 0.
  30. */
  31. int_port--;
  32. ext_port--;
  33. //根据codec和cpu的主从关系 配置ssi和aud的连接以及各条线的输入输出
  34. //本质是设置寄存器的值 具体参照数据手册Chapter 15 Digital Audio Multiplexer (AUDMUX)
  35. ret = imx_audmux_v2_configure_port ( ext_port,
  36. IMX_AUDMUX_V2_PTCR_SYN |
  37. IMX_AUDMUX_V2_PTCR_TFSEL ( int_port ) |
  38. IMX_AUDMUX_V2_PTCR_TCSEL ( int_port ) |
  39. IMX_AUDMUX_V2_PTCR_TFSDIR |
  40. IMX_AUDMUX_V2_PTCR_TCLKDIR,
  41. IMX_AUDMUX_V2_PDCR_RXDSEL ( int_port ) );
  42. if ( ret )
  43. {
  44. dev_err ( &pdev->dev, "audmux internal port setup failed\n" );
  45. return ret;
  46. }
  47. ret = imx_audmux_v2_configure_port ( int_port,
  48. IMX_AUDMUX_V2_PTCR_SYN,
  49. IMX_AUDMUX_V2_PDCR_RXDSEL ( ext_port ) );
  50. if ( ret )
  51. {
  52. dev_err ( &pdev->dev, "audmux external port setup failed\n" );
  53. return ret;
  54. }
  55. priv->pdev = pdev;
  56. //获取platform和codec节点
  57. cpu_np = of_parse_phandle ( pdev->dev.of_node, "cpu-dai", 0 );
  58. codec_np = of_parse_phandle ( pdev->dev.of_node, "audio-codec", 0 );
  59. if ( !cpu_np || !codec_np )
  60. {
  61. dev_err ( &pdev->dev, "phandle missing or invalid\n" );
  62. ret = -EINVAL;
  63. goto fail;
  64. }
  65. cpu_pdev = of_find_device_by_node ( cpu_np );
  66. if ( !cpu_pdev )
  67. {
  68. dev_err ( &pdev->dev, "failed to find SSI platform device\n" );
  69. ret = -EINVAL;
  70. goto fail;
  71. }
  72. codec_dev = of_find_i2c_device_by_node ( codec_np );
  73. if ( !codec_dev || !codec_dev->dev.driver )
  74. {
  75. dev_err ( &pdev->dev, "failed to find codec platform device\n" );
  76. ret = -EINVAL;
  77. goto fail;
  78. }
  79. data = devm_kzalloc ( &pdev->dev, sizeof ( *data ), GFP_KERNEL );
  80. if ( !data )
  81. {
  82. ret = -ENOMEM;
  83. goto fail;
  84. }
  85. //读取设备树codec-master 是否存在
  86. if ( of_property_read_bool ( pdev->dev.of_node,"codec-master" ) )
  87. {
  88. data->is_codec_master = true;
  89. }
  90. //获取code时钟
  91. data->codec_clk = devm_clk_get ( &codec_dev->dev, "mclk" );
  92. if ( IS_ERR ( data->codec_clk ) )
  93. {
  94. ret = PTR_ERR ( data->codec_clk );
  95. dev_err ( &pdev->dev, "failed to get codec clk: %d\n", ret );
  96. goto fail;
  97. }
  98. //使能时钟
  99. data->clk_frequency = clk_get_rate ( data->codec_clk );
  100. ret = clk_prepare_enable ( data->codec_clk );
  101. if ( ret )
  102. {
  103. dev_err ( &codec_dev->dev, "failed to enable codec clk: %d\n", ret );
  104. goto fail;
  105. }
  106. data->dai.name = "HiFi";
  107. data->dai.stream_name = "HiFi";
  108. data->dai.codec_dai_name = "tas2505-hifi"; //此处名字需与codec中snd_soc_dai_driver结构中的name相同
  109. data->dai.cpu_dai_name = dev_name ( &cpu_pdev->dev ); //得到设备的名字 该名字是在设备树转化成platform device被赋予
  110. data->dai.codec_of_node = codec_np;
  111. data->dai.platform_of_node = cpu_np;
  112. printk ( "cpu_dai_name = %s\n",data->dai.cpu_dai_name );
  113. data->dai.init = &imx_tas2505_dai_init; //
  114. data->dai.ops = &imx_hifi_ops;
  115. //设置与codec的通信格式为I2S cpu为主 codec为从
  116. data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
  117. SND_SOC_DAIFMT_CBS_CFS;
  118. data->card.dev = &pdev->dev;
  119. ret = snd_soc_of_parse_card_name ( &data->card, "model" );
  120. if ( ret )
  121. {
  122. goto fail;
  123. }
  124. //找到设备树名字为audio-routing的项 并取出sink和source
  125. ret = snd_soc_of_parse_audio_routing ( &data->card, "audio-routing" );
  126. if ( ret )
  127. {
  128. goto fail;
  129. }
  130. data->card.num_links = 1;
  131. data->card.owner = THIS_MODULE;
  132. data->card.dai_link = &data->dai;
  133. data->card.dapm_widgets = imx_tas2505_dapm_widgets; //注册不由codec寄存器控制的外部器件
  134. data->card.num_dapm_widgets = ARRAY_SIZE ( imx_tas2505_dapm_widgets );
  135. //设置私有数据
  136. platform_set_drvdata ( pdev, &data->card );
  137. snd_soc_card_set_drvdata ( &data->card, data );
  138. //注册声卡
  139. ret = devm_snd_soc_register_card ( &pdev->dev, &data->card );
  140. if ( ret )
  141. {
  142. dev_err ( &pdev->dev, "snd_soc_register_card failed (%d)\n", ret );
  143. goto fail;
  144. }
  145. of_node_put ( cpu_np );
  146. of_node_put ( codec_np );
  147. return 0;
  148. fail:
  149. if ( data && !IS_ERR ( data->codec_clk ) )
  150. {
  151. clk_put ( data->codec_clk );
  152. }
  153. if ( cpu_np )
  154. {
  155. of_node_put ( cpu_np );
  156. }
  157. if ( codec_np )
  158. {
  159. of_node_put ( codec_np );
  160. }
  161. return ret;
  162. }

 

2、codec驱动

  1. pinctrl_i2c2: i2c2grp {
  2. fsl,pins = <
  3. MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
  4. MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
  5. MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 //reset pin
  6. >;
  7. };
  8. &iomuxc {
  9. pinctrl-names = "default";
  10. pinctrl-0 = <&pinctrl_hog>;
  11. ......
  12. pinctrl_hog: hoggrp {
  13. fsl,pins = <
  14. ......
  15. /*CCM_CLKO1 为codec的时钟提供脚*/
  16. MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0
  17. ......
  18. >;
  19. };
  20. ......
  21. };
  22. &i2c2 {
  23. clock-frequency = <100000>;
  24. pinctrl-names = "default";
  25. pinctrl-0 = <&pinctrl_i2c2>;
  26. status = "okay";
  27. codec: tas2505@18{
  28. compatible = "ti,tas2505";
  29. reg = <0x18>;
  30. clocks = <&clks IMX6QDL_CLK_CKO>;
  31. reset-gpio = <&gpio2 2 1>;
  32. clock-names = "mclk";
  33. };
  34. };

reg:i2c设备地址,注意设备树中需要的是高7位地址。

clocks:时钟源,IMX6QDL_CLK_CKO定义的时钟源是MCLK,默认频率为24MHz。此时钟实际上也是可以修改的,参照How to change audio clock from 24M to 24.576M

IMX6QDL_CLK_CKO 定义在include/dt-bindings/clock/imx6qdl-clock.h

设备树中也需要定义codec的时钟提供脚。

reset-gpio:设备的复位引脚

 

codec驱动中的probe函数如下:

  1. static int tas2505_i2c_probe ( struct i2c_client* client,
  2. const struct i2c_device_id* id )
  3. {
  4. struct device* dev = &client->dev;
  5. struct device_node* np = dev->of_node;
  6. struct tas2505_data* tas2505;
  7. int ret;
  8. tas2505 = devm_kzalloc ( &client->dev, sizeof ( *tas2505 ), GFP_KERNEL );
  9. if ( tas2505 == NULL )
  10. {
  11. return -ENOMEM;
  12. }
  13. //获取设备树的中reset-gpio脚
  14. tas2505->reset_gpio = of_get_named_gpio ( np, "reset-gpio", 0 );
  15. if ( gpio_is_valid ( tas2505->reset_gpio ) )
  16. {
  17. //设置成输出高
  18. ret = devm_gpio_request_one ( dev, tas2505->reset_gpio,
  19. GPIOF_OUT_INIT_HIGH,
  20. "reset" );
  21. if ( ret )
  22. {
  23. dev_err ( dev, "unable to get reset pin \n" );
  24. return ret;
  25. }
  26. }
  27. tas2505->regmap = devm_regmap_init_i2c ( client, &tas2505_regmap_config ); //注册i2c regmap
  28. if ( IS_ERR ( tas2505->regmap ) )
  29. {
  30. ret = PTR_ERR ( tas2505->regmap );
  31. dev_err ( dev, "Failed to allocate register map: %d\n",
  32. ret );
  33. return ret;
  34. }
  35. tas2505->dev = dev;
  36. //设置私有数据 sound核心层需要读取regmap 之后可以使用snd_soc_write等相关函数直接操作寄存器
  37. dev_set_drvdata ( tas2505->dev, tas2505 );
  38. //注册codec
  39. ret = snd_soc_register_codec ( dev,
  40. &soc_codec_dev_tas2505,
  41. tas2505_dai, ARRAY_SIZE ( tas2505_dai ) );
  42. if ( ret < 0 )
  43. {
  44. dev_err ( dev, "Failed to register codec: %d\n", ret );
  45. }
  46. return ret;
  47. }

 

platform驱动基本不需要更改,我也没细看,所以就不分析了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/933208
推荐阅读
相关标签
  

闽ICP备14008679号