关于ASoC中的aux设备及prefix(基于MTK mt6799 平台)

关于ASoC中的aux设备及prefix(基于MTK mt6799 平台)

tags : audio



1 前言

最近研究了一下mtk6799平台下的音频,跟qcom比起来mtk在这块显得就非常简陋和落后了。mtk平台上对于音频最主要的工作应该是在于他们自己搞的hal层的那一套框架了,本来打算对这块做点笔记的,但是发现这块的内容mtk自己的文档已经说的很清楚了,文档上没说的稍微看下代码就能明白,所以就放弃了,后面会稍微记录一下。

这里主要是想记录一下aux devs和prefix相关的东西,只是借用手头的mtk平台来分析,这两个东西虽然不难,但是网上似乎并没有看到专门介绍这块的东西,所以就简单记录一下。

2 aux devs

2.1 什么是aux devs

关于aux_devs是什么,为什么要有这个东西这个问题,中文介绍还真是少见,就拿个最权威的说明放这里吧。

This Linux kernel change “ASoC: pcm: Fix DPCM for aux_devs” is included in the Linux 3.5 release. This change is authored by Mark Brown <broonie [at] opensource.wolfsonmicro.com> on Tue May 8 10:33:47 2012 +0100. The commit for this change in Linux stable tree is b3bba9a (patch).


ASoC: pcm: Fix DPCM for aux_devs

When we instantiate an aux_dev we use a fake rtd as part of the process
which doesn’t have a dai_link associated with it. Fix the dpcm startup
code to cope with this.

Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com
Acked-by: Liam Girdwood lrg@ti.com


2.2 为什么要用aux devs

回答这个问题的话先用一个邮件里面的记录来说明,这封邮件是Mark Brown对于一些问题的回复,这里引用其中一段:

Mark Brown Dec. 15, 2015, 11:23 a.m. UTC | #7
On Tue, Dec 15, 2015 at 04:06:14PM +0800, Mengdong Lin wrote:

I still have some basic questions:

  1. What are the typical usages for aux_dev?
    For CODEC<->CODEC link or external headset detection chip?

Neither, it’s for analogue devices.

  1. Why we need the rtd array ‘rtd_aux’ for the aux_devs?
    If the codec has DAIs and used by a DAI link, the ASoC will create a rtd
    for the link.

There are (or were at the time) assumptions in drivers that there will
be a rtd there so it was easier to provide a stub.


以上是这篇邮件中的一部分,这篇邮件的主题其实不是关于aux devs的作用而展开的,主要是关于添加一个aux component的讨论,原文地址:ASoC: The soc card can have auxiliary components

其实我个人认为,只要是需要在音频链路中插入一些widgets或者controls,并且期待这些东西以codec的形式(此codec没有dai)被加载入声卡驱动时就可以使用aux,至于音频数据或者信号是否真的经过了这个“codec”都无所谓。

举个栗子:
有个设备xxx会通过gpio对整个音频链路的物理通路产生影响,那么对于开发人员来说,可以简单粗暴的为这个xxx设备编写驱动,然后提供接口,再在音频播放的过程中找个地方调用xxx设备的驱动接口实现对音频链路的控制。这样可行,但不优雅或者说不符合ASoC的设计理念。那么这个时候我们应该用widgets或control的方式来控制xxx设备,那么这个时候xxx设备就可以抽象成一个codec设备,然后ASoC会自动把codec设备中的widgets和controls注册好。
说了一圈,好像没有aux啥事,其实上面所描述的还有一个问题没有解决,那就是codec设备中的widgets和controls是怎么被注册进声卡的,虽然在注册codec的时候会调用snd_soc_register_codec()函数,但其实这个函数只是把这个codec封装成了一个component,然后挂载到了声卡的component list当中,至于这个component里面的内容其实并没有执行。那么一般的codec真正被加载到声卡中是在声卡实例化的时候,这个时候声卡会根据dai link找到对应的codec,然后进行真正的加载工作,但是像前面提到的xxx设备,他可能只是一个控制器,并没有dai,这个时候就没法跟他写dai link,那么xxx设备就不能通过dai link的方式被加载。为了解决这个问题,就有了aux,aux就是专治没有dai的codec的东西。这个时候只要把xxx设备抽象出来的codec加入aux list中(怎么加后面记录),那么该codec就可以像dai link中的codec一样被正常加载了。

2.3 怎么用aux devs

  • 1、注册一个codec设备
  • 2、在machine driver中为card->aux_dev创建一块内存,并按照要求填写相关信息
  • 3、在machine driver中填写正确的card->num_aux_devs的值

其实就是这么简单就好了,然后ASoC会自动把aux设备中的widgets和control添加到声卡中,这里具体的代码就不记录了,详见:soc-core.c文件中snd_soc_instantiate_card()函数。后面的过程和有dai link的codec基本上一样,这块就不展了。

这里要注意一点,card->aux_dev中需要存放所有的aux设备的信息,所以在分配内存的时候要把所有aux的内存一次分配好

3 关于prefix

3.1 为什么需要prefix

关于为什么需要prefix,这里就拿一个最常见的场景来说:
如果一个stereo的流,左右声道会分别通过两个不同的物理设备,但是两个物理设备又是完全一样,比如spkr,左声道从左声道spkr走,右声道从右声道spkr走,两个spkr完全独立,但两个spkr又完全一样。这时如果我要为这两个spkr开发driver(虽然从来没听说过谁给spkr开发了driver……这里只是举个栗子),那么我们肯定期望只开发一个driver,既能满足左声道也能满足右声道。一般从功能上来说,driver都是这样的,无论i2c bus有多少个,其driver都是一样的,不会因为i2c master的地址不同而导致driver不同。所以如果我为spkr开发一个driver,从功能上来说,由于左声道和右声道的功能完全一样,从而我只要开发一套dirver就能同时给左声道和右声道使用了。然而事情并不总是这样……如果我想把spkr抽象成一个codec设备,同时为其加上widgets或者control,然后通过control的方式来控制这两个spkr的话问题就来了。因为widgets的名称必须在driver中静态定义好,那么如果dirver是一样的,左声道和右声道的widgets的名称就会重复,这个时候我们就没办法区分现在控制的是左声道的spkr还是右声道的spkr了。这时就需要用到prefix,在machine driver这个层级中,在machine driver所对应的dts/dtsi文件定义出prefix,比如左声道的spkr,就在dts中把这个设备的prefix定义成SpkrL,右声道的spkr就把prefix定义成SpkrR,然后再在machine driver中去解析这些prefix,并且进行正确的操作,这时ASoC会自动的为指定的widgets或者control的driver中所定义的名称前加上prefix,这样就可以顺利区分出同一个driver下不同声道的spkr设备了。

这里还有一点想说明一下,这件事为什么放在machine driver中做,因为machine driver是一个板卡级的驱动,也就是负责掌管板卡信息的,那么这个板卡上面到底有几个spkr,每个spkr叫什么名字自然是板卡最清楚,spkr的生产厂家不会去关心我的spkr被你用到了哪个声道上,或者codec厂家也不关心你的板卡上放了几个codec,所以这个问题上只有让板卡设计者自己填写这些信息,prefix一定是定义在machine driver所解析的dts文件中对应声卡的设备节点里。

3.2 定义一个prefix

如上节所说,prefix一定是定义在machine driver所解析的dts文件中对应声卡的设备节点里面,比如mt6799平台:

	audio: audio@10c00000 {
		compatible = "mediatek,audio", "syscon";
		reg = <0 0x10c00000 0 0x1000>;
		interrupts = <GIC_SPI 175 IRQ_TYPE_LEVEL_LOW>;
		#clock-cells = <1>;
		
		aux-max-num = <2>;
		aux-devs = <&aux_dev_0>, <&aux_dev_1>;
		aux-devs-prefix = "SpkrLeft", "SpkrRight";
	};

这里定义了两个aux设备,以及两个prefix,至于这两个prefix是谁的prefix,在dts中是体现不出来的,这里是靠machine driver的代码逻辑来决定的,或者说是这个编写machine driver的人来决定的。
比如我在代码里面可以把解析到的aux-devs-prefix中的第一个元素认为是aux-devs中的第一个元素的prefix,也可以写成aux-devs-prefix中的第一个元素是aux-devs中的第二个元素的prefix,也可以这个prefix跟aux-devs没有任何关系,这里怎么映射是machine driver逻辑的事情,所以在这里写prefix的时候我们必然也要在machine driver中加上我们解析这个prefix的逻辑代码,这样才能使这个prefix正确的映射到我们期望的设备上。

3.3 如何使用prefix

这一小节就记录一下,在machine driver中如果操作prefix,让prefix正确映射到我们期望的aux设备上。
我这里就直接贴qcom msm8996平台的上一段源码来说明这个问题。(mtk平台自己没有用这个,所以就用qcom的代码来说明这个问题)

……
static struct snd_soc_aux_dev *msm8996_aux_dev;
static struct snd_soc_codec_conf *msm8996_codec_conf;
……
static int msm8996_init_wsa_dev(struct platform_device *pdev,
				struct snd_soc_card *card)
{
……
	/* Get count of WSA device phandles for this platform */
	wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node,
						 "qcom,wsa-devs", NULL);
……
	card->num_aux_devs = wsa_max_devs;
	card->num_configs = wsa_max_devs;
	/* 这里以上的代码都是在计算和校验aux dev和prefix的个数 */

	/* Alloc array of AUX devs struct */
	msm8996_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs,
				       sizeof(struct snd_soc_aux_dev),
				       GFP_KERNEL);
……
	/* Alloc array of codec conf struct */
	msm8996_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs,
					  sizeof(struct snd_soc_codec_conf),
					  GFP_KERNEL);
……
    /* 逐个从dts中解析aux dev和prefix */
	for (i = 0; i < card->num_aux_devs; i++) {
    ……
        /* 从dts中获取prefix */
		ret = of_property_read_string_index(pdev->dev.of_node,
						    "qcom,wsa-aux-dev-prefix",
						    wsa881x_dev_info[i].index,
						    wsa_auxdev_name_prefix);
	……
	    /* 填写aux dev信息 */
		snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i);
		msm8996_aux_dev[i].name = dev_name_str;
		msm8996_aux_dev[i].codec_name = NULL;
		/* 这里的of_node其实是之前从dts中按照顺序解析出来的,与i的顺序一一对应 */
		msm8996_aux_dev[i].codec_of_node =
					wsa881x_dev_info[i].of_node;
		msm8996_aux_dev[i].init = msm8996_wsa881x_init;
		
		/* 填写conf信息,prefix就是保存在这里 */
		msm8996_codec_conf[i].dev_name = NULL;
		msm8996_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0];
		/* 这里的of_node同上,是aux dev的of_node,这里就绑定了此prefix与aux dev的对应关系 */
		msm8996_codec_conf[i].of_node =
					wsa881x_dev_info[i].of_node;
	}
	/* 把填写好的aux_dev和conf信息传给card,完成声卡的aux信息及prefix信息填写 */
	card->codec_conf = msm8996_codec_conf;
	card->aux_dev = msm8996_aux_dev;

	return 0;
}

其实可以看到,这里的过程就跟前面那一节里面的步骤2和步骤3是一样的,而步骤1则是定义在aux设备自己的driver中。

4 一个容易忽略的问题

上面一节中有如下一段代码

	card->codec_conf = msm8996_codec_conf;
	card->aux_dev = msm8996_aux_dev;

这里就是填写声卡的card->aux_devcard->codec_conf,这两个东西的内存是在解析aux设备的过程中通过devm_kcalloc出来的,所以当该platform_device被release的时候,这个内存也就一并release掉了,然而card的定义往往是machine driver中的一个全局变量,所以,当platform_device被release后,card->aux_dev的值还是指向之前的内存地址,但是那片地址已经不存在了,所以如果这时使用card->aux_dev,则会出现内存错误。

上面这个问题看上去好像没什么,因为正常过程中,一旦系统正常工作后,谁也不会去release这个该platform_device,即使这个该platform_device被release了,那么由于声卡已经被卸载了card->aux_dev也没人回去使用了,所以不会发生错误。

但是,在系统启动的时候,声卡确实是会被多次加载和释放的,一旦在msm8996_init_wsa_dev()或者其他实现该类似功能的函数中通过去判断card->aux_dev是否为空来决定是否需要为其开辟内存,则这种操作必然会导致内存错误。

顺带提一句devm_kcalloc以及devm_xxx,这个内存管理体系设计的其实挺有意思,可以让人有一种写c#或者java之类的感觉,创建一个实例,用完直接销毁掉,至于内存回收完全不用操心,关于devm_xxx的实现这里就不记录了,网上很多写。同时这种方式也带来了诸如上面的弊端,也就是说a还在使用b的内存时,如果b被销毁了,a其实是不知道的,那么就会造成内存错误,但是真正像c#之类的,只要引用还在这个实例就不会被释放,当引用归零后回收机制自动进行gc。很早以前自己在一个C语言开发的项目中写过一套类似的东西,后来就是因为上面这个问题无法解决最终还是决定放弃在实际项目中使用了。

5 顺便说下mtk平台

其实之前就一直打算把mtk平台的调试内容记录一下的,但是发现mtk平台上除了hal层的一些东西,就没什么别的了,并且hal层的东西在他们的文档上也已经大致描述了,整个hal层的代码从逻辑上来看也不算太复杂,所以就不详细记录了。

5.1 关于mtk平台对ASoC的适配

这一块,mtk可以说是做的非常简陋,就记录一个地方吧,offload流,mtk在这里没有把offload数据纳入ASoC,而是自己另辟蹊径,创造了一个"offloadservice"设备,所有的offload数据hal层通过该设备交给kernel,同时hal层通过"audio_ipi"设备与dsp交互控制指令,以此实现了mp3的硬解码功能……

5.2 不得不吐槽一下

ASoC这块,mtk基本上还是停留在很过去的实现方式上,没有DPCM,没有widgets,control的配置基本上是在代码里面写死的,offload流也是另辟蹊径,完全没有纳入ASoC的体系,总的来说,跟qcom相比,mtk就是给人满满的落后以及穷的感觉。
当然,最要吐槽的可能算是mtk的错误了……hal层中的aurisys部分的代码里面居然有明显的逻辑错误!!官方给出的文档也有个别地方与代码所展现的功能或操作方式上有偏差。
种种这些……看来mtk被qcom碾压成现今这种状态也是合情合理的……

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值