platform to register by calling snd_soc_register_platform. snd_soc_register_platform () This function is used to register a snd_soc_platform, only after the registration, it can be driven using the 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. First, the structure of the memory allocation of snd_soc_platform, snd_soc_platform described platform currently registered, snd_soc_platform_driver platform is the corresponding 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 to initialize the platform, filling structures in the body member, and register.
/** * 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 to initialize the component members of the platform by snd_soc_component_initialize.
snd_soc_component structure as follows:
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 is a very important structure, platform / codec / dai contains component. component contains conponent driver, an important component of the information and dai list dai driver, dapm contex, dapm widget , damp route and so on.
These information is important assignment in snd_soc_component_initializer. When subsequent register card, is to find the platform by matching component-> name.
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 members assigned to the driver's platform.
/* 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 consists mainly of a DMA operation functions. The following is an example of a platform driver, from 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 calls snd_soc_component_add_unlocked the platform of the component is added to the global list 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); }
The platform 2.4 is added to the global list of platform_list.
So far snd_soc_platform has been allocated, initialization is complete, and added to platform_list in.
In a subsequent snd_soc_register_card-> snd_soc_instantiate_card soc_bind_dai_link will call the function, this function loops through PLATFORM_LIST, Comparative platform-> component.name and dai_link-> platform_name, to find the corresponding 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; }
In the following Machine, for example, dai_link of platform_name is 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, };
Find pxa-pcm-audio names platform, the corresponding platform driver as follows,
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, };
Probe will be in soc_probe_link_components platform in the component:
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; }