赞
踩
首先就不介绍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
使用的是设备树中的sound节点
- sound {
- compatible = "fsl,imx-audio-tas2505";
- model = "tas2505-audio";
- cpu-dai = <&ssi1>;
- audio-codec = <&codec>;
-
- audio-routing =
- "Speaker Driver", "DAC Channel" ,
- "Speaker", "Speaker Driver" ;
-
- mux-int-port = <1>;
- mux-ext-port = <3>;
- };
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修改成对应的接口
- pinctrl_audmux: audmuxgrp {
- fsl,pins = <
- MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0
- MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0
- MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0
- MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0
- >;
- };
-
- &audmux {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux>;
- status = "okay";
- };
machine驱动文件中的probe函数如下:
- static int imx_tas2505_probe ( struct platform_device* pdev )
- {
- struct device_node* cpu_np, *codec_np,*gpr_np;
- struct device_node* np = pdev->dev.of_node;
- struct platform_device* cpu_pdev;
- struct imx_priv* priv = &card_priv;
- struct i2c_client* codec_dev;
- struct device_node* asrc_np;
- struct imx_tas2505_data* data = NULL;
- struct platform_device* asrc_pdev = NULL;
- int int_port, ext_port;
- int ret;
-
- //读取设备树中的mux-int-port属性的值
- 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;
- }
-
- //读取设备树中的mux-ext-port属性的值
- 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--;
- //根据codec和cpu的主从关系 配置ssi和aud的连接以及各条线的输入输出
- //本质是设置寄存器的值 具体参照数据手册Chapter 15 Digital Audio Multiplexer (AUDMUX)
- ret = imx_audmux_v2_configure_port ( ext_port,
- IMX_AUDMUX_V2_PTCR_SYN |
- IMX_AUDMUX_V2_PTCR_TFSEL ( int_port ) |
- IMX_AUDMUX_V2_PTCR_TCSEL ( int_port ) |
- IMX_AUDMUX_V2_PTCR_TFSDIR |
- IMX_AUDMUX_V2_PTCR_TCLKDIR,
- IMX_AUDMUX_V2_PDCR_RXDSEL ( int_port ) );
- if ( ret )
- {
- dev_err ( &pdev->dev, "audmux internal port setup failed\n" );
- return ret;
- }
- ret = imx_audmux_v2_configure_port ( int_port,
- IMX_AUDMUX_V2_PTCR_SYN,
- IMX_AUDMUX_V2_PDCR_RXDSEL ( ext_port ) );
- if ( ret )
- {
- dev_err ( &pdev->dev, "audmux external port setup failed\n" );
- return ret;
- }
-
-
- priv->pdev = pdev;
-
- //获取platform和codec节点
- cpu_np = of_parse_phandle ( pdev->dev.of_node, "cpu-dai", 0 );
- codec_np = of_parse_phandle ( pdev->dev.of_node, "audio-codec", 0 );
- if ( !cpu_np || !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 SSI 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;
- }
-
- //读取设备树codec-master 是否存在
- if ( of_property_read_bool ( pdev->dev.of_node,"codec-master" ) )
- {
- data->is_codec_master = true;
- }
-
- //获取code时钟
- 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;
- }
-
- //使能时钟
- data->clk_frequency = clk_get_rate ( data->codec_clk );
- ret = clk_prepare_enable ( data->codec_clk );
- if ( ret )
- {
- dev_err ( &codec_dev->dev, "failed to enable codec clk: %d\n", ret );
- goto fail;
- }
-
- data->dai.name = "HiFi";
- data->dai.stream_name = "HiFi";
- data->dai.codec_dai_name = "tas2505-hifi"; //此处名字需与codec中snd_soc_dai_driver结构中的name相同
- data->dai.cpu_dai_name = dev_name ( &cpu_pdev->dev ); //得到设备的名字 该名字是在设备树转化成platform device被赋予
- data->dai.codec_of_node = codec_np;
- data->dai.platform_of_node = cpu_np;
-
- printk ( "cpu_dai_name = %s\n",data->dai.cpu_dai_name );
-
- data->dai.init = &imx_tas2505_dai_init; //
- data->dai.ops = &imx_hifi_ops;
-
- //设置与codec的通信格式为I2S cpu为主 codec为从
- data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS;
-
- data->card.dev = &pdev->dev;
- ret = snd_soc_of_parse_card_name ( &data->card, "model" );
- if ( ret )
- {
- goto fail;
- }
-
- //找到设备树名字为audio-routing的项 并取出sink和source
- ret = snd_soc_of_parse_audio_routing ( &data->card, "audio-routing" );
-
- if ( ret )
- {
- goto fail;
- }
- data->card.num_links = 1;
- data->card.owner = THIS_MODULE;
- data->card.dai_link = &data->dai;
- data->card.dapm_widgets = imx_tas2505_dapm_widgets; //注册不由codec寄存器控制的外部器件
- data->card.num_dapm_widgets = ARRAY_SIZE ( imx_tas2505_dapm_widgets );
-
- //设置私有数据
- 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;
- }
-
- of_node_put ( cpu_np );
- of_node_put ( codec_np );
-
- return 0;
-
- fail:
- if ( data && !IS_ERR ( data->codec_clk ) )
- {
- clk_put ( data->codec_clk );
- }
- if ( cpu_np )
- {
- of_node_put ( cpu_np );
- }
- if ( codec_np )
- {
- of_node_put ( codec_np );
- }
-
- return ret;
- }
- pinctrl_i2c2: i2c2grp {
- fsl,pins = <
- MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
- MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
- MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 //reset pin
- >;
- };
-
-
- &iomuxc {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_hog>;
- ......
- pinctrl_hog: hoggrp {
- fsl,pins = <
- ......
- /*CCM_CLKO1 为codec的时钟提供脚*/
- MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0
- ......
- >;
- };
- ......
- };
-
- &i2c2 {
- clock-frequency = <100000>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2>;
- status = "okay";
-
- codec: tas2505@18{
- compatible = "ti,tas2505";
- reg = <0x18>;
- clocks = <&clks IMX6QDL_CLK_CKO>;
- reset-gpio = <&gpio2 2 1>;
- clock-names = "mclk";
- };
-
- };
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函数如下:
- static int tas2505_i2c_probe ( struct i2c_client* client,
- const struct i2c_device_id* id )
- {
- struct device* dev = &client->dev;
- struct device_node* np = dev->of_node;
- struct tas2505_data* tas2505;
- int ret;
-
- tas2505 = devm_kzalloc ( &client->dev, sizeof ( *tas2505 ), GFP_KERNEL );
- if ( tas2505 == NULL )
- {
- return -ENOMEM;
- }
-
- //获取设备树的中reset-gpio脚
- tas2505->reset_gpio = of_get_named_gpio ( np, "reset-gpio", 0 );
-
- if ( gpio_is_valid ( tas2505->reset_gpio ) )
- {
- //设置成输出高
- ret = devm_gpio_request_one ( dev, tas2505->reset_gpio,
- GPIOF_OUT_INIT_HIGH,
- "reset" );
- if ( ret )
- {
- dev_err ( dev, "unable to get reset pin \n" );
- return ret;
- }
- }
-
- tas2505->regmap = devm_regmap_init_i2c ( client, &tas2505_regmap_config ); //注册i2c regmap
- if ( IS_ERR ( tas2505->regmap ) )
- {
- ret = PTR_ERR ( tas2505->regmap );
- dev_err ( dev, "Failed to allocate register map: %d\n",
- ret );
- return ret;
- }
-
- tas2505->dev = dev;
-
- //设置私有数据 sound核心层需要读取regmap 之后可以使用snd_soc_write等相关函数直接操作寄存器
- dev_set_drvdata ( tas2505->dev, tas2505 );
-
- //注册codec
- ret = snd_soc_register_codec ( dev,
- &soc_codec_dev_tas2505,
- tas2505_dai, ARRAY_SIZE ( tas2505_dai ) );
- if ( ret < 0 )
- {
- dev_err ( dev, "Failed to register codec: %d\n", ret );
- }
-
- return ret;
- }
platform驱动基本不需要更改,我也没细看,所以就不分析了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。