参考:
https://elixir.bootlin.com/linux/v4.9.218/source/sound/soc/soc-core.c#L3314
https://blog.csdn.net/DroidPhone/article/details/7283833
在移动设备中,Codec的作用可以归结为4种,分别是:
对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号
对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号
对音频通路进行控制,比如播放音乐,收听调频收音机,又或者接听电话时,音频信号在codec内的流通路线是不一样的
对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等
codec通过调用snd_soc_register_codec进行注册。
在snd_soc_register_codec函数内主要创建和分配snd_soc_codec结构体的内存,将snd_soc_codec_driver 赋值给codec->driver,snd_soc_dai_driver注册到codec->component里,将创建的codec->component加到全局列表component_list中,将创建的codec加到全局列表codec_list中,方便后续注册Machine driver时调用到。
/** * snd_soc_register_codec - Register a codec with the ASoC core * * @dev: The parent device for this codec * @codec_drv: Codec driver * @dai_drv: The associated DAI driver * @num_dai: Number of DAIs */ 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) { struct snd_soc_dapm_context *dapm; struct snd_soc_codec *codec; struct snd_soc_dai *dai; int ret, i; dev_dbg(dev, "codec register %s\n", dev_name(dev)); codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; codec->component.codec = codec; ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev); if (ret) goto err_free; if (codec_drv->probe) codec->component.probe = snd_soc_codec_drv_probe; if (codec_drv->remove) codec->component.remove = snd_soc_codec_drv_remove; if (codec_drv->write) codec->component.write = snd_soc_codec_drv_write; if (codec_drv->read) codec->component.read = snd_soc_codec_drv_read; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; dapm = snd_soc_codec_get_dapm(codec); dapm->idle_bias_off = codec_drv->idle_bias_off; dapm->suspend_bias_off = codec_drv->suspend_bias_off; if (codec_drv->seq_notifier) dapm->seq_notifier = codec_drv->seq_notifier; if (codec_drv->set_bias_level) dapm->set_bias_level = snd_soc_codec_set_bias_level; codec->dev = dev; codec->driver = codec_drv; codec->component.val_bytes = codec_drv->reg_word_size; #ifdef CONFIG_DEBUG_FS codec->component.init_debugfs = soc_init_codec_debugfs; codec->component.debugfs_prefix = "codec"; #endif if (codec_drv->get_regmap) codec->component.regmap = codec_drv->get_regmap(dev); for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); } ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); if (ret < 0) { dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; } list_for_each_entry(dai, &codec->component.dai_list, list) dai->codec = codec; mutex_lock(&client_mutex); snd_soc_component_add_unlocked(&codec->component); list_add(&codec->list, &codec_list); mutex_unlock(&client_mutex); dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", codec->component.name); return 0; err_cleanup: snd_soc_component_cleanup(&codec->component); err_free: kfree(codec); return ret; }
1.首先分配snd_soc_codec的内存。
/* SoC Audio Codec device */ struct snd_soc_codec { struct device *dev; const struct snd_soc_codec_driver *driver; struct list_head list; struct list_head card_list; /* runtime */ unsigned int cache_bypass:1; /* Suppress access to the cache */ unsigned int suspended:1; /* Codec is in suspend PM state */ unsigned int cache_init:1; /* codec cache has been initialized */ /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ hw_write_t hw_write; void *reg_cache; /* component */ struct snd_soc_component component; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_reg; #endif };
2.调用snd_soc_component_initiallize来初始化codec->component, 主要使用snd_soc_codec_driver的成员component_driver来初始化component。
snd_soc_codec_driver结构体如下:
/* codec driver */ struct snd_soc_codec_driver { /* driver ops */ int (*probe)(struct snd_soc_codec *); int (*remove)(struct snd_soc_codec *); int (*suspend)(struct snd_soc_codec *); int (*resume)(struct snd_soc_codec *); struct snd_soc_component_driver component_driver; /* codec wide operations */ int (*set_sysclk)(struct snd_soc_codec *codec, int clk_id, int source, unsigned int freq, int dir); int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source, unsigned int freq_in, unsigned int freq_out); /* codec IO */ struct regmap *(*get_regmap)(struct device *); unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); unsigned int reg_cache_size; short reg_cache_step; short reg_word_size; const void *reg_cache_default; /* codec bias level */ int (*set_bias_level)(struct snd_soc_codec *, enum snd_soc_bias_level level); bool idle_bias_off; bool suspend_bias_off; void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int); bool ignore_pmdown_time; /* Doesn't benefit from pmdown delay */ };
snd_soc_component_driver结构体如下,主要包含controls, dapm_widget, dapm_routes,在Codec DAI中,dapm主要描述了codec内部widget是如何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.设置codec->component的probe,remove,write,read函数,即为snd_soc_codec_driver相应的成员函数。
4.调用snd_soc_register_dais来注册codec 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_add_dai函数中,创建snd_soc_dai结构体dai,并使用dai_drv对snd_soc_dai进行初始化,最后将创建的dai添加到component->dai_list中。在此函数中还会将dai_drv的name copy给dai的name,后续Machine driver就是通过这个name来找到codec 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; }
5.设置codec DAI的成员codec为当前创建的codec。
6.调用snd_soc_component_add_unlocked将codec->component加到全局列表component,并将创建的codec加到全局列表codec_list.