ASOC之machine driver浅析(一)

terminology

硬件特性:所谓的硬件特性指:DAIs 之间的链结;通过某个 GPIO 打开 Amplifier;通过某个 GPIO 检测耳机插拔;使用某个时钟如 MCLK/External OSC 作为 I2S、CODEC 模块的基准时钟源等等

移植性:platform 和 CODEC 驱动一般是可以重用的,而 Machine 有它特定的硬件特性,几乎是不可重用的。

overview

仅有 codec、platform 驱动是不能工作的,需要一个角色把 codec、codec_dai、cpu_dai、pcm_dma 给链结起来才能构成一个完整的音频回路,这个角色就由
machine_drv 承担了。
顺带引入kernel 对应的参考文献可自行了解 目录:Document/sound/alsa/soc/machine.txt

功能特性

链结platform和codec载体

作为一个链结,如上构成音频链路,所需 struct snd_soc_dai_link.承担媒介的作用。对应codec有的codec_name,codec_dai_name成员.对应platform 有platform_name,platform_dai_name成员。

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 struct device_node  *codec_of_node ;
const char  *platform_name ; /* for multi-platform */
const struct device_node  *platform_of_node ;
const char  *cpu_dai_name ;
const struct device_node  *cpu_dai_of_node ;
const char  *codec_dai_name ;
unsigned int dai_fmt ; /* format to set on init */
/* Keep DAI active over suspend */
unsigned int ignore_suspend;
/* Symmetry requirements */
unsigned int symmetric_rates;
/* pmdown_time is ignored at stop */
unsigned int ignore_pmdown_time;
/* 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 ;
};
个人理解:成员ignore_suspend 以及 ignore_pmdown_time 是针对Android的睡眠唤醒机制,expect snd_soc_dai_link,machine drv可以加入音频控件,音频事件,例如耳机插拔,外部功放的开关,正因有如上(两个成员的支持)可以实现实现在Android浅睡眠(early suspend)的状态下,对插拔的检测。

声卡注册

函数嵌调时序图流程浅析:
在soc_probe中,或许平台私有数据,里面存有snd_soc_card实例,代码如下

static int soc_probe(struct platform_device *pdev)
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);

	/*
	 * no card, so machine driver should be registering card
	 * we should not be here in that case so ret error
	 */
	if (!card)
		return -EINVAL;

	dev_warn(&pdev->dev,
		 "ASoC: machine %s should use snd_soc_register_card()\n",
		 card->name);

	/* Bodge while we unpick instantiation */
	card->dev = &pdev->dev;

	return snd_soc_register_card(card);
}

snd_soc_register_card()为每个 dai_link 分配一个 snd_soc_pcm_runtime 实例,别忘了之前提过 snd_soc_pcm_runtime是 ASoC 的桥梁,保存着 codec、codec_dai、platform、cpu_dai、pcm_dma 等 runtime devices。每个 snd_soc_pcm_runtime都对应着各自的 dai_link;

随后的工作都在 snd_soc_instantiate_card()进行:

遍历 dai_list、codec_list、platform_list 链表,为每个音频链路匹配 cpu_dai、codec_dai、codec、platform;找到的 cpu_dai、codec_dai、codec、platform 实例保存到 dai_link 对应的 snd_soc_pcm_runtime 中,完成 dai_link 的 runtime devices 绑定工作;

初始化 codec 的寄存器缓存,调用 snd_card_create()创建声卡实例;

soc_probe_dai_link()依次回调 cpu_dai、codec、platform、codec_dai 的 probe()函数,完成各个子设备的初始化,随后调用 **soc_new_pcm()**创建 pcm 实例;

最后调用 snd_card_register()注册声卡。

参考资料:
http://blog.csdn.net/sepnic

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是将TinyALSA转换为ALSA SoC驱动程序的示例代码: 1. 定义硬件描述符 ``` static struct snd_soc_dai_driver my_dai = { .name = "my_dai", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, }; ``` 2. 注册SoC驱动程序 ``` static int my_probe(struct platform_device *pdev) { int ret; ret = snd_soc_register_dai(&pdev->dev, &my_dai); if (ret) { dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret); return ret; } return 0; } static int my_remove(struct platform_device *pdev) { snd_soc_unregister_dai(&pdev->dev); return 0; } static const struct of_device_id my_of_match[] = { { .compatible = "my,codec" }, {}, }; MODULE_DEVICE_TABLE(of, my_of_match); static struct platform_driver my_driver = { .driver = { .name = "my-driver", .owner = THIS_MODULE, .of_match_table = my_of_match, }, .probe = my_probe, .remove = my_remove, }; module_platform_driver(my_driver); ``` 3. 在SoC DAI中实现PCM操作 ``` static int my_dai_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; switch (cmd) { case SNDRV_PCM_TRIGGER_START: // 实现开始播放的操作 break; case SNDRV_PCM_TRIGGER_STOP: // 实现停止播放的操作 break; } return 0; } static int my_dai_playback_prepare(struct snd_pcm_substream *substream) { return 0; } static struct snd_soc_ops my_dai_ops = { .trigger = my_dai_playback_trigger, .prepare = my_dai_playback_prepare, }; static struct snd_soc_dai_driver my_dai = { .name = "my_dai", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &my_dai_ops, }; ``` 以上是将TinyALSA转换为ALSA SoC驱动程序的示例代码,其中包括定义硬件描述符、注册SoC驱动程序、在SoC DAI中实现PCM操作等步骤。开发者可以根据自己的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值