Linux音频子系统(6)- ASoC Codec

  • 了解ASoC架构Codec

1.概述

  对于Codec,uda1341主要完成以下功能:

  • 音频播放,应用程序将音频文件转换成PCM数据,然后codec芯片对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号;
  • 录音,对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号;
  • 对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等。本章讨论基于Codec芯片UDA1341,弄清楚codec芯片怎么来完成上面的三个功能

1.1.uda1341基本原理

  对于音频的处理使用了标准的I2S接口,对于音频的控制使用的是L3接口,其管脚定义如下:
在这里插入图片描述
L3信号线:
在这里插入图片描述
IIS信号线:
在这里插入图片描述
2.2. Codec的注册

  因为Codec驱动的代码要做到平台无关性,要使得Machine驱动能够使用该Codec,Codec驱动的首要任务就是确定snd_soc_codec(音频的控制)和snd_soc_dai(数据处理)的实例,并把它们注册到系统中,注册后的codec和dai才能为Machine驱动所用。

  以uda1341为例:

sound/soc/codecs/uda134x.c:
static struct platform_driver uda134x_codec_driver = {
	.driver = {
		.name = "uda134x-codec",
		.owner = THIS_MODULE,
	},
	.probe = uda134x_codec_probe,
	.remove = uda134x_codec_remove,
};
module_platform_driver(uda134x_codec_driver);

uda134x_codec_probe:

static int uda134x_codec_probe(struct platform_device *pdev)
{
	return snd_soc_register_codec(&pdev->dev,
			&soc_codec_dev_uda134x, &uda134x_dai, 1);
}

  Codec驱动定义snd_soc_codec_driver和snd_soc_dai_driver的实例,然后调用snd_soc_register_codec函数对Codec进行注册。进入snd_soc_register_codec函数:

int snd_soc_register_codec(struct device *dev,
			   const struct snd_soc_codec_driver *codec_drv,
			   struct snd_soc_dai_driver *dai_drv,
			   int num_dai)
{
	struct snd_soc_codec *codec;
	struct snd_soc_dai *dai;
	int ret, i;

	dev_dbg(dev, "codec register %s\n", dev_name(dev));

	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
	if (codec == NULL)
		return -ENOMEM;

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

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

	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);

#ifdef CONFIG_DEBUG_FS
	codec->component.init_debugfs = soc_init_codec_debugfs;
	codec->component.debugfs_prefix = "codec";
#endif

	if (codec_drv->get_regmap)
		codec->component.regmap = codec_drv->get_regmap(dev);

	for (i = 0; i < num_dai; i++) {
		fixup_codec_formats(&dai_drv[i].playback);
		fixup_codec_formats(&dai_drv[i].capture);
	}

	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;
	}

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

	mutex_lock(&client_mutex);
	snd_soc_component_add_unlocked(&codec->component);
	list_add(&codec->list, &codec_list);
	mutex_unlock(&client_mutex);

	dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n",
		codec->component.name);
	return 0;

err_cleanup:
	snd_soc_component_cleanup(&codec->component);
err_free:
	kfree(codec);
	return ret;
}
  • 它申请了一个snd_soc_codec结构的实例:kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
  • 初始化各个字段
  • 通过snd_soc_register_dais函数对本Codec的dai进行注册,挂到dai_list中
  • 它把codec实例链接到全局链表codec_list中

3.codec初始化

  codec的初始化工作就在该回调中完成。

/sound/soc/codecs/uda134x.c:
static const struct snd_soc_codec_driver soc_codec_dev_uda134x = {
	.probe =        uda134x_soc_probe,
	.set_bias_level = uda134x_set_bias_level,
	.suspend_bias_off = true,

	.component_driver = {
		.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_codec_driver结构体对应L3接口,读写里面的寄存器,而对应的probe函数,只是完成了一些L3接口的初始化。该接口主要完成以下功能

  • 访问寄存器,通过read/write接口,寄存器相关的都储存在uda134x_reg
  • set_bias_level来改变芯片的工作状态,主要是录音状态的变化
  • widege和routes的配置

4.dai的初始化

  snd_soc_dai_driver结构体 .name 对应snd_soc_card(sound/soc/samsung/s3c24xx-uda134x.c)结构体.dai_link里.codec_dai_name,具有录音和播放功能,还有一个operation结构体。

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_dai_driver里描述了dai接口,其中.ops 字段很重要,它指向codec_dai的操作函数集,定义了dai的时钟配置、格式配置、硬件参数配置等回调。

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,
}

  此时Codec driver只是在注册的时候提供了控制接口和dai的接口,里面提供了播放和录音的接口,也就是snd_soc_codec_driver 描述了使用哪一款codec芯片snd_soc_dai_driver 描述了使用codec 芯片的哪一种dai接口,dai接口的参数等。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值