找到突破口,machine相关代码,因为它是连接platform和codec的桥梁
Rk_es8323.c (sound\soc\rockchip)
一.入口函数:
1.重要结构体
static const struct of_device_id rockchip_es8323_of_match[] = {
{ .compatible = "rockchip-es8323", }, //匹配dts里面的节点
{},};
MODULE_DEVICE_TABLE(of, rockchip_es8323_of_match);
static struct platform_driver rockchip_es8323_audio_driver = {
.driver = {
.name = "rockchip-es8323",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops, //音频相关的操作函数,包括resume,suspend和poweroff,暂不分析
.of_match_table = of_match_ptr(rockchip_es8323_of_match),
},
.probe = rockchip_es8323_audio_probe,
.remove = rockchip_es8323_audio_remove,
};
2.probe函数
static struct snd_soc_ops rk29_ops = {
.hw_params = rk29_hw_params, //声卡硬件方面的配置
};
static struct snd_soc_dai_link rk29_dai = {
.name = "ES8323",
.stream_name = "ES8323 PCM",
.codec_dai_name = "ES8323 HiFi",
.init = rk29_es8323_init, //声卡初始化
.ops = &rk29_ops, //声卡操作函数
};
static struct snd_soc_card rockchip_es8323_snd_card = {
.name = "RK_ES8323", //声卡名字
.dai_link = &rk29_dai, //接口
.num_links = 1, //表示只有一个链接
};
dts的声卡节点:
rockchip_es8388 {
status = "okay";
compatible = "rockchip-es8323";
dais {
dai0 {
audio-codec = <&es8323>;
audio-controller = <&i2s1>;
format = "i2s";
};
};
};
module_platform_driver(rockchip_es8323_audio_driver); //加载之后匹配device,进入probe
rockchip_es8323_audio_probe
struct snd_soc_card *card = &rockchip_es8323_snd_card; //声卡结构体赋值
card->dev = &pdev->dev;
ret = rockchip_of_get_sound_card_info(card); //获取声卡相关的信息
rockchip_of_get_sound_card_info_(card, true);
dai_node = of_get_child_by_name(card->dev->of_node, "dais"); //获取dts节点rockchip-es8323的子节点dais
for_each_child_of_node(dai_node, child_dai_node) //再获取dais的子节点
//解析dts里面的一些数据,这里只有format,所以card->dai_link[dai_num].dai_fmt=SND_SOC_DAIFMT_I2S
//意思就是接口是i2s
card->dai_link[dai_num].dai_fmt = snd_soc_of_parse_daifmt(child_dai_node, NULL);
//这里得到 card->dai_link[dai_num].codec_of_node=&es8323;,codec是es8323
card->dai_link[dai_num].codec_of_node = of_parse_phandle( child_dai_node,"audio-codec", 0);
//这里得到cpu的节点,card->dai_link[dai_num].cpu_of_node=&i2s1;连接cpu的i2s1
card->dai_link[dai_num].cpu_of_node = of_parse_phandle(child_dai_node, "audio-controller", 0);
//平台的dai和cpu的是一样的
card->dai_link[dai_num].platform_of_node = card->dai_link[dai_num].cpu_of_node;
ret = snd_soc_register_card(card); //Register a card with the ASoC core
.......//一些参数的检查
snd_soc_initialize_card_lists(card); //初始化声卡相关链表
INIT_LIST_HEAD(&card->dai_dev_list); //dai的链表
INIT_LIST_HEAD(&card->codec_dev_list); //codec链表
INIT_LIST_HEAD(&card->platform_dev_list); //platform链表
INIT_LIST_HEAD(&card->widgets); //widgets链表
INIT_LIST_HEAD(&card->paths); //paths链表
INIT_LIST_HEAD(&card->dapm_list); //dapm链表
soc_init_card_debugfs(card); //路径是/sys/kernel/debug/asoc/RK_ES8323/
//分配snd_soc_pcm_runtime结构体的内存,这里是一个
card->rtd = devm_kzalloc(card->dev,sizeof(struct snd_soc_pcm_runtime) * (card->num_links + card->num_aux_devs), GFP_KERNEL);
card->rtd_aux = &card->rtd[card->num_links]; //赋值snd_soc_pcm_runtime结构体
card->rtd[i].dai_link = &card->dai_link[i]; //赋值dai link
INIT_LIST_HEAD(&card->list); //初始化两个链表
INIT_LIST_HEAD(&card->dapm_dirty);
ret = snd_soc_instantiate_card(card); //实例化声卡,单独分析
2.snd_soc_instantiate_card实例化声卡
Soc-core.c (sound\soc)
/* bind DAIs */
ret = soc_bind_dai_link(card, i); //声卡相关的绑定,单独分析
/* check aux_devs too */
ret = soc_check_aux_dev(card, i); //这里没有Auxiliary(辅助)的缩写,辅助音频输入输出设备,我们这里没有
/* initialize the register cache for each available codec */
list_for_each_entry(codec, &codec_list, list) //codec_list找到codec
/* by default we don't override the compress_type */
compress_type = 0; //默认情况,我们不会覆盖之前的压缩类型
/* check to see if we need to override the compress_type */
for (i = 0; i < card->num_configs; ++i) //搜索所有的配置,然后找到相应的,并赋值
codec_conf = &card->codec_conf[i];
if (!strcmp(codec->name, codec_conf->dev_name)) //名字相同
compress_type = codec_conf->compress_type;
ret = snd_soc_init_codec_cache(codec, compress_type); //初始化codec的缓存,单独分析
/* card bind complete so register a sound card */
//创建和初始化一个声卡机构体,单独分析
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card);
card->snd_card->dev = card->dev;
card->dapm.bias_level = SND_SOC_BIAS_OFF;
card->dapm.dev = card->dev;
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list); //把card->dapm.list加入card->dapm_list前面
snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); //创建调试节点./kernel/debug/asoc
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); //延迟唤醒工作
if (card->dapm_widgets)
//widgets,我们这里没有
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,card->num_dapm_widgets); //重要
/* initialise the sound card only once */
if (card->probe) { //声卡的probe函数,我们这里没有
ret = card->probe(card);
/* probe all components used by DAI links on this card */
//探测用dai相关联的所有器件,包括初始化dapm_widgets,controls和dapm_routes,单独分析
ret = soc_probe_link_components(card, i, order);
/* probe all DAI links on this card */
ret = soc_probe_link_dais(card, i, order); //探测所有的DAI连接,单独分析
for (i = 0; i < card->num_aux_devs; i++) {
ret = soc_probe_aux_dev(card, i); //暂时没有
snd_soc_dapm_link_dai_widgets(card); //需要单独分析。。。
if (card->controls) //暂时没有
snd_soc_add_card_controls(card, card->controls, card->num_controls);
if (card->dapm_routes) //暂时没有,但是需要分析一下
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,card->num_dapm_routes);
snd_soc_dapm_new_widgets(&card->dapm); //这个已经单独分析了
//dai_fmt will and need to be setted in hw_parsms for codecs, so ignore here.
//因为这个设置会在hw_parsms 里面做,所有我们跳过
#ifndef CONFIG_SND_RK_SOC
for (i = 0; i < card->num_links; i++)
dai_link = &card->dai_link[i];
dai_fmt = dai_link->dai_fmt;
if (dai_fmt)
ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai, dai_fmt);
/* If this is a regular CPU link there will be a platform */
if (dai_fmt && (dai_link->platform_name || dai_link->platform_of_node))
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, dai_fmt);
else if (dai_fmt)
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,dai_fmt);
#endif
if (card->late_probe) { //暂时没有
ret = card->late_probe(card);
snd_soc_dapm_new_widgets(&card->dapm); //这个已经单独分析了
if (card->fully_routed) //暂时没有
list_for_each_entry(codec, &card->codec_dev_list, card_list)
snd_soc_dapm_auto_nc_codec_pins(codec);
ret = snd_card_register(card->snd_card); //单独分析,注册device
#ifdef CONFIG_SND_SOC_AC97_BUS
for (i = 0; i < card->num_rtd; i++) //暂时没有,先不分析
ret = soc_register_ac97_dai_link(&card->rtd[i]);
card->instantiated = 1;
snd_soc_dapm_sync(&card->dapm);
3. soc_bind_dai_link分析
struct snd_soc_dai_link *dai_link = &card->dai_link[num]; //声卡的dai_link结构体
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
/* Find CPU DAI from registered DAIs*/
list_for_each_entry(cpu_dai, &dai_list, list) //从dai_list链表里面找到相应的cpu_dai
rtd->cpu_dai = cpu_dai;
/* Find CODEC from registered CODECs */
list_for_each_entry(codec, &codec_list, list) //从codec_list链表里面找到相应的codec
rtd->codec = codec;
/*
* CODEC found, so find CODEC DAI from registered DAIs from
* this CODEC
*/
list_for_each_entry(codec_dai, &dai_list, list) ////从dai_list链表里面找到相应的codec_dai
rtd->codec_dai = codec_dai;
/* if there's no platform we match on the empty platform */
platform_name = dai_link->platform_name;
platform_name = "snd-soc-dummy"; //如果没有就实例化一个空的
/* find one from the set of registered platforms */
list_for_each_entry(platform, &platform_list, list) //从platform_list链表里面找到相应的platform
rtd->platform = platform;
card->num_rtd++; //card的数量加一,这样就实例化了一个声卡,所有信息都绑定到rtd上,也是就是snd_soc_pcm_runtime结构体
4.snd_soc_init_codec_cache分析
重要结构体
/* an array of all supported compression types */
static const struct snd_soc_cache_ops cache_types[] = {
/* Flat *must* be the first entry for fallback */
{
.id = SND_SOC_FLAT_COMPRESSION,
.name = "flat",
.init = snd_soc_flat_cache_init,
.exit = snd_soc_flat_cache_exit,
.read = snd_soc_flat_cache_read,
.write = snd_soc_flat_cache_write,
.sync = snd_soc_flat_cache_sync
},
};
/* override the compress_type if necessary */
ret = snd_soc_cache_init(codec);
codec->cache_ops = &cache_types[i]; //只允许一直压缩方式,SND_SOC_FLAT_COMPRESSION
codec->cache_ops->init(codec); //这里调用snd_soc_flat_cache_init
codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); //分配缓存
codec->cache_init = 1; //赋值,代表已经初始化
5.snd_card_create分析
为给定的数据指针创建一个新的设备组件。该设备将被分配到卡片上并一起管理的卡片。
重要结构体
static struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register, //重要函数
.dev_disconnect = snd_ctl_dev_disconnect,
};
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); //分配snd_card结构体,extra_size=0
//如果传入的声卡编号idx 为-1,自动分配一个索引编号
if (idx < 0)
module_slot_match(module, idx2)
if (idx >= snd_ecards_limit)
snd_ecards_limit = idx + 1; /* increase the limit 加入了声卡,就增加这个限制*/
card->number = idx; //赋值编号
card->module = module; //赋值card->owner,snd_soc_card结构体指针
INIT_LIST_HEAD(&card->devices); //初始化一些链表
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
INIT_LIST_HEAD(&card->files_list);
init_waitqueue_head(&card->shutdown_sleep); //初始化等待队列
init_waitqueue_head(&card->power_sleep);
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
//控制接口不能从用户空间访问,直到sndcardsbitmask和sndcard使用sndcardregister设置为止
err = snd_ctl_create(card); //创建card的proc接口
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); //ops是开头的结构体
struct snd_device *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配snd_device 结构体
ev->card = card;
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
dev->ops = ops;
list_add(&dev->list, &card->devices); /* add to the head of list */ 把dev->list加入到card->devices前面,实现一个栈
err = snd_info_card_create(card); //创建card信息相关的东西
sprintf(str, "card%i", card->number);
if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; //设置属性
snd_info_register(entry)
p = proc_mkdir_mode(entry->name, entry->mode, root); //创建/proc/asound/card0
6.soc_probe_link_components探测
/* probe the CPU-side component, if it is a CODEC */
/* probe ordering - for components with runtime dependencies */ 路径管理
if (cpu_dai->codec && !cpu_dai->codec->probed && cpu_dai->codec->driver->probe_order == order) { //如果需要有序的执行
ret = soc_probe_codec(card, cpu_dai->codec);
codec->card = card;
codec->dapm.card = card;
///sys/kernel/debug/asoc/RK_ES8323/ES8323.1-0011,
///sys/kernel/debug/asoc/RK_ES8323/ES8323.1-0011/dapm/
soc_init_codec_debugfs(codec); //初始化调试节点,目录
if (driver->dapm_widgets) //这里我们的codec没有,但是还是需要分析
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, driver->num_dapm_widgets);
/* Create DAPM widgets for each DAI stream */
list_for_each_entry(dai, &dai_list, list)
snd_soc_dapm_new_dai_widgets(&codec->dapm, dai); //单独分析
if (driver->probe)
ret = driver->probe(codec); //这里见rk音频驱动分析之codec的es8323_probe分析
/* If the driver didn't set I/O up try regmap */
if (driver->controls) //这里没有,但是需要单独分析一下
snd_soc_add_codec_controls(codec, driver->controls, driver->num_controls);
if (driver->dapm_routes) //这里没有
snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, driver->num_dapm_routes);
/* mark codec as probed and add to card codec list */
codec->probed = 1;
list_add(&codec->card_list, &card->codec_dev_list); //把codec->card_list加到card->codec_dev_list链表
list_add(&codec->dapm.list, &card->dapm_list); //把codec->dapm.list加到card->dapm_list链表
/* probe the CODEC-side component */
if (!codec_dai->codec->probed && codec_dai->codec->driver->probe_order == order) {
ret = soc_probe_codec(card, codec_dai->codec); //这里和cpu端不同是这里codec端的dai
/* probe the platform */
if (!platform->probed && platform->driver->probe_order == order) {
ret = soc_probe_platform(card, platform); platform相关初始化
const struct snd_soc_platform_driver *driver = platform->driver;
platform->card = card;
platform->dapm.card = card;
soc_init_platform_debugfs(platform); ///sys/kernel/debug/asoc/RK_ES8323/100b0000.i2s1/
if (driver->dapm_widgets) //这里没有
snd_soc_dapm_new_controls(&platform->dapm, driver->dapm_widgets, driver->num_dapm_widgets);
/* Create DAPM widgets for each DAI stream */
list_for_each_entry(dai, &dai_list, list)
snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); //已经分析了
if (driver->probe)
ret = driver->probe(platform); //这里没有
if (driver->controls) //这里没有
snd_soc_add_platform_controls(platform, driver->controls, driver->num_controls);
if (driver->dapm_routes) //这里没有
snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes, driver->num_dapm_routes);
/* mark platform as probed and add to card platform list */
platform->probed = 1;
list_add(&platform->card_list, &card->platform_dev_list); //把codec->dapm.list加到card->platform_dev_list链表
list_add(&platform->dapm.list, &card->dapm_list);//把platform->dapm.list加到card->dapm_list链表
7.soc_probe_link_dais
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
/* config components */
cpu_dai->platform = platform;
codec_dai->card = card;
cpu_dai->card = card;
/* set default power off timeout */
rtd->pmdown_time = pmdown_time;
/* probe the cpu_dai */
if (!cpu_dai->probed && cpu_dai->driver->probe_order == order) //暂时没有
list_add(&cpu_dai->dapm.list, &card->dapm_list);
snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); //已经分析,只是这里加入到cpu_dai->dapm
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe(cpu_dai); //见rk音频驱动之platform里面的rockchip_i2s_dai_probe
cpu_dai->probed = 1;
/* mark cpu_dai as probed and add to card dai list */
list_add(&cpu_dai->card_list, &card->dai_dev_list); //把cpu_dai->card_list加入card->dai_dev_list
/* probe the CODEC DAI */
if (!codec_dai->probed && codec_dai->driver->probe_order == order)
ret = codec_dai->driver->probe(codec_dai); //这里没有
/* mark codec_dai as probed and add to card dai list */
codec_dai->probed = 1;
list_add(&codec_dai->card_list, &card->dai_dev_list); //把codec_dai->card_list加入card->dai_dev_list
ret = soc_post_component_init(card, codec, num, 0); //单独分析
ret = device_create_file(rtd->dev, &dev_attr_pmdown_time); //sys/devices/rockchip_es8388.26/ES8323/pmdown_time
if (cpu_dai->driver->compress_dai) //这里没有
/*create compress_device"*/
ret = soc_new_compress(rtd, num);
else
if (!dai_link->params) //进入这里
/* create the pcm */
ret = soc_new_pcm(rtd, num); //单独分析
else //这里我们没有,暂不分析
/* link the DAI widgets */
play_w = codec_dai->playback_widget;
capture_w = cpu_dai->capture_widget;
if (play_w && capture_w) {
ret = snd_soc_dapm_new_pcm(card, dai_link->params, capture_w, play_w);
/* add platform data for AC97 devices */
if (rtd->codec_dai->driver->ac97_control) //没有,暂不分析
snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata);
8. snd_soc_dapm_new_dai_widgets(&codec->dapm, dai)
struct snd_soc_dapm_widget template;
struct snd_soc_dapm_widget *w;
template.reg = SND_SOC_NOPM; //没有pm寄存器
if (dai->driver->playback.stream_name) //名字是Playback
template.id = snd_soc_dapm_dai_in; //link to DAI structure
template.name = dai->driver->playback.stream_name; //名字是Playback
template.sname = dai->driver->playback.stream_name; //名字是Playback
w = snd_soc_dapm_new_control(dapm, &template);
w = dapm_cnew_widget(widget) /* create a new dapm widget */
//通过kmemdup将一种类型的数据赋值给另外同一个类型的数据
//就是要将_widget的数据赋值给w
return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
switch (w->id)
case snd_soc_dapm_regulator_supply: //不是这个类型
case snd_soc_dapm_clock_supply: //不是这个类型
w->name = kmalloc(name_len, GFP_KERNEL); //分配名字内存
switch (w->id)
...............
case snd_soc_dapm_dai_in:
w->power_check = dapm_dac_check_power; ///* Check to see if a DAC has power */
..............
w->dapm = dapm;
w->codec = dapm->codec;
w->platform = dapm->platform;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets); //把w->list加入到dapm->card->widgets链表前面
w->priv = dai;
dai->playback_widget = w; //赋值
if (dai->driver->capture.stream_name)
template.id = snd_soc_dapm_dai_out;
template.name = dai->driver->capture.stream_name; //名字Capture
template.sname = dai->driver->capture.stream_name;
w = snd_soc_dapm_new_control(dapm, &template);
case snd_soc_dapm_dai_out: //这里与Playback不同,其他一样
w->power_check = dapm_adc_check_power; //这个是adc
w->priv = dai;
dai->capture_widget = w;
9.soc_post_component_init(card, codec, num, 0);
if (!dailess) //这里dailess=0
dai_link = &card->dai_link[num];
rtd = &card->rtd[num];
name = dai_link->name;
else
aux_dev = &card->aux_dev[num];
rtd = &card->rtd_aux[num];
name = aux_dev->name;
rtd->card = card;
/* Make sure all DAPM widgets are instantiated */
snd_soc_dapm_new_widgets(&codec->dapm); //确认所有DAPM widgets都实例化了,单独分析
/* machine controls, routes and widgets are not prefixed */
temp = codec->name_prefix;
codec->name_prefix = NULL;
/* do machine specific initialization */
ret = dai_link->init(rtd); //调用dai_link的init函数,单独分析rk29_es8323_init,设置一组时钟
codec->name_prefix = temp;
/* register the rtd device */
rtd->codec = codec;
rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL); //分配内存device
device_initialize(rtd->dev); //初始化rtd->dev,与系统相关联
rtd->dev->parent = card->dev;
rtd->dev->release = rtd_release;
rtd->dev->init_name = name;
dev_set_drvdata(rtd->dev, rtd);
//初始化链表
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients);
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
ret = device_add(rtd->dev); //注册设备
rtd->dev_registered = 1;
/* add DAPM sysfs entries for this codec */
ret = snd_soc_dapm_sys_add(rtd->dev); //加入sysfs调试借口,sys/devices/rockchip_es8388.26/ES8323/dapm_widget
/* add codec sysfs entries */
//codec调试接口sys/devices/rockchip_es8388.26/ES8323/codec_reg
ret = device_create_file(rtd->dev, &dev_attr_codec_reg);
10.snd_soc_dapm_new_widgets(&codec->dapm); //确认所有DAPM widgets都实例化了
list_for_each_entry(w, &dapm->card->widgets, list)
if (w->new) //如果已经初始化,就继续
continue;
if (w->num_kcontrols) //有kcontrols
//给snd_kcontrol 结构体指针分配内存
w->kcontrols = kzalloc(w->num_kcontrols * sizeof(struct snd_kcontrol *), GFP_KERNEL);
switch(w->id)
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
dapm_new_mixer(w); //这个是混音器,单独分析
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
dapm_new_mux(w); //多路选择器
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
dapm_new_pga(w); //音量控制器
/* Read the initial power state from the device */
if (w->reg >= 0) {
val = soc_widget_read(w, w->reg); //读取相应的寄存器
if (w->codec)
return snd_soc_read(w->codec, reg);
ret = codec->read(codec, reg); //codec的读函数
else if (w->platform)
return snd_soc_platform_read(w->platform, reg);
ret = platform->driver->read(platform, reg); //platform的读函数
val &= 1 << w->shift; //进行位移
if (w->invert) //需要翻转
val = !val;
if (val)
w->power = 1; //设置电源状态
w->new = 1; //已经初始化
dapm_mark_dirty(w, "new widget");
if (!dapm_dirty_widget(w)) //如果之前没有初始化
list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty); //把w->dirty加入到w->dapm->card->dapm_dirty链表
///sys/kernel/debug/asoc/RK_ES8323/100b0000.i2s1/dapm
dapm_debugfs_add_widget(w); //加入到调试fs
dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); //单独分析
11.dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); //单独分析
LIST_HEAD(up_list);
LIST_HEAD(down_list);
list_for_each_entry(d, &card->dapm_list, list)
if (d->idle_bias_off) //偏压状态
d->target_bias_level = SND_SOC_BIAS_OFF; //关闭
else
d->target_bias_level = SND_SOC_BIAS_STANDBY \\待机
dapm_reset(card);
memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); //卡的dapm_stats置0
list_for_each_entry(w, &card->widgets, list) //把所有widgets
w->power_checked = false;
w->inputs = -1;
w->outputs = -1;
/* Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down. We
* only check widgets that have been flagged as dirty but note
* that new widgets may be added to the dirty list while we
* iterate.
*/
//检查我们需要的哪些小部件,并将它们存储在列表中,指示它们是否应该启动或关闭。我们只检查被标记为dirty的小部件,但是注意
在we迭代时,新的小部件可能会被添加到脏列表中。
list_for_each_entry(w, &card->dapm_dirty, dirty) //找出所有dirty的widgets
dapm_power_one_widget(w, &up_list, &down_list);
switch (w->id)
case snd_soc_dapm_pre: ///* machine specific pre widget - exec first */
dapm_seq_insert(w, down_list, false); ///* Insert a widget in order into a DAPM power sequence. */ down_list
list_for_each_entry(w, list, power_list)
if (dapm_seq_compare(new_widget, w, power_up) < 0)
list_add_tail(&new_widget->power_list, &w->power_list);
list_add_tail(&new_widget->power_list, list);
case snd_soc_dapm_post:
dapm_seq_insert(w, up_list, true); ///* Insert a widget in order into a DAPM power sequence. */up_list
default:
power = dapm_widget_power_check(w); //获取power的状态
dapm_widget_set_power(w, power, up_list, down_list);
/* If we changed our power state perhaps our neigbours changed also.*/
//如果我们改变自己的power状态,也许邻居的也有改变
list_for_each_entry(path, &w->sources, list_sink)
if (path->source)
dapm_widget_set_peer_power(path->source, power, path->connect);
switch (w->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
/* Supplies can't affect their outputs, only their inputs */
供给不能影响他们的输出,只有影响输入
default:
list_for_each_entry(path, &w->sinks, list_source)
if (path->sink)
dapm_widget_set_peer_power(path->sink, power, path->connect);
if (power)
dapm_seq_insert(w, up_list, true);
else
dapm_seq_insert(w, down_list, false);
w->power = power;
list_for_each_entry(w, &card->widgets, list)
switch (w->id)
case snd_soc_dapm_pre:
case snd_soc_dapm_post:
/* These widgets always need to be powered */
default:
list_del_init(&w->dirty); //从链表把自己删除
if (w->power) //是power状态
d = w->dapm;
/*Supplies and micbiases only bring the context up to STANDBY as unless something else is active and passing audio they generally don't require full power. Signal generators are virtual pins and have no power impact themselves.*/
供电和偏压只会让上下文处于待机状态,除非有其他的东西是活跃的,并且通过音频,否则通常不需要全部的电源。信号发生器是虚拟的插脚,本身没有电力影响。
case snd_soc_dapm_siggen:
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_micbias:
if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
d->target_bias_level = SND_SOC_BIAS_STANDBY;
default:
d->target_bias_level = SND_SOC_BIAS_ON;
如果没有地面引用,将卡片中的所有上下文强制执行相同的偏差状态。
bias = SND_SOC_BIAS_OFF;
list_for_each_entry(d, &card->dapm_list, list) //取出动态调节上下文snd_soc_dapm_context
if (d->target_bias_level > bias) //如果大于0,也就是off
bias = d->target_bias_level; //赋值
list_for_each_entry(d, &card->dapm_list, list)
if (!d->idle_bias_off) ///* Use BIAS_OFF instead of STANDBY */,如果不是用BIAS_OFF代替STANDBY
d->target_bias_level = bias;
/* Run all the bias changes in parallel */
list_for_each_entry(d, &dapm->card->dapm_list, list)
//在某一域中为异步执行安排一个函数
async_schedule_domain(dapm_pre_sequence_async, d, &async_domain);
async_synchronize_full_domain(&async_domain); //同步某个域中的所有异步函数
/* Power down widgets first; try to avoid amplifying pops. */
dapm_seq_run(dapm, &down_list, event, false);
dapm_widget_update(dapm);
/* Now power up. */
dapm_seq_run(dapm, &up_list, event, true);
/* Run all the bias changes in parallel */
list_for_each_entry(d, &dapm->card->dapm_list, list)
async_schedule_domain(dapm_post_sequence_async, d, &async_domain);
async_synchronize_full_domain(&async_domain);
/* do we need to notify any clients that DAPM event is complete */
list_for_each_entry(d, &card->dapm_list, list) {
if (d->stream_event)
d->stream_event(d, event);
pop_wait(card->pop_time);
12.rk29_es8323_init分析
struct snd_soc_dai *codec_dai = rtd->codec_dai;
//configure DAI system or master clock ,配置DAI系统和主时钟
ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12288000 /*11289600*/, SND_SOC_CLOCK_IN);
return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); //调用rk音频驱动分析之codec的es8323_set_dai_sysclk函数
13.soc_new_pcm(rtd, num);
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
。。。。。。。。。
else //我们进入else
if (codec_dai->driver->playback.channels_min && cpu_dai->driver->playback.channels_min)
playback = 1;
if (codec_dai->driver->capture.channels_min && cpu_dai->driver->capture.channels_min)
capture = 1;
* create the PCM */
if (rtd->dai_link->no_pcm) //这里我们没有
ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, playback, capture, &pcm);
else
if (rtd->dai_link->dynamic) //没有
。。。。。。。
else
snprintf(new_name, sizeof(new_name), "%s %s-%d", rtd->dai_link->stream_name, codec_dai->name, num);
ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, capture, &pcm); //create a new PCM instance
_snd_pcm_new(card, id, device, playback_count, capture_count, false, rpcm);
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,};
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); //分配snd_pcm结构体
pcm->card = card;
pcm->device = device;
pcm->internal = internal;
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id)); //复制名字
err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count) //单独分析
err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count) //和上面那个基本一致
err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)
dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配snd_device结构体
dev->card = card;
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
dev->ops = ops;
//dev->list加入到card->devices链表
list_add(&dev->list, &card->devices); /* add to the head of list */
/* DAPM dai link stream work */
INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); //
rtd->pcm = pcm;
pcm->private_data = rtd;
/* ASoC PCM operations */
if (rtd->dai_link->dynamic)
。。。。。。
else //我们是这里,一些PCM操作函数
rtd->ops.open = soc_pcm_open;
rtd->ops.hw_params = soc_pcm_hw_params;
rtd->ops.prepare = soc_pcm_prepare;
rtd->ops.trigger = soc_pcm_trigger;
rtd->ops.hw_free = soc_pcm_hw_free;
rtd->ops.close = soc_pcm_close;
rtd->ops.pointer = soc_pcm_pointer;
rtd->ops.ioctl = soc_pcm_ioctl;
if (platform->driver->ops) //赋值rk音频驱动之platform里面,但是下面这些函数没有
rtd->ops.ack = platform->driver->ops->ack;
rtd->ops.copy = platform->driver->ops->copy;
rtd->ops.silence = platform->driver->ops->silence;
rtd->ops.page = platform->driver->ops->page;
rtd->ops.mmap = platform->driver->ops->mmap;
if (playback)
//设置操作函数
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
substream->ops = ops;
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); //和上面一样
if (platform->driver->pcm_new)
ret = platform->driver->pcm_new(rtd); //调用rk音频驱动之platform的dmaengine_pcm_new
pcm->private_free = platform->driver->pcm_free;///调用rk音频驱动之platform的dmaengine_pcm_free
14.snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)
struct snd_pcm_str *pstr = &pcm->streams[stream];
struct snd_pcm_substream *substream, *prev;
pstr->stream = stream;
pstr->pcm = pcm;
pstr->substream_count = substream_count; //这里是1
if (substream_count > 0 && !pcm->internal)
err = snd_pcm_stream_proc_init(pstr); //创建proc接口/proc/asound/card0/
for (idx = 0, prev = NULL; idx < substream_count; idx++) //这里substream_count=1
substream = kzalloc(sizeof(*substream), GFP_KERNEL); //分配snd_pcm_substream结构体
substream->pcm = pcm;
substream->pstr = pstr;
substream->number = idx;
substream->stream = stream;
sprintf(substream->name, "subdevice #%i", idx);
substream->buffer_bytes_max = UINT_MAX;
pstr->substream = substream;
if (!pcm->internal)
err = snd_pcm_substream_proc_init(substream); ///proc/asound/card0/pcm0c/sub0
substream->group = &substream->self_group;
INIT_LIST_HEAD(&substream->self_group.substreams);
//substream->link_list加入到substream->self_group.substreams
list_add_tail(&substream->link_list, &substream->self_group.substreams);
prev = substream;
16.snd_soc_dapm_link_dai_widgets(card);
从snd_soc_dapm_dai_in和snd_soc_dapm_dai_out开始,找到其他同属于一个stream,但不是snd_soc_dapm_dai_out和snd_soc_dapm_dai_in类型的widgets,加入到路径里面
/* For each DAI widget... */
list_for_each_entry(dai_w, &card->widgets, list) //列出card上所有的dapm_widget
switch (dai_w->id) { //如果是snd_soc_dapm_dai_in和snd_soc_dapm_dai_out就跳出,进行下面的操作
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
break;
default:
continue;
dai = dai_w->priv;
* ...find all widgets with the same stream and link them */
list_for_each_entry(w, &card->widgets, list) //列出所有的dapm_widget
//如果是snd_soc_dapm_dai_in,snd_soc_dapm_dai_out就继续找,不是,就继续执行
switch (w->id) {
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
continue;
default:
break;
//如果属于同一个playback的stream
if (dai->driver->playback.stream_name && strstr(w->sname, dai->driver->playback.stream_name))
r.source = dai->playback_widget->name; //playback_widget加入到这个snd_soc_dapm_route里面,路径
r.sink = w->name; //widget name的名字
snd_soc_dapm_add_route(w->dapm, &r); //单独分析
//如果属于同一个capture的stream
if (dai->driver->capture.stream_name && strstr(w->sname, dai->driver->capture.stream_name))
r.source = w->name;
r.sink = dai->capture_widget->name;
snd_soc_dapm_add_route(w->dapm, &r);
17.snd_soc_dapm_add_route(w->dapm, &r);
/* source (input) and sink (output) widgets */
const char *control = route->control;
if (dapm->codec && dapm->codec->name_prefix) //这里没有
。。。。。。
else
sink = route->sink;
source = route->source;
/* find src and dest widgets over all widgets but favor a widget from current DAPM context */
list_for_each_entry(w, &dapm->card->widgets, list)
if (!wsink && !(strcmp(w->name, sink))) //名字一样的widgets
wtsink = w;
if (w->dapm == dapm) //如果是一样的snd_soc_dapm_context
wsink = w;
continue;
if (!wsource && !(strcmp(w->name, source)))
wtsource = w;
if (w->dapm == dapm)
wsource = w;
/* use widget from another DAPM context if not found from this */
if (!wsink)
wsink = wtsink;
if (!wsource)
wsource = wtsource;
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); //分配snd_soc_dapm_path
path->source = wsource;
path->sink = wsink;
path->connected = route->connected;
INIT_LIST_HEAD(&path->list);
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);
/* check for external widgets */
if (wsink->id == snd_soc_dapm_input) //如果它的输出端是snd_soc_dapm_input
if (wsource->id == snd_soc_dapm_micbias || wsource->id == snd_soc_dapm_mic || wsource->id == snd_soc_dapm_line ||
wsource->id == snd_soc_dapm_output) //它的输入端是这些
wsink->ext = 1; //置1
if (wsource->id == snd_soc_dapm_output) //如果它的输出端是snd_soc_dapm_output
if (wsink->id == snd_soc_dapm_spk || wsink->id == snd_soc_dapm_hp || wsink->id == snd_soc_dapm_input)
wsource->ext = 1;
/* connect static paths */
if (control == NULL)
list_add(&path->list, &dapm->card->paths); //path->list加入到dapm->card->paths
list_add(&path->list_sink, &wsink->sources); //path->list_sink加入到wsink->sources
list_add(&path->list_source, &wsource->sinks); //path->list_source加入到wsource->sinks
path->connect = 1;
/* connect dynamic paths */
switch (wsink->id)
case snd_soc_dapm_adc:
case snd_soc_dapm_dac:
。。。。。。
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
case snd_soc_dapm_dai_link:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 1;
.......
case snd_soc_dapm_mux
ret = dapm_connect_mux(dapm, wsource, wsink, path, control, &wsink->kcontrol_news[0]);
case snd_soc_dapm_mixer:
ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
dapm_mark_dirty(wsource, "Route added"); //加到dirty里面,说明是可以用的
list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty);
dapm_mark_dirty(wsink, "Route added");
18.snd_card_register(card->snd_card)
注册device,创建一些调试节点
if (!card->card_dev)
//device创建,名字是card0,,,,,
card->card_dev = device_create(sound_class, card->dev, MKDEV(0, 0), card, "card%i", card->number);
err = snd_device_register_all(card)
list_for_each_entry(dev, &card->devices, list) //列举出card上所有的devices
if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) //这里有dev_register函数
err = dev->ops->dev_register(dev) //单独分析,注册控制接口
dev->state = SNDRV_DEV_REGISTERED;
if (snd_cards[card->number]) //如果已经注册
return 0;
if (*card->id) //弄个独一无二的名字
/* make a unique id name from the given string */
char tmpid[sizeof(card->id)];
memcpy(tmpid, card->id, sizeof(card->id));
snd_card_set_id_no_lock(card, tmpid, tmpid);
else //从shortname 和longname创建一个id
/* create an id from either shortname or longname */
const char *src;
src = *card->shortname ? card->shortname : card->longname;
snd_card_set_id_no_lock(card, src, retrieve_id_from_card_name(src));
snd_cards[card->number] = card; //加入到snd_cards数组里面
init_info_for_card(card);
err = snd_info_card_register(card)
//proc/asound目录下的符号链接RKES8323 -> card0
p = proc_symlink(card->id, snd_proc_root, card->proc_root->name); //创建符号链接
entry = snd_info_create_card_entry(card, "id", card->proc_root) ///proc/asound/card0/id
entry->c.text.read = snd_card_id_read; //读取id ,名字
snd_info_register(entry)
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
if (S_ISDIR(entry->mode)) //文件夹
p = proc_mkdir_mode(entry->name, entry->mode, root);
else //文件,可以读写,有读写函数
p = proc_create_data(entry->name, entry->mode, root, &snd_info_entry_operations, entry);
proc_set_size(p, entry->size);
entry->p = p;
if (entry->parent)
list_add_tail(&entry->list, &entry->parent->children);
if (card->card_dev)
///devices/rockchip_es8388.26/sound/card0/id,可以读写
err = device_create_file(card->card_dev, &card_id_attrs); //
///devices/rockchip_es8388.26/sound/card0/number,可以读写
err = device_create_file(card->card_dev, &card_number_attrs);
19.snd_soc_add_codec_controls(codec, driver->controls, driver->num_controls);
把codec所有的controls实例化成snd_kcontrol结构体,并加入card->controls链表
add an array of controls to a codec
struct snd_card *card = codec->card->snd_card;
return snd_soc_add_controls(card, codec->dev, controls, num_controls, codec->name_prefix, codec);
for (i = 0; i < num_controls; i++) //列出数组的每一个control
const struct snd_kcontrol_new *control = &controls[i];
//snd_soc_cnew(control, data, control->name, prefix)得到snd_kcontrol结构体,也就是snd_kcontrol_new实例化为snd_kcontrol
err = snd_ctl_add(card, snd_soc_cnew(control, data, control->name, prefix));
id = kcontrol->id;
if (snd_ctl_find_id(card, &id)) //如果已经注册,就跳过
........
if (snd_ctl_find_hole(card, kcontrol->count) < 0) //这里不知道
list_add_tail(&kcontrol->list, &card->controls); //kcontrol加入到card->controls
card->controls_count += kcontrol->count; //加上加入的control个数
kcontrol->id.numid = card->last_numid + 1; //给这个kcontrol分配一个id
card->last_numid += kcontrol->count;
count = kcontrol->count;
for (idx = 0; idx < count; idx++, id.index++, id.numid++) //通知有新的kcontrol加入
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
list_for_each_entry(ctl, &card->ctl_files, list) //找到snd_ctl_file
list_for_each_entry(ev, &ctl->events, list) //找到snd_kctl_event
ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
if (ev) {
ev->id = *id;
ev->mask = mask;
list_add_tail(&ev->list, &ctl->events); //添加一个snd_kctl_event
先分析snd_soc_cnew(control, data, control->name, prefix)
struct snd_kcontrol_new template;
struct snd_kcontrol *kcontrol;
memcpy(&template, _template, sizeof(template)); //复制传入的kcontrol信息
template.name = long_name;
//创建一个control 的实例
kcontrol = snd_ctl_new1(&template, data); //create a control instance from the template
kctl.id.iface = ncontrol->iface;
kctl.id.device = ncontrol->device;
kctl.id.subdevice = ncontrol->subdevice;
strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); //赋值名字
kctl.id.index = ncontrol->index;
kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_VOLATILE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
kctl.info = ncontrol->info;
kctl.get = ncontrol->get;
kctl.put = ncontrol->put;
kctl.tlv.p = ncontrol->tlv.p;
kctl.private_value = ncontrol->private_value;
kctl.private_data = private_data; //codec结构体的指针
return snd_ctl_new(&kctl, access);
//分配snd_kcontrol+snd_kcontrol_volatile的内存
kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
*kctl = *control; //赋值之前的结构体,已经初始化
for (idx = 0; idx < kctl->count; idx++) //赋值权限
kctl->vd[idx].access = access;
20.snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, driver->num_dapm_widgets);
for (i = 0; i < num; i++)
w = snd_soc_dapm_new_control(dapm, widget);
21.w = snd_soc_dapm_new_control(dapm, &template);
注册widget的control,需要注意
w = dapm_cnew_widget(widget) /* create a new dapm widget */
//通过kmemdup将一种类型的数据赋值给另外同一个类型的数据
//就是要将_widget的数据赋值给w
return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
switch (w->id)
case snd_soc_dapm_regulator_supply: //不是这个类型
case snd_soc_dapm_clock_supply: //不是这个类型
w->name = kmalloc(name_len, GFP_KERNEL); //分配名字内存
switch (w->id)
...............
case snd_soc_dapm_dai_in:
w->power_check = dapm_dac_check_power; ///* Check to see if a DAC has power */
..............
w->dapm = dapm;
w->codec = dapm->codec;
w->platform = dapm->platform;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets); //把w->list加入到dapm->card->widgets链表前面
22. dapm_new_mixer(w); //这个是混音器
/* add kcontrol */
for (i = 0; i < w->num_kcontrols; i++)
/* match name */
//搜索这个widget的输入链表的所有path
list_for_each_entry(path, &w->sources, list_sink)
/* mixer/mux paths name must match control name */
if (path->name != (char *)w->kcontrol_news[i].name)
continue;
if (w->kcontrols[i])
path->kcontrol = w->kcontrols[i]; //赋值给这个path
continue;
ret = dapm_create_or_share_mixmux_kcontrol(w, i, path);
23. dapm_create_or_share_mixmux_kcontrol(w, i, path);
//检查是否已经有这个kcontrol了,如果 有就不再创建
shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci], &kcontrol);
list_for_each_entry(w, &dapm->card->widgets, list)
//如果是同一个widget,或者不是一个电源域,就跳过
if (w == kcontrolw || w->dapm != kcontrolw->dapm)
continue;
for (i = 0; i < w->num_kcontrols; i++)
//如果它属于这个widget的kcontrol_news数组里面
if (&w->kcontrol_news[i] == kcontrol_new)
*kcontrol = w->kcontrols[i]; //加入
return 1;
if (kcontrol)
wlist = kcontrol->private_data; //kcontrol私有变量,存放着什么,忘记了
wlistentries = wlist->num_widgets + 1; //widgets 的个数
wlistsize = sizeof(struct snd_soc_dapm_widget_list) + wlistentries * sizeof(struct snd_soc_dapm_widget *);
/* A list of widgets associated with an object, typically a snd_kcontrol */
//这是个widgets 的列表
wlist = krealloc(wlist, wlistsize, GFP_KERNEL); //重新分配内存
wlist->num_widgets = wlistentries;
wlist->widgets[wlistentries - 1] = w;
if (!kcontrol) //还是没有
if (shared) //如果是share的
wname_in_long_name = false;
kcname_in_long_name = true;
else
switch (w->id)
case snd_soc_dapm_switch: | snd_soc_dapm_mixer //如果是mixer或者switch的widgets,两个名字都有
wname_in_long_name = true;
kcname_in_long_name = true;
break;
case snd_soc_dapm_mixer_named_ctl: //如果是命名控制,没有wname_in_long_name
wname_in_long_name = false;
kcname_in_long_name = true;
break;
//如果是mux,没有kcname_in_long_name
case snd_soc_dapm_mux: | snd_soc_dapm_virt_mux | snd_soc_dapm_value_mux
wname_in_long_name = true;
kcname_in_long_name = false;
break;
if (wname_in_long_name && kcname_in_long_name) //如果是mixer
name_len = strlen(w->name) - prefix_len + 1 + strlen(w->kcontrol_news[kci].name) + 1;
long_name = kmalloc(name_len, GFP_KERNEL); //分配长名字
/* The control will get a prefix from the control creation process but we're also using the same
* prefix for widgets so cut the prefix off the front of the widget name. */
//这个control 会从control 创造程序得到一个前缀,但是widgets我们也会用相同的前缀,
//所以在小部件名称前面去掉前缀
snprintf(long_name, name_len, "%s %s", w->name + prefix_len, w->kcontrol_news[kci].name);
name = long_name;
else if (wname_in_long_name) //这里是mux
long_name = NULL;
name = w->name + prefix_len;
else
long_name = NULL;
name = w->kcontrol_news[kci].name;
//得到snd_kcontrol结构体,也就是snd_kcontrol_new实例化为snd_kcontrol
kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name, prefix);
ret = snd_ctl_add(card, kcontrol); //把这个kcontrol加入,前面有分析
path->long_name = long_name; //所以只有mixer有long_name
kcontrol->private_data = wlist; //指向widget_list
w->kcontrols[kci] = kcontrol; //加入到widgets数组里面
path->kcontrol = kcontrol; //放到这个path里面
24.snd_soc_dapm_add_routes
for (i = 0; i < num; i++)
r = snd_soc_dapm_add_route(dapm, route); //前面已经分析
route++;
25.err = dev->ops->dev_register(dev) //单独分析,注册控制接口
Control.c (kernel\sound\core)
这是用户空间调用的接口函数/dev/snd/controlC*
static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
static int snd_ctl_dev_register(struct snd_device *device)
cardnum = card->number;
sprintf(name, "controlC%i", cardnum);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops, card, name)) < 0)
//Register the ALSA device file for the card
snd_register_device_for_dev(type, card, dev, f_ops, private_data, name, snd_card_get_device_link(card));
int minor;
struct snd_minor *preg;
preg = kmalloc(sizeof *preg, GFP_KERNEL); //分配snd_minor 结构体
preg->type = type;
preg->card = card ? card->number : -1;
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
preg->card_ptr = card;
//根据声卡的类型,获取次设备号
minor = snd_kernel_minor(type, card, dev);
snd_minors[minor] = preg; //加入到次设备号数组
//把这个声卡注册到dev目录下
preg->dev = device_create(sound_class, device, MKDEV(major, minor), private_data, "%s", name);
26./dev/snd/pcmC0D0注册
snd_device_register_all调用dev->ops->dev_register(dev) 到snd_pcm_dev_register
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}};
static int snd_pcm_dev_register(struct snd_device *device)
struct snd_pcm *pcm;
pcm = device->device_data;
err = snd_pcm_add(pcm);
list_for_each_entry(pcm, &snd_pcm_devices, list) //从snd_pcm_devices找出
if (pcm->card->number > newpcm->card->number || (pcm->card == newpcm->card && pcm->device > newpcm->device)
list_add(&newpcm->list, pcm->list.prev); 加入到pcm->list.prev的前面
for (cidx = 0; cidx < 2; cidx++)
switch (cidx)
case SNDRV_PCM_STREAM_PLAYBACK: //如果是播放
sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
case SNDRV_PCM_STREAM_CAPTURE: //如果是录音
sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
/* device pointer to use, pcm->dev takes precedence if it is assigned, otherwise fall back to card's device if possible */
//device指针被使用,如果分配pcm->dev优先,否则有可能回到card设备
dev = snd_card_get_device_link(pcm->card); //获取card->card_dev
/* register pcm */
//注册pcm,注意后面应用使用pcm通过snd_pcm_f_ops函数
err = snd_register_device_for_dev(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, str, dev);
//用次设备号注册
preg->dev = device_create(sound_class, device, MKDEV(major, minor), private_data, "%s", name);
27.声卡硬件设置
Rk_es8323.c (kernel\sound\soc\rockchip)
static int rk29_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
return dai->driver->ops->set_fmt(dai, fmt); //调用到rk音频驱动分析之codec的es8323_set_dai_fmt函数
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
return dai->driver->ops->set_fmt(dai, fmt); //调用到rk音频驱动之platform的rockchip_i2s_set_fmt
switch(params_rate(params)) //选择传输速率
case 48000:
pll_out = 12288000;
。。。。。。。。
///* codec clk & FRM slave */ 设置codec时钟和
if ((dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFS)
snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); //调用rk音频驱动之platform的rockchip_i2s_set_sysclk函数
snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4)/params_rate(params)-1);
return dai->driver->ops->set_clkdiv(dai, div_id, div); //调用rk音频驱动之platform的rockchip_i2s_set_clkdiv
snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3);