設計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] static LIST_HEAD(card_list);
static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
static LIST_HEAD(card_list);
static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
第九部分 soc聲卡設備snd_soc_card
1.soc聲卡設備
[cpp] struct snd_soc_card {
const char *name; //設備名
struct device *dev; //設備文件
struct snd_card *snd_card; //所屬聲卡
struct module *owner;
struct list_head list;
struct mutex mutex;
bool instantiated; //實例化標志
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
/* 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. */
int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
int (*resume_pre)(struct platform_device *pdev);
int (*resume_post)(struct platform_device *pdev);
/* callbacks */
int (*set_bias_level)(struct snd_soc_card *,enum snd_soc_bias_level level);
long pmdown_time;
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link; //dai link
int num_links;
struct snd_soc_pcm_runtime *rtd;
int num_rtd;
struct work_struct deferred_resume_work;
/* lists of probed devices belonging to this card */
struct list_head codec_dev_list;
struct list_head platform_dev_list;
struct list_head dai_dev_list;
};
struct snd_soc_card {
const char *name; //設備名
struct device *dev; //設備文件
struct snd_card *snd_card; //所屬聲卡
struct module *owner;
struct list_head list;
struct mutex mutex;
bool instantiated; //實例化標志
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
/* 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. */
int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
int (*resume_pre)(struct platform_device *pdev);
int (*resume_post)(struct platform_device *pdev);
/* callbacks */
int (*set_bias_level)(struct snd_soc_card *,enum snd_soc_bias_level level);
long pmdown_time;
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link; //dai link
int num_links;
struct snd_soc_pcm_runtime *rtd;
int num_rtd;
struct work_struct deferred_resume_work;
/* lists of probed devices belonging to this card */
struct list_head codec_dev_list;
struct list_head platform_dev_list;
struct list_head dai_dev_list;
};
snd_soc_card包含了snd_card,可以理解為聲卡驅動的一個封裝.
2.soc pcm
[cpp] struct snd_soc_pcm_runtime {
struct device dev; //設備文件
struct snd_soc_card *card; //soc聲卡設備
struct snd_soc_dai_link *dai_link; //dai link
unsigned int complete:1;
unsigned int dev_registered:1;
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;
long pmdown_time;
/* runtime devices */
struct snd_pcm *pcm; //pcm結構體
struct snd_soc_codec *codec; //codec設備
struct snd_soc_platform *platform; //soc平台設備
struct snd_soc_dai *codec_dai; //dai設備 codec
struct snd_soc_dai *cpu_dai; //dai設備 cpu
struct delayed_work delayed_work;
};
struct snd_soc_pcm_runtime {
struct device dev; //設備文件
struct snd_soc_card *card; //soc聲卡設備
struct snd_soc_dai_link *dai_link; //dai link
unsigned int complete:1;
unsigned int dev_registered:1;
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;
long pmdown_time;
/* runtime devices */
struct snd_pcm *pcm; //pcm結構體
struct snd_soc_codec *codec; //codec設備
struct snd_soc_platform *platform; //soc平台設備
struct snd_soc_dai *codec_dai; //dai設備 codec
struct snd_soc_dai *cpu_dai; //dai設備 cpu
struct delayed_work delayed_work;
};
snd_soc_pcm_runtime結構體中包含一個snd_pcm結構體,所以可以認為它是pcm聲卡設備的一個封裝,其次他也是Asoc各個組件的一個關系網點
3.soc聲卡設備的匹配過程
在sound/soc/soc-core.c中定義了一個平台設備驅動
[cpp] static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
.pm = &soc_pm_ops,
},
.probe = soc_probe,
.remove = soc_remove,
};
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
.pm = &soc_pm_ops,
},
.probe = soc_probe,
.remove = soc_remove,
};我們知道平台設備驅動和平台設備的匹配靠.driver.name名字,也就是在另一處代碼中必須定義了平台設備platform_device且設備名必須為"soc-audio",
這樣平台設備和驅動才能匹配,才會調用平台驅動的probe方法,既soc_probe
所以一般聲卡的驅動程序中會按以下格式設計平台設備
[cpp] int ret;
static struct platform_device *xxx;
xxx=platform_device_alloc("soc-audio", 0); //分配平台驅動
//平台資源的添加
ret=platform_device_add(xxx); //添加平台設備
if(ret)
platform_device_put(xxx); //增加引用計數
int ret;
static struct platform_device *xxx;
xxx=platform_device_alloc("soc-audio", 0); //分配平台驅動
//平台資源的添加
ret=platform_device_add(xxx); //添加平台設備
if(ret)
platform_device_put(xxx); //增加引用計數
4.匹配調用的probe方法soc_probe
[cpp] static int soc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev); //獲取soc聲卡設備
int ret = 0;
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
INIT_LIST_HEAD(&card->dai_dev_list); //初始化dai_dev_list鏈表
INIT_LIST_HEAD(&card->codec_dev_list); //初始化codec_dev_list鏈表
INIT_LIST_HEAD(&card->platform_dev_list); //初始化platform_dev_list鏈表
ret = snd_soc_register_card(card); //注冊soc聲卡設備
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
return 0;
}
static int soc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev); //獲取soc聲卡設備
int ret = 0;
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
INIT_LIST_HEAD(&card->dai_dev_list); //初始化dai_dev_list鏈表
INIT_LIST_HEAD(&card->codec_dev_list); //初始化codec_dev_list鏈表
INIT_LIST_HEAD(&card->platform_dev_list); //初始化platform_dev_list鏈表
ret = snd_soc_register_card(card); //注冊soc聲卡設備
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
return 0;
}這裡調用了snd_soc_register_card注冊了soc聲卡設備
5.注冊soc聲卡設備
[cpp] static int snd_soc_register_card(struct snd_soc_card *card)
{
int i;
if (!card->name || !card->dev)
return -EINVAL;
card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * card->num_links,GFP_KERNEL); //分配多個soc pcm內存
if (card->rtd == NULL)
return -ENOMEM;
for (i = 0; i < card->num_links; i++)
card->rtd[i].dai_link = &card->dai_link[i]; //dai link數組
INIT_LIST_HEAD(&card->list);
card->instantiated = 0; //soc聲卡實例化標志設置為0
mutex_init(&card->mutex);
mutex_lock(&client_mutex);
list_add(&card->list, &card_list); //添加soc聲卡到全局card_list鏈表
snd_soc_instantiate_cards(); //實例化所有soc聲卡
mutex_unlock(&client_mutex);
dev_dbg(card->dev, "Registered card '%s'\n", card->name);
return 0;
}