设计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个重要结构体的链表头
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声卡设备
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
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中定义了一个平台设备驱动
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
所以一般声卡的驱动程序中会按以下格式设计平台设备
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
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声卡设备
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;
}
最终调用snd_soc_instantiate_cards实例化所有声卡
第十部分 soc平台 snd_soc_platform
1.soc平台设备
struct snd_soc_platform {
const char *name; //设备名
int id; //设备id
struct device *dev; //设备文件
struct snd_soc_platform_driver *driver; //soc平台驱动
unsigned int suspended:1; /* platform is suspended */
unsigned int probed:1; //"probe"标志
struct snd_soc_card *card; //soc声卡设备
struct list_head list;
struct list_head card_list;
};
2.soc平台驱动
struct snd_soc_platform_driver {
int (*probe)(struct snd_soc_platform *);
int (*remove)(struct snd_soc_platform *);
int (*suspend)(struct snd_soc_dai *dai);
int (*resume)(struct snd_soc_dai *dai);
/* pcm creation and destruction */
int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,struct snd_pcm *);
void (*pcm_free)(struct snd_pcm *);
snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,struct snd_soc_dai *);
/* platform stream ops */
struct snd_pcm_ops *ops;
};
3.注册soc平台驱动
int snd_soc_register_platform(struct device *dev,struct snd_soc_platform_driver *platform_drv)
{
struct snd_soc_platform *platform; //声明soc平台设备
dev_dbg(dev, "platform register %s\n", dev_name(dev));
platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL); //分配soc平台内存
if (platform == NULL)
return -ENOMEM;
/* create platform component name */
platform->name = fmt_single_name(dev, &platform->id); //设置soc平台设备名及id
if (platform->name == NULL) {
kfree(platform);
return -ENOMEM;
}
platform->dev = dev; //设备文件
platform->driver = platform_drv; //捆绑soc平台设备驱动
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list); //添加到全局platform_list链表
snd_soc_instantiate_cards(); //实例化所有soc声卡设备
mutex_unlock(&client_mutex);
pr_debug("Registered platform '%s'\n", platform->name);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_register_platform);
注册soc平台驱动需要驱动自己去调用snd_soc_register_platform注册.
最终调用snd_soc_instantiate_cards实例化所有声卡
4.注销soc平台驱动
void snd_