arm平台:IMX6Q
内核:Linux4.1.15
系统:Android6.0
codec:wm8960
主要内容为Machine、Platform和Codec三大部分之间的关系和实现。文章中的举例和代码均为imx6q-Android6.0源码和文件。
文章中如有错误和不严谨内容,欢迎指正。

一、注册platform device

kernel_imx/sound/soc/fsl/imx-wm8960.c

在文件中包含关于wm8960相关的设备结构体。设备结构体重包含snd_soc_card结构体。

struct imx_wm8960_data {struct snd_soc_card card;  //主要内容struct clk *codec_clk;unsigned int clk_frequency;bool is_codec_master;bool is_stream_in_use[2];bool is_stream_opened[2];struct regmap *gpr;unsigned int hp_det[2];u32 asrc_rate;u32 asrc_format;
};

在imx-wm8960.c文件中注册platform driver和设备树中的Platform Device进行匹配。然后执行imx_wm8960_probe函数,注册声卡。

static struct snd_soc_ops imx_hifi_ops = {.hw_params = imx_hifi_hw_params,.hw_free = imx_hifi_hw_free,.startup   = imx_hifi_startup,.shutdown  = imx_hifi_shutdown,
};
static struct snd_soc_dai_link imx_wm8960_dai[] = {{.name = "HiFi",.stream_name = "HiFi",.codec_dai_name = "wm8960-hifi",.ops = &imx_hifi_ops,},{.name = "HiFi-ASRC-FE",.stream_name = "HiFi-ASRC-FE",.codec_name = "snd-soc-dummy",.codec_dai_name = "snd-soc-dummy-dai",.dynamic = 1,.ignore_pmdown_time = 1,.dpcm_playback = 1,.dpcm_capture = 1,},{.name = "HiFi-ASRC-BE",.stream_name = "HiFi-ASRC-BE",.codec_dai_name = "wm8960-hifi",.platform_name = "snd-soc-dummy",.no_pcm = 1,.ignore_pmdown_time = 1,.dpcm_playback = 1,.dpcm_capture = 1,.ops = &imx_hifi_ops,.be_hw_params_fixup = be_hw_params_fixup,},
};struct imx_wm8960_data *data;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);data->card.dai_link = imx_wm8960_dai;/*把snd_soc_card结构体放到platform_device的dev.drvdata字段*/
platform_set_drvdata(pdev, &data->card);
/*把data结构体保存到snd_soc_card结构体下的drvdata字段*/
snd_soc_card_set_drvdata(&data->card, data);
/*注册声卡到asoc核心*/
devm_snd_soc_register_card(&pdev->dev, &data->card);

machine部分的平台设备驱动代码主要是snd_soc_card结构体。有引出了machine驱动的另外两个数据结构(snd_soc_dai_link、snd_soc_ops)。在snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后在Machine平台驱动层中将会利用这些名字去匹配已经在系统中注册的platform、codec、dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的,这样看来,Machine驱动的设备初始化代码无非就是选择合适Platform和Codec以及dai,用他们填充以上几个数据结构,然后注册Platform设备即可。当然还要实现连接Platform和Codec的dai_link对应的ops实现。

二、snd_soc_dai_link

在snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字。
codec_dai_name、cpu_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 *cpu_name;                  /*cpu控制接口name*/const char *cpu_dai_name;                /*cpu数据传输接口name*/const char *codec_name;                    /*codec设备name*/const char *codec_dai_name;              /*codec数据传输接口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;...
};/*在wm8960的machine部分中的定义*/
static struct snd_soc_dai_link imx_wm8960_dai[] = {...{.name = "HiFi-ASRC-BE",.stream_name = "HiFi-ASRC-BE",.codec_dai_name = "wm8960-hifi",.platform_name = "snd-soc-dummy",.no_pcm = 1,.ignore_pmdown_time = 1,.dpcm_playback = 1,.dpcm_capture = 1,.ops = &imx_hifi_ops,.be_hw_params_fixup = be_hw_params_fixup,},
};
/*在imx_wm8960_probe中*/
imx_wm8960_dai[2].cpu_dai_name = dev_name(&cpu_pdev->dev);

三、snd_soc_register_card

kernel_imx/sound/soc/soc-core.c

目的:

  1. 检测snd_soc_dai_link结构体中的codec_dai_name、cpu_dai_name是否存在。
  2. 初始化有关链表
  3. 创建snd_soc_pcm_runtime结构体
  4. 初始化声卡(重要)
int snd_soc_register_card(struct snd_soc_card *card)
{int i, j, ret;if (!card->name || !card->dev)return -EINVAL;/*遍历snd_soc_card下的dai_link,确保codec_dai_name、cpu_dai_name存在*/for (i = 0; i < card->num_links; i++) {struct snd_soc_dai_link *link = &card->dai_link[i];ret = snd_soc_init_multicodec(card, link);if (ret) {dev_err(card->dev, "ASoC: failed to init multicodec\n");return ret;}for (j = 0; j < link->num_codecs; j++) {/** Codec must be specified by 1 of name or OF node,* not both or neither.*/if (!!link->codecs[j].name ==!!link->codecs[j].of_node) {dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",link->name);return -EINVAL;}/* Codec DAI name must be specified */if (!link->codecs[j].dai_name) {dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",link->name);return -EINVAL;}}/** Platform may be specified by either name or OF node, but* can be left unspecified, and a dummy platform will be used.*/if (link->platform_name && link->platform_of_node) {dev_err(card->dev,"ASoC: Both platform name/of_node are set for %s\n",link->name);return -EINVAL;}/** CPU device may be specified by either name or OF node, but* can be left unspecified, and will be matched based on DAI* name alone..*/if (link->cpu_name && link->cpu_of_node) {dev_err(card->dev,"ASoC: Neither/both cpu name/of_node are set for %s\n",link->name);return -EINVAL;}/** At least one of CPU DAI name or CPU device name/node must be* specified*/if (!link->cpu_dai_name &&!(link->cpu_name || link->cpu_of_node)) {dev_err(card->dev,"ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",link->name);return -EINVAL;}}dev_set_drvdata(card->dev, card);/*内联函数INIT_LIST_HEAD(&card->codec_dev_list);INIT_LIST_HEAD(&card->widgets);INIT_LIST_HEAD(&card->paths);INIT_LIST_HEAD(&card->dapm_list);初始化各个部件的链表*/snd_soc_initialize_card_lists(card);card->rtd = devm_kzalloc(card->dev,sizeof(struct snd_soc_pcm_runtime) *(card->num_links + card->num_aux_devs),GFP_KERNEL);if (card->rtd == NULL)return -ENOMEM;card->num_rtd = 0;card->rtd_aux = &card->rtd[card->num_links];/*把平台数据snd_soc_card结构体中的dai_link拷贝到snd_soc_pcm_runtime结构体的对应成员中*/for (i = 0; i < card->num_links; i++) {card->rtd[i].card = card;card->rtd[i].dai_link = &card->dai_link[i];card->rtd[i].codec_dais = devm_kzalloc(card->dev,sizeof(struct snd_soc_dai *) *(card->rtd[i].dai_link->num_codecs),GFP_KERNEL);if (card->rtd[i].codec_dais == NULL)return -ENOMEM;}for (i = 0; i < card->num_aux_devs; i++)card->rtd_aux[i].card = card;INIT_LIST_HEAD(&card->dapm_dirty);card->instantiated = 0;mutex_init(&card->mutex);mutex_init(&card->dapm_mutex);/*初始化声卡*/ret = snd_soc_instantiate_card(card);if (ret != 0)return ret;/* deactivate pins to sleep state */......return ret;
}

四、snd_soc_instantiate_card

static int snd_soc_instantiate_card(struct snd_soc_card *card)
{struct snd_soc_codec *codec;...
/*
ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、
Platform都在注册时连接到这三个全局链表上。soc_bind_dai_link函数逐个扫描这三个链表,根据
card->dai_link[]中的名称(name)进行匹配,匹配成功后把相应的codec、dai和platform实例赋值到card->rtd[]
中(snd_soc_pcm_runtime)。经过这个过程后,snd_soc_pcm_runtime结构体(card->rtd)中保存了本Machine中
使用的Codec,DAI和Platform驱动的信息。
*//* bind DAIs */for (i = 0; i < card->num_links; i++) {ret = soc_bind_dai_link(card, i);}/* bind aux_devs too */for (i = 0; i < card->num_aux_devs; i++) {ret = soc_bind_aux_dev(card, i);}/* initialize the register cache for each available codec */list_for_each_entry(codec, &codec_list, list) {snd_soc_init_codec_cache(codec);}/*创建声卡实例*/snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card);snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, card->num_dapm_widgets);/* probe all components used by DAI links on this card */for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;order++) {for (i = 0; i < card->num_links; i++) {ret = soc_probe_link_components(card, i, order);if (ret < 0) {dev_err(card->dev,"ASoC: failed to instantiate card %d\n",ret);goto probe_dai_err;}}}/* probe all DAI links on this card */for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;order++) {for (i = 0; i < card->num_links; i++) {ret = soc_probe_link_dais(card, i, order);//关键函数if (ret < 0) {dev_err(card->dev,"ASoC: failed to instantiate card %d\n",ret);goto probe_dai_err;}}}for (i = 0; i < card->num_aux_devs; i++) {ret = soc_probe_aux_dev(card, i);if (ret < 0) {dev_err(card->dev,"ASoC: failed to add auxiliary devices %d\n",ret);goto probe_aux_dev_err;}}snd_soc_dapm_link_dai_widgets(card);snd_soc_dapm_connect_dai_link_widgets(card);if (card->controls)snd_soc_add_card_controls(card, card->controls, card->num_controls);if (card->dapm_routes)snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,card->num_dapm_routes);if (card->of_dapm_routes)snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes,card->num_of_dapm_routes);snd_soc_dapm_new_widgets(card);snd_card_register(card->snd_card);......
}

五、soc_probe_link_dais

static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
{/*执行cpu_dai下的probe函数*/ret = soc_probe_dai(cpu_dai, order);if (ret)return ret;/* probe the CODEC DAI *//*执行codec_dai下的prove函数*/for (i = 0; i < rtd->num_codecs; i++) {ret = soc_probe_dai(rtd->codec_dais[i], order);if (ret)return ret;}/* 调用标准的alsa的接口函数snd_pcm_new()函数用于创建标准alsa驱动的pcm逻辑设备 */soc_new_pcm(rtd, num);
}

六、soc_new_pcm

static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{ret = snd_pcm_new(rtd->card->snd_card, new_name,num, playback, capture, &pcm);  // 创建标准alsa的pcm逻辑设备rtd->pcm = pcm;  // 让snd_soc_pcm_runtime中的pcm指向刚刚创建的pcmpcm->private_data = rtd;/* 设置真正操作底层声卡的操作函数!!一共有3条路径 */(1):用platform中的驱动层的ops来填充
if (platform->driver->ops) {soc_pcm_ops.mmap = platform->driver->ops->mmap;soc_pcm_ops.pointer = platform->driver->ops->pointer;soc_pcm_ops.ioctl = platform->driver->ops->ioctl;soc_pcm_ops.copy = platform->driver->ops->copy;soc_pcm_ops.silence = platform->driver->ops->silence;soc_pcm_ops.ack = platform->driver->ops->ack;soc_pcm_ops.page = platform->driver->ops->page;}
(2):用标准的alsa的api接口来创建opsif (playback)snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);if (capture)snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);(3):调用pcm_new函数,来创建一个if (platform->driver->pcm_new) {ret = platform->driver->pcm_new(rtd);}
}

基于imx6q-Android6.0的ASOC架构 -- Machine部分(一)相关推荐

  1. Linux ALSA声卡驱动之六:ASoC架构中的Machine

    前面一节的内容我们提到,ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上 ...

  2. 编译可在Nexus5上运行的CyanogenMod13.0 ROM(基于Android6.0)

    编译可在Nexus5上运行的CyanogenMod13.0 ROM (基于Android6.0) 作者:寻禹@阿里聚安全 前言 下文中无特殊说明时CM代表CyanogenMod的缩写. 下文中说的&q ...

  3. Android6.0 Sensor架构和问题分析

    本文在借鉴网友的资料后再重新梳理了一遍,都是站在前人的基础.巨人的肩膀上再次总结分析出来的,仅供大家参考! 本文主要描述了在Android 6.0系统.MTK6755平台上sensor相关软硬件的体系 ...

  4. 基于ASP.NET Core 6.0的整洁架构

    背景 最近尝试录制了一个系列视频:<ASP.NET Core 6.0+Vue.js 3 实战开发>,本节是视频内部整洁架构的理论和实战的文字稿.因为在录制之前,我通常会编写完整的文字内容作 ...

  5. adb root android 6,ADB获得ROOT权限的探究——基于Android6.0

    ADB概述 adb的全称是Android Debug Bridge,是对Android设备进行调试的工具.这是调试通信的方式是基于C/S构架.在Android设备,运行ADBD服务,等待Client端 ...

  6. 基于Android6.0~9.0的适配

    前言 大家还记得Android 6.0权限适配的泪水吗?而现在谷歌已经出了Android P的稳定版,而且谷歌粑粑,为了大家能给辛苦熬夜加班,特地的和个大市场合作,要强制推出9.0的适配,而近期在下不 ...

  7. 红米4A Android 版本,红米4A评测:4A搭载深度定制基于Android6.0.1系统MIUI 8 - 红米4A评测:双11手机单品销量第一 怎么样好不好用?...

    标签: 系统方面,红米4A搭载了小米今年刚推出的MIUI 8,这是基于Android 6.0.1深度定制的ROM.内置的默认主题风格很有小米的味道,非常简洁. 手机主界面 默认锁屏的界面下,底部左侧右 ...

  8. vivox9按Android版本,vivo X9真实评测:基于Android6.0系统的Funtouch OS3.0 - vivo X9最真评测:X9也用上高通芯了!性能暴强双摄不错...

    系统基于Android 6.0 大家都基于安卓,但最终做出来的东西却各不相同.或许,这是外观和硬件同质化严重的当下,厂商所能做的难得的突破了.vivo X9运行的是基于Android 6.0的Funt ...

  9. 小米2s升级android6.0,小米2/2S率先升级基于Android 5.0的MIUI6

    [天极网IT新闻频道][Yesky新闻频道消息]4月3日下午5点,经过三个月的内测之后,小米向小米2和小米2S推送了基于Android 5.0版的MIUI 6更新. 小米向小米2以及小米2S推送了MI ...

最新文章

  1. 脑机接口主流算法解析课程视频汇总
  2. python怎么用excel-Python使用xlwt模块操作Excel的方法详解
  3. Bash:把粘贴板上的内容拷贝的文件中。(脚本)
  4. php 常用的日期函数,常用php日期函数总结
  5. C++ 引用本质就是一个指针常量
  6. android多个activity绑定一个service,8.1.2 绑定Activity和Service
  7. Mac切换英文大写,不能锁定,该如何解决
  8. go语言读取文件的方法-1
  9. 华硕开机画面修改_电脑开机密码忘记,进不了系统,一招教你轻松解决!
  10. 教你3招解决PDF文字无法复制的所有问题!!!
  11. DB2操作指南及命令大全
  12. camera一些常见名词缩写
  13. 如何让随机出现的列表元素只出现一次 python——求解
  14. 实例7:stc8a8k定时器0,定时50ms,进入中断之后,加数20次到1秒,计算时间。
  15. fastadmin框架里基于x-editable实现的表格无刷新行内编辑功能的插件的‘datetime‘时间类型无法使用的解决方法
  16. python中sklearn实现决策树及模型评估_Python sklearn决策树算法实践
  17. android manifest相关属性
  18. 【运筹优化】Python调用OR-Tools求解TSP问题
  19. 百度地图开发之实现运动轨迹 一
  20. ubuntu下编写C语言程序

热门文章

  1. 话费API 洗车API接口源码分享
  2. 又一波利好,FN难道又要起飞了?
  3. 关于未来几年家庭信息通讯的几点遥想
  4. 【干货】最右视频去水印下载使用介绍
  5. 姓名是成人高考计算机类,成人高考计算机专业就业前景怎么样?
  6. 稳定版全网淘宝商品接口,淘宝主图接口取分类,主图,标题,价格,旺旺等
  7. c语言 尾递归,尾递归的笔记
  8. Java8Lambda表达式浅尝辄止
  9. What is ESI?
  10. 解决IDEA的HTML文件格式的显示问题