alsa音频架构2-ASoc

news/2024/11/24 8:32:57/

设计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 linkint 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 linkunsigned 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设备 codecstruct snd_soc_dai *cpu_dai;	//dai设备 cpustruct 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声卡实例化标志设置为0mutex_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;	//设备idstruct 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平台设备名及idif (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_soc_unregister_platform(struct device *dev)
{struct snd_soc_platform *platform;list_for_each_entry(platform, &platform_list, list) {	//遍历全局platform_list链表if (dev == platform->dev)	//查找到匹配的soc平台goto found;}return;found:mutex_lock(&client_mutex);list_del(&platform->list);	//移除链表mutex_unlock(&client_mutex);pr_debug("Unregistered platform '%s'\n", platform->name);kfree(platform->name);kfree(platform);
}
EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);


 

第十一部分 codec设备 snd_soc_codec

1.codec设备结构体

struct snd_soc_codec {const char *name;	//设备名int id;				//设备id号struct device *dev;	//设备文件struct snd_soc_codec_driver *driver;	//所属的codec驱动struct mutex mutex;struct snd_soc_card *card;	//soc声卡设备struct list_head list;struct list_head card_list;int num_dai;	//dai设备(codec_dai)个数/* runtime */struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */unsigned int active;unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */unsigned int cache_only:1;  /* Suppress writes to hardware */unsigned int cache_sync:1; /* Cache needs to be synced to hardware */unsigned int suspended:1; /* Codec is in suspend PM state */unsigned int probed:1; //"probe"标志unsigned int ac97_registered:1; /* Codec has been AC97 registered */unsigned int ac97_created:1; /* Codec has been created by SoC */unsigned int sysfs_registered:1; /* codec has been sysfs registered *//* codec IO */void *control_data; /* codec control (i2c/3wire) data */hw_write_t hw_write;unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);void *reg_cache;	//cache/* dapm */u32 pop_time;struct list_head dapm_widgets;struct list_head dapm_paths;enum snd_soc_bias_level bias_level;enum snd_soc_bias_level suspend_bias_level;struct delayed_work delayed_work;
#ifdef CONFIG_DEBUG_FSstruct dentry *debugfs_codec_root;struct dentry *debugfs_reg;struct dentry *debugfs_pop_time;struct dentry *debugfs_dapm;
#endif
};

2.codec驱动结构体

struct snd_soc_codec_driver {/* driver ops */int (*probe)(struct snd_soc_codec *);int (*remove)(struct snd_soc_codec *);int (*suspend)(struct snd_soc_codec *,pm_message_t state);int (*resume)(struct snd_soc_codec *);/* codec IO */unsigned int (*read)(struct snd_soc_codec *, unsigned int);int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);int (*display_register)(struct snd_soc_codec *, char *,size_t, unsigned int);int (*volatile_register)(unsigned int);int (*readable_register)(unsigned int);short reg_cache_size;short reg_cache_step;short reg_word_size;const void *reg_cache_default;/* codec bias level */int (*set_bias_level)(struct snd_soc_codec *,enum snd_soc_bias_level level);
};


3.注册codec驱动

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)
{struct snd_soc_codec *codec;	//声明codec设备int ret, i;dev_dbg(dev, "codec register %s\n", dev_name(dev));codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);	//分配codec设备内存if (codec == NULL)return -ENOMEM;/* create CODEC component name */codec->name = fmt_single_name(dev, &codec->id);	//创建codec设备名及id号if (codec->name == NULL) {kfree(codec);return -ENOMEM;}/* allocate CODEC register cache */	//cache设置if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {if (codec_drv->reg_cache_default)codec->reg_cache = kmemdup(codec_drv->reg_cache_default,codec_drv->reg_cache_size * codec_drv->reg_word_size, GFP_KERNEL);elsecodec->reg_cache = kzalloc(codec_drv->reg_cache_size *codec_drv->reg_word_size, GFP_KERNEL);if (codec->reg_cache == NULL) {kfree(codec->name);kfree(codec);return -ENOMEM;}}codec->dev = dev;	//捆绑设备文件codec->driver = codec_drv;	//捆绑codec驱动codec->bias_level = SND_SOC_BIAS_OFF;codec->num_dai = num_dai;	//dai设备个数mutex_init(&codec->mutex);INIT_LIST_HEAD(&codec->dapm_widgets);INIT_LIST_HEAD(&codec->dapm_paths);for (i = 0; i < num_dai; i++) {	//格式化dai设备驱动的playback和capture PCM流fixup_codec_formats(&dai_drv[i].playback);fixup_codec_formats(&dai_drv[i].capture);}/* register any DAIs */if (num_dai) {ret = snd_soc_register_dais(dev, dai_drv, num_dai);	//注册dai设备驱动if (ret < 0)goto error;}mutex_lock(&client_mutex);list_add(&codec->list, &codec_list);	//添加进全局codec_list链表snd_soc_instantiate_cards();	//实例化所有soc声卡设备mutex_unlock(&client_mutex);pr_debug("Registered codec '%s'\n", codec->name);return 0;
error:if (codec->reg_cache)kfree(codec->reg_cache);kfree(codec->name);kfree(codec);return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_codec);


 

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

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

4.注销codec驱动

void snd_soc_unregister_codec(struct device *dev)
{struct snd_soc_codec *codec;int i;list_for_each_entry(codec, &codec_list, list) {	//遍历全局codec_list链表if (dev == codec->dev)	//查找到匹配的codec设备goto found;}return;found:if (codec->num_dai)for (i = 0; i < codec->num_dai; i++)	//注销codec设备下的dai设备(codec_dai)snd_soc_unregister_dai(dev);mutex_lock(&client_mutex);list_del(&codec->list);	//移除链表mutex_unlock(&client_mutex);pr_debug("Unregistered codec '%s'\n", codec->name);if (codec->reg_cache)kfree(codec->reg_cache);kfree(codec->name);kfree(codec);
}
EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);


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

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

struct snd_soc_dai {const char *name;	//设备名int id;				//设备idstruct device *dev;	//设备文件void *ac97_pdata;	/* platform_data for the ac97 codec *//* driver ops */struct snd_soc_dai_driver *driver;	//所属dai声卡驱动/* DAI runtime info */unsigned int capture_active:1;		/* stream is in use */unsigned int playback_active:1;		/* stream is in use */unsigned int symmetric_rates:1;struct snd_pcm_runtime *runtime;	//pcm runtimeunsigned int active;unsigned char pop_wait:1;unsigned char probed:1;	//"probe"标志/* DAI DMA data */void *playback_dma_data;void *capture_dma_data;/* parent platform/codec */union {struct snd_soc_platform *platform;	//platform设备struct snd_soc_codec *codec;	//codec设备};struct snd_soc_card *card;	//soc声卡设备struct list_head list;struct list_head card_list;
};

2.dai声卡驱动

struct snd_soc_dai_driver {/* DAI description */const char *name;	//名字unsigned int id;	//设备idint ac97_control;/* DAI driver callbacks */int (*probe)(struct snd_soc_dai *dai);int (*remove)(struct snd_soc_dai *dai);int (*suspend)(struct snd_soc_dai *dai);int (*resume)(struct snd_soc_dai *dai);/* ops */struct snd_soc_dai_ops *ops;	//dai操作函数集/* DAI capabilities */struct snd_soc_pcm_stream capture;	//soc pcm流-捕获struct snd_soc_pcm_stream playback;	//soc pcm流-回放unsigned int symmetric_rates:1;
};

3.dai link

struct snd_soc_dai_link {/* config - must be set by machine driver */const char *name;			/* Codec name */const char *stream_name;		/* Stream name */const char *codec_name;		/* for multi-codec */const char *platform_name;	/* for multi-platform */const char *cpu_dai_name;const char *codec_dai_name;/* Keep DAI active over suspend */unsigned int ignore_suspend:1;/* Symmetry requirements */unsigned int symmetric_rates:1;/* codec/machine specific init - e.g. add machine controls */int (*init)(struct snd_soc_pcm_runtime *rtd);/* machine stream operations */struct snd_soc_ops *ops;	//soc操作函数集
};

3.注册若干个dai驱动

int snd_soc_register_dais(struct device *dev,struct snd_soc_dai_driver *dai_drv, size_t count)
{struct snd_soc_dai *dai;	//dai设备声明int i, ret = 0;dev_dbg(dev, "dai register %s #%Zu\n", dev_name(dev), count);for (i = 0; i < count; i++) {	//dai声卡设备个数dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);	//分配内存if (dai == NULL) {ret = -ENOMEM;goto err;}/* create DAI component name */dai->name = fmt_multiple_name(dev, &dai_drv[i]);	//格式化dai声卡设备名if (dai->name == NULL) {kfree(dai);ret = -EINVAL;goto err;}dai->dev = dev;	//设备文件dai->driver = &dai_drv[i];	//捆绑dai声卡驱动if (dai->driver->id)	//设置dai声卡设备iddai->id = dai->driver->id;elsedai->id = i;if (!dai->driver->ops)	//若dai声卡驱动未指定dai操作函数集dai->driver->ops = &null_dai_ops;	//则使用默认的空函数集mutex_lock(&client_mutex);list_add(&dai->list, &dai_list);	//添加dai设备到全局dai_list链表mutex_unlock(&client_mutex);pr_debug("Registered DAI '%s'\n", dai->name);}mutex_lock(&client_mutex);snd_soc_instantiate_cards();	//实例化所有soc声卡设备mutex_unlock(&client_mutex);return 0;err:for (i--; i >= 0; i--)snd_soc_unregister_dai(dev);return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_dais);




 4.注销若干个dai驱动

void snd_soc_unregister_dais(struct device *dev, size_t count)
{int i;for (i = 0; i < count; i++)snd_soc_unregister_dai(dev);	//注销dai设备
}
EXPORT_SYMBOL_GPL(snd_soc_unregister_dais);

5.注册一个dai驱动

int snd_soc_register_dai(struct device *dev,struct snd_soc_dai_driver *dai_drv)
{struct snd_soc_dai *dai;dev_dbg(dev, "dai register %s\n", dev_name(dev));dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);	//分配dai设备内存if (dai == NULL)return -ENOMEM;/* create DAI component name */dai->name = fmt_single_name(dev, &dai->id);	//设置名字if (dai->name == NULL) {kfree(dai);return -ENOMEM;}dai->dev = dev;	//设备文件dai->driver = dai_drv;	//捆绑dai驱动if (!dai->driver->ops)	//若为指定dai驱动操作函数集dai->driver->ops = &null_dai_ops;	//则设置默认函数集mutex_lock(&client_mutex);list_add(&dai->list, &dai_list);	//添加进全局dai_list链表snd_soc_instantiate_cards();	//实例化所有soc声卡设备mutex_unlock(&client_mutex);pr_debug("Registered DAI '%s'\n", dai->name);return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_register_dai);

6.注销一个dai设备

void snd_soc_unregister_dai(struct device *dev)
{struct snd_soc_dai *dai;list_for_each_entry(dai, &dai_list, list) {	//遍历全局dai_list链表if (dev == dai->dev)	//查找到匹配的dai设备goto found;}return;found:mutex_lock(&client_mutex);list_del(&dai->list);	//移除链表mutex_unlock(&client_mutex);pr_debug("Unregistered DAI '%s'\n", dai->name);kfree(dai->name);kfree(dai);
}
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声卡

static void snd_soc_instantiate_cards(void)	//实例化所有soc声卡设备
{struct snd_soc_card *card;list_for_each_entry(card, &card_list, list)	//遍历声卡全局链表card_listsnd_soc_instantiate_card(card);	//实例化soc声卡设备
}

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

2.snd_soc_instantiate_card 实例化一个soc声卡

static void snd_soc_instantiate_card(struct snd_soc_card *card)	//实例化soc声卡设备
{struct platform_device *pdev = to_platform_device(card->dev);	//获取平台设备int ret, i;mutex_lock(&card->mutex);if (card->instantiated) {	//调用soc声卡设备已经实例化(instantiated标志为1)mutex_unlock(&card->mutex);return;}/* bind DAIs */for (i = 0; i < card->num_links; i++)soc_bind_dai_link(card, i);	//绑定cpu_dai/codec_dai/codec/platform/* bind completed ? */if (card->num_rtd != card->num_links) {	//所有的都绑定好了?mutex_unlock(&card->mutex);return;}/* card bind complete so register a sound card */ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card);	//创建声卡if (ret < 0) {printk(KERN_ERR "asoc: can't create sound card for card %s\n",card->name);mutex_unlock(&card->mutex);return;}card->snd_card->dev = card->dev;	//设备文件
#ifdef CONFIG_PM/* deferred resume work */INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif/* initialise the sound card only once */if (card->probe) {	//soc声卡设备存在probe方法ret = card->probe(pdev);	//则调用其probe方法if (ret < 0)goto card_probe_error;}for (i = 0; i < card->num_links; i++) {ret = soc_probe_dai_link(card, i);	//调用dai的probe方法if (ret < 0) {pr_err("asoc: failed to instantiate card %s: %d\n",card->name, ret);goto probe_dai_err;}}snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),"%s",  card->name);snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),"%s", card->name);ret = snd_card_register(card->snd_card);	//注册声卡if (ret < 0) {printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);goto probe_dai_err;}
#ifdef CONFIG_SND_SOC_AC97_BUS/* register any AC97 codecs */for (i = 0; i < card->num_rtd; i++) {ret = soc_register_ac97_dai_link(&card->rtd[i]);if (ret < 0) {printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name);while (--i >= 0)soc_unregister_ac97_dai_link(&card->rtd[i]);goto probe_dai_err;}}
#endifcard->instantiated = 1;	//实例化标志置1mutex_unlock(&card->mutex);return;
probe_dai_err:for (i = 0; i < card->num_links; i++)soc_remove_dai_link(card, i);
card_probe_error:if (card->remove)card->remove(pdev);snd_card_free(card->snd_card);mutex_unlock(&card->mutex);
}

该函数用到 前面一篇文章(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

static int soc_bind_dai_link(struct snd_soc_card *card, int num)
{struct snd_soc_dai_link *dai_link = &card->dai_link[num];	//获取dai linkstruct snd_soc_pcm_runtime *rtd = &card->rtd[num];	//获取soc pcmstruct snd_soc_codec *codec;struct snd_soc_platform *platform;struct snd_soc_dai *codec_dai, *cpu_dai;if (rtd->complete)return 1;dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);/* do we already have the CPU DAI for this link ? */if (rtd->cpu_dai) {	//若dai设备(cpu_dai)已经设置了goto find_codec;	//则直接查找dai设备(codec_dai)}/* no, then find CPU DAI from registered DAIs*/list_for_each_entry(cpu_dai, &dai_list, list) {	//遍历全局dai_list链表if (!strcmp(cpu_dai->name, dai_link->cpu_dai_name)) {	//查找符合的cpu_dai设备if (!try_module_get(cpu_dai->dev->driver->owner))return -ENODEV;rtd->cpu_dai = cpu_dai;	//查找到匹配,则设置soc pcm的dai设备(cpu_dai)goto find_codec;	//跳转查找dai设备(codec_dai)}}dev_dbg(card->dev, "CPU DAI %s not registered\n",dai_link->cpu_dai_name);find_codec:	//查找dai设备(codec_dai)/* do we already have the CODEC for this link ? */if (rtd->codec) {	//若dai设备(codec_dai)已经设置了goto find_platform;	//则直接查找dai设备(cpu_dai)}/* no, then find CODEC from registered CODECs*/list_for_each_entry(codec, &codec_list, list) {	//遍历全局codec_list链表if (!strcmp(codec->name, dai_link->codec_name)) {	//查找符合的codec设备rtd->codec = codec;	//soc pcm捆绑codec设备if (!try_module_get(codec->dev->driver->owner))return -ENODEV;/* CODEC found, so find CODEC DAI from registered DAIs from this CODEC*/list_for_each_entry(codec_dai, &dai_list, list) {	//遍历全局dai_list链表if (codec->dev == codec_dai->dev &&!strcmp(codec_dai->name, dai_link->codec_dai_name)) {	//查找符合的dai设备(codec_dai)rtd->codec_dai = codec_dai;	//查找到匹配,则设置soc pcm的dai设备(codec_dai)goto find_platform;}}dev_dbg(card->dev, "CODEC DAI %s not registered\n",dai_link->codec_dai_name);goto find_platform;}}dev_dbg(card->dev, "CODEC %s not registered\n",dai_link->codec_name);find_platform:	//查找soc平台/* do we already have the CODEC DAI for this link ? */if (rtd->platform) {	goto out;}/* no, then find CPU DAI from registered DAIs*/list_for_each_entry(platform, &platform_list, list) {	//遍历全局platform_list链表if (!strcmp(platform->name, dai_link->platform_name)) {	//查找符合的soc平台if (!try_module_get(platform->dev->driver->owner))return -ENODEV;rtd->platform = platform;	//pcm pcm捆绑soc平台goto out;}}dev_dbg(card->dev, "platform %s not registered\n",dai_link->platform_name);return 0;out:/* mark rtd as complete if we found all 4 of our client devices */if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) {	//四种设备都设置齐全了rtd->complete = 1;	//设置soc pcm完成标志complete为1card->num_rtd++;}return 1;
}


 

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

static int soc_probe_dai_link(struct snd_soc_card *card, int num)
{struct snd_soc_dai_link *dai_link = &card->dai_link[num];	//获取dai linkstruct snd_soc_pcm_runtime *rtd = &card->rtd[num];	//获取soc pcmstruct snd_soc_codec *codec = rtd->codec;	//获取codec设备struct snd_soc_platform *platform = rtd->platform;	//获取soc平台struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;//获取dai设备数组(codec_dai/cpu_dai)int ret;
//___________________________________捆绑多种设备关系_________________________________________//dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num);/* config components */codec_dai->codec = codec;		//dai设备(codec_dai)捆绑codec设备codec->card = card;				//codec设备捆绑soc声卡设备cpu_dai->platform = platform;	//dai设备(cpu_dai)捆绑soc平台rtd->card = card;				//soc pcm捆绑soc声卡设备rtd->dev.parent = card->dev;	codec_dai->card = card;			//dai设备(codec_dai)捆绑soc声卡设备cpu_dai->card = card;			//dai设备(cpu_dai)捆绑soc声卡设备/* set default power off timeout */rtd->pmdown_time = pmdown_time;
//___________________________________probe多种设备_____________________________________________//		/* probe the cpu_dai */	//------------"probe" dai(cpu_dai)设备if (!cpu_dai->probed) {	//dai设备(cpu_dai)probed标志为0if (cpu_dai->driver->probe) {	//若dai声卡驱动存在probe方法ret = cpu_dai->driver->probe(cpu_dai);	//则调用其probe方法if (ret < 0) {printk(KERN_ERR "asoc: failed to probe CPU DAI %s\n",cpu_dai->name);return ret;}}cpu_dai->probed = 1;	//设置dai设备(cpu_dai)probed标志/* mark cpu_dai as probed and add to card cpu_dai list */list_add(&cpu_dai->card_list, &card->dai_dev_list);	//添加dai设备(cpu_dai)到soc声卡设备的dai_dev_list链表}/* probe the CODEC */	//------------"probe" codec设备if (!codec->probed) {	//codec设备的probed标志为0if (codec->driver->probe) {	//codec驱动存在probe方法ret = codec->driver->probe(codec);	//则调用其probe方法if (ret < 0) {printk(KERN_ERR "asoc: failed to probe CODEC %s\n",codec->name);return ret;}}soc_init_codec_debugfs(codec);	//初始化codec/* mark codec as probed and add to card codec list */codec->probed = 1;	//codec设备的probed标志置1list_add(&codec->card_list, &card->codec_dev_list);	//添加codec设备到soc声卡设备的codec_dev_list链表}/* probe the platform */	//------------"probe" platform设备if (!platform->probed) {	//platform设备的probed标志位0if (platform->driver->probe) {	//platform设备驱动存在probe方法ret = platform->driver->probe(platform);	//则调用其probe方法if (ret < 0) {printk(KERN_ERR "asoc: failed to probe platform %s\n",platform->name);return ret;}}/* mark platform as probed and add to card platform list */platform->probed = 1;	//platform设备probed标志置1list_add(&platform->card_list, &card->platform_dev_list);	//添加platform设备到soc声卡设备的platform_dev_list链表}/* probe the CODEC DAI */	//------------"probe" dai(codec_dai)设备if (!codec_dai->probed) {	//dai设备(codec_dai)probed标志为0if (codec_dai->driver->probe) {	//dai设备驱动存在probe方法ret = codec_dai->driver->probe(codec_dai);	//则调用其probe方法if (ret < 0) {printk(KERN_ERR "asoc: failed to probe CODEC DAI %s\n",codec_dai->name);return ret;}}/* mark cpu_dai as probed and add to card cpu_dai list */codec_dai->probed = 1;	//设置dai设备(codec_dai)probed标志list_add(&codec_dai->card_list, &card->dai_dev_list);	//添加dai设备(codec_dai)到soc声卡设备的dai_dev_list链表}
//_____________________________________________________________________________________//	/* DAPM dai link stream work */INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);	//初始化工作队列/* now that all clients have probed, initialise the DAI link */if (dai_link->init) {	//dai link存在init方法ret = dai_link->init(rtd);	//则调用其init方法if (ret < 0) {printk(KERN_ERR "asoc: failed to init %s\n", dai_link->stream_name);return ret;}}/* Make sure all DAPM widgets are instantiated */snd_soc_dapm_new_widgets(codec);snd_soc_dapm_sync(codec);/* register the rtd device */rtd->dev.release = rtd_release;rtd->dev.init_name = dai_link->name;ret = device_register(&rtd->dev);	//注册soc pcm设备文件if (ret < 0) {printk(KERN_ERR "asoc: failed to register DAI runtime device %d\n", ret);return ret;}rtd->dev_registered = 1;ret = device_create_file(&rtd->dev, &dev_attr_pmdown_time);	//创建soc pcm设备文件if (ret < 0)printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n");/* add DAPM sysfs entries for this codec */ret = snd_soc_dapm_sys_add(&rtd->dev);if (ret < 0)printk(KERN_WARNING "asoc: failed to add codec dapm sysfs entries\n");/* add codec sysfs entries */ret = device_create_file(&rtd->dev, &dev_attr_codec_reg);	//创建soc pcm设备属性文件if (ret < 0)printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");/* create the pcm */ret = soc_new_pcm(rtd, num);	//创建新pcm设备if (ret < 0) {printk(KERN_ERR "asoc: can't create pcm %s\n", dai_link->stream_name);return ret;}/* add platform data for AC97 devices */if (rtd->codec_dai->driver->ac97_control)snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata);return 0;
}


 

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

4.1.soc_new_pcm

static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{struct snd_soc_codec *codec = rtd->codec;	//获取codec设备struct snd_soc_platform *platform = rtd->platform;	//获取soc平台struct snd_soc_dai *codec_dai = rtd->codec_dai;	//获取dai(codec_dai)设备struct snd_soc_dai *cpu_dai = rtd->cpu_dai;	//获取dai(cpu_dai)设备struct snd_pcm *pcm;	//pcm结构体声明char new_name[64];int ret = 0, playback = 0, capture = 0;/* check client and interface hw capabilities */snprintf(new_name, sizeof(new_name), "%s %s-%d",rtd->dai_link->stream_name, codec_dai->name, num);if (codec_dai->driver->playback.channels_min) //最小通道数不为0也就是存在回放通道playback = 1;     //则将其通道数设置为1if (codec_dai->driver->capture.channels_min)  //最小通道数不为0也就是存在捕捉通道capture = 1;      //则将其通道数设置为1dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);ret = snd_pcm_new(rtd->card->snd_card, new_name,num, playback, capture, &pcm);	//创建pcm声卡设备if (ret < 0) {printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);return ret;}rtd->pcm = pcm;	//捆绑soc pcm和pcm结构体pcm->private_data = rtd;	//soc pcm放在pcm结构体的私有数据段//soc_pcm_ops部分方法函数集设置为soc平台驱动对应的方法soc_pcm_ops.mmap = platform->driver->ops->mmap;soc_pcm_ops.pointer = platform->driver->ops->pointer;soc_pcm_ops.ioctl = platform->driver->ops->ioctl;soc_pcm_ops.copy = platform->driver->ops->copy;soc_pcm_ops.silence = platform->driver->ops->silence;soc_pcm_ops.ack = platform->driver->ops->ack;soc_pcm_ops.page = platform->driver->ops->page;if (playback)snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);if (capture)snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);ret = platform->driver->pcm_new(rtd->card->snd_card, codec_dai, pcm);	//调用soc平台的pcm_new方法if (ret < 0) {printk(KERN_ERR "asoc: platform pcm constructor failed\n");return ret;}pcm->private_free = platform->driver->pcm_free;printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,cpu_dai->name);return ret;
}

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

4.1.1 snd_pcm_set_ops

void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)
{struct snd_pcm_str *stream = &pcm->streams[direction];	//获取pcm流struct snd_pcm_substream *substream;for (substream = stream->substream; substream != NULL; substream = substream->next)	//遍历pcm子流substream->ops = ops;	//设置pcm子流的ops操作函数集
}

这个函数是设置了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组件)

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

 

//dai设备(cpu_dai)
davinci-mcasp.c/davinci-hdmi.c
{
snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]);
}//soc声卡设备
ti81xx-dvr.c
{
platform_device_alloc("soc-audio", 0);
platform_device_alloc("soc-audio", 1);
}//codec设备/dai设备(codec_dai)
ti81xx_hdmi.c /tlv320aic3x.c /tvp5158-audio.c
{
snd_soc_register_codec(&pdev->dev, &soc_codec_tvp5158,&tvp5158_dai, 1);
snd_soc_register_codec(&pdev->dev, &soc_codec_hdmi,&ti81xx_dai, 1);
snd_soc_register_codec(&i2c->dev,&soc_codec_dev_aic3x, &aic3x_dai, 1);
}//soc平台
davinci-pcm.c{
snd_soc_register_platform(&pdev->dev, &davinci_soc_platform);m
}

下面是文档的说明:

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的桥梁


http://www.ppmy.cn/news/703578.html

相关文章

UAP是什么

UAP是什么&#xff1f; 大中型企业与公共组织的统一应用平台 用友UAP&#xff08;Unified Application Platform&#xff09;是面向大中型企业与公共组织的统一应用平台&#xff0c;它是用友公司从多年应用软件研制过程中提炼出来的模型、模板、开发工具、应用框架、中间件、基…

黑苹果详细安装教程-基于OpenCore官网指导-UPUPMO(macOS Monterey)

文章大纲 01. 必备知识 02. 作者当前硬件说明 03. 主板 BIOS 版本升级 04. 确定声卡、网卡信息 05. 配置 EFI 驱动 06. 配置 ACPI&#xff08;SSDTs&#xff09; 07. 配置 config.plist 08. 制作启动盘&#xff08;苹果官网恢复镜像&#xff09; 09. 配置主板 BIOS 10. 开始安…

At91sam9261的AIC

摘自&#xff1a;http://blog.csdn.net/llf021421/article/details/6967116 1. 中断源使能 对于每一个中断源&#xff0c;包括中断源0中的FIQ&#xff0c;可以通过命令寄存器AIC_IECR(中断使能命令寄存器)和AIC_IDCR(中断禁用命令寄存器)被使能和禁止. 中断的屏蔽状态可以在A…

AH8696_非隔离IC,AC220V降12V500MA,超高效简约电路,电源芯片IC*

AC220V转换为DC12V非隔离AC-DC降压芯片型号是AH8696, AH8696是一款高性能低成本PWM 控制功率开关&#xff0c;适用于离线式小功率降压型应用场合&#xff0c;外围电路简单 、外围元件数目极少 应用方案&#xff0c;220V转直流12V&#xff0c;交流220V转5V&#xff0c;交流220V转…

Linux ALSA 之十:ALSA ASOC Machine Driver

ALSA ASOC Machine Driver 一、Machine 简介二、ASoC Machine Driver2.1 Machine Driver 的 Platform Driver & Platform Device 驱动模型2.2 在 Probe() 中注册声卡 三、snd_soc_register_card 函数3.1 bind DAIs3.2 New a sound card3.3 Create card new widgets3.4 Prob…

AURIX TC397 ADC EVADC EDSADC

目录 EVADCEDSADCExample_ADC_Queued_ScanExample_ADC_Filtering微信公众号 EVADC EVADC, Enhanced Versatile Analog-to-Digital Converter, 增强型多功能模数转换器. EVADC提供了一系列模拟输入通道, 这些通道使用逐次逼近寄存器(SAR, Successive Approximation Register)原…

ALC5631Q

ALC5631Q是瑞昱公司推出的一款高性能、强大的I2S音频编码解码器。宽压自适应电源以及低能耗的特点&#xff0c;使其广泛应用于便携式电子设备中&#xff0c;如&#xff1a;智能本、上网本。ALC5631Q有两种封装方式&#xff1a;ALC5631Q-GR、ALC5631Q-GRT。 ALC5631Q的采购信息…

I2C协议及PCA9685控制芯片

I2C总线协议 自驾仪的主控芯片一般具有多种资源与传感器或其他外设进行通信&#xff0c; 包括串口、I2C、SPI、QSPI等。I2C总线具有简单、有效的特点&#xff0c; 能够有效减少芯片管脚和线路连接的数量&#xff0c; 本文介绍的PCA9685舵机控制器就是采用I2C与主控芯片进行通信…