参考:
https://elixir.bootlin.com/linux/v4.9.218/source/sound/soc/soc-core.c#L3159
https://blog.csdn.net/DroidPhone/article/details/7316061
platform通过调用snd_soc_register_platform来注册。snd_soc_register_platform() 该函数用于注册一个snd_soc_platform,只有注册以后,它才可以被Machine驱动使用。
/** * snd_soc_register_platform - Register a platform with the ASoC core * * @dev: The device for the platform * @platform_drv: The driver for the platform */ 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 ", 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; }
1.首先分配snd_soc_platform的结构体的内存,snd_soc_platform描述当前注册的platform,snd_soc_platform_driver是platform对应的driver.
struct snd_soc_platform { struct device *dev; const struct snd_soc_platform_driver *driver; struct list_head list; struct snd_soc_component component; };
2.snd_soc_add_platform对platform进行初始化,填充结构体内成员,并进行注册。
/** * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform * @platform: The platform to add * @platform_drv: The driver for the platform */ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, const struct snd_soc_platform_driver *platform_drv) { int ret; ret = snd_soc_component_initialize(&platform->component, &platform_drv->component_driver, dev); if (ret) return ret; platform->dev = dev; platform->driver = platform_drv; if (platform_drv->probe) platform->component.probe = snd_soc_platform_drv_probe; if (platform_drv->remove) platform->component.remove = snd_soc_platform_drv_remove; #ifdef CONFIG_DEBUG_FS platform->component.debugfs_prefix = "platform"; #endif mutex_lock(&client_mutex); snd_soc_component_add_unlocked(&platform->component); list_add(&platform->list, &platform_list); mutex_unlock(&client_mutex); dev_dbg(dev, "ASoC: Registered platform '%s' ", platform->component.name); return 0; }
2.1通过snd_soc_component_initialize来初始化platform的component 成员。
snd_soc_component结构体如下:
struct snd_soc_component { const char *name; int id; const char *name_prefix; struct device *dev; struct snd_soc_card *card; unsigned int active; unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ unsigned int registered_as_component:1; struct list_head list; struct list_head list_aux; /* for auxiliary component of the card */ struct snd_soc_dai_driver *dai_drv; int num_dai; const struct snd_soc_component_driver *driver; struct list_head dai_list; int (*read)(struct snd_soc_component *, unsigned int, unsigned int *); int (*write)(struct snd_soc_component *, unsigned int, unsigned int); struct regmap *regmap; int val_bytes; struct mutex io_mutex; /* attached dynamic objects */ struct list_head dobj_list; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root; #endif /* * DO NOT use any of the fields below in drivers, they are temporary and * are going to be removed again soon. If you use them in driver code the * driver will be marked as BROKEN when these fields are removed. */ /* Don't use these, use snd_soc_component_get_dapm() */ struct snd_soc_dapm_context dapm; const struct snd_kcontrol_new *controls; unsigned int num_controls; const struct snd_soc_dapm_widget *dapm_widgets; unsigned int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; unsigned int num_dapm_routes; struct snd_soc_codec *codec; int (*probe)(struct snd_soc_component *); void (*remove)(struct snd_soc_component *); /* machine specific init */ int (*init)(struct snd_soc_component *component); #ifdef CONFIG_DEBUG_FS void (*init_debugfs)(struct snd_soc_component *component); const char *debugfs_prefix; #endif };
snd_soc_component是非常重要的结构体,platform/codec/cpu dai都包含component。component中包含conponent driver, 该component的dai list和dai driver, dapm contex, dapm widget, damp route等重要信息。
在snd_soc_component_initializer就是对这些重要信息进行赋值。后续在register card时,就是通过匹配component->name来找到相应的platform.
static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { struct snd_soc_dapm_context *dapm; component->name = fmt_single_name(dev, &component->id); if (!component->name) { dev_err(dev, "ASoC: Failed to allocate name "); return -ENOMEM; } component->dev = dev; component->driver = driver; component->probe = component->driver->probe; component->remove = component->driver->remove; dapm = &component->dapm; dapm->dev = dev; dapm->component = component; dapm->bias_level = SND_SOC_BIAS_OFF; dapm->idle_bias_off = true; if (driver->seq_notifier) dapm->seq_notifier = snd_soc_component_seq_notifier; if (driver->stream_event) dapm->stream_event = snd_soc_component_stream_event; component->controls = driver->controls; component->num_controls = driver->num_controls; component->dapm_widgets = driver->dapm_widgets; component->num_dapm_widgets = driver->num_dapm_widgets; component->dapm_routes = driver->dapm_routes; component->num_dapm_routes = driver->num_dapm_routes; INIT_LIST_HEAD(&component->dai_list); mutex_init(&component->io_mutex); return 0; }
2.2 将snd_soc_platform_driver 赋值给platform的driver成员。
/* SoC platform interface */ struct snd_soc_platform_driver { int (*probe)(struct snd_soc_platform *); int (*remove)(struct snd_soc_platform *); struct snd_soc_component_driver component_driver; /* pcm creation and destruction */ int (*pcm_new)(struct snd_soc_pcm_runtime *); void (*pcm_free)(struct snd_pcm *); /* * For platform caused delay reporting. * Optional. */ snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *, struct snd_soc_dai *); /* platform stream pcm ops */ const struct snd_pcm_ops *ops; /* platform stream compress ops */ const struct snd_compr_ops *compr_ops; int (*bespoke_trigger)(struct snd_pcm_substream *, int); };
snd_soc_platform_driver主要包含了对DMA的操作函数。以下是一个platform driver的例子,来源于sound/soc/pxa/pxa2xx-pcm.c
static struct snd_pcm_ops pxa2xx_pcm_ops = { .open = __pxa2xx_pcm_open, .close = __pxa2xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pxa2xx_pcm_hw_params, .hw_free = pxa2xx_pcm_hw_free, .prepare = __pxa2xx_pcm_prepare, .trigger = pxa2xx_pcm_trigger, .pointer = pxa2xx_pcm_pointer, .mmap = pxa2xx_pcm_mmap, }; static struct snd_soc_platform_driver pxa2xx_soc_platform = { .ops = &pxa2xx_pcm_ops, .pcm_new = pxa2xx_soc_pcm_new, .pcm_free = pxa2xx_pcm_free_dma_buffers, };
2.3调用snd_soc_component_add_unlocked将platform的component加到全局列表component_list中。
static void snd_soc_component_add_unlocked(struct snd_soc_component *component) { if (!component->write && !component->read) { if (!component->regmap) component->regmap = dev_get_regmap(component->dev, NULL); if (component->regmap) snd_soc_component_setup_regmap(component); } list_add(&component->list, &component_list); INIT_LIST_HEAD(&component->dobj_list); }
2.4将platform加到全局列表platform_list中。
至此snd_soc_platform已经分配,初始化完成,并添加到platform_list中。
在后续snd_soc_register_card->snd_soc_instantiate_card中会调用soc_bind_dai_link函数,在此函数中会遍历整个platform_list,比较platform->component.name和dai_link->platform_name,从而找到对应的platform.
/* 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->component.name, platform_name)) continue; } rtd->platform = platform; } if (!rtd->platform) { dev_err(card->dev, "ASoC: platform %s not registered ", dai_link->platform_name); goto _err_defer; }
以下面的Machine为例, dai_link的platform_name为pxa-pcm-audio
static struct snd_soc_dai_link corgi_dai = { .name = "WM8731", .stream_name = "WM8731", .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm8731.0-001b", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ops = &corgi_ops, };
会找到pxa-pcm-audio名字的platform,对应的platform driver如下,
static struct snd_soc_platform_driver pxa2xx_soc_platform = { .ops = &pxa2xx_pcm_ops, .pcm_new = pxa2xx_soc_pcm_new, .pcm_free = pxa2xx_pcm_free_dma_buffers, }; static int pxa2xx_soc_platform_probe(struct platform_device *pdev) { return devm_snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform); } #ifdef CONFIG_OF static const struct of_device_id snd_soc_pxa_audio_match[] = { { .compatible = "mrvl,pxa-pcm-audio" }, { } }; MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match); #endif static struct platform_driver pxa_pcm_driver = { .driver = { .name = "pxa-pcm-audio", .of_match_table = of_match_ptr(snd_soc_pxa_audio_match), }, .probe = pxa2xx_soc_platform_probe, };
在soc_probe_link_components中会对platform的component进行probe:
static int soc_probe_link_components(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, int order) { struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; int i, ret; /* probe the CPU-side component, if it is a CODEC */ component = rtd->cpu_dai->component; if (component->driver->probe_order == order) { ret = soc_probe_component(card, component); if (ret < 0) return ret; } /* probe the CODEC-side components */ for (i = 0; i < rtd->num_codecs; i++) { component = rtd->codec_dais[i]->component; if (component->driver->probe_order == order) { ret = soc_probe_component(card, component); if (ret < 0) return ret; } } /* probe the platform */ if (platform->component.driver->probe_order == order) { ret = soc_probe_component(card, &platform->component); if (ret < 0) return ret; } return 0; }