ALSA的理解

以mini210-wm8960参考,内核版本2.6.35,(3.0内核版本不一样)学习alsa驱动相关注册及流程笔记记录如下

(部分内容参考linux audio(alsa) 驱动注册的简明流程.)


大体结构: 

cpu_dai-------指的是I2C通路.
codec_dai-----codec的功能,如录音放音参数。
codec_dev-----解码设备,重点
platform------据我看只是做了pcm的操作函数和分配dma用的

整个ASoC是由一些列数据结构组成,要搞清楚ASoC的工作机理,必须要理解这一系列数据结构之间的关系和作用,下面的关系图展示了ASoC中重要的数据结构之间的关联方式:


                                                                                                      图 Kernel-2.6.35-ASoC中各个结构的静态关系




static struct snd_soc_dai_link mini210_dai = {
    .name = "MINI210",
    .stream_name = "WM8960 HiFi",    
    //#define MAX_I2SV3  2
    .cpu_dai = &s3c64xx_i2s_dai[0], //IIS2
    .codec_dai = &wm8960_dai,
    .init = mini210_wm8960_init,
    .ops = &mini210_wm8960_ops,
};


static struct snd_soc_card mini210_soc_card = {
    .name = "mini210",
    .platform = &s3c_dma_wrapper, //,
    .dai_link = &mini210_dai,
    .num_links = 1,
};
                         
static struct snd_soc_device mini210_snd_devdata = {
    .card = &mini210_soc_card,
    .codec_dev = &soc_codec_dev_wm8960,
};
1. 在文件中注册soc-audio设备,以唤醒soc-core中的probe
我们的注册如下:

    mini210_snd_device = platform_device_alloc("soc-audio", -1);
    if ( !mini210_snd_device ){
        return -ENOMEM;
    }
    platform_set_drvdata( mini210_snd_device, &mini210_snd_devdata );

将这些结构通过函数platform_set_drvdata放入了platform_device的 private data中;
omini210_snd_device 中包括card和codec_dev, card中包括snd_soc_dai_link, 
snd_soc_dai_link中包括cpu_dai,和codec_dai, 这些比较简单,不需要赘述了.

 按照Linux的设备模型,有platform_device,就一定会有platform_driver。ASoC的platform_driver在以下文件中定义:sound/soc/soc-core.c。
我们看到platform_driver的name字段为soc-audio,正好与platform_device中的名字相同,按照Linux的设备模型,platform总线会匹配这两个名字相同的device和driver,同时会触发soc_probe的调用,它正是整个ASoC驱动初始化的入口。
soc_probe->snd_soc_register_card->snd_soc_instantiate_cards->snd_soc_instantiate_card

在snd_soc_instantiate_card中匹配platform
    /*匹配platform*/
    list_for_each_entry(platform, &platform_list, list)
        if (card->platform == platform) {
            found = 1;
            break;
        }
 
接下来匹配cpu_dai
    for (i = 0; i < card->num_links; i++) {
        found = 0;
        list_for_each_entry(dai, &dai_list, list)
            if (card->dai_link[i].cpu_dai == dai) {
                found = 1;
                break;

            }

        ...

  }


再是codec_dai
if (card->dai_link[i].codec_dai == dai)...
 
如果都找到了,就进行card->probe, 一般都没有。
if (card->probe)
 
再进行cpu_dai的初始化,就是i2s接口的初始化。
ret = cpu_dai->probe(pdev, cpu_dai);
 
再进行codec_dev的probe, 就是soc_codec_dev_wm8960
ret = codec_dev->probe(pdev);        //codec_dev.probe->mini210_snd_devdata->soc_codec_dev_wm8960->wm8960_probe

这个probe一般也都没有。 
ret = platform->probe(pdev);
 
初始化等待队列
INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
 
重头戏来了, codec_dev的probe
 static int wm8960_probe(struct platform_device *pdev)
{
    struct snd_soc_device *socdev = platform_get_drvdata(pdev);
    struct snd_soc_codec *codec;
    int ret = 0;

    if (wm8960_codec == NULL) {
        dev_err(&pdev->dev, "Codec device not registered\n");
        return -ENODEV;
    }

    socdev->card->codec = wm8960_codec;
    codec = wm8960_codec;

    /* register pcms */
    ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
    if (ret < 0) {
        dev_err(codec->dev, "failed to create pcms: %d\n", ret);
        goto pcm_err;
    }

    snd_soc_add_controls(codec, wm8960_snd_controls,
                 ARRAY_SIZE(wm8960_snd_controls));
    wm8960_add_widgets(codec);

    return ret;

pcm_err:
    return ret;
}
1. snd_soc_new_pcms中调用snd_card_create创建1个基于PCM的声卡设备。 
 
snd_card_create创建声卡,进入snd_card_create中,
snd_ctl_create定义声卡device的操作方法,snd_device_new创建出声卡设备并注册到链表中。
 
2. 回到snd_soc_new_pcms中,看soc_new_pcm。
 
将codec与dai建立链接,snd_pcm_new中先创建SNDRV_PCM_STREAM_PLAYBACK和SNDRV_PCM_STREAM_CAPTURE流
然后snd_device_new创建pcm的device.
 
3. 回到soc_new_pcm中为soc_pcm_ops赋值,其实就是将platform中的ops全部指过来。
 
4. 然后调用platform中的new, 创建dma. 
ret = platform->pcm_new(codec->card, codec_dai, pcm);
至此PCM的数据路就注册好了,下面就要注册控制信息了,就是snd_kcontrol_new结构体。
 
    snd_soc_add_controls(codec, wm8960_snd_controls,
                 ARRAY_SIZE(wm8960_snd_controls));
    wm8960_add_widgets(codec);
 
待续...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值