首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux音频驱动-AOSC之Codec

Linux音频驱动-AOSC之Codec

作者头像
DragonKingZhu
发布2020-03-24 16:43:48
3.1K0
发布2020-03-24 16:43:48
举报

概述

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结构:

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中发现。

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函数

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结构。

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结构:

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参数。

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组件,详细代码不在贴出。

ret = snd_soc_component_initialize(&codec->component,
		&codec_drv->component_driver, dev);

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

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

	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接口。

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中。

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。

list_for_each_entry(dai, &codec->component.dai_list, list)
	dai->codec = codec;

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

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 删除。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • Codec代码分析
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档