linux 音频驱动alsa-asoc-codec代码分析
使用的codec芯片是 wm9081.废话不说直接开始:
1 static struct i2c_driver wm9081_i2c_driver = { 2 .driver = { 3 .name = "wm9081", 4 .owner = THIS_MODULE, 5 }, 6 .probe = wm9081_i2c_probe, 7 .remove = wm9081_i2c_remove, 8 .id_table = wm9081_i2c_id, 9 }; 10 11 12 module_i2c_driver(wm9081_i2c_driver); 13 14 MODULE_DESCRIPTION("ASoC WM9081 driver"); 15 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 16 MODULE_LICENSE("GPL");
进入到 wm9081_i2c_probe 中
static int wm9081_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm9081_priv *wm9081; unsigned int reg; int ret; wm9081 = devm_kzalloc(&i2c->dev, sizeof(struct wm9081_priv), GFP_KERNEL); if (wm9081 == NULL) return -ENOMEM; i2c_set_clientdata(i2c, wm9081); wm9081->regmap = devm_regmap_init_i2c(i2c, &wm9081_regmap); if (IS_ERR(wm9081->regmap)) { ret = PTR_ERR(wm9081->regmap); dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); return ret; } ret = regmap_read(wm9081->regmap, WM9081_SOFTWARE_RESET, ®); if (ret != 0) { dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); return ret; } if (reg != 0x9081) { dev_err(&i2c->dev, "Device is not a WM9081: ID=0x%x\n", reg); return -EINVAL; } ret = wm9081_reset(wm9081->regmap); if (ret < 0) { dev_err(&i2c->dev, "Failed to issue reset\n"); return ret; } if (dev_get_platdata(&i2c->dev)) memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev), sizeof(wm9081->pdata)); reg = 0; if (wm9081->pdata.irq_high) reg |= WM9081_IRQ_POL; if (!wm9081->pdata.irq_cmos) reg |= WM9081_IRQ_OP_CTRL; regmap_update_bits(wm9081->regmap, WM9081_INTERRUPT_CONTROL, WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg); regcache_cache_only(wm9081->regmap, true); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm9081, &wm9081_dai, 1); if (ret < 0) return ret; return 0; }
直接看关键部分 snd_soc_register_codec() 此函数将codec driver 和dai driver注册进链表 在machine驱动注册的时候将会在此链表中匹配snd_soc_dai_link中定义的各部分的名字
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_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.dapm_ptr = &codec->dapm; codec->component.codec = codec; ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev); if (ret) goto err_free;
申请 snd_soc_codec 并设置 component 参数 然后初始化component,让我们进入到初始化函数中看看
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; if (!component->dapm_ptr) component->dapm_ptr = &component->dapm; dapm = component->dapm_ptr; 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; }
此函数主要工作是:
1.设置name参数 。注意:在machine驱动注册过程中 将会用snd_soc_dai_link.codec_name 和这里的name做匹配 如果 snd_soc_dai_link.codec_name和这个地方不一样 则会出现probe不上codec的问题
2.通过snd_soc_component_driver 结果成员 填充 snd_soc_component 成员
3.初始化 component 的 dai_list 成员
if (codec_drv->controls) { codec->component.controls = codec_drv->controls; codec->component.num_controls = codec_drv->num_controls; } if (codec_drv->dapm_widgets) { codec->component.dapm_widgets = codec_drv->dapm_widgets; codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets; } if (codec_drv->dapm_routes) { codec->component.dapm_routes = codec_drv->dapm_routes; codec->component.num_dapm_routes = codec_drv->num_dapm_routes; } 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; codec->dapm.idle_bias_off = codec_drv->idle_bias_off; codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off; if (codec_drv->seq_notifier) codec->dapm.seq_notifier = codec_drv->seq_notifier; if (codec_drv->set_bias_level) codec->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);
继续初始化剩余部分
1 for (i = 0; i < num_dai; i++) { 2 fixup_codec_formats(&dai_drv[i].playback); 3 fixup_codec_formats(&dai_drv[i].capture); 4 } 5 6 ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); 7 if (ret < 0) { 8 dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); 9 goto err_cleanup; 10 }
1.设置steam.format 参数
2.初始化 struct snd_soc_dai
1 static int snd_soc_register_dais(struct snd_soc_component *component, 2 struct snd_soc_dai_driver *dai_drv, size_t count, 3 bool legacy_dai_naming) 4 { 5 struct device *dev = component->dev; 6 struct snd_soc_dai *dai; 7 unsigned int i; 8 int ret; 9 10 dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count); 11 12 component->dai_drv = dai_drv; 13 component->num_dai = count; 14 15 16 for (i = 0; i < count; i++) { 17 18 dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); 19 if (dai == NULL) { 20 ret = -ENOMEM; 21 goto err; 22 } 23 24 /* 25 * Back in the old days when we still had component-less DAIs, 26 * instead of having a static name, component-less DAIs would 27 * inherit the name of the parent device so it is possible to 28 * register multiple instances of the DAI. We still need to keep 29 * the same naming style even though those DAIs are not 30 * component-less anymore. 31 */ 32 if (count == 1 && legacy_dai_naming) { 33 dai->name = fmt_single_name(dev, &dai->id); 34 35 } else { 36 dai->name = fmt_multiple_name(dev, &dai_drv[i]); 37 if (dai_drv[i].id) 38 dai->id = dai_drv[i].id; 39 else 40 dai->id = i; 41 } 42 if (dai->name == NULL) { 43 kfree(dai); 44 ret = -ENOMEM; 45 goto err; 46 } 47 48 dai->component = component; 49 dai->dev = dev; 50 dai->driver = &dai_drv[i]; 51 if (!dai->driver->ops) 52 dai->driver->ops = &null_dai_ops; 53 54 list_add(&dai->list, &component->dai_list); 55 56 dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); 57 } 58 return 0; 59 60 err: 61 snd_soc_unregister_dais(component); 62 63 return ret; 64 }
1.首先分配 struct snd_soc_dai
2.设置dai->name 参数 注意:在machine驱动注册过程中 将会用snd_soc_dai_link.codec_dai_name 和这里的name做匹配 如果 snd_soc_dai_link.codec_dai_name和这个地方不一样 则会出现probe不上dai的问题
3.将 dai 加入到 component->dai_list 中
1 list_for_each_entry(dai, &codec->component.dai_list, list) 2 dai->codec = codec;
遍历dai_list 设置其所属的 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; }
将codec加入到list中
将component 也加入到component_list中
至此注册结束
总结:通过调用snd_soc_register_codec函数之后,将分配的codec最终放入了codec_list链表中,codec下的component放入component_list链表中,codec下的dai全部存放入code->component.dai_list中 在machine驱动中 machine匹配codec_name 是从component_list中获取的 ,codec_dai是从component->dai_list中获取的。