目录

  • 1. 基本介绍
  • 2. 源码分析
    • 2.1. Machine数据结构 struct snd_soc_dai_link
  • 3. 声卡
    • 3.1. 数据结构struct snd_soc_card
    • 3.2. 注册声卡 snd_soc_register_card()
    • 3.3. soc_probe()函数

1. 基本介绍

Linux 音频驱动(二) ASoC音频驱动之Platform驱动和Linux 音频驱动(三) ASoC音频驱动之Codec驱动分别介绍了platform驱动、codec驱动,但仅有platform驱动、codec驱动是不能工作的,需要一个角色把codec、codec_dai、cpu_dai、platform给链结起来才能构成一个完整的音频回路,这个角色就由machine承担了。
Machine 可以理解为对开发板的抽象,开发板可能包括多个声卡,对应machine部分包含多个link。

注:上面论述中出现了两种platform,其实它们本质是不一样的。
还记得在platform驱动中,PCM DMA是用struct snd_soc_platform_driver描述的吗?所以红色字体的platform其实指代PCM DMA。

对于Machine 驱动程序Linux官方解释如下: Machine class driver: The machine driver class acts as the glue that describes and binds the other component drivers together to form an ALSA “sound card device”. It handles any machine specific controls and machine level audio events (e.g. turning on an amp at start of playback).
Machine 驱动程序充当描述和绑定其他组件驱动程序以形成ALSA “声卡设备” 的粘合剂。它可以处理任何机器特定的控制和机器级音频事件(例如,在播放开始时打开放大器)。

从这论述中我们可以得出:
Machine 驱动是粘合剂,它将其他组件驱动 “粘在一起” 形成了 “声卡设备”。

2. 源码分析

Kernel 版本:3.10
Machine 驱动控制管理platform和codec之间的连接匹配,其抽象的结构体为struct snd_soc_dai_link。

2.1. Machine数据结构 struct snd_soc_dai_link

struct snd_soc_dai_link {/* config - must be set by machine driver */const char *name;           /* Codec name */const char *stream_name;        /* Stream name *//** You MAY specify the link's CPU-side device, either by device name,* or by DT/OF node, but not both. If this information is omitted,* the CPU-side DAI is matched using .cpu_dai_name only, which hence* must be globally unique. These fields are currently typically used* only for codec to codec links, or systems using device tree.*/const char *cpu_name;const struct device_node *cpu_of_node;/** You MAY specify the DAI name of the CPU DAI. If this information is* omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node* only, which only works well when that device exposes a single DAI.*/const char *cpu_dai_name;/** You MUST specify the link's codec, either by device name, or by* DT/OF node, but not both.*/const char *codec_name;const struct device_node *codec_of_node;/* You MUST specify the DAI name within the codec */const char *codec_dai_name;/** You MAY specify the link's platform/PCM/DMA driver, either by* device name, or by DT/OF node, but not both. Some forms of link* do not need a platform.*/const char *platform_name;const struct device_node *platform_of_node;int be_id;    /* optional ID for machine driver BE identification */const struct snd_soc_pcm_stream *params;unsigned int dai_fmt;           /* format to set on init */enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM *//* Keep DAI active over suspend */unsigned int ignore_suspend:1;/* Symmetry requirements */unsigned int symmetric_rates:1;/* Do not create a PCM for this DAI link (Backend link) */unsigned int no_pcm:1;/* This DAI link can route to other DAI links at runtime (Frontend)*/unsigned int dynamic:1;/* pmdown_time is ignored at stop */unsigned int ignore_pmdown_time:1;/* codec/machine specific init - e.g. add machine controls */int (*init)(struct snd_soc_pcm_runtime *rtd);/* optional hw_params re-writing for BE and FE sync */int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,struct snd_pcm_hw_params *params);/* machine stream operations */const struct snd_soc_ops *ops;const struct snd_soc_compr_ops *compr_ops;
};

注释比较详细,重点介绍如下几个字段:

codec_name:音频链路需要绑定的codec名称标识,soc-core中会遍历codec_list,找到同名的codec并绑定;
platform_name:音频链路需要绑定的platform名称标识,soc-core中会遍历platform_list,找到同名的platform并绑定;
cpu_dai_name:音频链路需要绑定的cpu_dai名称标识,soc-core中会遍历dai_list,找到同名的dai并绑定;
codec_dai_name:音频链路需要绑定的codec_dai名称标识,soc-core中会遍历dai_list,找到同名的dai并绑定;
ops:machine数据流操作函数集。重点留意hw_params回调,一般来说这个回调是要实现的,用于配置codec、cpu_dai的时钟、格式。

3. 声卡

3.1. 数据结构struct snd_soc_card

/* SoC card */
struct snd_soc_card {const char *name;......bool instantiated;int (*probe)(struct snd_soc_card *card);....../* CPU <--> Codec DAI links  */struct snd_soc_dai_link *dai_link;int num_links;struct snd_soc_pcm_runtime *rtd;int num_rtd;......const struct snd_kcontrol_new *controls;int num_controls;......
};

注释比较详细,重点介绍如下几个字段:

instantiated:用于标记声卡是否已经初始化完毕,在snd_soc_instantiate_card()函数最后会置1;
probe:声卡的probe函数,由snd_soc_instantiate_card()回调;
dai_link:声卡音频回路集合指针,即machine driver指针;
rtd:非常重要的数据指针,整个ASoC都以snd_soc_pcm_runtime为桥梁来操作,可以这么理解:每一个音频物理链路对应一个dai_link,而每个dai_link都有着自身的设备私有数据,这些私有数据保存在snd_soc_pcm_runtime中;
controls:声卡控件指针;

3.2. 注册声卡 snd_soc_register_card()

在我们学习音频驱动时,多次碰到了platform。但是,每次提platform时都要结合上下文,才能确定其真正指代什么。要注意区分:
1. 当我们谈ASoC驱动包含platform driver、codec driver、machine driver时,此时说的platform特指某款SoC平台,如exynos、omap、qcom、mtk等等;
2. 当我们谈machine driver时,即snd_soc_dai_link时,此时说的platform特指PCM DMA,即snd_soc_platform_driver;
3. 当我们谈注册声卡时,声卡被抽象为挂在platform bus上的platform device。因此,才会有创建名字为 “soc-audio” 的标准Linux平台设备(platform_device),注册标准的Linux平台驱动(platform_driver),此时说的platform特指虚拟的platform bus。

下面我们用实例来看一下注册声卡的过程,本例是MTK平台声卡注册实例。
./kernel-3.10/sound/soc/soc-core.c 里将声卡驱动定义为了标准的Linux平台驱动platform_driver,其name=“soc-audio”。

// ./kernel-3.10/sound/soc/soc-core.c, line 1945
static struct platform_driver soc_driver = {.driver        = {.name       = "soc-audio",.owner     = THIS_MODULE,.pm      = &snd_soc_pm_ops,},.probe     = soc_probe,.remove        = soc_remove,
};
// ./kernel-3.10/sound/soc/soc-core.c, line 4451
static int __init snd_soc_init(void)
{......return platform_driver_register(&soc_driver);
}

./kernel-3.10/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_machine.c 里定义了mt_soc_dai_common[],本例列出了两个音频链路,分别用于MultiMedia1和HDMI_OUT。

// ./kernel-3.10/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_machine.c, line 633
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt_soc_dai_common[] =
{{.name = "MultiMedia1",.stream_name = MT_SOC_DL1_STREAM_NAME,.cpu_dai_name   = MT_SOC_DL1DAI_NAME,.platform_name  = MT_SOC_DL1_PCM,.codec_dai_name = MT_SOC_CODEC_TXDAI_NAME,.codec_name = MT_SOC_CODEC_NAME,.init = mt_soc_audio_init,.ops = &mt_machine_audio_ops,},......{.name = "HDMI_OUT",.stream_name = MT_SOC_HDMI_STREAM_NAME,.cpu_dai_name   = MT_SOC_HDMI_NAME,.platform_name  = MT_SOC_HDMI_PCM,.codec_dai_name = MT_SOC_CODEC_HDMI_DUMMY_DAI_NAME,.codec_name = MT_SOC_CODEC_DUMMY_NAME,.init = mt_soc_audio_init,.ops = &mt_machine_audio_ops,},......
}

使用自定义的snd_soc_dai_link填充snd_soc_card_mt.dai_link数据成员,如下代码段第4行。
创建声卡设备(标准的Linux platform_device),如下代码段:
第19行,调用platform_device_alloc()分配一个name="soc-audio"的platform_device实例;
第21行,调用platform_set_drvdata()设定platform_device的私有数据指向snd_soc_card;
第22行,调用platform_device_add()注册platform_device实例到系统中;此时,platform_device的name和platform_driver的name匹配成功,调用platform_driver里定义的probe函数,即调用soc_probe()。

// ./kernel-3.10/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_machine.c, line 925
static struct snd_soc_card snd_soc_card_mt =
{.name       = "mt-snd-card",.dai_link   = mt_soc_dai_common,.num_links  = ARRAY_SIZE(mt_soc_dai_common),.controls = mt_soc_controls,.num_controls = ARRAY_SIZE(mt_soc_controls),
};static struct platform_device *mt_snd_device;static int __init mt_soc_snd_init(void)
{int ret;struct snd_soc_card *card = &snd_soc_card_mt;printk("mt_soc_snd_init card addr = %p \n", card);mt_snd_device = platform_device_alloc("soc-audio", -1);......platform_set_drvdata(mt_snd_device, &snd_soc_card_mt);ret = platform_device_add(mt_snd_device);......
}

3.3. soc_probe()函数

soc_probe()函数直接调用snd_soc_register_card(),完成声卡的注册。
snd_soc_register_card()时序图如下,重点的步骤已经注释或标注为红色,需要重点关注。

Linux 音频驱动(四) ASoC音频驱动之Machine驱动相关推荐

  1. Linux 音频驱动(一) ASoC音频框架简介

    目录 1. ALSA简介 2. ASoC音频驱动构成 3. PCM数据流 4. 数据结构简介 5. ASoC音频驱动注册流程 1. ALSA简介 Native ALSA Application:tin ...

  2. Android音频开发(四):音频播放模式

    一.Android音频开发(一):音频基础知识 二.Android音频开发(二):录制音频(WAV及MP3格式) 三.Android音频开发(三):使用ExoPlayer播放音频 四.Android音 ...

  3. Linux 音频驱动(三) ASoC音频驱动之Codec驱动

    目录 1. 简介 2. 源码分析 2.1. Codec 2.1.1. 数据结构struct snd_soc_codec_driver 2.1.2. 注册Codec 2.2. Codec DAI 2.2 ...

  4. Linux 音频驱动(二) ASoC音频驱动之Platform驱动

    目录 1. 简介 2. 源码分析 2.1. CPU DAI 2.1.1. 数据结构struct snd_soc_dai_driver 2.1.2. 注册CPU DAI:snd_soc_register ...

  5. Linux 音频驱动(五) ALSA音频驱动之PCM逻辑设备

    目录 1. 前言 2. PCM逻辑设备 2.1. 创建 PCM逻辑设备: 2.2. PCM逻辑设备文件操作函数集:snd_pcm_f_ops[] 2.3. Open PCM逻辑设备 2.4. Writ ...

  6. Android音频开发(一):音频基础知识

    一.Android音频开发(一):音频基础知识 二.Android音频开发(二):录制音频(WAV及MP3格式) 三.Android音频开发(三):使用ExoPlayer播放音频 四.Android音 ...

  7. linux驱动:音频驱动(三)ASoc之machine驱动及card初始化

    一.machine驱动及card初始化

  8. 音频2-ALSA/ASOC音频驱动框架

    计划分成下面8章来详细展开,后面再根据实际情况做调整. 1.基础知识(硬件,音频相关概念) 2.ALSA/ASOC音频驱动框架 3.codec 驱动dapm 相关(kcontrol.widget.ro ...

  9. Linux ALSA音频驱动一:音频系统概述

    音频系统概述 音频系统通过数据+控制总线与CODEC连接,控制通路用I2C,数据通常为I2S,框图如图1所示. I2C:寄存器读写,用于配置CODEC控制通路. I2S:音频数据传输,通常与platf ...

最新文章

  1. 如何发布自己的NPM包(模块)?
  2. 贾扬清感谢信:阿里开源10年,致敬千万开源人
  3. SecureCR 改变背景色和文字颜色
  4. grep 打印出过滤字段及后3行内容
  5. 菠萝蜜\菠萝蜜和榴莲有什么区别?
  6. eclipse android或者Java应用查看jdk路径和版本与android studio查看jdk版本
  7. 曾365天排队,如今被嫌弃!火了13年的网红鼻祖,要过气了吗?
  8. Google Cloud
  9. atlas对webpart的增强
  10. AT89C51的矩阵键盘、跑马灯和呼吸灯设计
  11. Repast Statecharts
  12. java会议记录管理系统实验报告代码_会议记录管理系统
  13. 我的世界(15)-删除服务器地图区块(MCedit)
  14. multisim14安装与卸载
  15. ios14描述文件无法与服务器连接,苹果手机的信任在哪里设置(ios14描述文件与设备管理)...
  16. emacs python_将Emacs打造成强大的Python代码编辑工具
  17. svn上传新项目的时候出现Can't set position pointer in file 'D:\***\*.rev':配额不足,无法完成请求的服务
  18. 数据分析实战(三):美国1800~2010年婴儿名字
  19. 【linux基础】vim快速移动光标至行首行尾、第一行和最后一行
  20. 配置项目外网访问(公网IP+DDNS)

热门文章

  1. 算法学习之道,应有三重境界
  2. 2021年中国民航及其重点企业对比分析(中航集团VS东航集团VS南航集团VS海航集团)[图]
  3. OSX 软件选择之编辑器
  4. 【集体智慧编程】第二章、提供推荐
  5. JAVA之迷你DVD管理器
  6. linux 读取png图片大小,读取 png 图片的宽高信息
  7. 2021-05-24 BUG修改
  8. 江苏移动CM101s-MV100-EMMC- M8233_强刷固件包
  9. 云服务器多个项目同时迁移至另一台服务器(阿里腾讯共享镜像用法)
  10. 【OR】二次规划(2):SCA方法