参考:
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\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;
}
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'\n",
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\n");
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\n",
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;
}