数字音频接口,即The Digital Audio Interface,简称DAI。
相关代码分析:
DAI driver
在codec驱动中注册驱动函数
int snd_soc_register_codec(struct device *dev,
const struct snd_soc_codec_driver *codec_drv,
struct snd_soc_dai_driver *dai_drv,
int num_dai)
注意,这里注册了两个驱动,一个是codec驱动,另一个dai驱动。在板子相关的注册声卡驱动时,成员dai_link中:
#define DEV_NAME_PCM "nxp-pcm"
static struct snd_soc_dai_link es8396_dai_link = {
.name = "ASOC-es8396",
.stream_name = "es8396 HiFi",
.cpu_dai_name = "nxp-i2s.0", /* nxp_snd_i2s_driver name */
.platform_name = DEV_NAME_PCM, /* nxp_snd_pcm_driver name */
.codec_dai_name = "es8396-sdp1", /* dai驱动的名称 */
.codec_name = "es8396-codec.0-0011", /* .0-001a es8396_i2c_driver name + '.' + bus + '-' + address(7bit) */
.ops = &es8396_ops,
.symmetric_rates = 1,
.init = es8396_dai_init,
//.ops = &es8396_ops,
};
//es8396.c
static struct snd_soc_dai_driver es8396_dai[] = {
{
.name = "es8396-sdp1",
.id = 0,
.playback = {
.stream_name = "SDP1 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = ES8396_RATES,
.formats = ES8396_FORMATS,
},
.capture = {
.stream_name = "SDP1 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = ES8396_RATES,
.formats = ES8396_FORMATS,
},
.ops = &es8396_aif1_dai_ops,
},
音频驱动包含了三部分,关于machine的,关于platform的,关于codec的。
codec_dai_name的名称即是es8396.c中定义的 snd_soc_dai_driver实体的名称。 codec级别
.cpu_dai_name指向的是nxp-i2.c中的i2s驱动,名称为“nxp-i2s” machine级别
.platform_name指向的是nxp-pcm.c中的驱动,相关代码如下: platform级别
static int nxp_pcm_new(struct snd_soc_pcm_runtime *runtime)
{
struct snd_card *card = runtime->card->snd_card;
struct snd_pcm *pcm = runtime->pcm;
int ret = 0;
/* dma mask */
if (!card->dev->dma_mask)
card->dev->dma_mask = &nxp_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = nxp_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto err;
}
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = nxp_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto err_free;
}
return 0;
err_free:
nxp_pcm_release_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
err:
return ret;
}
static void nxp_pcm_free(struct snd_pcm *pcm)
{
nxp_pcm_release_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
nxp_pcm_release_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
}
static struct snd_soc_platform_driver pcm_platform = {
.ops = &nxp_pcm_ops,
.pcm_new = nxp_pcm_new,
.pcm_free = nxp_pcm_free,
};
static int __devinit nxp_pcm_probe(struct platform_device *pdev)
{
int ret = snd_soc_register_platform(&pdev->dev, &pcm_platform);
printk(KERN_INFO "snd pcm: %s sound platform '%s'\n", ret?"fail":"register", pdev->name);
return ret;
}
static struct platform_device pcm_device = {
.name = DEV_NAME_PCM, //”nxp-pcm”
.id = -1,
};
static int __init nxp_pcm_init(void)
{
platform_device_register(&pcm_device);
return platform_driver_register(&pcm_driver);
}
cpu_dia
codec_name指向的是es8396驱动(I2C驱动,名称是”es8396-codec”, I2C在总线0上,I2C地址是0x11)。
在调用snd_soc_register_card()的过程中,根据定义好的card->dai_link来生成card->rtd,参考snd_soc_register_card()的代码:
/* bind DAIs */
for (i = 0; i < card->num_links; i++)
soc_bind_dai_link(card, i); //每次成功调用后card->num_rtd加1。
初始化成功后,card->num_rtd==card->num_link。
在 soc_bind_dai_link函数中,它根据card→dai_link中的cpu_dai_name、codec_dai_name 、codec_name 、platform_name、,分别生成card→rtd[i].cpu_dai、rtd-rtd→codec、rtd->codec_dai、rtd->codec、rtd->platform。