Linux音频之ASOC

2023-11-19

参考:https://blog.csdn.net/droidphone/article/details/7165482

1.ASOC简介

ASoC--ALSA System on Chip ,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系

2.ASOC架构

ASOC在硬件件上被分为 Platform   Machine  codec三大部分。


  • Machine  是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出Machine几乎是不可重用的,每个Machine上的硬件实现可能都不一样,CPU不一样,Codec不一样,音频的输入、输出设备也不一样,Machine为CPU、Codec、输入输出设备提供了一个载体。
  • Platform  一般是指某一个SoC平台,比如pxaxxx,s3cxxxx,omapxxx等等,与音频相关的通常包含该SoC中的时钟、DMA、I2S、PCM等等,只要指定了SoC,那么我们可以认为它会有一个对应的Platform,它只与SoC相关,与Machine无关,这样我们就可以把Platform抽象出来,使得同一款SoC不用做任何的改动,就可以用在不同的Machine中。实际上,把Platform认为是某个SoC更好理解。
  • Codec  字面上的意思就是编解码器,Codec里面包含了I2S接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line-out),Codec和Platform一样,是可重用的部件,同一个Codec可以被不同的Machine使用。嵌入式Codec通常通过I2C对内部的寄存器进行控制。

在软件层面,ASOC也同样分为Platform驱动   Machine驱动  codec驱动三大部分。

  • Codec驱动  ASoC中的一个重要设计原则就是要求Codec驱动是平台无关的,它包含了一些音频的控件(Controls),音频接口,DAMP(动态音频电源管理)的定义和某些Codec IO功能。为了保证硬件无关性,任何特定于平台和机器的代码都要移到Platform和Machine驱动中。所有的Codec驱动都要提供以下特性:
    • Codec DAI 和 PCM的配置信息;
    • Codec的IO控制方式(I2C,SPI等);
    • Mixer和其他的音频控件;
    • Codec的ALSA音频操作接口;

必要时,也可以提供以下功能:

    • DAPM描述信息;
    • DAPM事件处理程序;
    • DAC数字静音控制
  • Platform驱动  它包含了该SoC平台的音频DMA和音频接口的配置和控制(I2S,PCM,AC97等等);它也不能包含任何与板子或机器相关的代码。
  • Machine驱动  Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。

以上主要参考:https://blog.csdn.net/droidphone/article/details/7165482

3.代码分析

单独的platform和codec不能够工作,必须要通过machine驱动将两者联系起来,才能正常工作。

下面我们将从代码的角度去分析 machine  platform  codec 三者间的联系。

软件上主要涉及如下几个文件,划分如下:

● Machine驱动:link-owl.c
● Platform驱动:pcm-owl.c dai-owl.c dmaengine-pcm-owl.c

● Codec驱动:atc2603c-audio-codec.c

platform:

我们先从platform驱动入手开始分析:

先来看看pcm-owl.c中干了什么事情。

分配并添加了一个名为“s900-pcm-audio”的设备,随后注册了一个name为 “s900-pcm-audio”的platform driver。

static struct platform_driver s900_pcm_driver = {
	.driver = {
			.name = "s900-pcm-audio",
			.owner = THIS_MODULE,
	},

	.probe = s900_pcm_probe,
	.remove = s900_pcm_remove,
};

	s900_pcm_device = platform_device_alloc("s900-pcm-audio", -1);
	if (!s900_pcm_device) {
		snd_dbg(
				"ASoC: Platform device s900-pcm-audio allocation failed\n");
		ret = -ENOMEM;
		goto err;
	}

	ret = platform_device_add(s900_pcm_device);
	if (ret) {
		snd_dbg(
				"ASoC: Platform device s900-pcm-audio add failed\n");
		goto err_device_add;
	}

	pcm_priv = kzalloc(sizeof(struct s900_pcm_priv), GFP_KERNEL);
	if (NULL == pcm_priv)
		return -ENOMEM;
	pcm_priv->output_mode = O_MODE_I2S;
	pcm_priv->input_mode = O_MODE_I2S;
	platform_set_drvdata(s900_pcm_device, pcm_priv);

	ret = platform_driver_register(&s900_pcm_driver);

平台设备驱动的机制决定了当平台设备的名字匹配上了平台driver的名字之后driver的probe函数将会被调用。

static int s900_pcm_probe(struct platform_device *pdev)
{
	dev_err(&pdev->dev,
			"s900_pcm_probe!!\n");
	pdev->dev.init_name = "s900-pcm-audio";
	return snd_soc_register_platform(&pdev->dev,   
			&s900_soc_platform);
}

分析下去主要工作就是将名为“s900-pcm-audio”的平台设备添加进platform_list

int snd_soc_register_platform(struct device *dev,
		const struct snd_soc_platform_driver *platform_drv)
{
	struct snd_soc_platform *platform;
	int ret;

	dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));

	platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
	if (platform == NULL)
		return -ENOMEM;

	ret = snd_soc_add_platform(dev, platform, platform_drv);
	if (ret)
		kfree(platform);

	return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_platform);
int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
		const struct snd_soc_platform_driver *platform_drv)
{
	/* create platform component name */
	platform->name = fmt_single_name(dev, &platform->id);
	if (platform->name == NULL) {
		kfree(platform);
		return -ENOMEM;
	}

	platform->dev = dev;
	platform->driver = platform_drv;
	platform->dapm.dev = dev;
	platform->dapm.platform = platform;
	platform->dapm.stream_event = platform_drv->stream_event;
	mutex_init(&platform->mutex);

	mutex_lock(&client_mutex);
	list_add(&platform->list, &platform_list);
	mutex_unlock(&client_mutex);

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

	return 0;
}

至此pcm-owl.c就分析完了。接着看看dai-owl.c

/* modify compatible to match diff IC */
static const struct of_device_id owl_i2s_of_match[] = {
	{.compatible = "actions,s700-audio-i2s", .data = &ic_s700,},
	{.compatible = "actions,s900-audio-i2s", .data = &ic_s900,},
	{},
};
static struct platform_driver s900_dai_driver = {
	.driver = {
		.name = "owl-audio-i2s",
		.owner = THIS_MODULE,
		.of_match_table = owl_i2s_of_match,
	},

	.probe = s900_dai_probe,
	.remove = s900_dai_remove,
};

/*static struct platform_device *s900_dai_device;*/

static int __init s900_dai_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&s900_dai_driver);
	if (ret) {
		snd_err("ASoC: Platform driver s900-dai register failed\n");
	}

	return ret;
}
干的事情很少,就注册了个s900_dai_driver驱动,其中如果在设备树文件中匹配到
"actions,s700-audio-i2s"

字段的话就那么s900_dai_driver的probe函数就会被调用。看了下dts中果然有

songchong@srv-artek-pad:~/AD700A/android/kernel/arch/arm64/boot/dts$ grep "s700-audio-i2s" -nr .

./s700.dtsi:551:                compatible = "actions,s700-audio-i2s";

因此s900_dai_probe函数会被调用。

static int s900_dai_probe(struct platform_device *pdev)
{

	/* get resource of i2s and hdmi from dts file */
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
	if (!res) {
		snd_err("no memory resource of i2s!\n");
		return -ENODEV;
	}
	/* get virtual base for i2s */


	dev_warn(&pdev->dev, "s900_dai_probe\n");

	pdev->dev.init_name = "owl-audio-i2s";  //设置pdev->dev.init_name 为“owl-audio-i2s”下文中会用到。最终跟machine中的cpu_dai_name匹配就是靠它

	return snd_soc_register_component(&pdev->dev, &s900_component,
					 &s900_dai, 1);

}
struct snd_soc_dai_driver s900_dai = {
	.name = "owl-audio-i2s",
	.id = S900_AIF_I2S,
	.playback = {
		.stream_name = "s900 dai Playback",
		.channels_min = 1,
		.channels_max = 8,
		.rates = S900_STEREO_PLAYBACK_RATES,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.capture = {
		.stream_name = "s900 dai Capture",
		.channels_min = 1,
		.channels_max = 4,
		.rates = S900_STEREO_CAPTURE_RATES,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.ops = &s900_dai_dai_ops,
};

主要是做了一些IO内存初始化CLK初始化DMA申请等工作,最后 snd_soc_register_component(&pdev->dev, &s900_component,&s900_dai, 1); 是关键。

int snd_soc_register_component(struct device *dev,
			 const struct snd_soc_component_driver *cmpnt_drv,
			 struct snd_soc_dai_driver *dai_drv,
			 int num_dai)
{
	struct snd_soc_component *cmpnt;
	int ret;

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

	cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL);
	if (!cmpnt) {
		dev_err(dev, "ASoC: Failed to allocate memory\n");
		return -ENOMEM;
	}

	cmpnt->name = fmt_single_name(dev, &cmpnt->id);//设置
	if (!cmpnt->name) {
		dev_err(dev, "ASoC: Failed to simplifying name\n");
		return -ENOMEM;
	}

	cmpnt->dev	= dev;
	cmpnt->driver	= cmpnt_drv;
	cmpnt->num_dai	= num_dai;

	/*
	 * snd_soc_register_dai()  uses fmt_single_name(), and
	 * snd_soc_register_dais() uses fmt_multiple_name()
	 * for dai->name which is used for name based matching
	 */
	if (1 == num_dai)
		ret = snd_soc_register_dai(dev, dai_drv);
	else
		ret = snd_soc_register_dais(dev, dai_drv, num_dai);
	if (ret < 0) {
		dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
		goto error_component_name;
	}

	mutex_lock(&client_mutex);
	list_add(&cmpnt->list, &component_list);
	mutex_unlock(&client_mutex);
}
EXPORT_SYMBOL_GPL(snd_soc_register_component);

其中将s900_component加入到component_list中去,接着看一下snd_soc_register_dais

static int snd_soc_register_dai(struct device *dev,
		struct snd_soc_dai_driver *dai_drv)
{
	struct snd_soc_codec *codec;
	struct snd_soc_dai *dai;

	dev_dbg(dev, "ASoC: dai register %s\n", dev_name(dev));

	dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
	if (dai == NULL)
		return -ENOMEM;

	/* create DAI component name */
	dai->name = fmt_single_name(dev, &dai->id);    //去取dev.init_name出来即owl-audio-i2s, 并设置给dai->name = “owl-audio-i2s”  下文中会用到这个name跟machine驱动中的cpu_dai_name字段去匹配
	dai->dev = dev;
	dai->driver = dai_drv;
	dai->dapm.dev = dev;
	if (!dai->driver->ops)
		dai->driver->ops = &null_dai_ops;

	mutex_lock(&client_mutex);

	list_for_each_entry(codec, &codec_list, list) {
		if (codec->dev == dev) {
			dev_dbg(dev, "ASoC: Mapped DAI %s to CODEC %s\n",
				dai->name, codec->name);
			dai->codec = codec;
			break;
		}
	}

	if (!dai->codec)
		dai->dapm.idle_bias_off = 1;

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

	mutex_unlock(&client_mutex);

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

	return 0;
}

主要是将dai_driver添加到dai_list中去。至于有什么作用我们后面会分析到。

codec:

接着我们看看codec中干了什么事情。

static int __init atc2603c_init(void)
{
        /*一些初始化工作省去*/
	ret = platform_driver_register(&atc2603c_platform_driver);

}
static struct platform_driver atc2603c_platform_driver = {
	.probe      = atc2603c_platform_probe,
	.remove     = atc2603c_platform_remove,
	.driver     = {
		.name   = "atc2603c-audio",
		.owner  = THIS_MODULE,
		.of_match_table = atc2603c_audio_of_match,
	},
	.shutdown	= atc2603c_platform_shutdown,
};

static const struct of_device_id atc2603c_audio_of_match[] = {
	{.compatible = "actions,atc2603c-audio",},
	{}
};

也就是注册了个platform driver ,如果dts文件中配置了"actions,atc2603c-audio"字段的话,atc2603c_platform_probe函数就会被调用。

static int atc2603c_platform_probe(struct platform_device *pdev)
{


	codec_res.clk = devm_clk_get(&pdev->dev, "audio_pll");

	codec_res.hdmia_clk = devm_clk_get(&pdev->dev, "hdmia");

	atc260x = dev_get_drvdata(pdev->dev.parent);
	platform_set_drvdata(pdev, atc260x);

	atc2603c_ictype = ATC260X_ICTYPE_2603C;
	pdev->dev.init_name = "atc260x-audio";  //关键点,设置pdev->dev.init_name为“atc260x-audio”下文会用到

	/* we use VMICEXT to detect earphone */
	/* bug fix: have no earphone and do not config earphone detect */
	if ((earphone_gpio_num < 0) && (adc_detect_mode == 0)
			&& (audio_hw_cfg.earphone_detect_method == 1)) {
		earphone_irq = platform_get_irq(pdev, 0);
		printk(KERN_INFO"what's my lucky draw %d\n", earphone_irq);
	}

	return snd_soc_register_codec(&pdev->dev, &soc_codec_atc2603c,
			codec_atc2603c_dai, ARRAY_SIZE(codec_atc2603c_dai));
}
主要是做一些初始化和时钟相关的操作,其中,snd_soc_register_codec(&pdev->dev, &soc_codec_atc2603c,

codec_atc2603c_dai, ARRAY_SIZE(codec_atc2603c_dai));是关键。

static struct snd_soc_codec_driver soc_codec_atc2603c = {
	.probe = atc2603c_probe,
	.remove = atc2603c_remove,

	.suspend = atc2603c_suspend,
	.resume = atc2603c_resume,
	/*.set_bias_level = atc2603c_set_bias_level,*/
	.idle_bias_off = true,

	.reg_cache_size = (ADC_ANALOG1 + 1),
	.reg_word_size = sizeof(u16),
	/*.reg_cache_default = atc2603c_reg,*/
	.volatile_register = atc2603c_volatile_register,
	.readable_register = atc2603c_readable_register,
	.reg_cache_step = 1,

	.controls = atc2603c_snd_controls,
	.num_controls = ARRAY_SIZE(atc2603c_snd_controls),
	.dapm_widgets = atc2603c_dapm_widgets,
	.num_dapm_widgets = ARRAY_SIZE(atc2603c_dapm_widgets),
	.dapm_routes = atc2603c_dapm_routes,
	.num_dapm_routes = ARRAY_SIZE(atc2603c_dapm_routes),
};

struct snd_soc_dai_driver codec_atc2603c_dai[] = {
	{
		.name = "atc2603c-dai",
		.id = ATC2603C_AIF,
		.playback = {
			.stream_name = "AIF Playback",
			.channels_min = 1,
			.channels_max = 8,
			.rates = ATC2603C_RATES,
			.formats = ATC2603C_FORMATS,
		},
		.capture = {
			.stream_name = "AIF Capture",
			.channels_min = 1,
			.channels_max = 4,
			.rates = ATC2603C_RATES,
			.formats = ATC2603C_FORMATS,
		},
		.ops = &atc2603c_aif_dai_ops,
	},
};	
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)
{
    codec->name = fmt_single_name(dev, &codec->id);    //取出上文中pdev->dev.init_name"atc260x-audio"; 设置codec->name为atc260x-audio
	if (codec_drv->compress_type)
		codec->compress_type = codec_drv->compress_type;
	else
		codec->compress_type = SND_SOC_FLAT_COMPRESSION;

	codec->write = codec_drv->write;
	codec->read = codec_drv->read;
	codec->volatile_register = codec_drv->volatile_register;
	codec->readable_register = codec_drv->readable_register;
	codec->writable_register = codec_drv->writable_register;
	codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
	codec->dapm.bias_level = SND_SOC_BIAS_OFF;
	codec->dapm.dev = dev;
	codec->dapm.codec = codec;
	codec->dapm.seq_notifier = codec_drv->seq_notifier;
	codec->dapm.stream_event = codec_drv->stream_event;
	codec->dev = dev;
	codec->driver = codec_drv;
	codec->num_dai = num_dai;
	mutex_init(&codec->mutex);

	if (codec_drv->reg_access_size && codec_drv->reg_access_default) {
		if (!codec->volatile_register)
			codec->volatile_register = snd_soc_default_volatile_register;
		if (!codec->readable_register)
			codec->readable_register = snd_soc_default_readable_register;
		if (!codec->writable_register)
			codec->writable_register = snd_soc_default_writable_register;
	}

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

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

	/* register any DAIs */
	ret = snd_soc_register_dais(dev, dai_drv, num_dai);
	if (ret < 0) {
		dev_err(codec->dev, "ASoC: Failed to regster DAIs: %d\n", ret);
		goto fail_codec_name;
	}
}


主要指定了codec driver 然后将code 加入到codec_list链表中去。在snd_soc_register_dais中将会把codec_atc2603c_dai放入dai_list中去。

static int snd_soc_register_dai(struct device *dev,
		struct snd_soc_dai_driver *dai_drv)
{

	/* create DAI component name */
       dai->name = fmt_multiple_name(dev, &dai_drv[i]);/设置dai->name为dai_drv->name 即"atc2603c-dai"下文会跟machine中的codec_dai_name字段匹配
	if (dai->name == NULL) {
		kfree(dai);
		return -ENOMEM;
	}

	dai->dev = dev;
	dai->driver = dai_drv;
	dai->dapm.dev = dev;
	if (!dai->driver->ops)
		dai->driver->ops = &null_dai_ops;

	list_for_each_entry(codec, &codec_list, list) {
		if (codec->dev == dev) {
			dev_dbg(dev, "ASoC: Mapped DAI %s to CODEC %s\n",
				dai->name, codec->name);
			dai->codec = codec;
			break;
		}
	}

	if (!dai->codec)
		dai->dapm.idle_bias_off = 1;

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

	return 0;
}

至此codec分析完毕。

machine:

接着我们看一下machine是如何将platform 和codec联系起来的。

首先在machine驱动中做一下相关gpio初始化的工作,然后分配并注册一个名为“soc-audio”的platform device 设备。

s900_link_snd_device = platform_device_alloc("soc-audio", -1);
	if (!s900_link_snd_device) {
		snd_err("ASoC: Platform device allocation failed\n");
		ret = -ENOMEM;
		goto platform_device_alloc_failed;
	}
	platform_set_drvdata(s900_link_snd_device,
			&snd_soc_s900_atc2603c_link);//添加私有数据,其中snd_soc_s900_atc2603c_link结构非常重要

	ret = platform_device_add(s900_link_snd_device);//添加platform设备

插入一段题外话为我们后续分析做准备,在这里我们先来看下snd_soc_s900_atc2603c_link结构的定义

static struct snd_soc_card snd_soc_s900_atc2603c_link = {
	.name = "s900_link",
	.owner = THIS_MODULE,
	.dai_link = s900_atc2603c_link_dai,
	.num_links = ARRAY_SIZE(s900_atc2603c_link_dai),
	.controls = owl_outpa_controls,
	.num_controls = ARRAY_SIZE(owl_outpa_controls),
};

在snd_soc_s900_atc2603c_link中定义了dai_link.其中dai_link就是串联 platform和codec驱动的关键。platform和codec就是靠s900_atc2603c_link_dai结构中定义的各种name字段来结合在一起。我们来看下这个关键的结构体定义吧。


static struct snd_soc_dai_link s900_atc2603c_link_dai[] = {
	{
		.name = "S900 ATC2603C",
		.stream_name = "ATC2603C PCM",
		.cpu_dai_name = "owl-audio-i2s",
		.codec_dai_name = "atc2603c-dai",
		.init = s900_link_snd_init,
		.platform_name = "s900-pcm-audio",
		.codec_name = "atc260x-audio",
		.ops = &s900_link_ops,
	},

	{
		.name = "S900 HDMI AUDIO",
		.stream_name = "HDMI PCM",
		.cpu_dai_name = "owl-audio-i2s",
		.codec_dai_name = "s900-hdmi-dai",
		.init = s900_link_snd_init,
		.platform_name = "s900-pcm-audio",
		.codec_name = "s900-hdmi-audio",
		.ops = &s900_link_ops,
	},

	{
		.name = "S900 PCM AUDIO",
		.stream_name = "BLUETOOTH PCM",
		.cpu_dai_name = "owl-audio-i2s",
		.codec_dai_name = "bluetooth-pcm-dai",
		.init = s900_link_snd_init,
		.platform_name = "s900-pcm-audio",
		.codec_name = "pcm-audio",
		.ops = &s900_link_ops,
	},
	
	{
		.name = "S900 SPDIF AUDIO",
		.stream_name = "SPDIF PCM",
		.cpu_dai_name = "owl-audio-i2s",
		.codec_dai_name = "s900-spdif-dai",
		.init = s900_link_snd_init,
		.platform_name = "s900-pcm-audio",
		.codec_name = "s900-spdif-audio",
		.ops = &s900_link_ops,
	}
	
};

在s900_atc2603c_link_dai中定义了四种设备分别是codec、HDMI、蓝牙、SPDIF。在内部分别制定了各种name字段。

回到machine驱动添加了一个名为“soc-audio”的platform设备。

根据linux平台设备驱动的特性,一定会有一个同名的platform driver会去注册。经过查找在kernel/sound/soc/soc-core.c中注册了“soc-audio”platform driver。

/* ASoC platform driver */
static struct platform_driver soc_driver = {
	.driver		= {
		.name		= "soc-audio",
		.owner		= THIS_MODULE,
		.pm		= &snd_soc_pm_ops,
	},
	.probe		= soc_probe,
	.remove		= soc_remove,
};
static int __init snd_soc_init(void)
{
	snd_soc_util_init();

	return platform_driver_register(&soc_driver);//注册soc_driver设备驱动程序
}

至此soc_probe函数会被调用,我们来看一下soc_probe

static int soc_probe(struct platform_device *pdev)
{
	struct snd_soc_card *card = platform_get_drvdata(pdev); //其实就是获取到了上文中的snd_soc_s900_atc2603c_link
	/*
	 * no card, so machine driver should be registering card
	 * we should not be here in that case so ret error
	 */
	if (!card)
		return -EINVAL;

	dev_warn(&pdev->dev,
		 "ASoC: machine %s should use snd_soc_register_card()\n",
		 card->name);

	/* Bodge while we unpick instantiation */
	card->dev = &pdev->dev;

	return snd_soc_register_card(card); //&snd_soc_s900_atc2603c_link
}
int snd_soc_register_card(struct snd_soc_card *card) //snd_soc_s900_atc2603c_link
{
	dev_set_drvdata(card->dev, card);            

	snd_soc_initialize_card_lists(card);

	soc_init_card_debugfs(card);

	card->rtd = devm_kzalloc(card->dev,
				 sizeof(struct snd_soc_pcm_runtime) *
				 (card->num_links + card->num_aux_devs),
				 GFP_KERNEL);
	if (card->rtd == NULL)
		return -ENOMEM;
	card->num_rtd = 0;
	card->rtd_aux = &card->rtd[card->num_links];

	for (i = 0; i < card->num_links; i++)
		card->rtd[i].dai_link = &card->dai_link[i]; //snd_soc_s900_atc2603c_link->dai_link[i];也就是我们上文中提到的s900_atc2609a_link_dai,一共有四组成员

	INIT_LIST_HEAD(&card->list);
	INIT_LIST_HEAD(&card->dapm_dirty);
	card->instantiated = 0;
	mutex_init(&card->mutex);
	mutex_init(&card->dapm_mutex);

	ret = snd_soc_instantiate_card(card);//实例化声卡
	if (ret != 0)
		soc_cleanup_card_debugfs(card);

	return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);

接着看看是如何实例化声卡的,此函数比较长,我截取关键部分

static int snd_soc_instantiate_card(struct snd_soc_card *card)//snd_soc_s900_atc2603c_link
{
	struct snd_soc_codec *codec;
	struct snd_soc_codec_conf *codec_conf;
	enum snd_soc_compress_type compress_type;
	struct snd_soc_dai_link *dai_link;

	/* bind DAIs */
	for (i = 0; i < card->num_links; i++) {
		ret = soc_bind_dai_link(card, i); //绑定dai的关键部分
                     //插入  soc_bind_dai_link函数分析一下
	             
static int soc_bind_dai_link(struct snd_soc_card *card, int num)
{
	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
	struct snd_soc_codec *codec;
	struct snd_soc_platform *platform;
	struct snd_soc_dai *codec_dai, *cpu_dai;
	const char *platform_name;

	dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);

	/* Find CPU DAI from registered DAIs*/
	list_for_each_entry(cpu_dai, &dai_list, list) {     //遍历dai_list链表
		if (dai_link->cpu_of_node &&
		    (cpu_dai->dev->of_node != dai_link->cpu_of_node))
			continue;
		if (dai_link->cpu_name &&
		    strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name))
			continue;
		if (dai_link->cpu_dai_name &&
		    strcmp(cpu_dai->name, dai_link->cpu_dai_name)) //通过比较dai_link->cpu_dai_name 和cpu_dai->name的设备
			continue;                                           //"owl-audio-i2s"   找不到cpu_dai->name         

		rtd->cpu_dai = cpu_dai;  
	}

	/* Find CODEC from registered CODECs */
	list_for_each_entry(codec, &codec_list, list) {
		if (dai_link->codec_of_node) {
			if (codec->dev->of_node != dai_link->codec_of_node)
				continue;
		} else {
			if (strcmp(codec->name, dai_link->codec_name))// dai_link-codec_name = "atc260x-audio"
				continue;                             // code->name = "atc2603c-audio",  匹配成功
		}                                                     

		rtd->codec = codec;
		/*
		 * CODEC found, so find CODEC DAI from registered DAIs from
		 * this CODEC
		 */
		list_for_each_entry(codec_dai, &dai_list, list) {
			if (codec->dev == codec_dai->dev &&
				!strcmp(codec_dai->name,               //codec_dai->name = "atc2603c-dai"
					dai_link->codec_dai_name)) {   //dai_link->codec_dai_name = "atc2603c-dai" 匹配成功

				rtd->codec_dai = codec_dai;
			}
		}
	}

	/* if there's no platform we match on the empty platform */
	platform_name = dai_link->platform_name;
	if (!platform_name && !dai_link->platform_of_node)
		platform_name = "snd-soc-dummy";

	/* find one from the set of registered platforms */
	list_for_each_entry(platform, &platform_list, list) {
		if (dai_link->platform_of_node) {
			if (platform->dev->of_node !=
			    dai_link->platform_of_node)
				continue;
		} else {
			if (strcmp(platform->name, platform_name))
				continue;
		}

		rtd->platform = platform;
	}
	if (!rtd->platform) {
		dev_err(card->dev, "ASoC: platform %s not registered\n",
			dai_link->platform_name);
		return -EPROBE_DEFER;
	}

	card->num_rtd++;

	return 0;
}

至此machine是如何绑定platform和codec就分析完了,其中有些细节没有涉及到。

推荐参考:https://blog.csdn.net/orz415678659/article/details/8982771  写的很详细





本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux音频之ASOC 的相关文章

  • 第一次播放声音时 AVAudioPlayer 启动缓慢

    我试图消除通过 iPhone 上的 AVAudioPlayer 播放 非常短 不到 2 秒 音频文件时的启动延迟 首先 代码 NSString audioFile NSString stringWithFormat caf NSBundle
  • Firefox createMediaStreamDestination 使用 rtc 的错误?

    我通过 rtc 流式传输音频并想要静音和取消静音音频 这有效 但没有增益控制 function stream getUserMedia stream console log Access granted to audio video pee
  • ffmpeg 用于屏幕捕获?

    所以我有一个小程序来捕获屏幕和计算机麦克风的声音 然后屏幕截图被编码为 ScreenVideo2 声音被编码为 AAC 如何使用 ffmpeg 逐帧混合 然后将混合输出发送到 wowza 媒体服务器 如果用ffmpeg无法完成 您能提供一些
  • 使用python同时播放两个正弦音

    我正在使用 python 来播放正弦音 音调基于计算机的内部时间 以分钟为单位 但我想根据秒同时播放一个音调 以获得和谐或双重的声音 这就是我到目前为止所拥有的 有人能指出我正确的方向吗 from struct import pack fr
  • python:如何改变音量?

    I used winsound Beep frequency duration 因为我想以指定的频率播放声音 现在 我需要更改这些声音的音量 我怎样才能做到这一点 我试图寻求帮助pyaudio but 如果您对外部库开放 您可以使用pydu
  • 严格标准:在 wordpress/wp-includes/class-oembed.php 第 116 行中,仅应通过引用传递变量

    我已经查看了很多类似的问题 但关于我的代码 我没有得到它 错误 Strict Standards Only variables should be passed by reference in wordpress wp includes c
  • 如何使用ffmpeg重叠和合并多个音频文件?

    我正在尝试将多个音频文件合并到一个文件中 但我可以使用以下命令来连接 而不是连接 ffmpeg v debug i file1 wav i file2 wav i file3 wav filter complex 0 0 concat n
  • 软件音频线路输入

    这可能是也可能不是询问的地方 如果不是 就直接扔掉它 我有一个正在输出音频的软件 我想将其路由到另一个软件 简单的解决方案是将耳机插孔连接到麦克风插孔或在计算机上启用立体声混音 但是 我想要做的进一步实现将在一台机器上发生 2 个这样的实例
  • 如何在 python 中生成音符或和弦?

    有人能给我指出一个在 python 2 7 中生成音符和和弦的好库吗 我查看了 PythonInfoWiki 但运气不佳 PyAudio 只是崩溃了 似乎没有其他东西可以生成音调 我不知道这是否有帮助 但这里有一些代码可以根据给定的频率和振
  • “初始化 MCI 时出现问题”播放声音问题

    我正在尝试使用 Playsound 播放代码文件夹中的文件 但是每次运行代码时 它似乎都能够调用该文件 但我总是收到以下输出 playsound PlaysoundException Error 277 for command open p
  • 如何捕获正在播放的音频?

    有谁知道如何以编程方式捕获正在播放的声音 即来自声卡的所有声音 而不是麦克风等输入设备 假设您正在谈论 Windows 则基本上可以通过三种方法来实现此目的 首先是打开音频设备的主输出作为录音源 这只有在驱动程序支持时才可能实现 尽管现在大
  • HTML5

    我想在随机位置开始和停止 HTML5 播放 并具有淡入和淡出周期 以平滑聆听体验 为此存在什么样的机制 使用 setTimeout 手动增加音量 jQuery 的方式 audio animate volume newVolume 1000
  • Android SoundPool 堆限制

    我正在使用 SoundPool 加载多个声音剪辑并播放它们 据我所知 它的功能 100 正确 但在 load 调用期间 我的日志中充斥着以下内容 06 09 11 30 26 110 ERROR AudioCache 23363 Heap
  • 如何让 JavaScript 发出蜂鸣声?

    我希望我的网页在用户超出我的最大字符限制时发出蜂鸣声
  • getAudioInputStream() 期间标记/重置异常

    我发布了问题的修复 如下所述 但无法确认它是否解决了问题 使用 Java 7 的人会尝试以下 Applet 并报告吗 我们将非常感激 音频混合器演示 http www hexara com VSL AudioMixerDemoWarOfWo
  • C#:将音频文件从服务器流式传输到客户端

    我目前正在编写一个应用程序 该应用程序将允许用户安装某种形式的应用程序 可能是 Windows 服务 该应用程序将在其 PC 上打开一个端口 并在硬盘上指定一个特定的目的地 然后能够流式传输 mp3 文件 然后 我将有另一个应用程序 该应用
  • 捕获当前正在播放的声音

    是否可以捕获计算机上当前播放的声音 如果能够将其保存为 mp3 就好了 但我认为这样做会存在一些法律问题 所以 wav 也可以 我环顾四周 有人建议使用虚拟音频线之类的东西 在 C 中捕获声音输出 https stackoverflow c
  • 使用 Google Translate API 获取单词的发音

    我正在尝试将法语单词的发音保存到 wav 或 mp3 文件中 我想知道 Google Translate API 上是否有任何地方 因为它有发音功能 可以让我实现这个目标 其他库也可以工作 自从提出这个问题以来 从谷歌翻译中 抓取 MP3
  • 我们可以使用 C# 录制发送到扬声器的声音吗

    我有一个软件 SoundTap Streaming Audio Recorder 它记录发送到扬声器的任何音频 无论流是来自网络还是来自某些文件或麦克风 我可以在桌面应用程序中制作这样的应用程序 以便我可以录制发送到扬声器的流 无论来源如何
  • 如何用Java捕获音频数据

    我想访问我的麦克风用 Java 录制的音频数据 我该怎么做呢 我的目标是保存录制的音频数据并同时向用户播放 如果您不需要 JMF 中的任何附加功能 我会避免使用它 因为开发已经停止 最后一个版本是 2004 年 它与 Java 6 存在兼容

随机推荐

  • Struts2知识汇总二

    Struts2中的调试 在Struts2中可以使用
  • java8 stream流排序

    原文出处 https www cnblogs com kuanglongblogs p 11230250 html 很多情况下sql不好解决的多表查询 临时表分组 排序 尽量用java8新特性stream进行处理 使用java8新特性 下面
  • Android PowerSupply (三)power_supply_sys

    目录 Android PowerSupply 一 总概 Android PowerSupply 二 power supply core Android PowerSupply 三 power supply sys Android Power
  • 深入理解ES6箭头函数中的this

    简要介绍 箭头函数中的this 指向与一般function定义的函数不同 比较容易绕晕 箭头函数this的定义 箭头函数中的this是在定义函数的时候绑定 而不是在执行函数的时候绑定 1 何为定义时绑定 我们来看下面这个例子 1 var x
  • UNIX环境高级编程读书笔记

    主要记录关键知识点 方便日后查阅 第一章 UNIX基础知识 UNIX体系结构 书中是这样画的 这篇文章认为这样画不合理https blog csdn net lyndon li article details 116956043 应该这样
  • jsp页面兼容谷歌浏览器相关问题

    1 js按键事件兼容 function document oncontextmenu ie8可运行 谷歌改为function document onkeydown 2 触发事件对象 IE浏览器支持window event srcElemen
  • [附源码]java+ssm计算机毕业设计java儿童福利院管理系统5d7wb【源码、数据库、LW、部署】

    项目运行 项目含有源码 文档 程序 数据库 配套开发软件 软件安装教程 环境配置 Jdk1 8 Tomcat7 0 Mysql HBuilderX Webstorm也行 Eclispe IntelliJ IDEA Eclispe MyEcl
  • 变度量法算法(DFP)求解无约束问题

    程序功能 1 变度量法算法 DFP 求解无约束问题 2 调用文件夹下Newton的子函数 nfx ndfx ndfx2 vectorLength 3 z3 A i b 计算当前d的值 矩阵计算可能存在奇异值 4 请根据不同的目标函数 设置精
  • android- Cause: Unknown command-line option '-X'.

    问题太简单了 直接解决办法 File gt Settings gt Build Execution Deployment gt Compiler 删除Command line options里面的内容 重新gradle 感谢博主 欢迎留言指
  • 全栈之前端

    欢迎关注 全栈工程师修炼指南 公众号 点击 下方卡片 即可关注我哟 设为 星标 每天带你 基础入门 到 进阶实践 再到 放弃学习 花开堪折直须折 莫待无花空折枝 作者主页 https www weiyigeek top 博客 https b
  • 2022面试题汇总

    目录 浏览器下两个页面的通讯都有什么方式 使用css与js做一个九宫格动画 请输出如下的代码打印结果 js如何实现页面地址发生变化 但页面不发生跳转 请用js实现 请用多种方式实现垂直居中 实现的方式越多越好 请实现一个getValue函数
  • 【深度学习】全面直观认识深度神经网络

    01深度学习的精准定义 一类通过多层非线性变换对高复杂性数据建模算法的集合 它的两个非常重要的特征是多层性和非线性 俗称多层非线性变换 所以深度学习要去线性化 为什么呢 因为线性模型存在局限性 任意线性模型得到组合仍然还是线性模型 所以只要
  • Linux如何找回或者重置root用户密码

    欢迎参与个人独立开发的阅时即查web APP公测 请扫码体验 第一个为旧版 第二个为2019年6月版 在Linux这样一个权限管理严格 系统安全性要求高的环境中 根用户 超级用户 root的的密码显得十分重要 但是还是有一些马大哈会忘记自己
  • 【LLM】深入剖析 GOOGLE PALM 2:全面概述

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 用一维字符数组存放字符串

    一 用一维字符数组存放字符串 1 C语言对字符串的约定 字符串是借助于字符型一维数组来存放的 并规定以字符 0 作为字符串的结束标志 0 作为标志占用存储空间 但不计入串的实际常量 2 C语言中表示字符串常量的约定 虽然c语言中没有字符串数
  • regex_replace()函数的应用与解析

    include
  • lua报错 module 'Module' not found

    这几天学习lua使用require关键字获取自己定义的模块式 发现报没有这个模块文件 询问老师 老师说是因为中文路径问题 的确这个可能会出现问题 但是我修改后还是报这个错误 老师就让我看他的源代码 我确定没写错 所以还是要靠自己来解决了 终
  • 【sql语句基础】——查(select)(合并查询)

    目录 合并查询 单独查询 合并查询 UNION ALL UNION ALL定义 UNION ALL代码示例 UNION ALL查询结果 合并查询 UNION ALL UNION 定义 UNION 代码示例 UNION 查询结果 合并查询 当
  • Android Button 背景高度被拉伸问题

  • Linux音频之ASOC

    参考 https blog csdn net droidphone article details 7165482 1 ASOC简介 ASoC ALSA System on Chip 是建立在标准ALSA驱动层上 为了更好地支持嵌入式处理器