1. 程式人生 > >alsa音效卡驅動分析總結 (二)

alsa音效卡驅動分析總結 (二)

alsa音效卡驅動分析總結 

來自:http://blog.chinaunix.net/uid-20672559-id-3515392.html

現在我們開始分析ASOC

ASoC被分為MachinePlatformCodec三大部分。其中的Machine驅動負責PlatformCodec之間的耦合和裝置或板子特定的程式碼。

看起來挺複雜,其實需要我們做的事情並不多,大部分核心已經完成。下面我們分析哪些是我們需要自己做的:

codec驅動:負責音訊解碼。這部分程式碼完全無平臺無關,裝置原廠提供,我們只需要把它加進核心編譯就好了。

platform驅動:與處理器晶片相關,這部分程式碼在該晶片商用之前方案產商提供的

demo板已完全確定了,也就是說我們只需要使用就可以了。

machine驅動:好了,到了最關鍵的地方了,machine驅動是耦合platformcodec驅動,同時與上層互動的程式碼。由於上層是標準的alsa架構,所以下層介面肯定要做了統一,所以我很負責的告訴你,這部分由machine本身的platform驅動和platform裝置組成(請跟asocplatform驅動區別)platform驅動核心幫我們完成了,所以你無須過多的關心你的驅動怎麼跟上層alsa怎麼衍接的問題,我們只需要註冊一個machineplatform裝置以及完成platformcodec耦合就ok

asoc的關係圖如下:(以下適應於

linux3.0linux2.6會有所不同)

                                                                        

上圖把asoc架構顯示的淋漓盡致,如果你分析了asoc你就會發現上圖描述的結構以及函式真的一個都跑不了。

Machie

Machine platform device:(~/sound/soc/samsung/smdk_wm8994.c)

  1. smdk_snd_device = platform_device_alloc("soc-audio", -1);  
  2. if (!smdk_snd_device)  
  3. return
     -ENOMEM;  
  4.     platform_set_drvdata(smdk_snd_device, &smdk);  
  5.     ret = platform_device_add(smdk_snd_device);  
  1. staticstruct snd_soc_dai_link smdk_dai[] = {  
  2.     { /* Primary DAI i/f */
  3.         .name = "WM8994 AIF1",  
  4.         .stream_name = "Pri_Dai",  
  5.         .cpu_dai_name = "samsung-i2s.0",  
  6.         .codec_dai_name = "wm8994-aif1",  
  7.         .platform_name = "samsung-audio",  
  8.         .codec_name = "wm8994-codec",  
  9.         .init = smdk_wm8994_init_paiftx,  
  10.         .ops = &smdk_ops,  
  11.     }, { /* Sec_Fifo Playback i/f */
  12.         .name = "Sec_FIFO TX",  
  13.         .stream_name = "Sec_Dai",  
  14.         .cpu_dai_name = "samsung-i2s.4",  
  15.         .codec_dai_name = "wm8994-aif1",  
  16.         .platform_name = "samsung-audio",  
  17.         .codec_name = "wm8994-codec",  
  18.         .ops = &smdk_ops,  
  19.     },  
  20. };  
  21. staticstruct snd_soc_card smdk = {  
  22.     .name = "SMDK-I2S",  
  23.     .owner = THIS_MODULE,  
  24.     .dai_link = smdk_dai,  
  25.     .num_links = ARRAY_SIZE(smdk_dai),  
  26. };  

通過snd_soc_card結構,又引出了Machine驅動的另外兩個個數據結構:

  • snd_soc_dai_link(例項:smdk_dai[] )
  • snd_soc_ops(例項:smdk_ops )

snd_soc_dai_link看名字就知道,很明顯它是起耦合連結作用的。它指定了PlatformCodeccodec_daicpu_dai的名字,稍後Machine驅動將會利用這些名字去匹配已經在系統中註冊的platformcodecdai

snd_soc_ops連線PlatformCodecdai_link對應的ops操作函式,本例就是smdk_ops,它只實現了hw_params函式:smdk_hw_params

到此為止,最主要的部分machine的平臺設備註冊我們完成了。

下面我們關注machine平臺驅動部分(這部分核心不需要我們實現,但我們需要知道它是怎麼工作的)

ASoCplatform_driver在以下檔案中定義:sound/soc/soc-core.c

還是先從模組的入口看起:

[cpp] view plaincopy

  1. staticint __init snd_soc_init(void)  
  2. {  
  3.     ......  
  4. return platform_driver_register(&soc_driver);  
  5. }  

soc_driver的定義如下:

[cpp] view plaincopy

  1. /* ASoC platform driver */
  2. staticstruct platform_driver soc_driver = {  
  3.     .driver     = {  
  4.         .name       = "soc-audio", //確保你註冊machine平臺裝置和它保持一致
  5.         .owner      = THIS_MODULE,  
  6.         .pm     = &soc_pm_ops,  
  7.     },  
  8.     .probe      = soc_probe,  
  9.     .remove     = soc_remove,  
  10. };  

初始化入口soc_probe()

soc_probe函式本身很簡單,它先從platform_device引數中取出snd_soc_card,然後呼叫snd_soc_register_card,通過snd_soc_register_card,為snd_soc_pcm_runtime陣列申請記憶體,每一個dai_link對應snd_soc_pcm_runtime陣列的一個單元,然後把snd_soc_card中的dai_link配置複製到相應的snd_soc_pcm_runtime中,最後,大部分的工作都在snd_soc_instantiate_card中實現,下面就看看snd_soc_instantiate_card做了些什麼:

該函式首先利用card->instantiated來判斷該卡是否已經例項化,如果已經例項化則直接返回,否則遍歷每一對dai_link,進行codecplatformdai的繫結工作,下只是程式碼的部分選節,詳細的程式碼請直接參考完整的程式碼樹。

static int soc_probe(struct platform_device *pdev){       struct snd_soc_card *card =platform_get_drvdata(pdev);//別忘記了machineplatform_set_drvdata //取出snd_soc_card       .............       ret =snd_soc_register_card(card);//註冊

        ............       }

下面我們看snd_soc_register_card()函式:

int snd_soc_register_card(struct snd_soc_card *card){。。。。。。。。       card->rtd =kzalloc(sizeof(struct snd_soc_pcm_runtime) *                        (card->num_links + card->num_aux_devs),                        GFP_KERNEL);//snd_soc_pcm_runtime陣列申請記憶體,每一個dai_link對應一個snd_soc_pcm_runtime陣列單元。。。。。。。。       for (i = 0; i <card->num_links; i++)              card->rtd[i].dai_link= &card->dai_link[i];//snd_soc_card中的dai_link複製到相應的snd_soc_pcm_runtime

。。。。。。。。

       snd_soc_instantiate_cards();//將呼叫snd_soc_instantiate_card()//最為重要}

下面我們分析snd_soc_instantiate_card()函式:

static void snd_soc_instantiate_card(struct snd_soc_card*card){。。。。。。。。       if (card->instantiated) {  //判斷該卡是否已經例項化,如果是就返回              mutex_unlock(&card->mutex);              return;       }       /* bind DAIs */       for (i = 0; i <card->num_links; i++) //否則遍例每一對dai_link,進行codec,flatrom,dai的繫結工作              soc_bind_dai_link(card,i);/*******************************************************************************************************************************************************************

ASoC定義了三個全域性的連結串列頭變數:codec_listdai_listplatform_list,系統中所有的CodecDAIPlatform都在註冊時連線到這三個全域性連結串列上。soc_bind_dai_link函式逐個掃描這三個連結串列,根據card->dai_link[]中的名稱進行匹配,匹配後把相應的codecdaiplatform例項賦值到card->rtd[]中(snd_soc_pcm_runtime)。經過這個過程後,snd_soc_pcm_runtime:(card->rtd)中儲存了本Machine中使用的CodecDAIPlatform驅動的資訊。

*****************************************************************************************************************************************************************/       /* bind completed ? */       if (card->num_rtd !=card->num_links) {              mutex_unlock(&card->mutex);              return;       }。。。。。。。。。       /* card bind complete soregister a sound card */       ret =snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,                     card->owner,0, &card->snd_card);

。。。。。。。。。       card->snd_card->dev =card->dev;       card->dapm.bias_level =SND_SOC_BIAS_OFF;       card->dapm.dev =card->dev;       card->dapm.card = card;       list_add(&card->dapm.list,&card->dapm_list);//初始化codec快取,建立音效卡例項。。。。。。。。。。。。。

//下面是最重要的probe匹配工作       if (card->probe) {              ret =card->probe(card);              if (ret < 0)                     gotocard_probe_error;       }。。。。。。。。。              ret =soc_probe_dai_link(card, i, order);//主要的耦合連結工作在此函式完成。。。。。。。。。              ret =soc_probe_aux_dev(card, i);。。。。。。。。。       if (card->late_probe) {//最後的音效卡初始化工作,              ret =card->late_probe(card);       }。。。。。。。。。       ret =snd_card_register(card->snd_card);//然後呼叫標準的alsa驅動的音效卡函式進行音效卡註冊

。。。。。。。。。       }到這裡音效卡已經註冊好了,音效卡可以正常工作了,我們再分析最後一個函式,也就是它們是怎麼匹配的 soc_probe_dai_link():

static int soc_probe_dai_link(struct snd_soc_card *card,int num, int order){。。。。。。。。。。       /* probe the cpu_dai */       if (!cpu_dai->probed&&                     cpu_dai->driver->probe_order== order) {              if(!try_module_get(cpu_dai->dev->driver->owner))                     return -ENODEV;              if(cpu_dai->driver->probe) {                     ret =cpu_dai->driver->probe(cpu_dai);                     if (ret < 0){                            printk(KERN_ERR"asoc: failed to probe CPU DAI %s\n",                                          cpu_dai->name);                            module_put(cpu_dai->dev->driver->owner);                            returnret;                     }              }              cpu_dai->probed = 1;              /* mark cpu_dai asprobed and add to card dai list */              list_add(&cpu_dai->card_list,&card->dai_dev_list);       }       /* probe the CODEC */       if (!codec->probed&&                     codec->driver->probe_order== order) {              ret =soc_probe_codec(card, codec);              if (ret < 0)                     return ret;       }       /* probe the platform */       if (!platform->probed&&                     platform->driver->probe_order== order) {              ret = soc_probe_platform(card,platform);              if (ret < 0)                     return ret;       }       /* probe the CODEC DAI */       if (!codec_dai->probed&& codec_dai->driver->probe_order == order) {              if(codec_dai->driver->probe) {                     ret =codec_dai->driver->probe(codec_dai);                     if (ret < 0){                            printk(KERN_ERR"asoc: failed to probe CODEC DAI %s\n",                                          codec_dai->name);                            returnret;                     }              }              /* mark codec_dai asprobed and add to card dai list */              codec_dai->probed =1;              list_add(&codec_dai->card_list,&card->dai_dev_list);       }       /* complete DAI probe duringlast probe */       if (order !=SND_SOC_COMP_ORDER_LAST)              return 0;

。。。。。。。。。。       /* create the pcm */       ret = soc_new_pcm(rtd, num);//如果上面都匹配成功將建立標準的alsapcm邏輯裝置。。。。。。。。。。       }好了,到此為止我們最主要的部分machine部分分析完成了。