linux 音频驱动alsa-asoc-codec代码分析

使用的codec芯片是 wm9081.废话不说直接开始:

Codec侧有四个重要的数据结构:
struct snd_soc_dai,struct snd_soc_dai_driver,struct snd_soc_codec,struct snd_soc_codec_driver
 
先看wm9081.c中
 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, &reg);
    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中获取的。

 

转载于:https://www.cnblogs.com/jianershier/p/6781352.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值