一、注册cpu_dai
前面说了machine是连接platform和Codec的桥梁,在machine章节中 zx297520v3_nau8810_dai_link中有cpu_dai_name和platform_name,如下:
static struct snd_soc_dai_link zx297520v3_nau8810_dai_link[] =
{
{
.name = "NAU8810_Media",
.stream_name = "MultiMedia",
.codec_name = "nau8810.1-001a", //nau8810.1-001a
.codec_dai_name = "nau8810-hifi",
.cpu_dai_name = "MultiMedia",
.ops = &zx297520v3_nau8810_ops,
.init = zx297520v3_nau8810_init_paiftx,
.platform_name = "zx29-pcm-audio",
},
{
.name = "voice_call",
.stream_name = "voice",
.codec_name = "nau8810.1-001a",
.codec_dai_name = "nau8810-hifi",
.cpu_dai_name = "voice", //"snd-soc-dummy-dai",
.platform_name = "snd-soc-dummy",
.init = zx297520v3_nau8810_init_paiftx,
.ops = &zx297520v3_nau8810_ops1,
}
};
代码工程中搜索cpu_dai_name “MultiMedia”,在zx29_i2s.c中找到snd_soc_dai_driver结构体:
static struct snd_soc_dai_driver zx_i2s_dais[] = {
{
.name = "MultiMedia",
.probe = zx29_i2s_dai_probe,
.suspend = zx29_i2s_suspend,
.resume = zx29_i2s_resume,
.playback = {
.stream_name = "MultiMedia Playback",
.channels_min = 1,
.channels_max = 8,
.rates = ZX29_I2S_RATES,
.formats = ZX29_I2S_FMTBIT,
},
.capture = {
.stream_name = "MultiMedia Capture",
.channels_min = 1,
.channels_max = 2,
.rates = ZX29_I2S_RATES,
.formats = ZX29_I2S_FMTBIT,
},
.ops = &zx29_i2s_dai_ops,
},
{
.name = "voice",
.probe = zx29_i2s_dai_probe1,
.playback = {
.stream_name = "voice Playback",
.rates = ZX29_I2S_RATES,
.formats = ZX29_I2S_FMTBIT,
.channels_min = 1,
.channels_max = 2,
},
.capture = {
.stream_name = "voice Capture",
.rates = ZX29_I2S_RATES,
.formats = ZX29_I2S_FMTBIT,
.channels_min = 1,
.channels_max = 2,
},
}
};
snd_soc_dai_driver结构体ops 指向snd_soc_dai_ops结构,用于配置和控制该dai;playback snd_soc_pcm_stream结构,用于指出该dai支持的声道数,码率,数据格式等能力;capture snd_soc_pcm_stream结构,用于指出该dai支持的声道数,码率,数据格式等能力;
snd_soc_dai_ops实例如下,
static const struct snd_soc_dai_ops zx29_i2s_dai_ops = {
.trigger = zx29_i2s_trigger,
.hw_params = zx29_i2s_hw_params,
.set_fmt = zx29_i2s_set_fmt,
.set_sysclk = zx29_i2s_set_sysclk,
.startup = zx29_i2s_startup,
.shutdown = zx29_i2s_shutdown,
};
trigger:数据传送的开始,暂停,恢复和停止时,该函数会被调用。
hw_params:驱动的hw_params阶段,该函数会被调用。
set_fmt:设置dai的格式
set_sysclk: 设置dai的主时钟
在整体看下注册cpu_dai的过程如下,通过snd_soc_register_dais就将上面说的snd_soc_dai_driver zx_i2s_dais关联起来了
zx29_i2s_dev_probe
—>snd_soc_register_dais
static __devinit int zx29_i2s_dev_probe(struct platform_device *pdev)
{
... ...
return snd_soc_register_dais(&pdev->dev, zx_i2s_dais, ARRAY_SIZE(zx_i2s_dais));
}
static struct platform_driver zx29_i2s_driver = {
.probe = zx29_i2s_dev_probe,
.remove = __devexit_p(zx29_i2s_dev_remove),
.driver = {
.name = "zx29_i2s",
.owner = THIS_MODULE,
},
};
二、注册platform
代码工程中搜索platform_name"zx29-pcm-audio",在zx29_pcm.c文件中找到platform_driver asoc_dma_driver如下:
static int __devinit zx29_asoc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&pdev->dev, &zx_asoc_platform);
}
static int __devexit zx29_asoc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&pdev->dev);
return 0;
}
static struct platform_driver asoc_dma_driver = {
.driver = {
.name = "zx29-pcm-audio",
.owner = THIS_MODULE,
},
.probe = zx29_asoc_platform_probe,
.remove = __devexit_p(zx29_asoc_platform_remove),
};
这里除了snd_soc_register_platform函数注册platform外,还要关注snd_soc_platform_driver zx_asoc_platform如下:
static struct snd_soc_platform_driver zx_asoc_platform = {
.ops = &dma_ops,
.pcm_new = zx29_dma_new,
.pcm_free = zx29_dma_free_dma_buffers,
};
static struct snd_pcm_ops dma_ops = {
.open = zx29_dma_open,
.close = zx29_dma_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = zx29_dma_hw_params,
.hw_free = zx29_dma_hw_free,
.prepare = zx29_dma_prepare,
.trigger = zx29_dma_trigger,
.pointer = zx29_dma_pointer,
.mmap = zx29_dma_mmap,
};
对于DMA的申请释、放使用pcm_new、pcm_free,而对于配置和开始传输使用dma_ops的操作函数集来完成。
至此ALSA驱动asoc框架之machine、ALSA驱动asoc框架之Codec、以及本章ALSA驱动asoc框架之Platform就简单说完了。