Asoc : ALSA system on chip 即嵌入式设备中的 ALSA
分为三个部分:
以展讯 Sc882x 平台为例分析
注: 代码相关的描述,只写出我们关心的部分
1. Machine : 与具体的单板相关
kernel\sound\soc\sprd\Sc882x.c
sc882x_modinit
{
//构造名为 soc-audio 的平台设备,根据 Linux 设备驱动模型,必定有一个名为 soc-audio 的平台驱动
platform_device_alloc("
soc-audio", -1);
platform_set_drvdata(sc882x_snd_device, &sc882x_card);
platform_device_add(sc882x_snd_device);
}
声卡结构:
static struct snd_soc_card sc882x_card =
{
.name = "sprdphone",
.dai_link = sc882x_dai,
}
// 这个结构体指定了 Codec、Codec Dai 和 Cpu、Cpu Dai 的名称
static
struct snd_soc_dai_link sc882x_dai[] =
{
.name = "sc882x-vbc",
.stream_name = "vbc-dac",
.codec_name = "sprd-codec",
.platform_name = "sprd-pcm-audio",
.cpu_dai_name = "vbc",
.codec_dai_name = "sprd-codec-i2s",
}
kernel\sound\soc\soc-core.c
static struct platform_driver soc_driver = {
.driver = {
.name = "
soc-audio",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = soc_probe,
.remove = soc_remove,
};
snd_soc_init
{
platform_driver_register(&soc_driver);
}
根据总线设备驱动模型的原理,当发现有一个名为 soc-audio 的平台设备,则调用 soc_probe 函数
soc_probe
{
// 注册声卡
snd_soc_register_card
}
如何注册?
snd_soc_register_card
{
//先分配一个 struct snd_soc_pcm_runtime *rtd 结构
// 再把 struct snd_soc_card sc882x_card -> struct snd_soc_dai_link sc882x_dai 的数据copy到
// struct snd_soc_pcm_runtime *rtd->dai_link
card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),GFP_KERNEL);
for (i = 0; i < card->num_links; i++)
card->rtd[i].dai_link = &card->dai_link[i];
//实例化一个声卡结构
snd_soc_instantiate_cards();
snd_soc_instantiate_card
}
如何实例化?
snd_soc_instantiate_card
{
根据 snd_soc_card 中的 dai_link 中的 platform、cpu_dai、codec_dai、codec 的名字在各自链绑定对应的结构
soc_bind_dai_link(card, i);
//声卡从设备的绑定完成后,开始创建声卡
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
//探测 platform、cpu_dai、codec_dai、codec 并执行各自 probe函数,最后创建PCM数据流
soc_probe_dai_link
{
.....
soc_new_pcm(rtd, num);//创建 PCM 设备
{
snd_pcm_new
static struct snd_device_ops
ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
//用来注册PCM设备
.dev_disconnect = snd_pcm_dev_disconnect,
};
//创建 播放 和 录音 数据流
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)
创建一个声卡的 PCM 从设备 PCM 逻辑设备,指定对应的 struct snd_device_ops ops,在稍后的注册声卡设备时会
调用snd_pcm_dev_register完成PCM设备的注册
snd_device_new(card, SNDRV_DEV_PCM, pcm, &
ops)
{
...
dev->ops = ops;
}
//指定播放和录音流的 PCM ops,间接调用到ALSA驱动的各个函数
static struct snd_pcm_ops
soc_pcm_ops = {
.open = soc_pcm_open,
.close = soc_codec_close,
.hw_params = soc_pcm_hw_params,
.hw_free = soc_pcm_hw_free,
.prepare = soc_pcm_prepare,
.trigger = soc_pcm_trigger,
.pointer = soc_pcm_pointer,
};
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &
soc_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &
soc_pcm_ops);
//调用 kernel\sound\soc\sprd\sprd_pcm.c 中的 .pcm_new = sprd_pcm_new,
platform->driver->pcm_new
}
}
// 注册声卡
snd_card_register
snd_device_register_all
//
会调用刚刚 pcm 设备的 snd_pcm_dev_register,来注册声卡上从设备,也包括其他的 Control.c、Hwdep.c、Rawmidi.c 等文件描述的声卡从设备
dev->ops->dev_register(dev)
snd_pcm_dev_register
{
case SNDRV_PCM_STREAM_PLAYBACK:
sprintf(str, "
pcmC%iD%ip", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
break;
case SNDRV_PCM_STREAM_CAPTURE:
sprintf(str, "
pcmC%iD%ic", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
// 注册声卡的从设备 PCM,最后会调用 device_create 创建 pcm 设备文件
snd_register_device_for_dev(devtype, pcm->card, pcm->device,&
snd_pcm_f_ops[cidx],pcm, str, dev)
}
// PCM 设备文件的 file_operation 结构
const struct file_operations
snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
}