Chinaunix首页 | 论坛 | 博客
  • 博客访问: 669460
  • 博文数量: 103
  • 博客积分: 2532
  • 博客等级: 大尉
  • 技术积分: 2039
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-26 16:07
文章分类

全部博文(103)

文章存档

2012年(38)

2011年(28)

2010年(16)

2009年(16)

2008年(5)

分类: LINUX

2012-04-09 14:37:18



首先我们关注一下源码的位置:sound/soc/s3c24xx/s3c24xx_uda134x.c
下面我们就其中的重要函数进行分析:
由module_init(s3c24xx_uda134x_init);引出:

  1. static int __init s3c24xx_uda134x_init(void)
  2. {
  3. return platform_driver_register(&s3c24xx_uda134x_driver); // 驱动arch/arm/mach-xx下注册的名为"s3c24xx_uda134x"的设备
  4. }

s3c24xx_uda134x_driver结构体如下:

  1. static struct platform_driver s3c24xx_uda134x_driver = {
  2. .probe = s3c24xx_uda134x_probe,
  3. .remove = s3c24xx_uda134x_remove,
  4. .driver = {
  5.    .name = "s3c24xx_uda134x",
  6.    .owner = THIS_MODULE,
  7. },
  8. };

下面我们可以关注下s3c24xx_uda134x_probe函数所做的工作,一步步就每一个语句分析:

  1. s3c24xx_uda134x_l3_pins = pdev->dev.platform_data; // 由platform设备传进来的l3的3个控制io口
  2. /*做了一个gpio口的封装,这段程序应该就是进行l3总线的初始化*/
  3. if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
  4.           "data") < 0) // 调用gpio_request申请io
  5.    return -EBUSY;
  6. if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
  7.           "clk") < 0) {
  8.    gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
  9.    return -EBUSY;
  10. }
  11. if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
  12.           "mode") < 0) {
  13.    gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
  14.    gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
  15.    return -EBUSY;
  16. }

紧接着我们申请一个名为“soc-audio“的设备

  1. s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);// 申请名为“soc-audio“设备
  2. platform_set_drvdata(s3c24xx_uda134x_snd_device,// 设备soc-audio“将由soc_driver声卡驱动管理
  3.         &s3c24xx_uda134x_snd_devdata); // s3c24xx_uda134x_snd_devdata声卡驱动soc_driver使用到的结构体
  4. ret = platform_device_add(s3c24xx_uda134x_snd_device);// 将申请的"soc-audio"设备注册到platform总线,由soc_driver声卡驱动进一步管理,此处触发soc-core.c中的soc-audio的soc_driver结构体的probe.

如同上面的注释所写,我们这里在注册的时候就会触发soc-audio的soc_driver结构体,而soc_driver结构体是由s3c24xx_uda134x_snd_devdata进行驱动的,所以在看soc_driver结构体之前我们先来看看s3c24xx_uda134x_snd_devdata结构体:

  1. static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
  2. .card = &snd_soc_s3c24xx_uda134x, // 定义的card声卡
  3. .codec_dev = &soc_codec_dev_uda134x,
  4. .codec_data = &s3c24xx_uda134x,
  5. };

接着我们跟进去看看card声卡是如何定义的:

  1. static struct snd_soc_card snd_soc_s3c24xx_uda134x = {// card声卡的结构体
  2. .name = "S3C24XX_UDA134X",
  3. .platform = &s3c24xx_soc_platform, // 使用s3c24xx_soc_platform平台
  4. .dai_link = &s3c24xx_uda134x_dai_link, // 使用s3c24xx_uda134x_dai_link中的cpu_dai和codec_dai解码流通道,他们将生成一个pcm实例,然后对声音流数据进行发送和接收处理.
  5. .num_links = 1,
  6. };

这个地方我们需要关注两点,一是platform,另一个就是dai_link。这两点我们分开来看,首先来看platform,我们使用s3c24xx_soc_platform平台,这个平台是在sound/soc/s3c24xx/s3c24xx-pcm.c进行定义的,我们进去看看:

  1. EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);

  2. static int __init s3c24xx_soc_platform_init(void)
  3. {
  4. return snd_soc_register_platform(&s3c24xx_soc_platform); // 登记注册24xx的声卡设备到声卡驱动专用的platform_list链表上
  5. }

这个EXPORT_SYMBOL_GPL就是我们能够使用它的缘由所在了。这个结构体如下:

  1. struct snd_soc_platform s3c24xx_soc_platform = {
  2. .name = "s3c24xx-audio",
  3. .pcm_ops = &s3c24xx_pcm_ops, // 本platform平台提供的pcm操作方法
  4. .pcm_new = s3c24xx_pcm_new,
  5. .pcm_free = s3c24xx_pcm_free_dma_buffers,
  6. };

好了,至此我们第一点的platform介绍就ok了,下面我们接着来看另一点dai_link。跟进dai_link的结构体看看:

  1. static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
  2. .name = "UDA134X",
  3. .stream_name = "UDA134X",
  4. .codec_dai = &uda134x_dai, // codec芯片接口控制结构体 详见/sound/soc/codecs/uda134x.c
  5. .cpu_dai = &s3c24xx_i2s_dai, // cpu内置的音频控制单元
  6. .ops = &s3c24xx_uda134x_ops, // pcm实例使用到自定义操作方法集
  7. };

这个结构体我们先看下codec_dai,uda134x_dai位于/sound/soc/codecs/uda134x.c,下面我就进去看看:
同样我们可以看到这样的语句:EXPORT_SYMBOL(uda134x_dai);不解释。

  1. static int __init uda134x_init(void)
  2. {
  3. return snd_soc_register_dai(&uda134x_dai);// 注册codec芯片音频驱动模块codec_dai到dai_list链表上
  4. }
  5. module_init(uda134x_init);

这是init,同样不解释。
下面就是uda134x_dai结构体的具体内容:

  1. struct snd_soc_dai uda134x_dai = {
  2. .name = "UDA134X",
  3. /* playback capabilities */
  4. .playback = { // 放音通道
  5.    .stream_name = "Playback",
  6.    .channels_min = 1,
  7.    .channels_max = 2,
  8.    .rates = UDA134X_RATES,
  9.    .formats = UDA134X_FORMATS,
  10. },
  11. /* capture capabilities */
  12. .capture = { // 录音通道
  13.    .stream_name = "Capture",
  14.    .channels_min = 1,
  15.    .channels_max = 2,
  16.    .rates = UDA134X_RATES,
  17.    .formats = UDA134X_FORMATS,
  18. },
  19. /* pcm operations */
  20. .ops = &uda134x_dai_ops,
  21. };

至此,我们对s3c24xx_uda134x_snd_devdata结构体的认识就告一段落了,下面我们就去关注一下soc_driver结构体,它的源码位于sound/soc/soc_core.c中
从module_init(snd_soc_init);引出
通过下面函数进行init

  1. static int __init snd_soc_init(void)
  2. {
  3. #ifdef CONFIG_DEBUG_FS
  4. debugfs_root = debugfs_create_dir("asoc", NULL);
  5. if (IS_ERR(debugfs_root) || !debugfs_root) {
  6.    printk(KERN_WARNING
  7.          "ASoC: Failed to create debugfs directory\n");
  8.    debugfs_root = NULL;
  9. }
  10. #endif

  11. return platform_driver_register(&soc_driver);
  12. }

进而我们来了解一下soc_driver结构体:

  1. /*s3c24xx_uda134x.c中s3c24xx_uda134x_probe==>platform_device_add(s3c24xx_uda134x_snd_device);
  2. *将直接触发这里soc_probe检测函数的进一步执行
  3. */
  4. /* ASoC platform driver */
  5. static struct platform_driver soc_driver = {
  6. .driver = {
  7.    .name = "soc-audio",
  8.    .owner = THIS_MODULE,
  9. },
  10. .probe = soc_probe,
  11. .remove = soc_remove,
  12. .suspend = soc_suspend,
  13. .resume = soc_resume,
  14. };

这里我们可以看到我们所说的soc-audio设备就是由这边的soc_driver进行管理的。下面我们来了解一下soc_probe都做了些什么工作。

  1. /* probes a new socdev */
  2. static int soc_probe(struct platform_device *pdev)
  3. {
  4. int ret = 0;
  5. struct snd_soc_device *socdev = platform_get_drvdata(pdev); // 对应上面的s3c24xx_uda134x_snd_devdata结构体
  6. struct snd_soc_card *card = socdev->card;// 对应上面的snd_soc_s3c24xx_uda134x

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

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

  16. return 0;
  17. }

我们可以看出,这个probe函数主要是做了一个注册生成声卡的操作,那么这个声卡究竟是怎样注册生成的呢,我们跟进去看看

  1. static int snd_soc_register_card(struct snd_soc_card *card) // 注册生成声卡
  2. {
  3. if (!card->name || !card->dev)
  4.    return -EINVAL;

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

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

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

  12. return 0;
  13. }

这个函数里面我们需要关注的就是对声卡进行实例化,生成pcm的snd_soc_instantiate_cards()函数,下面进去看看:

  1. /*
  2. * Attempt to initialise any uninitalised cards. Must be called with
  3. * client_mutex.
  4. */
  5. static void snd_soc_instantiate_cards(void) // 对声卡进行实例化,生成pcm.
  6. {
  7. struct snd_soc_card *card;
  8. list_for_each_entry(card, &card_list, list) // 遍历声卡链表card_list上所有声卡,如果该声卡的instantiated等于0,那么将被执行实例化,生成该声卡描述的所有pcm流通道.
  9.    snd_soc_instantiate_card(card);// 实例化声卡内部所有stream流通道为pcm
  10. }

这个函数的主要工作就是遍历声卡list上的所有声卡,然后实例化内部所有通道的pcm,那么具体如何进行实例化的呢,let’s go,(我先把代码贴出来,有些简单的注释,然后会就具体的一些需要重点注意的地方再做个说明)

  1. static void snd_soc_instantiate_card(struct snd_soc_card *card)// 作声卡实例化
  2. {
  3. struct platform_device *pdev = container_of(card->dev,
  4.           struct platform_device,
  5.           dev);
  6. struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
  7. struct snd_soc_platform *platform;
  8. struct snd_soc_dai *dai;
  9. int i, found, ret, ac97;

  10. if (card->instantiated) // 该card声卡已经完成实例化,简单的返回
  11.    return;

  12. found = 0;
  13. list_for_each_entry(platform, &platform_list, list)// 搜索platform_list上登记的s3c24xx_soc_platform平台驱动
  14.    if (card->platform == platform) {
  15.     found = 1;
  16.     break;
  17.    }
  18. if (!found) {
  19.    dev_dbg(card->dev, "Platform %s not registered\n",
  20.     card->platform->name);
  21.    return;
  22. }

  23. ac97 = 0;
  24. for (i = 0; i < card->num_links; i++) {
  25.    found = 0;
  26.    list_for_each_entry(dai, &dai_list, list)// 检查dai_list是否已经注册登记了.cpu_dai引用到的驱动模块
  27.     if (card->dai_link[i].cpu_dai == dai) { // 这里就是s3c24xx_uda134x_dai_link中的.cpu_dai = &s3c24xx_i2s_dai
  28.      found = 1; // 他在s3c24xx_i2s_init中完成注册登记
  29.      break;
  30.     }
  31.    if (!found) {
  32.     dev_dbg(card->dev, "DAI %s not registered\n",
  33.      card->dai_link[i].cpu_dai->name);
  34.     return;
  35.    }

  36.    if (card->dai_link[i].cpu_dai->ac97_control)
  37.     ac97 = 1;
  38. }

  39. /* If we have AC97 in the system then don't wait for the
  40. * codec. This will need revisiting if we have to handle
  41. * systems with mixed AC97 and non-AC97 parts. Only check for
  42. * DAIs currently; we can't do this per link since some AC97
  43. * codecs have non-AC97 DAIs.
  44. */
  45. if (!ac97)// 如果不是ac97设备
  46.    for (i = 0; i < card->num_links; i++) {
  47.     found = 0;
  48.     list_for_each_entry(dai, &dai_list, list)
  49.      if (card->dai_link[i].codec_dai == dai) {// 检查dai_list是否已经注册登记了.codec_dai引用到的驱动模块
  50.       found = 1; // 这里就是s3c24xx_uda134x_dai_link中的.codec_dai = &uda134x_dai
  51.       break; // 他在uda134x_init中完成注册登记.
  52.      }
  53.     if (!found) {
  54.      dev_dbg(card->dev, "DAI %s not registered\n",
  55.       card->dai_link[i].codec_dai->name);
  56.      return;
  57.     }
  58.    }

  59. /* Note that we do not current check for codec components */

  60. dev_dbg(card->dev, "All components present, instantiating\n");

  61. // ok,所有依赖的模块都已经就位,那么下面开始创建card声卡.

  62. /* Found everything, bring it up */
  63. if (card->probe) {
  64.    ret = card->probe(pdev); // 对于snd_soc_s3c24xx_uda134x没有定义probe
  65.    if (ret < 0)
  66.     return;
  67. }

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

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

  87. /* DAPM stream work */
  88. INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
  89. #ifdef CONFIG_PM
  90. /* deferred resume work */
  91. INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
  92. #endif

  93. card->instantiated = 1;

  94. return;

  95. platform_err:
  96. if (codec_dev->remove)
  97.    codec_dev->remove(pdev);

  98. cpu_dai_err:
  99. for (i--; i >= 0; i--) {
  100.    struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
  101.    if (cpu_dai->remove)
  102.     cpu_dai->remove(pdev, cpu_dai);
  103. }

  104. if (card->remove)
  105.    card->remove(pdev);
  106. }



阅读(1847) | 评论(0) | 转发(4) |
给主人留下些什么吧!~~