参考: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 写的很详细