----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4开发板eMMC :16GBLPDDR3 :4GB显示屏 :15.6英寸HDMI接口显示屏u-boot :2023.04linux :6.3----------------------------------------------------------------------------------------------------------------------------
在Rockchip RK3399 - ALSA子系统我们介绍了ALSA子系统的软件架构,同时介绍了ALSA CORE核心数据结构和相关API。本节我们将会介绍ASoC软件体系中音频三大驱动模块(Codec、Platform 和Machine)中的Machine。
Machine driver描述了如何控制platform、codec、cpu dai(Digital Audio Interface,数字音频接口)和codec dai,使得互相配合在一起工作。单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。
一、核心数据结构
ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动等等,描述Machine 的最主要的几个数据结构分别是:snd_soc_card,snd_soc_dai,snd_soc_dai_driver、snd_soc_dai_link;当然了此外还有一些操作集相关的数据结构,比如snd_soc_ops、snd_soc_dai_ops;
- snd_soc_card:ASoC中的核心数据结构,和ASLA CORE中的snd_card地位一样;用于对ASocC中的声卡设备进行统一抽象;可以认为snd_soc_card是整个 ASoc 数据结构的根本,由它开始,引出一系列的数据结构用于表述音频的各个特性和功能;snd_soc_card数据结构中引出了snd_soc_dai_link结构;
- snd_soc_dai和snd_soc_dai_driver:用于描述dai以及dai驱动,根据codec端和soc端,分为codec_dai 和cpu_dai,在ASoC的Platform驱动和Codec驱动中也会使用到;所以这个我们单独拎出来说;
- snd_soc_dai_link:用来描述音频数据链路以及板级操作函数,在snd_soc_dai_link中,指定了platform、codec、codec_dai、cpu_dai的名字;
1.1 struct snd_soc_card
ASoC中使用struct snd_soc_card数据结构来描述SoC声卡的所有信息,需要将该数据结构与我们上一节介绍的ALSA CORE中的struct snd_card区分开来;struct snd_soc_card定义在include/sound/soc.h;
/* SoC card */
struct snd_soc_card {
const char *name;
const char *long_name;
const char *driver_name;
const char *components;
#ifdef CONFIG_DMI
char dmi_longname[80];
#endif /* CONFIG_DMI */
char topology_shortname[32];
struct device *dev;
struct snd_card *snd_card;
struct module *owner;
struct mutex mutex;
struct mutex dapm_mutex;
/* Mutex for PCM operations */
struct mutex pcm_mutex;
enum snd_soc_pcm_subclass pcm_subclass;
int (*probe)(struct snd_soc_card *card);
int (*late_probe)(struct snd_soc_card *card);
void (*fixup_controls)(struct snd_soc_card *card);
int (*remove)(struct snd_soc_card *card);
/* the pre and post PM functions are used to do any PM work before and
* after the codec and DAI's do any PM work. */
int (*suspend_pre)(struct snd_soc_card *card);
int (*suspend_post)(struct snd_soc_card *card);
int (*resume_pre)(struct snd_soc_card *card);
int (*resume_post)(struct snd_soc_card *card);
/* callbacks */
int (*set_bias_level)(struct snd_soc_card *,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
int (*set_bias_level_post)(struct snd_soc_card *,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
int (*add_dai_link)(struct snd_soc_card *,
struct snd_soc_dai_link *link);
void (*remove_dai_link)(struct snd_soc_card *,
struct snd_soc_dai_link *link);
long pmdown_time;
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link; /* predefined links only */
int num_links; /* predefined links only */
struct list_head rtd_list;
int num_rtd;
/* optional codec specific configuration */
struct snd_soc_codec_conf *codec_conf;
int num_configs;
/*
* optional auxiliary devices such as amplifiers or codecs with DAI
* link unused
*/
struct snd_soc_aux_dev *aux_dev;
int num_aux_devs;
struct list_head aux_comp_list;
const struct snd_kcontrol_new *controls;
int num_controls;
/*
* Card-specific routes and widgets.
* Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in.
*/
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
const struct snd_soc_dapm_widget *of_dapm_widgets;
int num_of_dapm_widgets;
const struct snd_soc_dapm_route *of_dapm_routes;
int num_of_dapm_routes;
/* lists of probed devices belonging to this card */
struct list_head component_dev_list;
struct list_head list;
struct list_head widgets;
struct list_head paths;
struct list_head dapm_list;
struct list_head dapm_dirty;
/* attached dynamic objects */
struct list_head dobj_list;
/* Generic DAPM context for the card */
struct snd_soc_dapm_context dapm;
struct snd_soc_dapm_stats dapm_stats;
struct snd_soc_dapm_update *update;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_card_root;
#endif
#ifdef CONFIG_PM_SLEEP
struct work_struct deferred_resume_work;
#endif
u32 pop_time;
/* bit field */
unsigned int instantiated:1;
unsigned int topology_shortname_created:1;
unsigned int fully_routed:1;
unsigned int disable_route_checks:1;
unsigned int probed:1;
unsigned int component_chaining:1;
void *drvdata;
};
这个数据结构的内容比较多,我们只挑一些重点说一下:
- name:声卡的名称;如果使用设备树,保存解析设备节点label或者simple-audio-card,name得到的信息;
- long_name:更详细的名称;
- driver_name:驱动程序的名称;
- components:component标识符;
- dev:分配给此声卡的设备;一般设置为平台设备的device;
- snd_card:ALSA CORE中的声卡设备;
- owner:指向驱动程序拥有者模块的指针;
- pcm_subclass:PCM子类的枚举类型;
- probe:probe是可选的函数,注册当前声卡设备时会回调该函数;
- late_probe:late_probe是可选的函数,注册当前声卡设备时会回调该函数;
- remove:remove是可选的函数,用于在设备被移除时执行特定的操作;
- add_dai_link:snd_soc_add_pcm_runtime函数执行时会回调该函数;
- remove_dai_link:
- dai_link:指向动态分配得到的数组,每个元素都一个struct snd_soc_dai_link,即每一元素描述了一条音频数据链路(如果使用设备树,保存解析设备节点simple-audio-card,cpu、simple-audio-card,codec得到的dai_link);
- num_links:dai_link指向数组的长度;
- rtd_list:保存pcm runtime的链表;链表中存储的是struct snd_soc_pcm_runtime;
- codec_conf:指向动态分配得到的数组,每个元素都是一个struct snd_soc_codec_conf,即每个元素描述一个codec_conf;
- num_configs:codec_conf指向数组的长度;
- aux_dev:指向动态分配得到的数组,每个元素都是一个struct snd_soc_aux_dev;如果使用设备树,保存解析设备节点simple-audio-card,aux-devs得到的aux_dev;
- num_aux_devs:aux_dev指向的数组的长度;
- controls:指向动态分配得到的数组,每个元素都是一个struct snd_kcontrol;如果使用设备树,保存解析设备节点simple-audio-card,pin-switches得到的kcontrol信息;
- num_controls:controls指向的数组的长度;
- dapm_widgets:指向动态分配得到的数组,每个元素都是一个struct snd_soc_dapm_widget ,即每个元素描述了一个widget控件;
- num_dapm_widgets:dapm_widgets指向的数组的长度;
- dapm_routes:指向动态分配得到的数组,每个元素都是一个struct snd_soc_dapm_route;
- num_dapm_routes:dapm_routes指向的数组的长度;
- of_dapm_widgets:指向动态分配得到的数组,每个元素都是一个struct snd_soc_dapm_widget ;如果使用设备树,保存解析设备节点simple-audio-card,widgets得到的音频控件信息;
- num_of_dapm_widgets:of_dapm_widgets指向的数组的长度;
- of_dapm_routes:指向动态分配得到的数组,每个元素都是一个struct snd_soc_dapm_route;如果使用设备树,保存解析设备节点simple-audio-card,routing得到的音频路由信息;
- num_of_dapm_routes:of_dapm_routes指向的数组的长度;
- component_dev_list:保存component的链表;链表中存放的数据类型为struct snd_soc_component;
- widgets:保存widget的链表;链表中存放的数据类型为struct snd_soc_dapm_widget;
- paths:保存path的链表;链表中存放的数据类型为struct snd_soc_dapm_path;
- dapm_list:保存dapm域的链表;链表中存放的数据类型为struct snd_soc_dapm_context;
- dapm_dirty:保存状态(包括电源状态、连接状态)发生改变的widget;链表中存放的数据类型为struct snd_soc_dapm_widget;
- dapm:dapm上下文,struct snd_soc_dapm_context类型;
- deferred_resume_work:用于在系统挂起(suspend)后执行延迟的恢复操作,func函数被设置为soc_resume_deferred;
- drvdata:驱动程序的私有数据结构;
1.2 struct snd_soc_dai_link
ASoC使用struct snd_soc_dai_link数据结构来描述音频数据链路以及板级操作函数,在snd_soc_dai_link中,指定了platform、codec、codec_dai、cpu_dai的名字,Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的。
struct snd_soc_dai_link定义在include/sound/soc.h:
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.
*/
/*
* 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.
*/
struct snd_soc_dai_link_component *cpus;
unsigned int num_cpus;
/*
* You MUST specify the link's codec, either by device name, or by
* DT/OF node, but not both.
*/
/* You MUST specify the DAI name within the codec */
struct snd_soc_dai_link_component *codecs;
unsigned int num_codecs;
/*
* 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. In such case, platforms are not mandatory.
*/
struct snd_soc_dai_link_component *platforms;
unsigned int num_platforms;
int id; /* optional ID for machine driver link identification */
const struct snd_soc_pcm_stream *params;
unsigned int num_params;
unsigned int dai_fmt; /* format to set on init */
enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_pcm_runtime *rtd);
/* codec/machine specific exit - dual of init() */
void (*exit)(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;
/* Mark this pcm with non atomic ops */
unsigned int nonatomic:1;
/* For unidirectional dai links */
unsigned int playback_only:1;
unsigned int capture_only:1;
/* Keep DAI active over suspend */
unsigned int ignore_suspend:1;
/* Symmetry requirements */
unsigned int symmetric_rate:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_sample_bits: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;
/* DPCM capture and Playback support */
unsigned int dpcm_capture:1;
unsigned int dpcm_playback:1;
/* DPCM used FE & BE merged format */
unsigned int dpcm_merged_format:1;
/* DPCM used FE & BE merged channel */
unsigned int dpcm_merged_chan:1;
/* DPCM used FE & BE merged rate */
unsigned int dpcm_merged_rate:1;
/* pmdown_time is ignored at stop */
unsigned int ignore_pmdown_time:1;
/* Do not create a PCM for this DAI link (Backend link) */
unsigned int ignore:1;
/* This flag will reorder stop sequence. By enabling this flag
* DMA controller stop sequence will be invoked first followed by
* CPU DAI driver stop sequence
*/
unsigned int stop_dma_first:1;
#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj; /* For topology */
#endif
};
这个数据结构的内容比较多,我们只挑一些重点说一下:
- name:指定名称,必须配置;
- stream_name:指定Stream名称,必须配置;
- cpus:指向动态分配得到的数组,每个元素都是一个struct snd_soc_dai_link_component(名称/设备树节点(二选一)以及dai的名称),即保存当前音频数据链路上的所有cpu设备;
- num_cpus:cpus指向的数组长度,一般来说1条音频数据链路只有1个CPU、1个Codec;
- codecs:指向动态分配得到的数组,每个元素都是一个struct snd_soc_dai_link_component(名称/设备树节点(二选一)以及dai名称),即保存当前音频数据链路上的所有codec设备;
- num_codecs:codec指向的数组长度,一般来说1条音频数据链路只有1个CPU、1个Codec;
- platforms:指向动态分配得到的数组,每个元素都是一个struct snd_soc_dai_link_component(通过名称/设备树节点字段指定cpu测的platform驱动名称,通常都是DMA驱动,用于音频数据传输);
- num_platforms:platforms指向的数组长度;
- id:可选的链接 ID,用于识别Machine driver link;
- params:指定PCM流参数;
- num_params:PCM流参数数量;
- dai_fmt:数字音频接口格式;
- trigger:DPCM(Direct Pulse Code Modulation)触发类型;
- init:初始化函数,例如添加Machine controls;
- exit:退出函数;
- be_hw_params_fixup:可选的硬件参数重写函数;
- ops:音频相关的操作集;重点留意 hw_params回调,一般来说这个回调是要实现的,用于配置 codec、codec_dai、cpu_dai 的数据格式和系统时钟;
- compr_ops:数据压缩操作函数;
- nonatomic:标记 PCM 是否使用非原子操作;
- playback_only:标记 PCM 流是否只支持播放;
- capture_only:标记 PCM 流是否只支持捕获;
- ignore_suspend:标记 PCM 是否在挂起时保持 DAI 活动状态;
- symmetric_rate:标记 PCM 采样率是否对称;
- symmetric_channels:标记 PCM 通道数是否对称;
- symmetric_sample_bits:标记 PCM 采样位数是否对称;
- no_pcm:标记 PCM 流是否需要创建;
- dynamic:标记该 dai link是否可以在运行时路由到其他 dai link,具体可以参考DPCM相关内容;
- dpcm_capture:标记是否支持 DPCM 捕获;
- dpcm_playback:标记是否支持 DPCM 播放;
- dpcm_merged_format:标记是否使用合并格式的 DPCM;
- dpcm_merged_chan:标记是否使用合并通道的 DPCM;
- dpcm_merged_rate:标记是否使用合并采样率的 DPCM;
- ignore_pmdown_time:标记是否忽略 pmdown_time 停止时间;pmdown_time 是一种 PCM 的停止时间戳,用于控制 PCM 流在空闲一段时间后自动停止以降低功耗;
- ignore:标记该dai link是否需要创建 PCM;
- stop_dma_first:标记是否对停止序列进行排序;
1.3.1 snd_soc_dai_link_component
ASoC使用struct snd_soc_dai_link_component来描述音频数据链路中的component,主要目的就是为了定位到snd_soc_component、snd_soc_dai,定义在include/sound/soc.h;
struct snd_soc_dai_link_component {
const char *name;
struct device_node *of_node;
const char *dai_name;
};
其中:
- name:名称,在非设备树场景使用;如果指定了这个,音频数据链路可以通过这个字段到全局链表component_list中查找与之匹配的component;
- of_node:设备树节点,在设备树场景使用;如果指定了这个,音频数据链路可以通过这个字段到全局链表component_list中查找与之匹配的component;
- dai_name:dai的名称,音频数据链路就是通过这个字段到匹配的component的dai_list链表中查找与之匹配的dai;
1.3.2 struct snd_soc_ops
ASoC使用struct snd_soc_ops来描述ALSA PCM操作集,定义在include/sound/soc.h;
/* SoC audio ops */
struct snd_soc_ops {
int (*startup)(struct snd_pcm_substream *);
void (*shutdown)(struct snd_pcm_substream *);
int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
int (*hw_free)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
int (*trigger)(struct snd_pcm_substream *, int);
}
其中:
- startup:在 PCM子流上启动音频传输期间调用;
- shutdown:在PCM子流上关闭音频传输期间调用;
- hw_params:更改PCM子流硬件参数期间调用;
- hw_free:释放PCM子流资源期间调用;
- prepare:准备PCM子流传输期间调用;
- trigger:在PCM子流上触发执行操作期间调用;
1.3 DAI
DAI全称数字音频接口,根据codec端和soc端,分为codec_dai、cpu_dai,实际上dai也是一种widget。
描述dai的最主要的几个数据结构分别是:snd_soc_dai、snd_soc_dai_driver、snd_soc_dai_ops;
以cpu dai driver为例,每个cpu dai driver必须提供以下功能:
- DAI描述信息;
- DAI配置信息;
- PCM描述信息;
- 系统时钟(SYSCLK)配置;
- 挂起和恢复(可选);
以codec dai driver为例,每个codec dai driver必须提供以下功能:
- Codec DAI和PCM的配置信息;
- Codec的控制接口,如I2C/SPI;
- Mixer和其它音频控件;
- Codec的音频操作;
1.3.1 struct snd_soc_dai
ALSA中使用struct snd_soc_dai数据结构来描述dai,定义在include/sound/soc-dai.h:
/*
* Digital Audio Interface runtime data.
*
* Holds runtime data for a DAI.
*/
struct snd_soc_dai {
const char *name;
int id;
struct device *dev;
/* driver ops */
struct snd_soc_dai_driver *driver;
/* DAI runtime info */
struct snd_soc_dai_stream stream[SNDRV_PCM_STREAM_LAST + 1];
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;
unsigned int channels;
unsigned int sample_bits;
/* parent platform/codec */
struct snd_soc_component *component;
struct list_head list;
/* function mark */
struct snd_pcm_substream *mark_startup;
struct snd_pcm_substream *mark_hw_params;
struct snd_pcm_substream *mark_trigger;
struct snd_compr_stream *mark_compr_startup;
/* bit field */
unsigned int probed:1;
};
该数据结构包含以下字段:
- name:dai的名称,音频数据链路在component的dai_list链表中查找dai时,就是通过该字段匹配来完成的;snd_soc_dai一般是在注册component时动态创建的,name字段值取自与之关联的snd_soc_dai_driver的name;
- id:dai的标识符;
- dev:指向包含该dai的设备的指针;
- driver:指向dai驱动结构的指针;
- stream:录音流和播放流的数组,其中包含有关流的信息;
- rate:如果强制对称,则为采样率;
- channels:如果强制对称,则为通道数;
- sample_bits:如果强制对称,则为采样位数;
- component:指向component(通常是 platform或codec)的指针;
- list:用于构建链表节点,用于将该dai添加到到component的dai_list链表中;
- mark_startup:用于标记PCM启动事件的指针;
- mark_hw_params:用于标记PCM硬件参数变化事件的指针;
- mark_trigger:用于标记PCM触发事件的指针;
- mark_compr_startup:用于标记压缩流启动事件的指针;
- probed:标记dai是否已经探测完成;
1.3.2 struct snd_soc_dai_stream
ALSA中使用struct snd_soc_dai_stream数据结构来表示音频数据流,定义在include/sound/soc-dai.h:
/* for Playback/Capture */
struct snd_soc_dai_stream {
struct snd_soc_dapm_widget *widget;
unsigned int active; /* usage count */
unsigned int tdm_mask; /* CODEC TDM slot masks and params (for fixup) */
void *dma_data; /* DAI DMA data */
};
数据结构中有一个widget指针,可以用来代表播放流/录音流。
1.3.3 struct snd_soc_dai_driver
ALSA中使用struct snd_soc_dai_driver数据结构来描述DAI驱动,包括DAI和PCM的能力和操作,定义在include/sound/soc-dai.h:
/*
* Digital Audio Interface Driver.
*
* Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
* operations and capabilities. Codec and platform drivers will register this
* structure for every DAI they have.
*
* This structure covers the clocking, formating and ALSA operations for each
* interface.
*/
struct snd_soc_dai_driver {
/* DAI description */
const char *name;
unsigned int id;
unsigned int base;
struct snd_soc_dobj dobj;
/* DAI driver callbacks */
int (*probe)(struct snd_soc_dai *dai);
int (*remove)(struct snd_soc_dai *dai);
/* compress dai */
int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
/* Optional Callback used at pcm creation*/
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai);
/* ops */
const struct snd_soc_dai_ops *ops;
const struct snd_soc_cdai_ops *cops;
/* DAI capabilities */
struct snd_soc_pcm_stream capture;
struct snd_soc_pcm_stream playback;
unsigned int symmetric_rate:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_sample_bits:1;
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
};
该数据结构包含了以下字段:
- name:指定dai的名称;
- id:可选的dai标识符,用于在注册期间区分多个dai;
- base:可选的dai寄存器基地址;
- dobj:dai对象,包含dai及其父组件的句柄;
- probe:可选的dai探测回调函数;注册声卡时回调;
- remove:可选的dai卸载回调函数;
- compress_new:可选的压缩dai创建回调函数;
- pcm_new:可选的 PCM 创建回调函数;
- ops:指向本dai的snd_soc_dai_ops结构,即dai的操作集,这个操作集非常重要,用于dai的时钟配置、格式配置、硬件参数配置;
- cops:dai压缩操作函数指针表;
- capture:描述capture的能力;如回放设备所支持的声道数、采样率、音频格式;非常重要的字段;
- playbackk:描述playback的能力;如录制设备所支持声道数、采样率、音频格式;非常重要的字段;
- symmetric_rate:标记dai采样率是否对称;
- symmetric_channels:标记dai通道数是否对称;
- symmetric_sample_bits:标记dai采样位数是否对称;
- probe_order:probe函数执行顺序;
- remove_order:remove函数执行顺序;
1.3.4 struct snd_soc_dai_ops
struct snd_soc_dai_ops {
/*
* DAI clocking configuration, all optional.
* Called by soc_card drivers, normally in their hw_params.
*/
int (*set_sysclk)(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out);
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);
/*
* DAI format configuration
* Called by soc_card drivers, normally in their hw_params.
*/
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
int (*xlate_tdm_slot_mask)(unsigned int slots,
unsigned int *tx_mask, unsigned int *rx_mask);
int (*set_tdm_slot)(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width);
int (*set_channel_map)(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot);
int (*get_channel_map)(struct snd_soc_dai *dai,
unsigned int *tx_num, unsigned int *tx_slot,
unsigned int *rx_num, unsigned int *rx_slot);
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
int (*set_stream)(struct snd_soc_dai *dai,
void *stream, int direction);
void *(*get_stream)(struct snd_soc_dai *dai, int direction);
/*
* DAI digital mute - optional.
* Called by soc-core to minimise any pops.
*/
int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);
/*
* ALSA PCM audio operations - all optional.
* Called by soc-core during audio PCM operations.
*/