Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1306369
  • 博文数量: 860
  • 博客积分: 425
  • 博客等级: 下士
  • 技术积分: 1464
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-20 19:57
个人简介

对技术执着

文章分类

全部博文(860)

文章存档

2019年(16)

2018年(12)

2015年(732)

2013年(85)

2012年(15)

我的朋友

分类: 嵌入式

2015-03-14 17:11:41

soc_codec_dev_uda134x
==> uda134x_soc_probe
==> snd_soc_new_pcms  为DAI建立对应的pcm实例,1路dai对应1路pcm,1路pcm可以有多条放音和录音流通道
==> snd_soc_init_card 注册声卡
==> snd_card_register
==> snd_device_register_all 调用dev->ops->dev_register注册card->devices链表上的所有pcm实例[luther.gliethttp]
==> 比如在snd_pcm_new中设置dev->ops->dev_register其值为snd_pcm_dev_register
这个snd_pcm_dev_register函数最终会调用下面挂到snd_pcm_notify_list设备通知链表上的所有期望添加pcm时得到通知的函数
  list_for_each_entry(notify, &snd_pcm_notify_list, list)
        notify->n_register(pcm);
其中就包括alsa_pcm_oss_init中snd_pcm_notify(&snd_pcm_oss_notify, 0)添加的方法snd_pcm_oss_notify其内部实现如下:[luther.gliethttp]
static struct snd_pcm_notify snd_pcm_oss_notify =
{
    .n_register =    snd_pcm_oss_register_minor,
    .n_disconnect = snd_pcm_oss_disconnect_minor,
    .n_unregister =    snd_pcm_oss_unregister_minor,
};

文件:sound/soc/s3c24xx/s3c24xx_uda134x.c
static int __init s3c24xx_uda134x_init(void)
{
    return platform_driver_register(&s3c24xx_uda134x_driver);   // 驱动arch/arm/mach-xx下注册的名为"s3c24xx_uda134x"的设备.
}
static struct platform_driver s3c24xx_uda134x_driver = {
    .probe  = s3c24xx_uda134x_probe,                            // 执行该probe进一步创建
    .remove = s3c24xx_uda134x_remove,
    .driver = {
        .name = "s3c24xx_uda134x",
        .owner = THIS_MODULE,
    },
};
下面是s3c24xx_uda134x_probe的具体实现代码:
static int s3c24xx_uda134x_probe(struct platform_device *pdev)
{
    int ret;

    printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");

    s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;          // 由platform设备传进来的l3的3个控制io口
    if (s3c24xx_uda134x_l3_pins == NULL) {
        printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
               "unable to find platform data\n");
        return -ENODEV;
    }
    s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
    s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;

    if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data, // 调用gpio_request申请io
                      "data") < 0)
        return -EBUSY;
    if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk, // 调用gpio_request申请io
                      "clk") < 0) {
        gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
        return -EBUSY;
    }
    if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode, // 调用gpio_request申请io
                      "mode") < 0) {
        gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
        gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
        return -EBUSY;
    }

    s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1); // 申请名为"soc-audio"设备
    if (!s3c24xx_uda134x_snd_device) {                              // 该设备将由soc_driver声卡驱动管理
        printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
               "Unable to register\n");
        return -ENOMEM;
    }

    platform_set_drvdata(s3c24xx_uda134x_snd_device,
                 &s3c24xx_uda134x_snd_devdata); // s3c24xx_uda134x_snd_devdata声卡驱动soc_driver使用到的结构体
    s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
    ret = platform_device_add(s3c24xx_uda134x_snd_device);          // 将申请的"soc-audio"设备注册到
    if (ret) {                                                      // platform总线,由soc_driver声卡驱动进一步管理
        printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
        platform_device_put(s3c24xx_uda134x_snd_device);
    }

    return ret;
}
// 下面是soc_driver声卡驱动将要使用到的结构体定义
static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
    .card = &snd_soc_s3c24xx_uda134x,               // 定义的card声卡
    .codec_dev = &soc_codec_dev_uda134x,
    .codec_data = &s3c24xx_uda134x,
};
struct snd_soc_codec_device soc_codec_dev_uda134x = {
    .probe =        uda134x_soc_probe,
    .remove =       uda134x_soc_remove,
    .suspend =      uda134x_soc_suspend,
    .resume =       uda134x_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);
static struct uda134x_platform_data s3c24xx_uda134x = {
    .l3 = {
        .setdat = setdat,
        .setclk = setclk,
        .setmode = setmode,
        .data_hold = 1,
        .data_setup = 1,
        .clock_high = 1,
        .mode_hold = 1,
        .mode = 1,
        .mode_setup = 1,
    },
};
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {// card声卡的结构体
    .name = "S3C24XX_UDA134X",
    .platform = &s3c24xx_soc_platform,              // 使用s3c24xx_soc_platform平台
    .dai_link = &s3c24xx_uda134x_dai_link,          // 使用s3c24xx_uda134x_dai_link中的cpu_dai和codec_dai解码流通道,他们将生成一个pcm实例,然后对声音流数据进行发送和接收处理.
    .num_links = 1,
};
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
    .name = "UDA134X",                              // 一个pcm实例将要对应的所有CPU <-> DAI信息.
    .stream_name = "UDA134X",
    .codec_dai = &uda134x_dai,                      // codec芯片接口控制结构体
    .cpu_dai = &s3c24xx_i2s_dai,                    // cpu内置的音频控制单元
    .ops = &s3c24xx_uda134x_ops,                    // pcm实例使用到自定义操作方法集
};
struct snd_soc_dai uda134x_dai = {                  // codec芯片接口音频控制结构体
    .name = "UDA134X",
    /* playback capabilities */
    .playback = {                                   // 放音通道
        .stream_name = "Playback",
        .channels_min = 1,
        .channels_max = 2,
        .rates = UDA134X_RATES,
        .formats = UDA134X_FORMATS,
    },
    /* capture capabilities */
    .capture = {                                    // 录音通道
        .stream_name = "Capture",
        .channels_min = 1,
        .channels_max = 2,
        .rates = UDA134X_RATES,
        .formats = UDA134X_FORMATS,
    },
    /* pcm operations */
    .ops = &uda134x_dai_ops,
};
EXPORT_SYMBOL(uda134x_dai);
static int __init uda134x_init(void)
{
    return snd_soc_register_dai(&uda134x_dai);      // 注册codec芯片音频驱动模块codec_dai到dai_list链表上[luther.gliethttp].
}
module_init(uda134x_init);
struct snd_soc_dai s3c24xx_i2s_dai = {              // cpu内置的音频控制单元
    .name = "s3c24xx-i2s",
    .id = 0,
    .probe = s3c24xx_i2s_probe,
    .suspend = s3c24xx_i2s_suspend,
    .resume = s3c24xx_i2s_resume,
    .playback = {                                   // cpu对放音通道的控制
        .channels_min = 2,
        .channels_max = 2,
        .rates = S3C24XX_I2S_RATES,
        .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
    .capture = {                                    // cpu对录音通道的控制
        .channels_min = 2,
        .channels_max = 2,
        .rates = S3C24XX_I2S_RATES,
        .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
    .ops = &s3c24xx_i2s_dai_ops,
};
static int __init s3c24xx_i2s_init(void)
{
    return snd_soc_register_dai(&s3c24xx_i2s_dai);  // 注册cpun内部音频驱动模块cpu_dai到dai_list链表上[luther.gliethttp].
}
module_init(s3c24xx_i2s_init);
static int __init s3c24xx_soc_platform_init(void)
{
    return snd_soc_register_platform(&s3c24xx_soc_platform);    // 登记注册24xx的声卡设备到声卡驱动专用的platform_list链表上
}
module_init(s3c24xx_soc_platform_init);
struct snd_soc_platform s3c24xx_soc_platform = {
    .name        = "s3c24xx-audio",
    .pcm_ops     = &s3c24xx_pcm_ops,                             // 本platform平台提供的pcm操作方法
    .pcm_new    = s3c24xx_pcm_new,
    .pcm_free    = s3c24xx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);

文件:sound/soc/soc-core.c
// 上面s3c24xx_uda134x_probe==>platform_device_add(s3c24xx_uda134x_snd_device);将直接触发
// 此处soc_probe检测函数的进一步执行[luther.gliethttp].
/* ASoC platform driver */
static struct platform_driver soc_driver = {
    .driver        = {
        .name        = "soc-audio",
        .owner        = THIS_MODULE,
    },
    .probe        = soc_probe,
    .remove        = soc_remove,
    .suspend    = soc_suspend,
    .resume        = soc_resume,
};
/* probes a new socdev */
static int soc_probe(struct platform_device *pdev)
{
    int ret = 0;
    struct snd_soc_device *socdev = platform_get_drvdata(pdev); // 对应上面的s3c24xx_uda134x_snd_devdata结构体[luther.gliethttp].
    struct snd_soc_card *card = socdev->card;                   // 对应上面的snd_soc_s3c24xx_uda134x

    /* Bodge while we push things out of socdev */
    card->socdev = socdev;

    /* Bodge while we unpick instantiation */
    card->dev = &pdev->dev;
    ret = snd_soc_register_card(card);                          // 注册生成声卡
    if (ret != 0) {
        dev_err(&pdev->dev, "Failed to register card\n");
        return ret;
    }

    return 0;
}
static int snd_soc_register_card(struct snd_soc_card *card)     // 注册生成声卡
{
    if (!card->name || !card->dev)
        return -EINVAL;

    INIT_LIST_HEAD(&card->list);
    card->instantiated = 0;                                     // 标识未初始化该声卡,因为该函数只在soc_probe中调用,所以可以保证确实是第1次创建

    mutex_lock(&client_mutex);
    list_add(&card->list, &card_list);                          // 所有注册的声卡都要添加到声卡设备链表card_list[luther.gliethttp].
    snd_soc_instantiate_cards();                                // 对声卡进行实例化,生成pcm.
    mutex_unlock(&client_mutex);

    dev_dbg(card->dev, "Registered card '%s'\n", card->name);

    return 0;
}
static void snd_soc_instantiate_cards(void)                     // 对声卡进行实例化,生成pcm.
{
    struct snd_soc_card *card;
    list_for_each_entry(card, &card_list, list)                 // 遍历声卡链表card_list上所有声卡,如果该声卡的instantiated等于0,那么将被执行实例化,生成该声卡描述的所有pcm流通道.
        snd_soc_instantiate_card(card);                         // 实例化声卡内部所有stream流通道为pcm.
}
static void snd_soc_instantiate_card(struct snd_soc_card *card) // 作声卡实例化
{
    struct platform_device *pdev = container_of(card->dev,
                            struct platform_device,
                            dev);
    struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
    struct snd_soc_platform *platform;
    struct snd_soc_dai *dai;
    int i, found, ret, ac97;

    if (card->instantiated)                                     // 该card声卡已经完成实例化,简单的返回[luther.gliethttp].
        return;
    found = 0;
    list_for_each_entry(platform, &platform_list, list)         // 搜索platform_list上登记的s3c24xx_soc_platform平台驱动,
        if (card->platform == platform) {                       // 因为s3c24xx_soc_platform在module_init(s3c24xx_soc_platform_init);中
            found = 1;                                          // 被登记,所以这就有一个模块加载顺序问题,s3c24xx_soc_platform_init必须先
            break;                                              // 先执行先被添加到platform_list链表中才能保证[luther.gliethttp].
        }                                                       // 但是从arch_initcall(customize_machine);可以发现,如果声卡built-in进内核
// 的话,MACHINE_START(EDB9312, "Cirrus Logic EDB9312 Evaluation Board")可能存在潜在危险.
// #define arch_initcall(fn)        __define_initcall("3",fn,3)
// #define device_initcall(fn)        __define_initcall("6",fn,6)
// #define __initcall(fn) device_initcall(fn)
// #define module_init(x)    __initcall(x);
    ac97 = 0;
    for (i = 0; i < card->num_links; i++) {
        found = 0;
        list_for_each_entry(dai, &dai_list, list)               // 检查dai_list是否已经注册登记了.cpu_dai引用到的驱动模块[luther.gliethttp].
            if (card->dai_link[i].cpu_dai == dai) {             // 这里就是s3c24xx_uda134x_dai_link中的.cpu_dai = &s3c24xx_i2s_dai
                found = 1;                                      // 他在s3c24xx_i2s_init中完成注册登记.
                break;
            }
        if (!found) {
            dev_dbg(card->dev, "DAI %s not registered\n",
                card->dai_link[i].cpu_dai->name);
            return;
        }

        if (card->dai_link[i].cpu_dai->ac97_control)
            ac97 = 1;
    }
    if (!ac97)                                                  // 如果不是ac97设备
        for (i = 0; i < card->num_links; i++) {
            found = 0;
            list_for_each_entry(dai, &dai_list, list)
                if (card->dai_link[i].codec_dai == dai) {       // 检查dai_list是否已经注册登记了.codec_dai引用到的驱动模块[luther.gliethttp].
                    found = 1;                                  // 这里就是s3c24xx_uda134x_dai_link中的.codec_dai = &uda134x_dai
                    break;                                      // 他在uda134x_init中完成注册登记.
                }
            if (!found) {
                dev_dbg(card->dev, "DAI %s not registered\n",
                    card->dai_link[i].codec_dai->name);
                return;
            }
        }
    /* Found everything, bring it up */                         // ok,所以依赖的模块都已经就位,那么下面开始创建card声卡.
    if (card->probe) {
        ret = card->probe(pdev);                                // 对于snd_soc_s3c24xx_uda134x没有定义probe
        if (ret < 0)
            return;
    }

    for (i = 0; i < card->num_links; i++) {
        struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
        if (cpu_dai->probe) {
            ret = cpu_dai->probe(pdev, cpu_dai);                // cpu集成的音频控制单元probe初始化,s3c24xx_i2s_dai.s3c24xx_i2s_probe
            if (ret < 0)                                        // 主要完成i2s控制器时钟启动和s3c24xx的i2s对应的io口配置初始化[luther.gliethttp].
                goto cpu_dai_err;
        }
    }
// 这里将完成设备节点'/dev/dsp'和alsa节点的所有创建工作[luther.gliethttp].
    if (codec_dev->probe) {                                     // soc_codec_dev_uda134x.soc_codec_dev_uda134x.uda134x_soc_probe初始化
        ret = codec_dev->probe(pdev);                           // 将执行snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
        if (ret < 0)                                            // 生成声卡内存结构体和创建所有pcms流通道,最后调用snd_soc_init_card(socdev);
            goto cpu_dai_err;                                   // 根据dsp_map[]索引号选择一个默认pcm流通道生成"/dev/dsp"节点和alsa节点[luther.gliethttp].
    }

    if (platform->probe) {                                      // s3c24xx_soc_platform没有提供probe来初始化
        ret = platform->probe(pdev);
        if (ret < 0)
            goto platform_err;
    }

    /* DAPM stream work */
    INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
    /* deferred resume work */
    INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif

    card->instantiated = 1;

    return;
}
阅读(419) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~