高通Audio中ASOC的codec驅動(二) ----mark 條例詳細

高通Audio中ASOC的codec驅動(二)

轉載原文:https://www.cnblogs.com/linhaostudy/p/8425337.html

閱讀目錄

 

正文

繼上一篇文章:高通Audio中ASOC的machine驅動(一)

ASOC的出現是爲了讓codec獨立於CPU,減少和CPU之間的耦合,這樣同一個codec驅動就無需修改就可以匹配任何一款平臺。

 

在Machine中已經知道,snd_soc_dai_link結構就指明瞭該Machine所使用的Platform和Codec。在Codec這邊通過codec_dai和Platform側的cpu_dai相互通信,既然相互通信,就需要遵守一定的規則,其中codec_dai和cpu_dai統一抽象爲struct snd_soc_dai結構,而將dai的相關操作使用snd_soc_dai_driver抽象。同時也需要對所有的codec設備進行抽象封裝,linux使用snd_soc_codec進行所有codec設備的抽象,而將codec的驅動抽象爲snd_soc_codec_driver結構。

 

 

回到頂部

1、重要的數據結構:

所有簡單來說,Codec側有四個重要的數據結構:

 

struct snd_soc_dai,struct snd_soc_dai_driver,struct snd_soc_codec,struct snd_soc_codec_driver

 

snd_soc_dai

 1 /*
 2  * Digital Audio Interface runtime data.
 3  *
 4  * Holds runtime data for a DAI.
 5  */
 6 struct snd_soc_dai {
 7     const char *name;  /* dai的名字 */
 8     struct device *dev;  /* 設備指針 */
 9 
10     /* driver ops */
11     struct snd_soc_dai_driver *driver;  /* 指向dai驅動結構的指針 */
12 
13     /* DAI runtime info */
14     unsigned int capture_active:1;        /* stream is in use */
15     unsigned int playback_active:1;        /* stream is in use */
16 
17     /* DAI DMA data */
18     void *playback_dma_data;  /* 用於管理playback dma */
19     void *capture_dma_data;  /* 用於管理capture dma */
20 
21     /* parent platform/codec */
22     union {
23         struct snd_soc_platform *platform;  /* 如果是cpu dai,指向所綁定的平臺 */
24         struct snd_soc_codec *codec;  /* 如果是codec dai指向所綁定的codec */
25     };
26     struct snd_soc_card *card;  /* 指向Machine驅動中的crad實例 */
27 };

snd_soc_dai_driver:

 1 /*
 2  * Digital Audio Interface Driver.
 3  *
 4  * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
 5  * operations and capabilities. Codec and platform drivers will register this
 6  * structure for every DAI they have.
 7  *
 8  * This structure covers the clocking, formating and ALSA operations for each
 9  * interface.
10  */
11 struct snd_soc_dai_driver {
12     /* DAI description */
13     const char *name;  /* dai驅動名字 */
14 
15     /* DAI driver callbacks */
16     int (*probe)(struct snd_soc_dai *dai);  /* dai驅動的probe函數,由snd_soc_instantiate_card回調 */
17     int (*remove)(struct snd_soc_dai *dai);  
18     int (*suspend)(struct snd_soc_dai *dai);  /* 電源管理 */
19     int (*resume)(struct snd_soc_dai *dai);  
20 
21     /* ops */
22     const struct snd_soc_dai_ops *ops;  /* 指向本dai的snd_soc_dai_ops結構 */
23 
24     /* DAI capabilities */
25     struct snd_soc_pcm_stream capture;  /* 描述capture的能力 */
26     struct snd_soc_pcm_stream playback;  /* 描述playback的能力 */
27 };

snd_soc_codec

 1 /* SoC Audio Codec device */
 2 struct snd_soc_codec {
 3     const char *name;  /* Codec的名字*/
 4     struct device *dev; /* 指向Codec設備的指針 */
 5     const struct snd_soc_codec_driver *driver; /* 指向該codec的驅動的指針 */
 6     struct snd_soc_card *card;    /* 指向Machine驅動的card實例 */
 7     int num_dai; /* 該Codec數字接口的個數,目前越來越多的Codec帶有多個I2S或者是PCM接口 */
 8     int (*volatile_register)(...);  /* 用於判定某一寄存器是否是volatile */
 9     int (*readable_register)(...);  /* 用於判定某一寄存器是否可讀 */
10     int (*writable_register)(...);  /* 用於判定某一寄存器是否可寫 */
11 
12     /* runtime */
13     ......
14     /* codec IO */
15     void *control_data; /* 該指針指向的結構用於對codec的控制,通常和read,write字段聯合使用 */
16     enum snd_soc_control_type control_type;/* 可以是SND_SOC_SPI,SND_SOC_I2C,SND_SOC_REGMAP中的一種 */
17     unsigned int (*read)(struct snd_soc_codec *, unsigned int);  /* 讀取Codec寄存器的函數 */
18     int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);  /* 寫入Codec寄存器的函數 */
19     /* dapm */
20     struct snd_soc_dapm_context dapm;  /* 用於DAPM控件 */
21 };

snd_soc_codec_driver:

 1 /* codec driver */
 2 struct snd_soc_codec_driver {
 3     /* driver ops */
 4     int (*probe)(struct snd_soc_codec *);  /* codec驅動的probe函數,由snd_soc_instantiate_card回調 */
 5     int (*remove)(struct snd_soc_codec *);  
 6     int (*suspend)(struct snd_soc_codec *);  /* 電源管理 */
 7     int (*resume)(struct snd_soc_codec *);  /* 電源管理 */
 8 
 9     /* Default control and setup, added after probe() is run */
10     const struct snd_kcontrol_new *controls;  /* 音頻控件指針 */
11     const struct snd_soc_dapm_widget *dapm_widgets;  /* dapm部件指針 */
12     const struct snd_soc_dapm_route *dapm_routes;  /* dapm路由指針 */
13 
14     /* codec wide operations */
15     int (*set_sysclk)(...);  /* 時鐘配置函數 */
16     int (*set_pll)(...);  /* 鎖相環配置函數 */
17 
18     /* codec IO */
19     unsigned int (*read)(...);  /* 讀取codec寄存器函數 */
20     int (*write)(...);  /* 寫入codec寄存器函數 */
21     int (*volatile_register)(...);  /* 用於判定某一寄存器是否是volatile */
22     int (*readable_register)(...);  /* 用於判定某一寄存器是否可讀 */
23     int (*writable_register)(...);  /* 用於判定某一寄存器是否可寫 */
24 
25     /* codec bias level */
26     int (*set_bias_level)(...);  /* 偏置電壓配置函數 */
27 
28 };

回到頂部

2、Codec代碼分析:

2.1 找到codec的代碼:

如何找到codec的代碼呢?  答案是通過machine中的snd_soc_dai_link結構:

 1 {
 2         .name = LPASS_BE_TERT_MI2S_TX,
 3         .stream_name = "Tertiary MI2S Capture",
 4         .cpu_dai_name = "msm-dai-q6-mi2s.2",
 5         .platform_name = "msm-pcm-routing",
 6         .codec_name     = MSM8X16_CODEC_NAME,
 7         .codec_dai_name = "msm8x16_wcd_i2s_tx1",
 8         .no_pcm = 1,
 9         .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
10         .be_hw_params_fixup = msm_tx_be_hw_params_fixup,
11         .ops = &msm8x16_mi2s_be_ops,
12         .ignore_suspend = 1,
13 },

由dai_link中codec_name,可以知道我們的codec驅動在哪。

高通Audio中ASOC的machine驅動這篇文章中的匹配並註冊相應驅動的那一章分析可知,codec驅動代碼就是msm8x16-wcd.c這個文件;

回到頂部

3、查看codec的probe函數:

因爲Codec驅動的代碼要做到平臺無關性,要使得Machine驅動能夠使用該Codec,Codec驅動的首要任務就是確定snd_soc_codec和snd_soc_dai的實例,並把它們註冊到系統中,註冊後的codec和dai才能爲Machine驅動所用。

 msm8x16_wcd_spmi_probe

SPMI總線是高通電源管理的一種規範,也就是通過PMU控制音頻(具體我也不夠了解,有待以後深入理解)

看看最重要的函數:

1 ret = snd_soc_register_codec(&spmi->dev, &soc_codec_dev_msm8x16_wcd,
2                      msm8x16_wcd_i2s_dai,
3                      ARRAY_SIZE(msm8x16_wcd_i2s_dai));

此函數通過snd_soc_register_codec函數註冊了wcd9320的codec,同時傳入了snd_soc_codec_driversnd_soc_dai_driver結構。

 1 static struct snd_soc_codec_driver soc_codec_dev_msm8x16_wcd = {
 2     .probe    = msm8x16_wcd_codec_probe,  /*codec驅動的probe函數,由snd_soc_instantiate_card回調*/
 3     .remove    = msm8x16_wcd_codec_remove,
 4 
 5     .read = msm8x16_wcd_read,
 6     .write = msm8x16_wcd_write,
 7 
 8     .suspend = msm8x16_wcd_suspend,    /*電源管理*/
 9     .resume = msm8x16_wcd_resume,     /*電源管理*/
10 
11     .readable_register = msm8x16_wcd_readable,
12     .volatile_register = msm8x16_wcd_volatile,
13 
14     .reg_cache_size = MSM8X16_WCD_CACHE_SIZE,
15     .reg_cache_default = msm8x16_wcd_reset_reg_defaults,
16     .reg_word_size = 1,
17 
18     .controls = msm8x16_wcd_snd_controls,    
19     .num_controls = ARRAY_SIZE(msm8x16_wcd_snd_controls),
20     .dapm_widgets = msm8x16_wcd_dapm_widgets,
21     .num_dapm_widgets = ARRAY_SIZE(msm8x16_wcd_dapm_widgets),
22     .dapm_routes = audio_map,
23     .num_dapm_routes = ARRAY_SIZE(audio_map),
24 };

 snd_soc_dai_driver結構:

 1 static struct snd_soc_dai_driver msm8x16_wcd_i2s_dai[] = {
 2     {
 3         .name = "msm8x16_wcd_i2s_rx1",
 4         .id = AIF1_PB,
 5         .playback = {
 6             .stream_name = "AIF1 Playback",
 7             .rates = MSM8X16_WCD_RATES,
 8             .formats = MSM8X16_WCD_FORMATS,
 9             .rate_max = 192000,
10             .rate_min = 8000,
11             .channels_min = 1,
12             .channels_max = 3,
13         },
14         .ops = &msm8x16_wcd_dai_ops,
15     },
16     {
17         .name = "msm8x16_wcd_i2s_tx1",
18         .id = AIF1_CAP,
19         .capture = {
20             .stream_name = "AIF1 Capture",
21             .rates = MSM8X16_WCD_RATES,
22             .formats = MSM8X16_WCD_FORMATS,
23             .rate_max = 192000,
24             .rate_min = 8000,
25             .channels_min = 1,
26             .channels_max = 4,
27         },
28         .ops = &msm8x16_wcd_dai_ops,
29     },
30 };

回到頂部

4、snd_soc_register_codec函數分析:

首先,它申請了一個snd_soc_codec結構的實例:

1 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);

確定codec的名字,這個名字很重要,Machine驅動定義的snd_soc_dai_link中會指定每個link的codec和dai的名字,進行匹配綁定時就是通過和這裏的名字比較,從而找到該Codec的!

1 codec->name = fmt_single_name(dev, &codec->id);

然後初始化它的各個字段,多數字段的值來自上面定義的snd_soc_codec_driver的實例:

 1     codec->write = codec_drv->write;
 2     codec->read = codec_drv->read;
 3     codec->volatile_register = codec_drv->volatile_register;
 4     codec->readable_register = codec_drv->readable_register;
 5     codec->writable_register = codec_drv->writable_register;
 6     codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
 7     codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 8     codec->dapm.dev = dev;
 9     codec->dapm.codec = codec;
10     codec->dapm.seq_notifier = codec_drv->seq_notifier;
11     codec->dapm.stream_event = codec_drv->stream_event;
12     codec->dev = dev;
13     codec->driver = codec_drv;
14     codec->num_dai = num_dai;

在做了一些寄存器緩存的初始化和配置工作後,通過snd_soc_register_dais函數對本Codec的dai進行註冊:

1 /* register any DAIs */
2 ret = snd_soc_register_dais(dev, dai_drv, num_dai);
3 if (ret < 0) {
4         dev_err(codec->dev, "ASoC: Failed to regster DAIs: %d\n", ret);
5         goto fail_codec_name;
6 }

它把codec實例鏈接到全局鏈表codec_list中:

1 mutex_lock(&client_mutex);
2 list_add(&codec->list, &codec_list);
3 mutex_unlock(&client_mutex);

 並且調用snd_soc_instantiate_cards對machine驅動進行一次匹配綁定的操作;

 

 

 至此,codec的註冊就分析完畢。

關於codec側驅動總結:

1.   分配名字爲"codec_name"的平臺驅動,註冊。
2.   定義struct snd_soc_codec_driver結構,設置,初始化。

3.   定義struct snd_soc_dai_driver結構,設置,初始化。

4.   調用snd_soc_register_codec函數註冊codec。

« 上一篇: HashTree【轉】
» 下一篇: ALSA聲卡驅動的DAPM(一)-DPAM詳解

相關博文:
· ALSA driver--Asoc
· Linux ALSA框架之八:ASoC架構中的Platform
· Linux ALSA框架之五:移動設備中的ALSA(ASoC)
· 音頻的採集和播放
· Linux設備驅動模型之platform(平臺)總線詳解
» 更多推薦...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章