参考:
https://elixir.bootlin.com/linux/v4.9.218/source/sound/soc/soc-core.c#L3034
https://blog.csdn.net/DroidPhone/article/details/7316061
CPU DAI通过调用snd_soc_register_component进行注册。
在snd_soc_register_component函数内主要创建和分配snd_soc_component结构体的内存,将snd_soc_component_driver 和snd_soc_dai_driver注册到创建的component里,将创建的component加到全局列表component_list中,方便后续注册Machine driver时调用到。
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; cmpnt = kzalloc(sizeof(*cmpnt), GFP_KERNEL); if (!cmpnt) { dev_err(dev, "ASoC: Failed to allocate memory\n"); return -ENOMEM; } ret = snd_soc_component_initialize(cmpnt, cmpnt_drv, dev); if (ret) goto err_free; cmpnt->ignore_pmdown_time = true; cmpnt->registered_as_component = true; ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true); if (ret < 0) { dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; } snd_soc_component_add(cmpnt); return 0; err_cleanup: snd_soc_component_cleanup(cmpnt); err_free: kfree(cmpnt); return ret; }
1.首先分配snd_soc_component的内存。
2.调用snd_soc_component_initiallize来初始化component, 主要使用snd_soc_component_driver来初始化component。
snd_soc_component_driver结构体如下,主要包含controls, dapm_widget, dapm_routes,在CPU DAI中,dapm主要描述了FE DAI和BE DAI是如何link的。
/* component interface */ struct snd_soc_component_driver { const char *name; /* Default control and setup, added after probe() is run */ 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; int (*probe)(struct snd_soc_component *); void (*remove)(struct snd_soc_component *); /* DT */ int (*of_xlate_dai_name)(struct snd_soc_component *component, struct of_phandle_args *args, const char **dai_name); void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, int subseq); int (*stream_event)(struct snd_soc_component *, int event); /* probe ordering - for components with runtime dependencies */ int probe_order; int remove_order; };
具体的初始化过程如下:将component driver赋值给component的driver成员,使用component driver dapm_widget和dapm_route赋值给component相应的成员,初始化component的dai_list.
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; }
3.调用snd_soc_register_dais来注册CPU DAI
static int snd_soc_register_dais(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, size_t count, bool legacy_dai_naming) { struct device *dev = component->dev; struct snd_soc_dai *dai; unsigned int i; int ret; dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count); component->dai_drv = dai_drv; for (i = 0; i < count; i++) { dai = soc_add_dai(component, dai_drv + i, count == 1 && legacy_dai_naming); if (dai == NULL) { ret = -ENOMEM; goto err; } } return 0; err: snd_soc_unregister_dais(component); return ret; }
在snd_soc_register_dais函数内遍历snd_soc_dai_driver 列表,通过soc_add_dai将每个dai都添加到component的dai_list。
snd_soc_dai_driver的结构体如下:
struct snd_soc_dai_driver { /* DAI description */ const char *name; unsigned int id; unsigned int base; struct snd_soc_dobj dobj; /* DAI driver callbacks */ int (*probe)(struct snd_soc_dai *dai); int (*remove)(struct snd_soc_dai *dai); int (*suspend)(struct snd_soc_dai *dai); int (*resume)(struct snd_soc_dai *dai); /* compress dai */ int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num); /* DAI is also used for the control bus */ bool bus_control; /* ops */ const struct snd_soc_dai_ops *ops; /* DAI capabilities */ struct snd_soc_pcm_stream capture; struct snd_soc_pcm_stream playback; unsigned int symmetric_rates:1; unsigned int symmetric_channels:1; unsigned int symmetric_samplebits:1; /* probe ordering - for components with runtime dependencies */ int probe_order; int remove_order; };
snd_soc_dai_driver 结构体关键成员如下:
probe、remove 回调函数,分别在声卡加载和卸载时被调用;
suspend、resume 电源管理回调函数;
ops 指向snd_soc_dai_ops结构,用于配置和控制该dai;
playback snd_soc_pcm_stream结构,用于指出该dai支持的声道数,码率,数据格式等能力;
capture snd_soc_pcm_stream结构,用于指出该dai支持的声道数,码率,数据格式等能力;
其中snd_soc_pcm_stream类型的playback 和capture,在soc_pcm_open()中调用soc_pcm_init_runtime_hw()来检查cpu_dai和codec_dai的支持能力。
其中ops包含的回调函数如下:
struct snd_soc_dai_ops { /* * DAI clocking configuration, all optional. * Called by soc_card drivers, normally in their hw_params. */ int (*set_sysclk)(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir); int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out); int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div); int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio); /* * DAI format configuration * Called by soc_card drivers, normally in their hw_params. */ int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt); int (*xlate_tdm_slot_mask)(unsigned int slots, unsigned int *tx_mask, unsigned int *rx_mask); int (*set_tdm_slot)(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); int (*set_channel_map)(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, unsigned int rx_num, unsigned int *rx_slot); int (*set_tristate)(struct snd_soc_dai *dai, int tristate); /* * DAI digital mute - optional. * Called by soc-core to minimise any pops. */ int (*digital_mute)(struct snd_soc_dai *dai, int mute); int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream); /* * ALSA PCM audio operations - all optional. * Called by soc-core during audio PCM operations. */ int (*startup)(struct snd_pcm_substream *, struct snd_soc_dai *); void (*shutdown)(struct snd_pcm_substream *, struct snd_soc_dai *); int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *, struct snd_soc_dai *); int (*hw_free)(struct snd_pcm_substream *, struct snd_soc_dai *); int (*prepare)(struct snd_pcm_substream *, struct snd_soc_dai *); /* * NOTE: Commands passed to the trigger function are not necessarily * compatible with the current state of the dai. For example this * sequence of commands is possible: START STOP STOP. * So do not unconditionally use refcounting functions in the trigger * function, e.g. clk_enable/disable. */ int (*trigger)(struct snd_pcm_substream *, int, struct snd_soc_dai *); int (*bespoke_trigger)(struct snd_pcm_substream *, int, struct snd_soc_dai *); /* * For hardware based FIFO caused delay reporting. * Optional. */ snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *, struct snd_soc_dai *); };
工作时钟配置函数 通常由machine驱动调用:
set_sysclk 设置dai的主时钟;
set_pll 设置PLL参数;
set_clkdiv 设置分频系数;
dai的格式配置函数 通常由machine驱动调用:
set_fmt 设置dai的格式;
set_tdm_slot 如果dai支持时分复用,用于设置时分复用的slot;
set_channel_map 声道的时分复用映射设置;
set_tristate 设置dai引脚的状态,当与其他dai并联使用同一引脚时需要使用该回调;
标准的snd_soc_ops回调 通常由soc-core在进行PCM操作时调用:
startup
shutdown
hw_params
hw_free
prepare
trigger
抗pop,pop声 由soc-core调用:
digital_mute
以下这些api通常被machine驱动使用,machine驱动在他的snd_pcm_ops字段中的hw_params回调中使用这些api:
snd_soc_dai_set_fmt() 实际上会调用snd_soc_dai_ops或者codec driver中的set_fmt回调;
snd_soc_dai_set_pll() 实际上会调用snd_soc_dai_ops或者codec driver中的set_pll回调;
snd_soc_dai_set_sysclk() 实际上会调用snd_soc_dai_ops或者codec driver中的set_sysclk回调;
snd_soc_dai_set_clkdiv() 实际上会调用snd_soc_dai_ops或者codec driver中的set_clkdiv回调;
在snd_add_dai函数中,创建snd_soc_dai结构体dai,并使用dai_drv对snd_soc_dai进行初始化,最后将创建的dai添加到component->dai_list中。在此函数中还会将dai_drv的name copy给dai的name,后续Machine driver就是通过这个name来找到dai的。
/* Create a DAI and add it to the component's DAI list */ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, bool legacy_dai_naming) { struct device *dev = component->dev; struct snd_soc_dai *dai; dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev)); dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); if (dai == NULL) return NULL; /* * Back in the old days when we still had component-less DAIs, * instead of having a static name, component-less DAIs would * inherit the name of the parent device so it is possible to * register multiple instances of the DAI. We still need to keep * the same naming style even though those DAIs are not * component-less anymore. */ if (legacy_dai_naming && (dai_drv->id == 0 || dai_drv->name == NULL)) { dai->name = fmt_single_name(dev, &dai->id); } else { dai->name = fmt_multiple_name(dev, dai_drv); if (dai_drv->id) dai->id = dai_drv->id; else dai->id = component->num_dai; } if (dai->name == NULL) { kfree(dai); return NULL; } dai->component = component; dai->dev = dev; dai->driver = dai_drv; if (!dai->driver->ops) dai->driver->ops = &null_dai_ops; list_add(&dai->list, &component->dai_list); component->num_dai++; dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); return dai; }
至此,component driver包含的dapm_widget和dapm_route已经添加到component里面,CPU DAI也创建完成,并添加到component的dai_list里面。
4.调用snd_soc_component_add将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); } static void snd_soc_component_add(struct snd_soc_component *component) { mutex_lock(&client_mutex); snd_soc_component_add_unlocked(component); mutex_unlock(&client_mutex); }
在后续注册card时(snd_soc_register_card),snd_soc_instantiate_card中会调用soc_bind_dai_link函数,在此函数中通过name来找到machine driver的某条dai_link上的cpu dai,具体如下:
struct snd_soc_dai_link_component cpu_dai_component; cpu_dai_component.name = dai_link->cpu_name; cpu_dai_component.of_node = dai_link->cpu_of_node; cpu_dai_component.dai_name = dai_link->cpu_dai_name; rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component); if (!rtd->cpu_dai) { dev_err(card->dev, "ASoC: CPU DAI %s not registered\n", dai_link->cpu_dai_name); goto _err_defer; }
通过dai_link的cpu_name和cpu_dai_name来找到相应的cpu dai,具体查找过程在snd_soc_find_dai函数内,先通过cpu_name找到component,然后遍历component->dai_list,使用cpu_dai_name来匹配dai的name。
struct snd_soc_dai *snd_soc_find_dai( const struct snd_soc_dai_link_component *dlc) { struct snd_soc_component *component; struct snd_soc_dai *dai; struct device_node *component_of_node; lockdep_assert_held(&client_mutex); /* Find CPU DAI from registered DAIs*/ list_for_each_entry(component, &component_list, list) { component_of_node = component->dev->of_node; if (!component_of_node && component->dev->parent) component_of_node = component->dev->parent->of_node; if (dlc->of_node && component_of_node != dlc->of_node) continue; if (dlc->name && strcmp(component->name, dlc->name)) continue; list_for_each_entry(dai, &component->dai_list, list) { if (dlc->dai_name && strcmp(dai->name, dlc->dai_name)) continue; return dai; } } return NULL; }
在soc_probe_link_components中会对CPU DAI的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; }
在soc_probe_component函数内,对CPU DAI进行probe,主要包括
将component 和dapm context的card赋值。
调用snd_soc_dapm_new_controls创建component的dapm_widget,并将widget加到card->widgets列表中.
调用snd_soc_dapm_new_dai_widgets对component dai_list每一个dai widget, widget的name为dai->driver->playback/capture.name.并将widget加到card->widgets列表中.
调用component的probe函数
调用snd_soc_dapm_add_routes对component的dapm_routes添加dapm path.
将component的dapm context加到card的dapm context列表。
static int soc_probe_component(struct snd_soc_card *card, struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dai *dai; int ret; if (!strcmp(component->name, "snd-soc-dummy")) return 0; if (component->card) { if (component->card != card) { dev_err(component->dev, "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n", card->name, component->card->name); return -ENODEV; } return 0; } if (!try_module_get(component->dev->driver->owner)) return -ENODEV; component->card = card; dapm->card = card; soc_set_name_prefix(card, component); soc_init_component_debugfs(component); if (component->dapm_widgets) { ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets, component->num_dapm_widgets); if (ret != 0) { dev_err(component->dev, "Failed to create new controls %d\n", ret); goto err_probe; } } list_for_each_entry(dai, &component->dai_list, list) { ret = snd_soc_dapm_new_dai_widgets(dapm, dai); if (ret != 0) { dev_err(component->dev, "Failed to create DAI widgets %d\n", ret); goto err_probe; } } if (component->probe) { ret = component->probe(component); if (ret < 0) { dev_err(component->dev, "ASoC: failed to probe component %d\n", ret); goto err_probe; } WARN(dapm->idle_bias_off && dapm->bias_level != SND_SOC_BIAS_OFF, "codec %s can not start from non-off bias with idle_bias_off==1\n", component->name); } /* machine specific init */ if (component->init) { ret = component->init(component); if (ret < 0) { dev_err(component->dev, "Failed to do machine specific init %d\n", ret); goto err_probe; } } if (component->controls) snd_soc_add_component_controls(component, component->controls, component->num_controls); if (component->dapm_routes) snd_soc_dapm_add_routes(dapm, component->dapm_routes, component->num_dapm_routes); list_add(&dapm->list, &card->dapm_list); /* This is a HACK and will be removed soon */ if (component->codec) list_add(&component->codec->card_list, &card->codec_dev_list); return 0; err_probe: soc_cleanup_component_debugfs(component); component->card = NULL; module_put(component->dev->driver->owner); return ret; }