alsa音频架构

本文详细介绍了ASoC声卡架构的目的及其组件:snd_soc_platform、snd_soc_codec、snd_soc_dai 和 snd_soc_card 的作用。通过具体代码示例展示了如何实现这些组件之间的交互,并解释了声卡设备注册、实例化以及匹配过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


设计ASoc的目的是为嵌入式系统片上处理器音频单元或外部的音频解码芯片提供更好的ALSA支持

ASoC有多个组件组成snd_soc_platform/snd_soc_codec/snd_soc_dai/snd_soc_card以及ALSA的snd_pcm

snd_soc_platform和snd_soc_codec就行平台与设备的关系缺一不可,snd_soc_card是它们实例化的一个对象

snd_soc_dai是snd_soc_platform和snd_soc_codec的数字音频接口,snd_soc_codec的dai为codec_dai,snd_soc_platform的dai为cpu_dai

snd_pcm是snd_soc_card实例化后注册的声卡类型

在sound/soc/soc-core.c中初始化了上面提到的4个重要结构体的链表头

[cpp]   view plain copy
  1. static LIST_HEAD(card_list);  
  2. static LIST_HEAD(dai_list);  
  3. static LIST_HEAD(platform_list);  
  4. static LIST_HEAD(codec_list);  

 

第九部分 soc声卡设备snd_soc_card

1.soc声卡设备

[cpp]   view plain copy
  1. struct snd_soc_card {  
  2.     const char *name;   //设备名  
  3.     struct device *dev; //设备文件  
  4.     struct snd_card *snd_card;  //所属声卡  
  5.     struct module *owner;  
  6.     struct list_head list;  
  7.     struct mutex mutex;  
  8.     bool instantiated;  //实例化标志  
  9.     int (*probe)(struct platform_device *pdev);  
  10.     int (*remove)(struct platform_device *pdev);  
  11.     /* the pre and post PM functions are used to do any PM work before and after the codec and DAI's do any PM work. */  
  12.     int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);  
  13.     int (*suspend_post)(struct platform_device *pdev, pm_message_t state);  
  14.     int (*resume_pre)(struct platform_device *pdev);  
  15.     int (*resume_post)(struct platform_device *pdev);  
  16.     /* callbacks */  
  17.     int (*set_bias_level)(struct snd_soc_card *,enum snd_soc_bias_level level);  
  18.     long pmdown_time;  
  19.     /* CPU <--> Codec DAI links  */  
  20.     struct snd_soc_dai_link *dai_link;  //dai link  
  21.     int num_links;  
  22.     struct snd_soc_pcm_runtime *rtd;  
  23.     int num_rtd;  
  24.     struct work_struct deferred_resume_work;  
  25.     /* lists of probed devices belonging to this card */  
  26.     struct list_head codec_dev_list;  
  27.     struct list_head platform_dev_list;  
  28.     struct list_head dai_dev_list;  
  29. };  

snd_soc_card包含了snd_card,可以理解为声卡驱动的一个封装.

2.soc pcm

[cpp]   view plain copy
  1. struct snd_soc_pcm_runtime  {  
  2.     struct device dev;          //设备文件  
  3.     struct snd_soc_card *card;  //soc声卡设备  
  4.     struct snd_soc_dai_link *dai_link;  //dai link  
  5.     unsigned int complete:1;  
  6.     unsigned int dev_registered:1;  
  7.     /* Symmetry data - only valid if symmetry is being enforced */  
  8.     unsigned int rate;  
  9.     long pmdown_time;  
  10.     /* runtime devices */  
  11.     struct snd_pcm *pcm;    //pcm结构体  
  12.     struct snd_soc_codec *codec;    //codec设备  
  13.     struct snd_soc_platform *platform;  //soc平台设备  
  14.     struct snd_soc_dai *codec_dai;  //dai设备 codec  
  15.     struct snd_soc_dai *cpu_dai;    //dai设备 cpu  
  16.     struct delayed_work delayed_work;  
  17. };  

snd_soc_pcm_runtime结构体中包含一个snd_pcm结构体,所以可以认为它是pcm声卡设备的一个封装,其次他也是Asoc各个组件的一个关系网点

3.soc声卡设备的匹配过程

在sound/soc/soc-core.c中定义了一个平台设备驱动

[cpp]   view plain copy
  1. static struct platform_driver soc_driver = {  
  2.     .driver     = {  
  3.         .name       = "soc-audio",  
  4.         .owner      = THIS_MODULE,  
  5.         .pm     = &soc_pm_ops,  
  6.     },  
  7.     .probe      = soc_probe,  
  8.     .remove     = soc_remove,  
  9. };  

我们知道平台设备驱动和平台设备的匹配靠.driver.name名字,也就是在另一处代码中必须定义了平台设备platform_device且设备名必须为"soc-audio",

这样平台设备和驱动才能匹配,才会调用平台驱动的probe方法,既soc_probe

所以一般声卡的驱动程序中会按以下格式设计平台设备

[cpp]   view plain copy
  1. int ret;  
  2. static struct platform_device *xxx;  
  3. xxx=platform_device_alloc("soc-audio", 0);  //分配平台驱动  
  4. //平台资源的添加  
  5. ret=platform_device_add(xxx);   //添加平台设备  
  6. if(ret)  
  7.     platform_device_put(xxx);   //增加引用计数  

 

4.匹配调用的probe方法soc_probe

[cpp]   view plain copy
  1. static int soc_probe(struct platform_device *pdev)  
  2. {  
  3.     struct snd_soc_card *card = platform_get_drvdata(pdev); //获取soc声卡设备  
  4.     int ret = 0;  
  5.     /* Bodge while we unpick instantiation */  
  6.     card->dev = &pdev->dev;  
  7.     INIT_LIST_HEAD(&card->dai_dev_list);     //初始化dai_dev_list链表  
  8.     INIT_LIST_HEAD(&card->codec_dev_list);       //初始化codec_dev_list链表  
  9.     INIT_LIST_HEAD(&card->platform_dev_list);    //初始化platform_dev_list链表  
  10.     ret = snd_soc_register_card(card);  //注册soc声卡设备  
  11.     if (ret != 0) {  
  12.         dev_err(&pdev->dev, "Failed to register card\n");  
  13.         return ret;  
  14.     }  
  15.     return 0;  
  16. }  

这里调用了snd_soc_register_card注册了soc声卡设备

5.注册soc声卡设备

[cpp]   view plain copy
  1. static int snd_soc_register_card(struct snd_soc_card *card)  
  2. {  
  3.     int i;  
  4.     if (!card->name || !card->dev)  
  5.         return -EINVAL;  
  6.     card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * card->num_links,GFP_KERNEL); //分配多个soc pcm内存  
  7.     if (card->rtd == NULL)  
  8.         return -ENOMEM;  
  9.     for (i = 0; i < card->num_links; i++)  
  10.         card->rtd[i].dai_link = &card->dai_link[i];   //dai link数组  
  11.     INIT_LIST_HEAD(&card->list);   
  12.     card->instantiated = 0;  //soc声卡实例化标志设置为0  
  13.     mutex_init(&card->mutex);  
  14.   
  15.     mutex_lock(&client_mutex);  
  16.     list_add(&card->list, &card_list);   //添加soc声卡到全局card_list链表  
  17.     snd_soc_instantiate_cards();    //实例化所有soc声卡  
  18.     mutex_unlock(&client_mutex);  
  19.     dev_dbg(card->dev, "Registered card '%s'\n", card->name);  
  20.     return 0;  
  21. }  




最终调用snd_soc_instantiate_cards实例化所有声卡

 

第十部分 soc平台 snd_soc_platform

1.soc平台设备

[cpp]   view plain copy
  1. struct snd_soc_platform {  
  2.     const char *name;   //设备名  
  3.     int id; //设备id  
  4.     struct device *dev; //设备文件  
  5.     struct snd_soc_platform_driver *driver; //soc平台驱动  
  6.     unsigned int suspended:1; /* platform is suspended */  
  7.     unsigned int probed:1;  //"probe"标志  
  8.     struct snd_soc_card *card;  //soc声卡设备  
  9.     struct list_head list;  
  10.     struct list_head card_list;  
  11. };  

2.soc平台驱动

[cpp]   view plain copy
  1. struct snd_soc_platform_driver {  
  2.     int (*probe)(struct snd_soc_platform *);  
  3.     int (*remove)(struct snd_soc_platform *);  
  4.     int (*suspend)(struct snd_soc_dai *dai);  
  5.     int (*resume)(struct snd_soc_dai *dai);  
  6.     /* pcm creation and destruction */  
  7.     int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,struct snd_pcm *);  
  8.     void (*pcm_free)(struct snd_pcm *);  
  9.     snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,struct snd_soc_dai *);  
  10.     /* platform stream ops */  
  11.     struct snd_pcm_ops *ops;  
  12. };  

3.注册soc平台驱动

[cpp]   view plain copy
  1. int snd_soc_register_platform(struct device *dev,struct snd_soc_platform_driver *platform_drv)  
  2. {  
  3.     struct snd_soc_platform *platform;  //声明soc平台设备  
  4.     dev_dbg(dev, "platform register %s\n", dev_name(dev));  
  5.     platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);    //分配soc平台内存  
  6.     if (platform == NULL)  
  7.             return -ENOMEM;  
  8.     /* create platform component name */  
  9.     platform->name = fmt_single_name(dev, &platform->id); //设置soc平台设备名及id  
  10.     if (platform->name == NULL) {  
  11.         kfree(platform);  
  12.         return -ENOMEM;  
  13.     }  
  14.     platform->dev = dev; //设备文件  
  15.     platform->driver = platform_drv; //捆绑soc平台设备驱动  
  16.     mutex_lock(&client_mutex);  
  17.     list_add(&platform->list, &platform_list);   //添加到全局platform_list链表  
  18.     snd_soc_instantiate_cards();    //实例化所有soc声卡设备  
  19.     mutex_unlock(&client_mutex);  
  20.     pr_debug("Registered platform '%s'\n", platform->name);  
  21.     return 0;  
  22. }  
  23. EXPORT_SYMBOL_GPL(snd_soc_register_platform);  

注册soc平台驱动需要驱动自己去调用snd_soc_register_platform注册.


最终调用snd_soc_instantiate_cards实例化所有声卡

4.注销soc平台驱动

[cpp]   view plain copy
  1. void snd_soc_unregister_platform(struct device *dev)  
  2. {  
  3.     struct snd_soc_platform *platform;  
  4.     list_for_each_entry(platform, &platform_list, list) {   //遍历全局platform_list链表  
  5.         if (dev == platform->dev)    //查找到匹配的soc平台  
  6.             goto found;  
  7.     }  
  8.     return;  
  9.       
  10. found:  
  11.     mutex_lock(&client_mutex);  
  12.     list_del(&platform->list);   //移除链表  
  13.     mutex_unlock(&client_mutex);  
  14.     pr_debug("Unregistered platform '%s'\n", platform->name);  
  15.     kfree(platform->name);  
  16.     kfree(platform);  
  17. }  
  18. EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);  


 

第十一部分 codec设备 snd_soc_codec

1.codec设备结构体

[cpp]   view plain copy
  1. struct snd_soc_codec {  
  2.     const char *name;   //设备名  
  3.     int id;             //设备id号  
  4.     struct device *dev; //设备文件  
  5.     struct snd_soc_codec_driver *driver;    //所属的codec驱动  
  6.     struct mutex mutex;  
  7.     struct snd_soc_card *card;  //soc声卡设备  
  8.     struct list_head list;  
  9.     struct list_head card_list;  
  10.     int num_dai;    //dai设备(codec_dai)个数  
  11.     /* runtime */  
  12.     struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */  
  13.     unsigned int active;  
  14.     unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */  
  15.     unsigned int cache_only:1;  /* Suppress writes to hardware */  
  16.     unsigned int cache_sync:1; /* Cache needs to be synced to hardware */  
  17.     unsigned int suspended:1; /* Codec is in suspend PM state */  
  18.     unsigned int probed:1; //"probe"标志  
  19.     unsigned int ac97_registered:1; /* Codec has been AC97 registered */  
  20.     unsigned int ac97_created:1; /* Codec has been created by SoC */  
  21.     unsigned int sysfs_registered:1; /* codec has been sysfs registered */  
  22.     /* codec IO */  
  23.     void *control_data; /* codec control (i2c/3wire) data */  
  24.     hw_write_t hw_write;  
  25.     unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);  
  26.     void *reg_cache;    //cache  
  27.     /* dapm */  
  28.     u32 pop_time;  
  29.     struct list_head dapm_widgets;  
  30.     struct list_head dapm_paths;  
  31.     enum snd_soc_bias_level bias_level;  
  32.     enum snd_soc_bias_level suspend_bias_level;  
  33.     struct delayed_work delayed_work;  
  34. #ifdef CONFIG_DEBUG_FS  
  35.     struct dentry *debugfs_codec_root;  
  36.     struct dentry *debugfs_reg;  
  37.     struct dentry *debugfs_pop_time;  
  38.     struct dentry *debugfs_dapm;  
  39. #endif  
  40. };  

2.codec驱动结构体

[cpp]   view plain copy
  1. struct snd_soc_codec_driver {  
  2.     /* driver ops */  
  3.     int (*probe)(struct snd_soc_codec *);  
  4.     int (*remove)(struct snd_soc_codec *);  
  5.     int (*suspend)(struct snd_soc_codec *,pm_message_t state);  
  6.     int (*resume)(struct snd_soc_codec *);  
  7.     /* codec IO */  
  8.     unsigned int (*read)(struct snd_soc_codec *, unsigned int);  
  9.     int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);  
  10.     int (*display_register)(struct snd_soc_codec *, char *,size_t, unsigned int);  
  11.     int (*volatile_register)(unsigned int);  
  12.     int (*readable_register)(unsigned int);  
  13.     short reg_cache_size;  
  14.     short reg_cache_step;  
  15.     short reg_word_size;  
  16.     const void *reg_cache_default;  
  17.     /* codec bias level */  
  18.     int (*set_bias_level)(struct snd_soc_codec *,enum snd_soc_bias_level level);  
  19. };  


3.注册codec驱动

[cpp]   view plain copy
  1. int snd_soc_register_codec(struct device *dev,struct snd_soc_codec_driver *codec_drv,struct snd_soc_dai_driver *dai_drv, int num_dai)  
  2. {  
  3.     struct snd_soc_codec *codec;    //声明codec设备  
  4.     int ret, i;  
  5.     dev_dbg(dev, "codec register %s\n", dev_name(dev));  
  6.     codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);  //分配codec设备内存  
  7.     if (codec == NULL)  
  8.         return -ENOMEM;  
  9.     /* create CODEC component name */  
  10.     codec->name = fmt_single_name(dev, &codec->id);   //创建codec设备名及id号  
  11.     if (codec->name == NULL) {  
  12.         kfree(codec);  
  13.         return -ENOMEM;  
  14.     }  
  15.     /* allocate CODEC register cache */ //cache设置  
  16.     if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {  
  17.         if (codec_drv->reg_cache_default)  
  18.             codec->reg_cache = kmemdup(codec_drv->reg_cache_default,codec_drv->reg_cache_size * codec_drv->reg_word_size, GFP_KERNEL);  
  19.         else  
  20.             codec->reg_cache = kzalloc(codec_drv->reg_cache_size *codec_drv->reg_word_size, GFP_KERNEL);  
  21.         if (codec->reg_cache == NULL) {  
  22.             kfree(codec->name);  
  23.             kfree(codec);  
  24.             return -ENOMEM;  
  25.         }  
  26.     }  
  27.     codec->dev = dev;    //捆绑设备文件  
  28.     codec->driver = codec_drv;   //捆绑codec驱动  
  29.     codec->bias_level = SND_SOC_BIAS_OFF;  
  30.     codec->num_dai = num_dai;    //dai设备个数  
  31.     mutex_init(&codec->mutex);  
  32.     INIT_LIST_HEAD(&codec->dapm_widgets);  
  33.     INIT_LIST_HEAD(&codec->dapm_paths);  
  34.     for (i = 0; i < num_dai; i++) {  //格式化dai设备驱动的playback和capture PCM流  
  35.         fixup_codec_formats(&dai_drv[i].playback);  
  36.         fixup_codec_formats(&dai_drv[i].capture);  
  37.     }  
  38.     /* register any DAIs */  
  39.     if (num_dai) {  
  40.         ret = snd_soc_register_dais(dev, dai_drv, num_dai); //注册dai设备驱动  
  41.         if (ret < 0)  
  42.             goto error;  
  43.     }  
  44.     mutex_lock(&client_mutex);  
  45.     list_add(&codec->list, &codec_list); //添加进全局codec_list链表  
  46.     snd_soc_instantiate_cards();    //实例化所有soc声卡设备  
  47.     mutex_unlock(&client_mutex);  
  48.     pr_debug("Registered codec '%s'\n", codec->name);  
  49.     return 0;  
  50. error:  
  51.     if (codec->reg_cache)  
  52.         kfree(codec->reg_cache);  
  53.     kfree(codec->name);  
  54.     kfree(codec);  
  55.     return ret;  
  56. }  
  57. EXPORT_SYMBOL_GPL(snd_soc_register_codec);  


 

这里调用了snd_soc_register_dais注册了几个dai(codec_dai),后面会解释

最终调用snd_soc_instantiate_cards实例化所有声卡

4.注销codec驱动

[cpp]   view plain copy
  1. void snd_soc_unregister_codec(struct device *dev)  
  2. {  
  3.     struct snd_soc_codec *codec;  
  4.     int i;  
  5.     list_for_each_entry(codec, &codec_list, list) { //遍历全局codec_list链表  
  6.         if (dev == codec->dev)   //查找到匹配的codec设备  
  7.             goto found;  
  8.     }  
  9.     return;  
  10.   
  11. found:  
  12.     if (codec->num_dai)  
  13.         for (i = 0; i < codec->num_dai; i++)  //注销codec设备下的dai设备(codec_dai)  
  14.             snd_soc_unregister_dai(dev);  
  15.     mutex_lock(&client_mutex);  
  16.     list_del(&codec->list);  //移除链表  
  17.     mutex_unlock(&client_mutex);  
  18.     pr_debug("Unregistered codec '%s'\n", codec->name);  
  19.     if (codec->reg_cache)  
  20.         kfree(codec->reg_cache);  
  21.     kfree(codec->name);  
  22.     kfree(codec);  
  23. }  
  24. EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);  


第十二部分 dai数字音频接口(dai声卡设备) snd_soc_dai

1.dai声卡设备(数字音频接口)

[cpp]   view plain copy
  1. struct snd_soc_dai {  
  2.     const char *name;   //设备名  
  3.     int id;             //设备id  
  4.     struct device *dev; //设备文件  
  5.     void *ac97_pdata;   /* platform_data for the ac97 codec */  
  6.     /* driver ops */  
  7.     struct snd_soc_dai_driver *driver;  //所属dai声卡驱动  
  8.     /* DAI runtime info */  
  9.     unsigned int capture_active:1;      /* stream is in use */  
  10.     unsigned int playback_active:1;     /* stream is in use */  
  11.     unsigned int symmetric_rates:1;  
  12.     struct snd_pcm_runtime *runtime;    //pcm runtime  
  13.     unsigned int active;  
  14.     unsigned char pop_wait:1;  
  15.     unsigned char probed:1; //"probe"标志  
  16.     /* DAI DMA data */  
  17.     void *playback_dma_data;  
  18.     void *capture_dma_data;  
  19.     /* parent platform/codec */  
  20.     union {  
  21.         struct snd_soc_platform *platform;  //platform设备  
  22.         struct snd_soc_codec *codec;    //codec设备  
  23.     };  
  24.     struct snd_soc_card *card;  //soc声卡设备  
  25.     struct list_head list;  
  26.     struct list_head card_list;  
  27. };  

2.dai声卡驱动

[cpp]   view plain copy
  1. struct snd_soc_dai_driver {  
  2.     /* DAI description */  
  3.     const char *name;   //名字  
  4.     unsigned int id;    //设备id  
  5.     int ac97_control;  
  6.     /* DAI driver callbacks */  
  7.     int (*probe)(struct snd_soc_dai *dai);  
  8.     int (*remove)(struct snd_soc_dai *dai);  
  9.     int (*suspend)(struct snd_soc_dai *dai);  
  10.     int (*resume)(struct snd_soc_dai *dai);  
  11.     /* ops */  
  12.     struct snd_soc_dai_ops *ops;    //dai操作函数集  
  13.     /* DAI capabilities */  
  14.     struct snd_soc_pcm_stream capture;  //soc pcm流-捕获  
  15.     struct snd_soc_pcm_stream playback; //soc pcm流-回放  
  16.     unsigned int symmetric_rates:1;  
  17. };  

3.dai link

[cpp]   view plain copy
  1. struct snd_soc_dai_link {  
  2.     /* config - must be set by machine driver */  
  3.     const char *name;           /* Codec name */  
  4.     const char *stream_name;        /* Stream name */  
  5.     const char *codec_name;     /* for multi-codec */  
  6.     const char *platform_name;  /* for multi-platform */  
  7.     const char *cpu_dai_name;  
  8.     const char *codec_dai_name;  
  9.     /* Keep DAI active over suspend */  
  10.     unsigned int ignore_suspend:1;  
  11.     /* Symmetry requirements */  
  12.     unsigned int symmetric_rates:1;  
  13.     /* codec/machine specific init - e.g. add machine controls */  
  14.     int (*init)(struct snd_soc_pcm_runtime *rtd);  
  15.     /* machine stream operations */  
  16.     struct snd_soc_ops *ops;    //soc操作函数集  
  17. };  

3.注册若干个dai驱动

[cpp]   view plain copy
  1. int snd_soc_register_dais(struct device *dev,struct snd_soc_dai_driver *dai_drv, size_t count)  
  2. {  
  3.     struct snd_soc_dai *dai;    //dai设备声明  
  4.     int i, ret = 0;  
  5.   
  6.     dev_dbg(dev, "dai register %s #%Zu\n", dev_name(dev), count);  
  7.     for (i = 0; i < count; i++) {    //dai声卡设备个数  
  8.         dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);  //分配内存  
  9.         if (dai == NULL) {  
  10.             ret = -ENOMEM;  
  11.             goto err;  
  12.         }  
  13.         /* create DAI component name */  
  14.         dai->name = fmt_multiple_name(dev, &dai_drv[i]); //格式化dai声卡设备名  
  15.         if (dai->name == NULL) {  
  16.             kfree(dai);  
  17.             ret = -EINVAL;  
  18.             goto err;  
  19.         }  
  20.         dai->dev = dev;  //设备文件  
  21.         dai->driver = &dai_drv[i];   //捆绑dai声卡驱动  
  22.         if (dai->driver->id)  //设置dai声卡设备id  
  23.             dai->id = dai->driver->id;  
  24.         else  
  25.             dai->id = i;  
  26.         if (!dai->driver->ops)    //若dai声卡驱动未指定dai操作函数集  
  27.             dai->driver->ops = &null_dai_ops; //则使用默认的空函数集  
  28.         mutex_lock(&client_mutex);  
  29.         list_add(&dai->list, &dai_list); //添加dai设备到全局dai_list链表  
  30.         mutex_unlock(&client_mutex);  
  31.         pr_debug("Registered DAI '%s'\n", dai->name);  
  32.     }  
  33.     mutex_lock(&client_mutex);  
  34.     snd_soc_instantiate_cards();    //实例化所有soc声卡设备  
  35.     mutex_unlock(&client_mutex);  
  36.     return 0;  
  37.   
  38. err:  
  39.     for (i--; i >= 0; i--)  
  40.         snd_soc_unregister_dai(dev);  
  41.     return ret;  
  42. }  
  43. EXPORT_SYMBOL_GPL(snd_soc_register_dais);  




 4.注销若干个dai驱动

[cpp]   view plain copy
  1. void snd_soc_unregister_dais(struct device *dev, size_t count)  
  2. {  
  3.     int i;  
  4.   
  5.     for (i = 0; i < count; i++)  
  6.         snd_soc_unregister_dai(dev);    //注销dai设备  
  7. }  
  8. EXPORT_SYMBOL_GPL(snd_soc_unregister_dais);  

5.注册一个dai驱动

[cpp]   view plain copy
  1. int snd_soc_register_dai(struct device *dev,struct snd_soc_dai_driver *dai_drv)  
  2. {  
  3.     struct snd_soc_dai *dai;  
  4.     dev_dbg(dev, "dai register %s\n", dev_name(dev));  
  5.     dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);  //分配dai设备内存  
  6.     if (dai == NULL)  
  7.             return -ENOMEM;  
  8.     /* create DAI component name */  
  9.     dai->name = fmt_single_name(dev, &dai->id);   //设置名字  
  10.     if (dai->name == NULL) {  
  11.         kfree(dai);  
  12.         return -ENOMEM;  
  13.     }  
  14.     dai->dev = dev;  //设备文件  
  15.     dai->driver = dai_drv;   //捆绑dai驱动  
  16.     if (!dai->driver->ops)    //若为指定dai驱动操作函数集  
  17.         dai->driver->ops = &null_dai_ops; //则设置默认函数集  
  18.     mutex_lock(&client_mutex);  
  19.     list_add(&dai->list, &dai_list); //添加进全局dai_list链表  
  20.     snd_soc_instantiate_cards();    //实例化所有soc声卡设备  
  21.     mutex_unlock(&client_mutex);  
  22.     pr_debug("Registered DAI '%s'\n", dai->name);  
  23.     return 0;  
  24. }  
  25. EXPORT_SYMBOL_GPL(snd_soc_register_dai);  

6.注销一个dai设备

[cpp]   view plain copy
  1. void snd_soc_unregister_dai(struct device *dev)  
  2. {  
  3.     struct snd_soc_dai *dai;  
  4.     list_for_each_entry(dai, &dai_list, list) { //遍历全局dai_list链表  
  5.         if (dev == dai->dev) //查找到匹配的dai设备  
  6.             goto found;  
  7.     }  
  8.     return;  
  9.   
  10. found:  
  11.     mutex_lock(&client_mutex);  
  12.     list_del(&dai->list);    //移除链表  
  13.     mutex_unlock(&client_mutex);  
  14.   
  15.     pr_debug("Unregistered DAI '%s'\n", dai->name);  
  16.     kfree(dai->name);  
  17.     kfree(dai);  
  18. }  
  19. EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);  


 一般的说snd_soc_register_dais是codec设备注册的时候调用的,用来注册dai设备(codec_dai类型)

而snd_soc_register_dai是驱动自己调用注册的,用来注册dai设备(一般是cpu_dai类型)

 不管是注册若干个或是一个,最终调用snd_soc_instantiate_cards实例化所有声卡

 

 第十三部分 soc声卡实例化

前面注册各种组件的时候,都会调用到snd_soc_instantiate_cards实例化所有声卡

1.snd_soc_instantiate_cards 实例化所有soc声卡

[cpp]   view plain copy
  1. static void snd_soc_instantiate_cards(void//实例化所有soc声卡设备  
  2. {  
  3.     struct snd_soc_card *card;  
  4.     list_for_each_entry(card, &card_list, list) //遍历声卡全局链表card_list  
  5.         snd_soc_instantiate_card(card); //实例化soc声卡设备  
  6. }  

遍历全局链表,实例化soc声卡设备,链表项是在注册soc声卡的时候添加进去的

2.snd_soc_instantiate_card 实例化一个soc声卡

[cpp]   view plain copy
  1. static void snd_soc_instantiate_card(struct snd_soc_card *card) //实例化soc声卡设备  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(card->dev);    //获取平台设备  
  4.     int ret, i;  
  5.     mutex_lock(&card->mutex);  
  6.     if (card->instantiated) {    //调用soc声卡设备已经实例化(instantiated标志为1)  
  7.         mutex_unlock(&card->mutex);  
  8.         return;  
  9.     }  
  10.     /* bind DAIs */  
  11.     for (i = 0; i < card->num_links; i++)  
  12.         soc_bind_dai_link(card, i); //绑定cpu_dai/codec_dai/codec/platform  
  13.     /* bind completed ? */  
  14.     if (card->num_rtd != card->num_links) {   //所有的都绑定好了?  
  15.         mutex_unlock(&card->mutex);  
  16.         return;  
  17.     }  
  18.     /* card bind complete so register a sound card */  
  19.     ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card);    //创建声卡  
  20.     if (ret < 0) {  
  21.         printk(KERN_ERR "asoc: can't create sound card for card %s\n",card->name);  
  22.         mutex_unlock(&card->mutex);  
  23.         return;  
  24.     }  
  25.     card->snd_card->dev = card->dev;   //设备文件  
  26. #ifdef CONFIG_PM  
  27.     /* deferred resume work */  
  28.     INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);  
  29. #endif  
  30.     /* initialise the sound card only once */  
  31.     if (card->probe) {   //soc声卡设备存在probe方法  
  32.         ret = card->probe(pdev); //则调用其probe方法  
  33.         if (ret < 0)  
  34.             goto card_probe_error;  
  35.     }  
  36.     for (i = 0; i < card->num_links; i++) {  
  37.         ret = soc_probe_dai_link(card, i);  //调用dai的probe方法  
  38.         if (ret < 0) {  
  39.             pr_err("asoc: failed to instantiate card %s: %d\n",card->name, ret);  
  40.             goto probe_dai_err;  
  41.         }  
  42.     }  
  43.     snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),"%s",  card->name);  
  44.     snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),"%s", card->name);  
  45.     ret = snd_card_register(card->snd_card); //注册声卡  
  46.     if (ret < 0) {  
  47.         printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);  
  48.         goto probe_dai_err;  
  49.     }  
  50. #ifdef CONFIG_SND_SOC_AC97_BUS  
  51.     /* register any AC97 codecs */  
  52.     for (i = 0; i < card->num_rtd; i++) {  
  53.         ret = soc_register_ac97_dai_link(&card->rtd[i]);  
  54.         if (ret < 0) {  
  55.             printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name);  
  56.             while (--i >= 0)  
  57.                 soc_unregister_ac97_dai_link(&card->rtd[i]);  
  58.             goto probe_dai_err;  
  59.         }  
  60.     }  
  61. #endif  
  62.     card->instantiated = 1;  //实例化标志置1  
  63.     mutex_unlock(&card->mutex);  
  64.     return;  
  65. probe_dai_err:  
  66.     for (i = 0; i < card->num_links; i++)  
  67.         soc_remove_dai_link(card, i);  
  68. card_probe_error:  
  69.     if (card->remove)  
  70.         card->remove(pdev);  
  71.     snd_card_free(card->snd_card);  
  72.     mutex_unlock(&card->mutex);  
  73. }  

该函数用到 前面一篇文章(alsa音频架构1)中讲到的 snd_card_create创建声卡,snd_card_register注册声卡

还没看见创建声卡设备哦,注册声卡的时候会调用snd_device_register_all注册声卡设备,所以声卡设备的创建应该在snd_card_registerr之前

这里还调用了几个重要的函数,下面分别解析下

3.soc_bind_dai_link 绑定cpu_dai/codec_dai/codec/platform

[cpp]   view plain copy
  1. static int soc_bind_dai_link(struct snd_soc_card *card, int num)  
  2. {  
  3.     struct snd_soc_dai_link *dai_link = &card->dai_link[num];    //获取dai link  
  4.     struct snd_soc_pcm_runtime *rtd = &card->rtd[num];   //获取soc pcm  
  5.     struct snd_soc_codec *codec;  
  6.     struct snd_soc_platform *platform;  
  7.     struct snd_soc_dai *codec_dai, *cpu_dai;  
  8.   
  9.     if (rtd->complete)  
  10.         return 1;  
  11.     dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);  
  12.     /* do we already have the CPU DAI for this link ? */  
  13.     if (rtd->cpu_dai) {  //若dai设备(cpu_dai)已经设置了  
  14.         goto find_codec;    //则直接查找dai设备(codec_dai)  
  15.     }  
  16.     /* no, then find CPU DAI from registered DAIs*/  
  17.     list_for_each_entry(cpu_dai, &dai_list, list) { //遍历全局dai_list链表  
  18.         if (!strcmp(cpu_dai->name, dai_link->cpu_dai_name)) { //查找符合的cpu_dai设备  
  19.             if (!try_module_get(cpu_dai->dev->driver->owner))  
  20.                 return -ENODEV;  
  21.             rtd->cpu_dai = cpu_dai;  //查找到匹配,则设置soc pcm的dai设备(cpu_dai)  
  22.             goto find_codec;    //跳转查找dai设备(codec_dai)  
  23.         }  
  24.     }  
  25.     dev_dbg(card->dev, "CPU DAI %s not registered\n",dai_link->cpu_dai_name);  
  26.   
  27. find_codec: //查找dai设备(codec_dai)  
  28.     /* do we already have the CODEC for this link ? */  
  29.     if (rtd->codec) {    //若dai设备(codec_dai)已经设置了  
  30.         goto find_platform; //则直接查找dai设备(cpu_dai)  
  31.     }  
  32.     /* no, then find CODEC from registered CODECs*/  
  33.     list_for_each_entry(codec, &codec_list, list) { //遍历全局codec_list链表  
  34.         if (!strcmp(codec->name, dai_link->codec_name)) { //查找符合的codec设备  
  35.             rtd->codec = codec;  //soc pcm捆绑codec设备  
  36.             if (!try_module_get(codec->dev->driver->owner))  
  37.                 return -ENODEV;  
  38.             /* CODEC found, so find CODEC DAI from registered DAIs from this CODEC*/  
  39.             list_for_each_entry(codec_dai, &dai_list, list) {   //遍历全局dai_list链表  
  40.                 if (codec->dev == codec_dai->dev &&!strcmp(codec_dai->name, dai_link->codec_dai_name)) {    //查找符合的dai设备(codec_dai)  
  41.                     rtd->codec_dai = codec_dai;  //查找到匹配,则设置soc pcm的dai设备(codec_dai)  
  42.                     goto find_platform;  
  43.                 }  
  44.             }  
  45.             dev_dbg(card->dev, "CODEC DAI %s not registered\n",dai_link->codec_dai_name);  
  46.             goto find_platform;  
  47.         }  
  48.     }  
  49.     dev_dbg(card->dev, "CODEC %s not registered\n",dai_link->codec_name);  
  50.   
  51. find_platform:  //查找soc平台  
  52.     /* do we already have the CODEC DAI for this link ? */  
  53.     if (rtd->platform) {   
  54.         goto out;  
  55.     }  
  56.     /* no, then find CPU DAI from registered DAIs*/  
  57.     list_for_each_entry(platform, &platform_list, list) {   //遍历全局platform_list链表  
  58.         if (!strcmp(platform->name, dai_link->platform_name)) {   //查找符合的soc平台  
  59.             if (!try_module_get(platform->dev->driver->owner))  
  60.                 return -ENODEV;  
  61.             rtd->platform = platform;    //pcm pcm捆绑soc平台  
  62.             goto out;  
  63.         }  
  64.     }  
  65.     dev_dbg(card->dev, "platform %s not registered\n",dai_link->platform_name);  
  66.     return 0;  
  67.   
  68. out:  
  69.     /* mark rtd as complete if we found all 4 of our client devices */  
  70.     if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) {    //四种设备都设置齐全了  
  71.         rtd->complete = 1;   //设置soc pcm完成标志complete为1  
  72.         card->num_rtd++;  
  73.     }  
  74.     return 1;  
  75. }  


 

 4.soc_probe_dai_link  "probe" cpu_dai/codec_dai/codec/platform各个组件

[cpp]   view plain copy
  1. static int soc_probe_dai_link(struct snd_soc_card *card, int num)  
  2. {  
  3.     struct snd_soc_dai_link *dai_link = &card->dai_link[num];    //获取dai link  
  4.     struct snd_soc_pcm_runtime *rtd = &card->rtd[num];   //获取soc pcm  
  5.     struct snd_soc_codec *codec = rtd->codec;    //获取codec设备  
  6.     struct snd_soc_platform *platform = rtd->platform;   //获取soc平台  
  7.     struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;//获取dai设备数组(codec_dai/cpu_dai)  
  8.     int ret;  
  9. //___________________________________捆绑多种设备关系_________________________________________//  
  10.     dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num);  
  11.     /* config components */  
  12.     codec_dai->codec = codec;        //dai设备(codec_dai)捆绑codec设备  
  13.     codec->card = card;              //codec设备捆绑soc声卡设备  
  14.     cpu_dai->platform = platform;    //dai设备(cpu_dai)捆绑soc平台  
  15.     rtd->card = card;                //soc pcm捆绑soc声卡设备  
  16.     rtd->dev.parent = card->dev;    
  17.     codec_dai->card = card;          //dai设备(codec_dai)捆绑soc声卡设备  
  18.     cpu_dai->card = card;            //dai设备(cpu_dai)捆绑soc声卡设备  
  19.     /* set default power off timeout */  
  20.     rtd->pmdown_time = pmdown_time;  
  21. //___________________________________probe多种设备_____________________________________________//         
  22.     /* probe the cpu_dai */ //------------"probe" dai(cpu_dai)设备  
  23.     if (!cpu_dai->probed) {  //dai设备(cpu_dai)probed标志为0  
  24.         if (cpu_dai->driver->probe) { //若dai声卡驱动存在probe方法  
  25.             ret = cpu_dai->driver->probe(cpu_dai);    //则调用其probe方法  
  26.             if (ret < 0) {  
  27.                 printk(KERN_ERR "asoc: failed to probe CPU DAI %s\n",cpu_dai->name);  
  28.                 return ret;  
  29.             }  
  30.         }  
  31.         cpu_dai->probed = 1; //设置dai设备(cpu_dai)probed标志  
  32.         /* mark cpu_dai as probed and add to card cpu_dai list */  
  33.         list_add(&cpu_dai->card_list, &card->dai_dev_list);   //添加dai设备(cpu_dai)到soc声卡设备的dai_dev_list链表  
  34.     }  
  35.     /* probe the CODEC */   //------------"probe" codec设备  
  36.     if (!codec->probed) {    //codec设备的probed标志为0  
  37.         if (codec->driver->probe) {   //codec驱动存在probe方法  
  38.             ret = codec->driver->probe(codec);    //则调用其probe方法  
  39.             if (ret < 0) {  
  40.                 printk(KERN_ERR "asoc: failed to probe CODEC %s\n",codec->name);  
  41.                 return ret;  
  42.             }  
  43.         }  
  44.         soc_init_codec_debugfs(codec);  //初始化codec  
  45.         /* mark codec as probed and add to card codec list */  
  46.         codec->probed = 1;   //codec设备的probed标志置1  
  47.         list_add(&codec->card_list, &card->codec_dev_list);   //添加codec设备到soc声卡设备的codec_dev_list链表  
  48.     }  
  49.     /* probe the platform */    //------------"probe" platform设备  
  50.     if (!platform->probed) { //platform设备的probed标志位0  
  51.         if (platform->driver->probe) {    //platform设备驱动存在probe方法  
  52.             ret = platform->driver->probe(platform);  //则调用其probe方法  
  53.             if (ret < 0) {  
  54.                 printk(KERN_ERR "asoc: failed to probe platform %s\n",platform->name);  
  55.                 return ret;  
  56.             }  
  57.         }  
  58.         /* mark platform as probed and add to card platform list */  
  59.         platform->probed = 1;    //platform设备probed标志置1  
  60.         list_add(&platform->card_list, &card->platform_dev_list); //添加platform设备到soc声卡设备的platform_dev_list链表  
  61.     }  
  62.     /* probe the CODEC DAI */   //------------"probe" dai(codec_dai)设备  
  63.     if (!codec_dai->probed) {    //dai设备(codec_dai)probed标志为0  
  64.         if (codec_dai->driver->probe) {   //dai设备驱动存在probe方法  
  65.             ret = codec_dai->driver->probe(codec_dai);    //则调用其probe方法  
  66.             if (ret < 0) {  
  67.                 printk(KERN_ERR "asoc: failed to probe CODEC DAI %s\n",codec_dai->name);  
  68.                 return ret;  
  69.             }  
  70.         }  
  71.         /* mark cpu_dai as probed and add to card cpu_dai list */  
  72.         codec_dai->probed = 1;   //设置dai设备(codec_dai)probed标志  
  73.         list_add(&codec_dai->card_list, &card->dai_dev_list); //添加dai设备(codec_dai)到soc声卡设备的dai_dev_list链表  
  74.     }  
  75. //_____________________________________________________________________________________//     
  76.     /* DAPM dai link stream work */  
  77.     INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);   //初始化工作队列  
  78.     /* now that all clients have probed, initialise the DAI link */  
  79.     if (dai_link->init) {    //dai link存在init方法  
  80.         ret = dai_link->init(rtd);   //则调用其init方法  
  81.         if (ret < 0) {  
  82.             printk(KERN_ERR "asoc: failed to init %s\n", dai_link->stream_name);  
  83.             return ret;  
  84.         }  
  85.     }  
  86.     /* Make sure all DAPM widgets are instantiated */  
  87.     snd_soc_dapm_new_widgets(codec);  
  88.     snd_soc_dapm_sync(codec);  
  89.     /* register the rtd device */  
  90.     rtd->dev.release = rtd_release;  
  91.     rtd->dev.init_name = dai_link->name;  
  92.     ret = device_register(&rtd->dev);    //注册soc pcm设备文件  
  93.     if (ret < 0) {  
  94.         printk(KERN_ERR "asoc: failed to register DAI runtime device %d\n", ret);  
  95.         return ret;  
  96.     }  
  97.     rtd->dev_registered = 1;  
  98.     ret = device_create_file(&rtd->dev, &dev_attr_pmdown_time);  //创建soc pcm设备文件  
  99.     if (ret < 0)  
  100.         printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n");  
  101.     /* add DAPM sysfs entries for this codec */  
  102.     ret = snd_soc_dapm_sys_add(&rtd->dev);  
  103.     if (ret < 0)  
  104.         printk(KERN_WARNING "asoc: failed to add codec dapm sysfs entries\n");  
  105.     /* add codec sysfs entries */  
  106.     ret = device_create_file(&rtd->dev, &dev_attr_codec_reg);    //创建soc pcm设备属性文件  
  107.     if (ret < 0)  
  108.         printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");  
  109.     /* create the pcm */  
  110.     ret = soc_new_pcm(rtd, num);    //创建新pcm设备  
  111.     if (ret < 0) {  
  112.         printk(KERN_ERR "asoc: can't create pcm %s\n", dai_link->stream_name);  
  113.         return ret;  
  114.     }  
  115.     /* add platform data for AC97 devices */  
  116.     if (rtd->codec_dai->driver->ac97_control)  
  117.         snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata);  
  118.     return 0;  
  119. }  


 

 果真在soc_probe_dai_link函数中(创建声卡和注册声卡之间)调用了 soc_new_pcm创建了声卡设备

4.1.soc_new_pcm

[cpp]   view plain copy
  1. static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)  
  2. {  
  3.     struct snd_soc_codec *codec = rtd->codec;    //获取codec设备  
  4.     struct snd_soc_platform *platform = rtd->platform;   //获取soc平台  
  5.     struct snd_soc_dai *codec_dai = rtd->codec_dai;  //获取dai(codec_dai)设备  
  6.     struct snd_soc_dai *cpu_dai = rtd->cpu_dai;  //获取dai(cpu_dai)设备  
  7.     struct snd_pcm *pcm;    //pcm结构体声明  
  8.     char new_name[64];  
  9.     int ret = 0, playback = 0, capture = 0;  
  10.   
  11.     /* check client and interface hw capabilities */  
  12.     snprintf(new_name, sizeof(new_name), "%s %s-%d",rtd->dai_link->stream_name, codec_dai->name, num);  
  13.     if (codec_dai->driver->playback.channels_min) //最小通道数不为0也就是存在回放通道  
  14.         playback = 1;     //则将其通道数设置为1  
  15.     if (codec_dai->driver->capture.channels_min)  //最小通道数不为0也就是存在捕捉通道  
  16.         capture = 1;      //则将其通道数设置为1  
  17.     dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);  
  18.     ret = snd_pcm_new(rtd->card->snd_card, new_name,num, playback, capture, &pcm);    //创建pcm声卡设备  
  19.     if (ret < 0) {  
  20.         printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);  
  21.         return ret;  
  22.     }  
  23.     rtd->pcm = pcm;  //捆绑soc pcm和pcm结构体  
  24.     pcm->private_data = rtd; //soc pcm放在pcm结构体的私有数据段  
  25.     //soc_pcm_ops部分方法函数集设置为soc平台驱动对应的方法  
  26.     soc_pcm_ops.mmap = platform->driver->ops->mmap;  
  27.     soc_pcm_ops.pointer = platform->driver->ops->pointer;  
  28.     soc_pcm_ops.ioctl = platform->driver->ops->ioctl;  
  29.     soc_pcm_ops.copy = platform->driver->ops->copy;  
  30.     soc_pcm_ops.silence = platform->driver->ops->silence;  
  31.     soc_pcm_ops.ack = platform->driver->ops->ack;  
  32.     soc_pcm_ops.page = platform->driver->ops->page;  
  33.     if (playback)  
  34.         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);  
  35.     if (capture)  
  36.         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);  
  37.     ret = platform->driver->pcm_new(rtd->card->snd_card, codec_dai, pcm);   //调用soc平台的pcm_new方法  
  38.     if (ret < 0) {  
  39.         printk(KERN_ERR "asoc: platform pcm constructor failed\n");  
  40.         return ret;  
  41.     }  
  42.     pcm->private_free = platform->driver->pcm_free;  
  43.     printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,cpu_dai->name);  
  44.     return ret;  
  45. }  

这里调用了snd_pcm_new创建声卡,前一篇文章也说了snd_pcm_new封装了 snd_device_new函数,也就是创建声卡设备的函数

4.1.1 snd_pcm_set_ops

[cpp]   view plain copy
  1. void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)  
  2. {  
  3.     struct snd_pcm_str *stream = &pcm->streams[direction];   //获取pcm流  
  4.     struct snd_pcm_substream *substream;  
  5.       
  6.     for (substream = stream->substream; substream != NULL; substream = substream->next)   //遍历pcm子流  
  7.         substream->ops = ops;    //设置pcm子流的ops操作函数集  
  8. }  

这个函数是设置了pcm子流的操作函数集

关于pcm的部分,待续...alsa第三篇

 

编写一个ASoc声卡驱动需要哪些工作呢?

1.平台设备的创建及平台资源的分配(platform_device_alloc/platform_device_add...),来匹配从而调用probe方法来创建snd_soc_card设备

 2.定义并赋值snd_soc_codec_driversnd_soc_dai_driver结构体并调用snd_soc_register_codec注册snd_soc_codec_driver(codec设备组件),同时也等价于注册了snd_soc_dai_driver(codec_dai组件)

 3.定义并赋值snd_soc_platform_driver结构体调用snd_soc_register_platform注册snd_soc_platform_driver(soc平台组件)

 4.定义并赋值snd_soc_dai_driver并调用snd_soc_register_dai注册snd_soc_dai_driver(cpu_dai组件)

下面是我的开发板平台的音频组件注册部分:

 

[cpp]   view plain copy
  1. //dai设备(cpu_dai)  
  2. davinci-mcasp.c/davinci-hdmi.c  
  3. {  
  4. snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]);  
  5. }  
  6.   
  7. //soc声卡设备  
  8. ti81xx-dvr.c  
  9. {  
  10. platform_device_alloc("soc-audio", 0);  
  11. platform_device_alloc("soc-audio", 1);  
  12. }  
  13.   
  14. //codec设备/dai设备(codec_dai)  
  15. ti81xx_hdmi.c /tlv320aic3x.c /tvp5158-audio.c  
  16. {  
  17. snd_soc_register_codec(&pdev->dev, &soc_codec_tvp5158,&tvp5158_dai, 1);  
  18. snd_soc_register_codec(&pdev->dev, &soc_codec_hdmi,&ti81xx_dai, 1);  
  19. snd_soc_register_codec(&i2c->dev,&soc_codec_dev_aic3x, &aic3x_dai, 1);  
  20. }  
  21.   
  22. //soc平台  
  23. davinci-pcm.c{  
  24. snd_soc_register_platform(&pdev->dev, &davinci_soc_platform);m  
  25. }  

下面是文档的说明:

The codec driver is generic and hardware independent code that configures the codec to provide audio capture and playback. It should contain no code that is
specific to the target platform or machine. All platform and machine specific code should be added to the platform and machine drivers respectively.

codec驱动是通用和硬件抽象的代码,用于配置编码提供音频捕捉和回放,它不允许包含特殊的针对平台或机器的代码,所有的平台和机器特殊代码必须添加到平台或机器驱动中

An ASoC platform driver can be divided into audio DMA and SoC DAI configuration
and control. The platform drivers only target the SoC CPU and must have no board
specific code.

一个ASoc平台驱动分成了音频DMA 和 Soc Dai配置和控制 两部分.平台驱动只针对片上CPU且必须不包含板级特殊代码

The ASoC machine (or board) driver is the code that glues together the platform
and codec drivers.

The machine driver can contain codec and platform specific code. It registers
the audio subsystem with the kernel as a platform device and is represented by
the following struct:-

ASoc机器或板级驱动是整合platform和codec代码的代码(这里的机器就是snd_soc_card)

机器驱动可以包含codec和platform。它作为一个平台设备注册音频子系统到内核

ASoC currently supports the three main Digital Audio Interfaces (DAI) found on
SoC controllers and portable audio CODECs today, namely AC97, I2S and PCM.

dai数字音频接口,Asoc当前支持3中主流数字音频接口(soc控制器和编写的音频编码芯片上):AC97,I2S,PCM

总的说来:codec跟硬件无关负责配置编码(回放/捕捉),dai是具体的数字音频接口(ac97/pcm/i2s),platform是跟芯片相关跟板无关的代码,机器(soc声卡设备)是platform和codec的桥

<think>我们正在讨论LinuxALSA音频框架的使用方法和实现细节。根据引用[1][2][3][4],ALSA是Linux下的音频子系统,包含内核驱动、用户空间库和工具。下面我将从使用和实现两个层面进行详细说明。##一、ALSA框架概述ALSA(AdvancedLinuxSoundArchitecture)由以下核心组件构成:1.**内核驱动**:直接控制声卡硬件,提供`/dev/snd/*`字符设备接口2.**用户空间库(libasound)**:提供标准API(如PCM、混音器、MIDI)3.**工具集**:alsa-utils包含`aplay/arecord/amixer`等实用工具##二、用户空间开发指南###1.PCM音频流操作流程```c#include<alsa/asoundlib.h>snd_pcm_t*handle;//1.打开PCM设备snd_pcm_open(&handle,"default",SND_PCM_STREAM_PLAYBACK,0);//2.配置硬件参数snd_pcm_hw_params_t*params;snd_pcm_hw_params_alloca(&params);snd_pcm_hw_params_any(handle,params);snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED);snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);snd_pcm_hw_params_set_rate(handle,params,44100,0);snd_pcm_hw_params_set_channels(handle,params,2);snd_pcm_hw_params(handle,params);//3.写入音频数据charbuffer[4096];while(1){//填充buffer(伪代码)//generate_audio(buffer,4096);snd_pcm_writei(handle,buffer,1024);//写入1024帧}//4.清理snd_pcm_drain(handle);//等待所有数据播放完成snd_pcm_close(handle);```###2.高级配置(asound.conf)```bash#创建软混音器pcm.softmix{typesoftmixslave.pcm"hw:0"}#设置默认设备pcm.!default{typeplugslave.pcm"softmix"}```##三、内核驱动开发###1.驱动初始化模板```c#include<linux/soundcard.h>staticstructsnd_card*card;staticint__initmydriver_init(void){//创建声卡对象snd_card_new(&card,"MySoundCard");//创建PCM设备structsnd_pcm*pcm;snd_pcm_new(card,"MyPCM",0,1,1,&pcm);snd_pcm_set_ops(pcm,SNDRV_PCM_STREAM_PLAYBACK,&my_playback_ops);//注册声卡snd_card_register(card);return0;}//PCM操作函数集staticstructsnd_pcm_opsmy_playback_ops={.open=my_pcm_open,.close=my_pcm_close,.ioctl=snd_pcm_lib_ioctl,.hw_params=my_hw_params,.trigger=my_trigger,.pointer=my_pointer,};```###2.DMA缓冲区管理```cstaticintmy_hw_params(structsnd_pcm_substream*substream,structsnd_pcm_hw_params*params){//分配DMA缓冲区snd_pcm_lib_malloc_pages(substream,params_buffer_bytes(params));//获取DMA地址structsnd_dma_buffer*dma_buf=&substream->dma_buffer;dma_addr_taddr=dma_buf->addr;return0;}```##四、性能优化技巧1.**周期大小设置**:周期大小(period_size)应等于DMA传输块大小,典型值1024/2048帧2.**双缓冲机制**:使用两个DMA缓冲区减少延迟$$延迟=\frac{周期大小\times周期数}{采样率}$$3.**内存布局**:使用`SND_PCM_ACCESS_RW_NONINTERLEAVED`处理非交错音频4.**实时优先级**:通过`mlock()`锁定内存,`sched_setscheduler()`设置实时调度策略##五、调试技巧```bash#查看PCM设备列表cat/proc/asound/pcm#实时监控ALSA状态watch-n0.1"cat/proc/asound/card0/pcm0p/sub0/status"#调试日志(内核)echo1>/proc/asound/card0/pcm0p/tracedmesg-w```##六、现代ALSA发展1.**音频拓扑**:内核4.2+引入拓扑API,支持DSP动态配置[^2]2.**USB音频类2.0**:支持高分辨率音频(192kHz/32bit)3.**HDMI透传**:支持DolbyTrueHD/DTS-HDMA§§相关问题§§1.ALSA与PulseAudio的关系是怎样的?2.如何实现多路音频混合播放?3.ALSA如何支持蓝牙A2DP音频?4.怎样进行低延迟音频处理(<10ms)?5.在嵌入式系统中如何裁剪ALSA?</think>## Linux ALSA音频框架使用指南与实现细节 ### 一、ALSA核心架构 ALSA(Advanced Linux Sound Architecture)是Linux的声音系统核心,包含以下组件: 1. **内核驱动层** - 提供字符设备接口(`/dev/snd/*`) - 处理底层硬件操作(DMA传输、中断处理) - 实现PCM、混音器、MIDI等核心功能 2. **用户空间库(alsa-lib)** - 提供`libasound.so`库 - 抽象化API:PCM接口、混音器控制、定时器等 - 配置文件路径:`/etc/asound.conf`或`~/.asoundrc` 3. **工具集(alsa-utils)** - `aplay`/`arecord`:音频播放/录制 - `amixer`:混音器控制 - `alsamixer`:交互式混音器界面 ### 二、PCM接口编程实战 #### 基本音频播放流程 ```c #include <alsa/asoundlib.h> int main() { snd_pcm_t *pcm_handle; snd_pcm_hw_params_t *params; // 1. 打开PCM设备 snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0); // 2. 分配参数对象 snd_pcm_hw_params_alloca(&params); snd_pcm_hw_params_any(pcm_handle, params); // 3. 设置参数(16位小端,44.1kHz立体声) snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_rate(pcm_handle, params, 44100, 0); snd_pcm_hw_params_set_channels(pcm_handle, params, 2); // 4. 应用参数 snd_pcm_hw_params(pcm_handle, params); // 5. 播放音频(伪代码) while(has_data) { short buffer[1024]; generate_audio(buffer, 1024); // 填充音频数据 snd_pcm_writei(pcm_handle, buffer, 512); // 写入512帧 } // 6. 清理资源 snd_pcm_drain(pcm_handle); snd_pcm_close(pcm_handle); return 0; } ``` #### 关键参数说明 | 参数 | 说明 | 典型值 | |------|------|--------| | `period_size` | 每次传输的帧数 | 1024 | | `buffer_size` | 环形缓冲区大小 | 4096 | | `access` | 数据布局 | `SND_PCM_ACCESS_RW_INTERLEAVED` | | `format` | 采样格式 | `SND_PCM_FORMAT_S16_LE` | ### 三、内核驱动开发指南 #### 驱动核心结构 ```c // 声卡对象 struct snd_card { int number; // 声卡ID char id[16]; // 标识符 struct list_head devices; // 设备列表 }; // PCM设备操作集 static struct snd_pcm_ops my_ops = { .open = my_pcm_open, .close = my_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = my_hw_params, .trigger = my_trigger, .pointer = my_pointer, }; // 初始化示例 static int __init my_driver_init(void) { struct snd_card *card; struct my_chip *chip; // 创建声卡对象 snd_card_new(&card, "MyAudioCard"); // 创建PCM设备 snd_pcm_new(card, "MyPCM", 0, 1, 1, &pcm); // 设置操作集 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &my_ops); // 注册声卡 snd_card_register(card); return 0; } ``` #### DMA缓冲区管理 ```c static int my_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { // 分配DMA缓冲区 snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); // 获取物理地址 struct snd_dma_buffer *dma_buf = &substream->dma_buffer; dma_addr_t addr = dma_buf->addr; // 配置硬件DMA引擎 my_chip->dma_addr = addr; return 0; } ``` ### 四、高级主题 #### 1. 插件系统 通过`asound.conf`配置虚拟设备: ```bash # 创建软混音器 pcm.softmix { type softmix slave.pcm "hw:0" } # 重采样插件 pcm.resampled { type rate slave { pcm "hw:0" rate 48000 } } ``` #### 2. 低延迟优化 - 使用`SND_PCM_NONBLOCK`非阻塞模式 - 减小`period_size`(最小可至64帧) - 实时线程优先级设置: ```c struct sched_param sched = { .sched_priority = 90 }; pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched); ``` #### 3. 调试技巧 ```bash # 查看PCM设备信息 cat /proc/asound/card0/pcm0p/info # 启用内核调试 echo 1 > /proc/asound/card0/pcm0p/trace dmesg -w ``` ### 五、性能优化公式 延迟计算公式: $$ \text{延迟(ms)} = \frac{\text{period\_size} \times 1000}{\text{sample\_rate}} $$ 当`period_size=256`, `sample_rate=48000`时: $$ \text{延迟} = \frac{256 \times 1000}{48000} \approx 5.33 \text{ ms} $$ ### 六、与PulseAudio集成 ALSA作为底层驱动,PulseAudio作为上层服务: ```mermaid graph TD A[应用程序] --> B[PulseAudio] B --> C[ALSA插件层] C --> D[ALSA内核驱动] D --> E[硬件] ``` ### 七、学习资源 1. **官方文档**: - [ALSA项目官网](https://www.alsa-project.org) - [内核文档](https://www.kernel.org/doc/html/latest/sound/index.html) 2. **实用工具**: - `alsa-lib`开发包 - `tinymix`:精简版混音器控制 - `speaker-test`:音频设备测试 3. **参考书籍**: - 《Linux Device Drivers, 3rd Edition》第6章[^4] - 《The Linux Sound Subsystem》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值