前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux音频驱动-AOSC之Codec

Linux音频驱动-AOSC之Codec

作者头像
DragonKingZhu
发布于 2020-03-24 08:43:48
发布于 2020-03-24 08:43:48
3.5K00
代码可运行
举报
运行总次数:0
代码可运行

概述

ASOC的出现是为了让Codec独立于CPU,减少和CPU之间的耦合,这样同一个Codec驱动无需修改就可以适用任何一款平台。还是以下图做参考例子:

在Machine中已经知道,snd_soc_dai_link结构就指明了该Machine所使用的Platform和Codec。在Codec这边通过codec_dai和Platform侧的cpu_dai相互通信,既然相互通信,就需要遵守一定的规则,其中codec_dai和cpu_dai统一抽象为struct snd_soc_dai结构,而将dai的相关操作使用snd_soc_dai_driver抽象。同时也需要对所有的codec设备进行抽象封装,linux使用snd_soc_codec进行所有codec设备的抽象,而将codec的驱动抽象为snd_soc_codec_driver结构。

所有简单来说,Codec侧有四个重要的数据结构:

struct snd_soc_dai,struct snd_soc_dai_driver,struct snd_soc_codec,struct snd_soc_codec_driver。

Codec代码分析

如何找到codec的代码呢? 答案是通过machine中的snd_soc_dai_link结构:

代码语言:javascript
代码运行次数:0
运行
复制
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
	.name = "UDA134X",
	.stream_name = "UDA134X",
	.codec_name = "uda134x-codec",
	.codec_dai_name = "uda134x-hifi",
	.cpu_dai_name = "s3c24xx-iis",
	.ops = &s3c24xx_uda134x_ops,
	.platform_name	= "s3c24xx-iis",
};

在内核中搜索codec_name="uda134x-codec",会在kernel/sound/soc/codec/uda134x.c中发现。

代码语言:javascript
代码运行次数:0
运行
复制
static struct platform_driver uda134x_codec_driver = {
	.driver = {
		.name = "uda134x-codec",
		.owner = THIS_MODULE,
	},
	.probe = uda134x_codec_probe,
	.remove = uda134x_codec_remove,
};

之间查看此platform_driver的probe函数

代码语言:javascript
代码运行次数:0
运行
复制
static int uda134x_codec_probe(struct platform_device *pdev)
{
	return snd_soc_register_codec(&pdev->dev,
			&soc_codec_dev_uda134x, &uda134x_dai, 1);
}

此出通过snd_soc_register_codec函数注册了uda134x的codec,同时传入了snd_soc_codec_driver和snd_soc_dai_driver结构。

代码语言:javascript
代码运行次数:0
运行
复制
static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
	.probe =        uda134x_soc_probe,
	.remove =       uda134x_soc_remove,
	.suspend =      uda134x_soc_suspend,
	.resume =       uda134x_soc_resume,
	.reg_cache_size = sizeof(uda134x_reg),
	.reg_word_size = sizeof(u8),
	.reg_cache_default = uda134x_reg,
	.reg_cache_step = 1,
	.read = uda134x_read_reg_cache,
	.write = uda134x_write,
	.set_bias_level = uda134x_set_bias_level,
	.dapm_widgets = uda134x_dapm_widgets,
	.num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
	.dapm_routes = uda134x_dapm_routes,
	.num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
};

snd_soc_dai_driver结构:

代码语言:javascript
代码运行次数:0
运行
复制
static const struct snd_soc_dai_ops uda134x_dai_ops = {
	.startup	= uda134x_startup,
	.shutdown	= uda134x_shutdown,
	.hw_params	= uda134x_hw_params,
	.digital_mute	= uda134x_mute,
	.set_sysclk	= uda134x_set_dai_sysclk,
	.set_fmt	= uda134x_set_dai_fmt,
};

static struct snd_soc_dai_driver uda134x_dai = {
	.name = "uda134x-hifi",
	/* playback capabilities */
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = UDA134X_RATES,
		.formats = UDA134X_FORMATS,
	},
	/* capture capabilities */
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = UDA134X_RATES,
		.formats = UDA134X_FORMATS,
	},
	/* pcm operations */
	.ops = &uda134x_dai_ops,
};

继续进入snd_soc_register_codec函数分析。

1. 分配一个snd_soc_codec结构,设置component参数。

代码语言:javascript
代码运行次数:0
运行
复制
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
	return -ENOMEM;

codec->component.dapm_ptr = &codec->dapm;
codec->component.codec = codec;

2. 调用snd_soc_component_initialize函数进行component部件的初始化,会根据snd_soc_codec_driver中的struct snd_soc_component_driver结构设置snd_soc_codec中的component组件,详细代码不在贴出。

代码语言:javascript
代码运行次数:0
运行
复制
ret = snd_soc_component_initialize(&codec->component,
		&codec_drv->component_driver, dev);

3. 根据snd_soc_codec_driver的参数,进行一系列的初始化,

代码语言:javascript
代码运行次数:0
运行
复制
if (codec_drv->controls) {
	codec->component.controls = codec_drv->controls;
	codec->component.num_controls = codec_drv->num_controls;
}
if (codec_drv->dapm_widgets) {
	codec->component.dapm_widgets = codec_drv->dapm_widgets;
	codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;
}
if (codec_drv->dapm_routes) {
	codec->component.dapm_routes = codec_drv->dapm_routes;
	codec->component.num_dapm_routes = codec_drv->num_dapm_routes;
}

if (codec_drv->probe)
	codec->component.probe = snd_soc_codec_drv_probe;
if (codec_drv->remove)
	codec->component.remove = snd_soc_codec_drv_remove;
if (codec_drv->write)
	codec->component.write = snd_soc_codec_drv_write;
if (codec_drv->read)
	codec->component.read = snd_soc_codec_drv_read;
codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
codec->dapm.idle_bias_off = codec_drv->idle_bias_off;
codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off;
if (codec_drv->seq_notifier)
	codec->dapm.seq_notifier = codec_drv->seq_notifier;
if (codec_drv->set_bias_level)
	codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;
codec->dev = dev;
codec->driver = codec_drv;
codec->component.val_bytes = codec_drv->reg_word_size;
mutex_init(&codec->mutex);

4. 根据snd_soc_dai_driver中的参数设置Format

代码语言:javascript
代码运行次数:0
运行
复制
	for (i = 0; i < num_dai; i++) {
		fixup_codec_formats(&dai_drv[i].playback);
		fixup_codec_formats(&dai_drv[i].capture);
	}

5. 调用snd_soc_register_dais接口注册dai,传入参数有dai的驱动,以及dai的参数,因为一个codec不止一个dai接口。

代码语言:javascript
代码运行次数:0
运行
复制
ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
if (ret < 0) {
	dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
	goto err_cleanup;
}

根据dai的数目,分配snd_soc_dai结构,根据dai的数目设置dai的名字,这是dai的传入参数驱动,然后将此dai加入到component的dai_list中。

代码语言:javascript
代码运行次数:0
运行
复制
for (i = 0; i < count; i++) {

	dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
	if (dai == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	/*
	 * Back in the old days when we still had component-less DAIs,
	 * instead of having a static name, component-less DAIs would
	 * inherit the name of the parent device so it is possible to
	 * register multiple instances of the DAI. We still need to keep
	 * the same naming style even though those DAIs are not
	 * component-less anymore.
	 */
	if (count == 1 && legacy_dai_naming) {
		dai->name = fmt_single_name(dev, &dai->id);
	} else {
		dai->name = fmt_multiple_name(dev, &dai_drv[i]);
		if (dai_drv[i].id)
			dai->id = dai_drv[i].id;
		else
			dai->id = i;
	}
	if (dai->name == NULL) {
		kfree(dai);
		ret = -ENOMEM;
		goto err;
	}

	dai->component = component;
	dai->dev = dev;
	dai->driver = &dai_drv[i];
	if (!dai->driver->ops)
		dai->driver->ops = &null_dai_ops;

	list_add(&dai->list, &component->dai_list);

	dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
}

6. 根据component的dai_list,对所有的dai设置其所属的codec。

代码语言:javascript
代码运行次数:0
运行
复制
list_for_each_entry(dai, &codec->component.dai_list, list)
	dai->codec = codec;

7. 将所有的组间加入到component_list中,然后将注册的codec存入codec_list中。

代码语言:javascript
代码运行次数:0
运行
复制
mutex_lock(&client_mutex);
snd_soc_component_add_unlocked(&codec->component);
list_add(&codec->list, &codec_list);
mutex_unlock(&client_mutex);

至此,codec的注册就分析完毕。 总结: 通过调用snd_soc_register_codec函数之后,分配的codec最终放入了codec_list链表中,codec下的component组件全部放入component_list链表中,codec下的dai全部存放入code->component.dai_list中。 可以在上节的Machine中看到,machine匹配codec_dai和cpu_dai也是从code->component.dai_list中获取的。

关于codec侧驱动总结:

1. 分配名字为"codec_name"的平台驱动,注册。 2. 定义struct snd_soc_codec_driver结构,设置,初始化。

3. 定义struct snd_soc_dai_driver结构,设置,初始化。

4. 调用snd_soc_register_codec函数注册codec。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
高通Audio中ASOC的codec驱动(二)
继上一篇文章:高通Audio中ASOC的machine驱动(一) ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台。 在M
233333
2018/03/29
3.6K0
高通Audio中ASOC的codec驱动(二)
Linux音频驱动-ASOC之Machine
在ASOC小节中描述了整个ASOC的架构,其中Machine是ASOC架构中的关键部件,没有Machine部件,单独的Codec和Platform是无法工作的。因此本节则先从Machine部分开始,那应该如何开始呢? 答案当然是从代码入手,先进入ASOC在kernel中的位置: kernel/sound/soc下
DragonKingZhu
2020/03/24
2.5K0
高通Audio中ASOC的machine驱动
ASoC被分为Machine、Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。 ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动
233333
2018/03/07
4.1K0
高通Audio中ASOC的machine驱动
Linux音频驱动-AOSC之Platform
在ASOC在Platform部分,主要是平台相关的DMA操作和音频管理。大概流程先将音频数据从内存通过DMA方式传输到CPU侧的dai接口,然后通过CPU的dai接口(通过I2S总线)将数据从达到Codec中,数据会在Codec侧会解码的操作,最终输出到耳机/音箱中。依然已下图作为参考:
DragonKingZhu
2020/03/24
2.9K0
Linux音频驱动-AOSC之Platform
ALSA子系统 | 如何添加虚拟声卡
我们知道,asoc框架里面主要包含machine codec platform 这三大部分:
刘盼
2023/01/05
2.2K0
ALSA声卡驱动的DAPM(二)-建立过程
在上一篇文章中,我们重点介绍了widget、path、route之间的关系及其widget的注册; http://www.cnblogs.com/linhaostudy/p/8509899.html 在最后一章中,我们已经简单介绍了snd_soc_dapm_new_controls函数用来创建widget。 实际上,这个函数只是创建widget的第一步,它为每一个widget分配内存,初始化; 要使widget之间具备连接能力,我们还需要第二个函数snd_soc_dapm_new_widgets:这个函数会
233333
2018/03/29
3.7K0
ALSA声卡驱动的DAPM(二)-建立过程
[ Linux驱动炼成记 ] 12 -音频驱动TAS5754添加EQ参数
每一个带有音频播放的产品,设备初期的时候都会调试设备的EQ参数。EQ通过将声音中各频率的组成泛音等级加以修改,专为某一类音乐进行优化,增强人们的感觉。常见包括:正常、摇滚、流行、舞曲、古典、柔和、爵士、金属、重低音和自定义。1
程序手艺人
2019/02/20
1.4K0
高通msm8909耳机调试
1、DTS相应修改: DTS相关代码:kernel/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi: 1 sound { 2 compatible = "qcom,msm8x16-audio-codec"; 3 qcom,model = "msm8909-skuc-snd-card"; 4 qcom,msm-snd-card-id = <0>; 5 qcom,msm-codec-typ
233333
2018/03/07
4K0
高通msm8909耳机调试
[ 物联网篇 ] 28 - Linux ES7210 Driver 调试
项目开发过程中,由于Broadcom平台音频数字接口比较少,所以采用模拟麦克风作为输入端,经顺芯ES7210 ADC 转换送至Broadcom PCM 数字音频接口。
程序手艺人
2020/09/15
4.2K2
[ 物联网篇 ] 28 - Linux ES7210 Driver 调试
Linux 自带的耳机拔插检测驱动
Linux 自带的耳机拔插检测驱动是混在声卡驱动中,耳机拔插状态通过 input 子系统上报。
Jasonangel
2024/03/21
5150
Linux 自带的耳机拔插检测驱动
ALSA声卡驱动的DAPM(一)-DPAM详解
最近使用tinymix 调试相应的音频通道,但是一直不知道音频通道的原理是什么。所以百度了一下,百度结果是与DPAM有关。 一、DAPM简介:  DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc core中完成。用户空间的应用程序无需对代码做出修改,也无需重新编译,DAPM根据当前
233333
2018/03/29
9.4K0
ALSA声卡驱动的DAPM(一)-DPAM详解
Linux音频驱动-PCM设备
pcm(Pulse-code modulation)脉冲编码调制,是将模拟信号转化为数字信号的一种方法。声音的转化的过程为,先对连续的模拟信号按照固定频率周期性采样,将采样到的数据按照一定的精度进行量化,量化后的信号和采样后的信号差值叫做量化误差,将量化后的数据进行最后的编码存储,最终模拟信号变化为数字信号。
DragonKingZhu
2020/03/24
9.2K0
Linux音频驱动-PCM设备
Linux ALSA声卡驱动之五:移动设备中的ALSA(ASoC)
ASoC--ALSA System on Chip ,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系。在ASoc出现之前,内核对于SoC中的音频已经有部分的支持,不过会有一些局限性:
用户6280468
2024/01/23
1K0
Linux ALSA声卡驱动之五:移动设备中的ALSA(ASoC)
Linux音频驱动-Card创建
在上节Linux音频驱动-ALSA概述中介绍了整个ALSA的构成,接口以及函数入口的分析。
DragonKingZhu
2020/03/24
3.1K0
Linux ALSA声卡驱动之二:声卡的创建
snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体。正因为如此,本节中,我们也从 struct cnd_card开始吧。
用户6280468
2024/01/10
1.5K0
Linux ALSA声卡驱动之二:声卡的创建
Linux设备驱动模型-Device
Linux将所有的设备统一抽象为struct device结构, 同时将所有的驱动统一抽象为struct device_driver结构。这样设计之后就方便驱动开发工程师编写驱动,只需要将具体的设备包含struct device结构,具体的驱动包含struct device_driver结构。最终会调用device_register和driver_register将驱动和设备注册到系统,表现出来就是在sys目录的device和driver目录下。本小节先分析device结构,以及相关API,以及如何注册到系统中,以及提供给上层的sys接口。
DragonKingZhu
2020/03/24
8.4K0
Linux设备驱动模型-Bus
在linux设备驱动模型中,总线可以看作是linux设备模型的核心,系统中的其他设备以及驱动都是以总线为核心围绕。不过驱动程序员在系统中创建一条总线的机会并不多。驱动模型中的总线可以是真是存在的物理总线(USB总线,I2C总线,PCI总线),也可以是为了驱动模型架构设计出的虚拟总线(Platform总线)。为此linux设备驱动模型都将围绕"总线--设备--驱动"来展开,因为符合linux设备驱动模型的设备与驱动都是必须挂载在一个总线上的,无论是实际存在的或者虚拟的。
DragonKingZhu
2020/03/24
4.3K0
alsa声卡分析alsa-utils调用过程(一)-tinyplay
如何分析tinyplay 播放音频和tinymix的过程?需要相应的工具来支持追查; 一、分析tinyplay和tinymix: 1.1 利用strace工具: strace -o tinyplay.log tinyplay 1.wav strace -o tinymixer.log tinymixer "SEC_MI2S_RX Audio Mixer MultiMedia1" 1  利用strace工具获取APP的log,从应用层往下看; 1.2 分析alsa-utils源码: tiny工具源码在andr
233333
2018/03/29
3.1K0
alsa声卡分析alsa-utils调用过程(一)-tinyplay
[Linux驱动炼成记] 05-存储eMMC配置
eMMC (Embedded Multi Media Card) 为MMC协会所订立的,eMMC 相当于 NandFlash+主控IC ,对外的接口协议与SD、TF卡一样,主要是针对手机或平板电脑等产品的内嵌式存储器标准规格。eMMC的一个明显优势是在封装中集成了一个控制器,它提供标准接口并管理闪存,使得手机厂商就能专注于产品开发的其它部分,并缩短向市场推出产品的时间。这些特点对于希望通过缩小光刻尺寸和降低成本的NAND供应商来说,同样的重要。1
程序手艺人
2019/02/20
3.5K0
一文搞懂 Linux 网络 Phy 驱动
上图来自 瑞昱半导体 (RealTek) 的 RTL8201F 系列网卡 PHY 芯片手册。按OSI 7层网络模型划分,网卡PHY 芯片(图中的RTL8201F)位于物理层,对应的软件层就是本文讨论的 PHY 驱动层;而 MAC 位于 数据链路层,也是通常软件上所说的网卡驱动层,它不是本文的重点,不做展开。另外,可通过 MDIO 接口对 PHY 芯片进行配置(如PHY芯片寄存器读写),而 PHY 和 MAC 通过 MII/RMII 进行数据传输。
刘盼
2023/11/05
3.8K0
一文搞懂 Linux 网络 Phy 驱动
相关推荐
高通Audio中ASOC的codec驱动(二)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档