ALSA之CODEC分析
2012-01-11 17:54
363 查看
最近在调试alsa-soc 的驱动,下面转载的几篇文章对这些问题都有很详细的讲解,
转载自: http://blog.csdn.net/sepnic/article/details/6140911
ALSA: Advanced Linux Sound Architecture,它包括内核驱动集合、API库和工具。用户层程序直接调用libsound的API库,不需要打开设备等操作,因此编程者不需要了解底层细节。
这里不分析ALSA的核心代码core,也不阐述如何在用户层进行声卡编程,仅仅简要介绍在ALSA的架构上添加一个声卡驱动,即上图中的Sound Driver。其实文档《wirte an alsa driver》很详尽的介绍如何写一个ALSA驱动,但是那是以PCI声卡为例的。在嵌入式中,音频数据传输一般用I2S接口,控制一般用I2c或SPI接口。如下仅以嵌入式声卡为例,其驱动代码一般放在sound/soc下面。
以数据结构为线索,简要解析其过程。每个重要结构体旁边有个类似的标号[XX],[xx]为[0]时,表明这个结构体是一个大类,包含标号为[1]的结构体……;[xx]为[EXT]时,表明该结构体不在本模块使用。
CODEC
驱动代码位于sound/soc/codec下,如uda134x.c。
struct snd_soc_dai [0]
[cpp]
view plaincopyprint?
/*
* Digital Audio Interface runtime data.
*
* Holds runtime data for a DAI.
*/
struct snd_soc_dai {
/* DAI description */
char *name;
unsigned int id;
int ac97_control;
struct device *dev;
void *ac97_pdata; /* platform_data for the ac97 codec */
/* DAI callbacks */
int (*probe)(struct platform_device *pdev,
struct snd_soc_dai *dai);
void (*remove)(struct platform_device *pdev,
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 capabilities */
struct snd_soc_pcm_stream capture;
struct snd_soc_pcm_stream playback;
unsigned int symmetric_rates:1;
/* DAI runtime info */
struct snd_pcm_runtime *runtime;
struct snd_soc_codec *codec;
unsigned int active;
unsigned char pop_wait:1;
void *dma_data;
/* DAI private data */
void *private_data;
/* parent platform */
struct snd_soc_platform *platform;
struct list_head list;
};
小结:以上的结构体看起来复杂,实现上基本结构是非常简单易懂的。所要做的工作有:
1/定义snd_soc_dai和snd_soc_dai_ops这两个结构体,前者设置好capture & playback的参数和声卡函数操作集合指针,该指针指向snd_soc_dai_ops结构体;
2/根据硬件数据手册编写相关操作函数如hw_params、set_fmt和digital_mute等;
3/编写模块初始化函数uda134x_init(),调用snd_soc_register_dai()注册之前定义好的snd_soc_dai。
注:关于2的相关操作函数,之前也提过控制一般用I2C或SPI接口的。但是在操作函数里,我们可以使用codec->hw_write()来操作。当然在probe函数中,hw_write是在probe初始化好的,如codec->hw_write = (hw_write_t)i2c_master_send;,这就使得控制接口抽象起来。
struct snd_soc_codec_device [EXT]
接下来有一个结构体snd_soc_codec_device要留意的,一般来说,这个结构体是在codec下定义,但是注册操作是在另外一个文件中进行的,以2410的UDA134X为例是在sound/soc/s3c24xx/s3c24xx_uda134x.c。这些留到以后分析,这里只是需要将这个结构体EXPORT出来就行了如:EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);。
先看snd_soc_codec_device结构体定义:
[cpp]
view plaincopyprint?
/* codec device */
struct snd_soc_codec_device {
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
int (*suspend)(struct platform_device *pdev, pm_message_t state);
int (*resume)(struct platform_device *pdev);
};
开始看到socdev = platform_get_drvdata(pdev)这句不免有点疑惑,到底pdev是在哪里初始化好了?答案是sound/soc/<SOC>目录下的文件中,如sound/soc/s3c24xx/s3c24xx_uda134x.c。在声卡的初始化过程中,其实首先是调用sound/soc/<SOC>下的相关驱动的probe函数,在probe有platform_set_drvdata()的操作,这里有个将指针类型的转换:(struct snd_soc_device *s) ==> (struct
platform_device *)。
snd_soc_add_controls()将操作集合挂到card->control链表上来,这个集合实现了音频播放时各个参数的设置,主要有.info、.get和.set。如playback volume control:SOC_DOUBLE_R_TLV("Playback Volume", SNDCARD_REG_L_GAIN, SNDCARD_REG_R_GAIN, 0, 192, 0, digital_tlv),其中SNDCARD_REG_L_GAIN和SNDCARD_REG_R_GAIN分别是左右声道音量增益寄存器偏移。最终要调用的函数都是在soc-core.c里面的,这里只是提供一些跟硬件相关的参数,大为增加了代码的复用性。
对于函数snd_soc_new_pcms()是这样描述的:Create a new sound card based upon the codec and interface pcms.这个函数非常重要,用于创建一个PCM实例以便播放数据流。函数里重要的是如下两句:
[cpp]
view plaincopyprint?
ret = snd_card_create(idx, xid, codec->owner, 0, &codec->card);
ret = soc_new_pcm(socdev, &card->dai_link[i], i);
[cpp]
view plaincopyprint?
static struct snd_soc_device SOC_SNDCARD_snd_devdata = {
.card = &snd_soc_s3c24xx_uda134x,
.codec_dev = &soc_codec_dev_uda134x,//就是CODEC定义的snd_soc_codec_device结构体
.codec_data = &s3c24xx_uda134x, //私有数据,一般存放SNDCARD控制接口信息,如I2C从设备地址等
};
可以看到CODEC定义的struct snd_soc_codec_device soc_codec_dev_uda134x在这里进行platform_device_add的,随后便执行soc_codec_dev_uda134x.probe,其过程可以复习CODEC中的uda134x_soc_probe()。
接下来是s3c24xx_uda134x_snd_devdata.card的来历。
struct snd_soc_card [1]
[cpp]
view plaincopyprint?
/* SoC card */
struct snd_soc_card {
char *name;
struct device *dev;
struct list_head list;
int 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);
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link;
int num_links;
struct snd_soc_device *socdev;
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct delayed_work delayed_work;
struct work_struct deferred_resume_work;
};
s3c24xx_soc_platform是定义在PCM中的,包含一系列pcm_ops操作函数集合等信息,这里不谈。成员.dai_link是本模块中的另外一个主角。
struct snd_soc_dai_link [2]
[cpp]
view plaincopyprint?
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
struct snd_soc_dai_link {
char *name; /* Codec name */
char *stream_name; /* Stream name */
/* DAI */
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
/* machine stream operations */
struct snd_soc_ops *ops;
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_codec *codec);
/* Symmetry requirements */
unsigned int symmetric_rates:1;
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;
/* DAI pcm */
struct snd_pcm *pcm;
};
[cpp]
view plaincopyprint?
snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S);//设置为I2S mode
snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, SND_SOC_CLOCK_IN);//设置主时钟MCLK频率
snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S);//设置为I2S mode
snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, SND_SOC_CLOCK_IN);//设置主时钟MCLK频率
注:如果留意到CODEC小结中的2/,那么就会明白,在这里定义的操作最终会调用到CODEC(或许还有PCM、I2S中的)里定义好的set_fmt、set_sysclk等回调函数。
小结:这一层的重要的结构体是一脉相承的,并没有复杂的分支,除了codec_dev是从CODEC Export过来,platform从PCM Export过来,cpu_dai从I2S Export过来。函数主要是module_init()和一个snd_soc_ops操作函数集合。这一层负责将音频设备的几部分模块与CPU平台挂起来。
注:底层硬件操作—
CODEC:控制接口及芯片基本初始化
PCM:pcm dma操作
I2S:i2s配置操作
之后i2s和pcm其实都跟codec差不多了,只需要理解alsa-core、<soc>、<codec、pcm、i2s>三层的关系。其中codec、pcm、i2s可以看做同层的,分别对于音频设备的control、dma、i2s接口;<codec、pcm、i2s>会分别export相关结构体给<soc>层,<soc>层将音频设备三部分与CPU Spec联结起来,其probe顺序是<SOC>.probe-><codec, pcm, i2s>.probe;另外<codec、pcm、i2s>在各自的module_init中将自身注册到alsa-core中。
有空再写一些pcm dma方面的。
转载自: http://blog.csdn.net/sepnic/article/details/6140911
ALSA: Advanced Linux Sound Architecture,它包括内核驱动集合、API库和工具。用户层程序直接调用libsound的API库,不需要打开设备等操作,因此编程者不需要了解底层细节。
这里不分析ALSA的核心代码core,也不阐述如何在用户层进行声卡编程,仅仅简要介绍在ALSA的架构上添加一个声卡驱动,即上图中的Sound Driver。其实文档《wirte an alsa driver》很详尽的介绍如何写一个ALSA驱动,但是那是以PCI声卡为例的。在嵌入式中,音频数据传输一般用I2S接口,控制一般用I2c或SPI接口。如下仅以嵌入式声卡为例,其驱动代码一般放在sound/soc下面。
以数据结构为线索,简要解析其过程。每个重要结构体旁边有个类似的标号[XX],[xx]为[0]时,表明这个结构体是一个大类,包含标号为[1]的结构体……;[xx]为[EXT]时,表明该结构体不在本模块使用。
CODEC
驱动代码位于sound/soc/codec下,如uda134x.c。
struct snd_soc_dai [0]
[cpp]
view plaincopyprint?
/*
* Digital Audio Interface runtime data.
*
* Holds runtime data for a DAI.
*/
struct snd_soc_dai {
/* DAI description */
char *name;
unsigned int id;
int ac97_control;
struct device *dev;
void *ac97_pdata; /* platform_data for the ac97 codec */
/* DAI callbacks */
int (*probe)(struct platform_device *pdev,
struct snd_soc_dai *dai);
void (*remove)(struct platform_device *pdev,
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 capabilities */
struct snd_soc_pcm_stream capture;
struct snd_soc_pcm_stream playback;
unsigned int symmetric_rates:1;
/* DAI runtime info */
struct snd_pcm_runtime *runtime;
struct snd_soc_codec *codec;
unsigned int active;
unsigned char pop_wait:1;
void *dma_data;
/* DAI private data */
void *private_data;
/* parent platform */
struct snd_soc_platform *platform;
struct list_head list;
};
[cpp] view plaincopyprint? struct snd_soc_dai uda134x_dai = { .name = "UDA134X", /* playback capabilities */ .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* pcm operations */ .ops = &uda134x_dai_ops, }; struct snd_soc_dai uda134x_dai = { .name = "UDA134X", /* playback capabilities */ .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* pcm operations */ .ops = &uda134x_dai_ops, };
小结:以上的结构体看起来复杂,实现上基本结构是非常简单易懂的。所要做的工作有:
1/定义snd_soc_dai和snd_soc_dai_ops这两个结构体,前者设置好capture & playback的参数和声卡函数操作集合指针,该指针指向snd_soc_dai_ops结构体;
2/根据硬件数据手册编写相关操作函数如hw_params、set_fmt和digital_mute等;
3/编写模块初始化函数uda134x_init(),调用snd_soc_register_dai()注册之前定义好的snd_soc_dai。
注:关于2的相关操作函数,之前也提过控制一般用I2C或SPI接口的。但是在操作函数里,我们可以使用codec->hw_write()来操作。当然在probe函数中,hw_write是在probe初始化好的,如codec->hw_write = (hw_write_t)i2c_master_send;,这就使得控制接口抽象起来。
struct snd_soc_codec_device [EXT]
接下来有一个结构体snd_soc_codec_device要留意的,一般来说,这个结构体是在codec下定义,但是注册操作是在另外一个文件中进行的,以2410的UDA134X为例是在sound/soc/s3c24xx/s3c24xx_uda134x.c。这些留到以后分析,这里只是需要将这个结构体EXPORT出来就行了如:EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);。
先看snd_soc_codec_device结构体定义:
[cpp]
view plaincopyprint?
/* codec device */
struct snd_soc_codec_device {
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
int (*suspend)(struct platform_device *pdev, pm_message_t state);
int (*resume)(struct platform_device *pdev);
};
[cpp] view plaincopyprint? static int uda134x_soc_probe(struct platform_device *pdev) { //获得snd_soc_device结构体 struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec; … //为codec分配内存 socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (socdev->card->codec == NULL) return ret; codec = socdev->card->codec; … //初始化codec codec->name = "uda134x"; codec->owner = THIS_MODULE; codec->dai = &uda134x_dai; //指向上面定义好的dai codec->num_dai = 1; codec->read = uda134x_read_reg_cache; //控制接口—读 codec->write = uda134x_write; //控制接口—写 … mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); … /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); … ret = snd_soc_add_controls(codec, uda134x_snd_controls, ARRAY_SIZE(uda134x_snd_controls)); … /* register card */ ret = snd_soc_init_card(socdev); } static int uda134x_soc_probe(struct platform_device *pdev) { //获得snd_soc_device结构体 struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec; … //为codec分配内存 socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (socdev->card->codec == NULL) return ret; codec = socdev->card->codec; … //初始化codec codec->name = "uda134x"; codec->owner = THIS_MODULE; codec->dai = &uda134x_dai; //指向上面定义好的dai codec->num_dai = 1; codec->read = uda134x_read_reg_cache; //控制接口—读 codec->write = uda134x_write; //控制接口—写 … mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); … /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); … ret = snd_soc_add_controls(codec, uda134x_snd_controls, ARRAY_SIZE(uda134x_snd_controls)); … /* register card */ ret = snd_soc_init_card(socdev); }
开始看到socdev = platform_get_drvdata(pdev)这句不免有点疑惑,到底pdev是在哪里初始化好了?答案是sound/soc/<SOC>目录下的文件中,如sound/soc/s3c24xx/s3c24xx_uda134x.c。在声卡的初始化过程中,其实首先是调用sound/soc/<SOC>下的相关驱动的probe函数,在probe有platform_set_drvdata()的操作,这里有个将指针类型的转换:(struct snd_soc_device *s) ==> (struct
platform_device *)。
snd_soc_add_controls()将操作集合挂到card->control链表上来,这个集合实现了音频播放时各个参数的设置,主要有.info、.get和.set。如playback volume control:SOC_DOUBLE_R_TLV("Playback Volume", SNDCARD_REG_L_GAIN, SNDCARD_REG_R_GAIN, 0, 192, 0, digital_tlv),其中SNDCARD_REG_L_GAIN和SNDCARD_REG_R_GAIN分别是左右声道音量增益寄存器偏移。最终要调用的函数都是在soc-core.c里面的,这里只是提供一些跟硬件相关的参数,大为增加了代码的复用性。
对于函数snd_soc_new_pcms()是这样描述的:Create a new sound card based upon the codec and interface pcms.这个函数非常重要,用于创建一个PCM实例以便播放数据流。函数里重要的是如下两句:
[cpp]
view plaincopyprint?
ret = snd_card_create(idx, xid, codec->owner, 0, &codec->card);
ret = soc_new_pcm(socdev, &card->dai_link[i], i);
[cpp] view plaincopyprint? /* SoC Device - the audio subsystem */ struct snd_soc_device { struct device *dev; struct snd_soc_card *card; struct snd_soc_codec_device *codec_dev; void *codec_data; }; /* SoC Device - the audio subsystem */ struct snd_soc_device { struct device *dev; struct snd_soc_card *card; struct snd_soc_codec_device *codec_dev; void *codec_data; };这个结构体用于向内核注册一个device。初始化一般如下:
[cpp]
view plaincopyprint?
static struct snd_soc_device SOC_SNDCARD_snd_devdata = {
.card = &snd_soc_s3c24xx_uda134x,
.codec_dev = &soc_codec_dev_uda134x,//就是CODEC定义的snd_soc_codec_device结构体
.codec_data = &s3c24xx_uda134x, //私有数据,一般存放SNDCARD控制接口信息,如I2C从设备地址等
};
[cpp] view plaincopyprint? static int s3c24xx_uda134x_probe(struct platform_device *pdev) { s3c24xx_uda134x_snd_devdata.codec_dev = &soc_codec_dev_uda134x; s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1); platform_set_drvdata(s3c24xx_uda134x_snd_device, & s3c24xx_uda134x_snd_devdata); s3c24xx_uda134x_snd_devdata.dev = & s3c24xx_uda134x_snd_device->dev; platform_device_add(s3c24xx_uda134x_snd_device); } static int s3c24xx_uda134x_probe(struct platform_device *pdev) { s3c24xx_uda134x_snd_devdata.codec_dev = &soc_codec_dev_uda134x; s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1); platform_set_drvdata(s3c24xx_uda134x_snd_device, & s3c24xx_uda134x_snd_devdata); s3c24xx_uda134x_snd_devdata.dev = & s3c24xx_uda134x_snd_device->dev; platform_device_add(s3c24xx_uda134x_snd_device); }
可以看到CODEC定义的struct snd_soc_codec_device soc_codec_dev_uda134x在这里进行platform_device_add的,随后便执行soc_codec_dev_uda134x.probe,其过程可以复习CODEC中的uda134x_soc_probe()。
接下来是s3c24xx_uda134x_snd_devdata.card的来历。
struct snd_soc_card [1]
[cpp]
view plaincopyprint?
/* SoC card */
struct snd_soc_card {
char *name;
struct device *dev;
struct list_head list;
int 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);
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link;
int num_links;
struct snd_soc_device *socdev;
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct delayed_work delayed_work;
struct work_struct deferred_resume_work;
};
[cpp] view plaincopyprint? static struct snd_soc_card snd_soc_s3c24xx_uda134x = { .name = "S3C24XX_UDA134X", .platform = &s3c24xx_soc_platform, .dai_link = &s3c24xx_uda134x_dai_link, .num_links = 1, }; static struct snd_soc_card snd_soc_s3c24xx_uda134x = { .name = "S3C24XX_UDA134X", .platform = &s3c24xx_soc_platform, .dai_link = &s3c24xx_uda134x_dai_link, .num_links = 1, };
s3c24xx_soc_platform是定义在PCM中的,包含一系列pcm_ops操作函数集合等信息,这里不谈。成员.dai_link是本模块中的另外一个主角。
struct snd_soc_dai_link [2]
[cpp]
view plaincopyprint?
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
struct snd_soc_dai_link {
char *name; /* Codec name */
char *stream_name; /* Stream name */
/* DAI */
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
/* machine stream operations */
struct snd_soc_ops *ops;
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_codec *codec);
/* Symmetry requirements */
unsigned int symmetric_rates:1;
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;
/* DAI pcm */
struct snd_pcm *pcm;
};
[cpp] view plaincopyprint? /* SoC audio ops */ struct snd_soc_ops { int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); int (*hw_free)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); int (*trigger)(struct snd_pcm_substream *, int); }; /* SoC audio ops */ struct snd_soc_ops { int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); int (*hw_free)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); int (*trigger)(struct snd_pcm_substream *, int); };在这里,一般需要实现.hw_paras、.startup和.shutdown,这些均是对pcm substream进行操作的。例如在s3c24xx_soc_hw_params()中,有:
[cpp]
view plaincopyprint?
snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S);//设置为I2S mode
snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, SND_SOC_CLOCK_IN);//设置主时钟MCLK频率
snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S);//设置为I2S mode
snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, SND_SOC_CLOCK_IN);//设置主时钟MCLK频率
注:如果留意到CODEC小结中的2/,那么就会明白,在这里定义的操作最终会调用到CODEC(或许还有PCM、I2S中的)里定义好的set_fmt、set_sysclk等回调函数。
小结:这一层的重要的结构体是一脉相承的,并没有复杂的分支,除了codec_dev是从CODEC Export过来,platform从PCM Export过来,cpu_dai从I2S Export过来。函数主要是module_init()和一个snd_soc_ops操作函数集合。这一层负责将音频设备的几部分模块与CPU平台挂起来。
注:底层硬件操作—
CODEC:控制接口及芯片基本初始化
PCM:pcm dma操作
I2S:i2s配置操作
之后i2s和pcm其实都跟codec差不多了,只需要理解alsa-core、<soc>、<codec、pcm、i2s>三层的关系。其中codec、pcm、i2s可以看做同层的,分别对于音频设备的control、dma、i2s接口;<codec、pcm、i2s>会分别export相关结构体给<soc>层,<soc>层将音频设备三部分与CPU Spec联结起来,其probe顺序是<SOC>.probe-><codec, pcm, i2s>.probe;另外<codec、pcm、i2s>在各自的module_init中将自身注册到alsa-core中。
有空再写一些pcm dma方面的。
相关文章推荐
- ALSA之CODEC分析
- 分析Linux ALSA系统架构以及Codec简介
- ALSA之CODEC分析
- Alsa音频子系统Codec---al5623.c内核代码框架分析
- ALSA之CODEC分析
- ALSA之CODEC分析
- ALSA之CODEC分析 一
- ALSA之Codec分析
- ALSA之CODEC分析
- ALSA之CODEC分析
- x264 编码器选项分析 (x264 Codec Strong and Weak Points) 1
- alsa声卡分析alsa-utils调用过程(一)-tinyplay
- Netty5源码分析(六) -- CodeC编解码分析
- FFMPEG结构体分析:AVCodecContext
- MediaCodec与ACodec通知分析
- ffdshow 源代码分析 7: libavcodec视频解码器类(TvideoCodecLibavcodec)
- x264 编码器选项分析 (x264 Codec Strong and Weak Points) 1
- ALSA之PCM分析
- alsa架构分析
- ASoC驱动开发 之 Codec芯片ALC5677 驱动代码分析