1、Platform概述

ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DA〉把音频数据传送给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音频信号。在具体实现上,ASoC又把Platform驱动分为两个部分: platform_driver和snd_soc_dai_driver。其中,platform_driver负责管理音频数据,把音频数据通过dma或其他操作传送至cpudai中,dai_driver则主要完成cpu一侧的dai的参数配置,同时也会通过一定的途径把必要的dma等参数与platform_driver进行交互。

cpu_dai_driver 部分:
        在嵌入式系统里面通常指SoC的I2S、PCM总线控制器,负责把音频数据从I2S tx FIFO搬运到CODEC(这是音频播放的情形,录制则方向相反)。cpu_dai通过snd_soc_register_dai()/devm_snd_soc_register_component()来注册。

注:DAI是 Digital Audio Interface的简称,分为cpu_dai和codec_dai,这两者通过 I2S/PCM 总线连接,AIF 是 Audio Interface 的简称,嵌入式系统中一般是I2S和PCM接口。

platform_driver部分:
        负责把dma buffer中的音频数据搬运到I2S tx FIFO。音频DMA驱动通过 platform_driver_register()/module_platform_driver() 来注册,故也常用platform来指代音频DMA驱动(这里的 platform 需要与 SoC Platform 区分开)。

2、snd_soc_dai_driver

2.1、snd_soc_dai_driver注册流程

DAI驱动通常对应cpu的一个或几个I2S/PCM接口,实现一个DAI驱动大致可以分为以下几个步骤:

1、定义一个snd_soc_dai_driver结构的实例;
                2、在对应的platform_driver中的probe回调中通过API: snd_soc_register_dai或者snd_soc_register_dais注册snd_soc_dai实例;
                3、实现snd_soc_dai_driver结构中的probe、suspend等回调;
                4、实现snd_soc_dai_driver结构中的snd_soc_dai_ops字段中的回调函数;

具体代码流程如下(sound/soc/codecs/wm8350.c)

/* snd_soc_dai_ops 结构体实例 */
static const struct snd_soc_dai_ops wm8350_dai_ops = {.hw_params   = wm8350_pcm_hw_params,.mute_stream    = wm8350_mute,.set_fmt = wm8350_set_dai_fmt,.set_sysclk   = wm8350_set_dai_sysclk,.set_pll   = wm8350_set_fll,.set_clkdiv   = wm8350_set_clkdiv,.no_capture_mute = 1,
};/* snd_soc_dai_driver结构体实例 */
static struct snd_soc_dai_driver wm8350_dai = {.name = "wm8350-hifi",.playback = {.stream_name = "Playback",.channels_min = 1,.channels_max = 2,.rates = WM8350_RATES,.formats = WM8350_FORMATS,},.capture = {.stream_name = "Capture",.channels_min = 1,.channels_max = 2,.rates = WM8350_RATES,.formats = WM8350_FORMATS,},.ops = &wm8350_dai_ops,
};/* platform平台probe函数 */
static int wm8350_probe(struct platform_device *pdev)
{/* 注册component组件参数为soc_component_dev_wm8350 wm8350_dai*/return devm_snd_soc_register_component(&pdev->dev,&soc_component_dev_wm8350,&wm8350_dai, 1);
}/*** devm_snd_soc_register_component - resource managed component registration* @dev: Device used to manage component* @cmpnt_drv: Component driver* @dai_drv: DAI driver* @num_dai: Number of DAIs to register** Register a component with automatic unregistration when the device is* unregistered.*/
/* 进入devm_snd_soc_register_component函数 */
int devm_snd_soc_register_component(struct device *dev,const struct snd_soc_component_driver *cmpnt_drv,struct snd_soc_dai_driver *dai_drv, int num_dai)
{const struct snd_soc_component_driver **ptr;int ret;/* 申请devm_component_release空间 */ptr = devres_alloc(devm_component_release, sizeof(*ptr), GFP_KERNEL);if (!ptr)return -ENOMEM;/*调用snd_soc_register_component注册cmpnt_drv、 dai_drv */ret = snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai);if (ret == 0) {*ptr = cmpnt_drv;devres_add(dev, ptr);} else {devres_free(ptr);}return ret;
}
EXPORT_SYMBOL_GPL(devm_snd_soc_register_component);/* 进入snd_soc_register_component函数 */
int snd_soc_register_component(struct device *dev,const struct snd_soc_component_driver *component_driver,struct snd_soc_dai_driver *dai_drv,int num_dai)
{struct snd_soc_component *component;int ret;/* 申请component空间 */component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL);if (!component)return -ENOMEM;/* 调用snd_soc_component_initialize函数注册component_driver */ret = snd_soc_component_initialize(component, component_driver, dev);if (ret < 0)return ret;/* 调用snd_soc_add_component注册 dai_drv */return snd_soc_add_component(component, dai_drv, num_dai);
}
EXPORT_SYMBOL_GPL(snd_soc_register_component);/* 进入snd_soc_add_component函数 */
int snd_soc_add_component(struct snd_soc_component *component,struct snd_soc_dai_driver *dai_drv,int num_dai)
{int ret;int i;mutex_lock(&client_mutex);if (component->driver->endianness) {for (i = 0; i < num_dai; i++) {convert_endianness_formats(&dai_drv[i].playback);convert_endianness_formats(&dai_drv[i].capture);}}/* 调用snd_soc_register_dais函数注册dai_drv */ret = snd_soc_register_dais(component, dai_drv, num_dai);if (ret < 0) {dev_err(component->dev, "ASoC: Failed to register DAIs: %d\n",ret);goto err_cleanup;}if (!component->driver->write && !component->driver->read) {if (!component->regmap)component->regmap = dev_get_regmap(component->dev,NULL);if (component->regmap)snd_soc_component_setup_regmap(component);}/* see for_each_component */list_add(&component->list, &component_list);err_cleanup:if (ret < 0)snd_soc_del_component_unlocked(component);mutex_unlock(&client_mutex);if (ret == 0)snd_soc_try_rebind_card();return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_add_component);/*** snd_soc_register_dais - Register a DAI with the ASoC core** @component: The component the DAIs are registered for* @dai_drv: DAI driver to use for the DAIs* @count: Number of DAIs*/
/* 进入snd_soc_register_dais函数 */
static int snd_soc_register_dais(struct snd_soc_component *component,struct snd_soc_dai_driver *dai_drv,size_t count)
{struct snd_soc_dai *dai;unsigned int i;int ret;for (i = 0; i < count; i++) {/* 最终调用snd_soc_register_dai函数注册dai_drv */dai = snd_soc_register_dai(component, dai_drv + i, count == 1 &&!component->driver->non_legacy_dai_naming);if (dai == NULL) {ret = -ENOMEM;goto err;}}return 0;err:snd_soc_unregister_dais(component);return ret;
}/*** snd_soc_register_dai - Register a DAI dynamically & create its widgets** @component: The component the DAIs are registered for* @dai_drv: DAI driver to use for the DAI* @legacy_dai_naming: if %true, use legacy single-name format;*     if %false, use multiple-name format;** Topology can use this API to register DAIs when probing a component.* These DAIs's widgets will be freed in the card cleanup and the DAIs* will be freed in the component cleanup.*/
/* 进入到snd_soc_register_dai函数 */
struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,struct snd_soc_dai_driver *dai_drv,bool legacy_dai_naming)
{struct device *dev = component->dev;struct snd_soc_dai *dai;dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev));lockdep_assert_held(&client_mutex);/* 申请dai空间 */dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);if (dai == NULL)return NULL;/** Back in the old days when we still had component-less DAIs,* instead of having a static name, component-less DAIs would* inherit the name of the parent device so it is possible to* register multiple instances of the DAI. We still need to keep* the same naming style even though those DAIs are not* component-less anymore.*/if (legacy_dai_naming &&(dai_drv->id == 0 || dai_drv->name == NULL)) {dai->name = fmt_single_name(dev, &dai->id);} else {dai->name = fmt_multiple_name(dev, dai_drv);if (dai_drv->id)dai->id = dai_drv->id;elsedai->id = component->num_dai;}if (!dai->name)return NULL;dai->component = component;dai->dev = dev;dai->driver = dai_drv;/* see for_each_component_dais *//* 将dai->list添加到component->dai_list中 */list_add_tail(&dai->list, &component->dai_list);component->num_dai++;dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);return dai;
}
EXPORT_SYMBOL_GPL(snd_soc_register_dai);/* 至此cpu_dai添加完成 */

2.2、snd_soc_dai结构体

/** 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 */unsigned int stream_active[SNDRV_PCM_STREAM_LAST + 1]; /* usage count */struct snd_soc_dapm_widget *playback_widget;struct snd_soc_dapm_widget *capture_widget;/* DAI DMA data */void *playback_dma_data;void *capture_dma_data;/* 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;/* CODEC TDM slot masks and params (for fixup) */unsigned int tx_mask;unsigned int rx_mask;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;
};

snd_soc_dai该结构在snd_soc_register_dai函数中通过动态内存申请获得.简要介绍一下几个重要字段:

1、driver指向关联的snd_soc_dai_driver结构,由注册时通过参数传入。
                2、playback_dma_data 用于保存该dai播放stream的dma信息目标地址,dma传送单元大小和通道号等。
                3、capture_dma_data 同上,用于录音stream。
                4、component指向关联的snd_soc_component结构体中。

2.3、snd_soc_dai_driver结构体

/** 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;
};

snd_soc_dai_driver结构体需要自己根据不同的soc芯片进行定义,这里只介绍几个关键字段:

1、probe、remove回调函数,分别在声卡加载和卸载时被调用。
                2、ops指向snd_soc_dai_ops结构,用于配置和控制该dai,后面细讲。
                3、playback snd_soc_pcm_stream结构,用于指出该dai支持的声道数,码率,数据格式等能力。
                4、capture snd_soc_pcm_stream结构,用于指出该dai支持的声道数,码率,数据格式等能力。

2.4、snd_soc_dai_ops结构体

        snd_soc_dai_driver结构体中的ops字段指向一个snd_soc_dai_ops结构体,该结构体实际上是一组回调函数的集合,dai的配置和控制几乎都是通过这些回调函数来实现的,这些回调函数基本可以分为3大类,驱动程序可以根据实际情况实现其中的一部分:

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.*/int (*startup)(struct snd_pcm_substream *,struct snd_soc_dai *);void (*shutdown)(struct snd_pcm_substream *,struct snd_soc_dai *);int (*hw_params)(struct snd_pcm_substream *,struct snd_pcm_hw_params *, struct snd_soc_dai *);int (*hw_free)(struct snd_pcm_substream *,struct snd_soc_dai *);int (*prepare)(struct snd_pcm_substream *,struct snd_soc_dai *);/** NOTE: Commands passed to the trigger function are not necessarily* compatible with the current state of the dai. For example this* sequence of commands is possible: START STOP STOP.* So do not unconditionally use refcounting functions in the trigger* function, e.g. clk_enable/disable.*/int (*trigger)(struct snd_pcm_substream *, int,struct snd_soc_dai *);int (*bespoke_trigger)(struct snd_pcm_substream *, int,struct snd_soc_dai *);/** For hardware based FIFO caused delay reporting.* Optional.*/snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,struct snd_soc_dai *);/** Format list for auto selection.* Format will be increased if priority format was* not selected.* see*   snd_soc_dai_get_fmt()*/u64 *auto_selectable_formats;int num_auto_selectable_formats;/* bit field */unsigned int no_capture_mute:1;
};

工作时钟配置函数通常由machine驱动调用:

1、set_sysclk设置dai的主时钟。
                2、set_pll设置PLL参数。
                3、set_clkdiv设置分频系数。

dai的格式配置参数,通常也由machine驱动调用:

1、set_fmt设置dai的格式。

2、set_tdm_slot如果dai支持时分复用,用于设置时分复用的slot、set_channel_map声道的时分复用映射设置。
                3、set_tristate设置dai引脚的状态,当与其他dai并联使用同一引脚时需要使用该回调。

标准的snd_soc_ops回调通常由soc-core在进行PCM操作时调用:

1、startup:打开设备,设备开始工作的时候回调
                2、shutdown:关闭设备前调用
                3、hw_params:设置硬件的相关参数
                4、trigger:DAM开始时传输,结束传输,暂停传世,恢复传输的时候被回调。

3、platform_driver

3.1、platform_driver注册流程

/* snd_soc_component_driver结构体实例化 */
static const struct snd_soc_component_driver soc_component_dev_wm8350 = {.probe            = wm8350_component_probe,.remove           = wm8350_component_remove,.set_bias_level      = wm8350_set_bias_level,.controls      = wm8350_snd_controls,.num_controls        = ARRAY_SIZE(wm8350_snd_controls),.dapm_widgets        = wm8350_dapm_widgets,.num_dapm_widgets    = ARRAY_SIZE(wm8350_dapm_widgets),.dapm_routes     = wm8350_dapm_routes,.num_dapm_routes  = ARRAY_SIZE(wm8350_dapm_routes),.suspend_bias_off = 1,.idle_bias_on      = 1,.use_pmdown_time   = 1,.endianness        = 1,.non_legacy_dai_naming = 1,
};/* 进入到wm8350_probe函数 */
static int wm8350_probe(struct platform_device *pdev)
{/* 通过devm_snd_soc_register_component函数注册soc_component_dev_wm8350 */return devm_snd_soc_register_component(&pdev->dev,&soc_component_dev_wm8350,&wm8350_dai, 1);
}/* 进入platform_driver函数 */
static struct platform_driver wm8350_codec_driver = {.driver = {.name = "wm8350-codec",},.probe = wm8350_probe,
};/* 通过module_platform_driver宏来注册platform_driver */
module_platform_driver(wm8350_codec_driver);

3.2、platform_driver结构体

在编写 platform 驱动的时候,首先定义一个platform_driver结构体变量,然后实现结构体中的各个成员变量,重点是实现匹配方法以及probe函数。当驱动和设备匹配成功以后 probe函数就会执行,具体的驱动程序在 probe 函数里面编写,比如字符设备驱动等等。

struct platform_driver {int (*probe)(struct platform_device *);int (*remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);struct device_driver driver;const struct platform_device_id *id_table;bool prevent_deferred_probe;/** For most device drivers, no need to care about this flag as long as* all DMAs are handled through the kernel DMA API. For some special* ones, for example VFIO drivers, they know how to manage the DMA* themselves and set this flag so that the IOMMU layer will allow them* to setup and manage their own I/O address space.*/bool driver_managed_dma;
};

platform_driver结构体用于注册驱动到Platform总线,此处只讲几个重点字段:

1、probe:当驱动与设备匹配成功以后probe函数就会执行。一般驱动的提供者会编写,如果自己要编写一个全新的驱动,那么 probe 就需要自行实现。
                2、driver:device_driver 结构体变量,Linux 内核里面大量使用到了面向对象的思维, device_driver相当于基类,提供了最基础的驱动框架。 plaform_driver继承了这个基类,然后在此基础上又添加了一些特有的成员变量。

3.3、snd_soc_component结构体

struct snd_soc_component {/* device_driver->name 和snd_soc_component_driver->id有关, */const char *name;int id;const char *name_prefix;struct device *dev;struct snd_soc_card *card;unsigned int active;unsigned int suspended:1; /* is in suspend PM state *//* 用于把自己挂载到全局链表component_list下, component_list在soc-core中保持的全局变量 */struct list_head list;struct list_head card_aux_list; /* for auxiliary bound components */struct list_head card_list;/* 指向下属的snd_soc_component_driver, 该结构体一般由底层平台驱动实现 */const struct snd_soc_component_driver *driver;/* 链表头, 挂接snd_soc_dai->list   list_add(&dai->list, &component->dai_list) */struct list_head dai_list;int num_dai;struct regmap *regmap;int val_bytes;struct mutex io_mutex;/* attached dynamic objects */struct list_head dobj_list;/** DO NOT use any of the fields below in drivers, they are temporary and* are going to be removed again soon. If you use them in driver code* the driver will be marked as BROKEN when these fields are removed.*//* Don't use these, use snd_soc_component_get_dapm() */struct snd_soc_dapm_context dapm;/* machine specific init */int (*init)(struct snd_soc_component *component);/* function mark */void *mark_module;struct snd_pcm_substream *mark_open;struct snd_pcm_substream *mark_hw_params;struct snd_pcm_substream *mark_trigger;struct snd_compr_stream  *mark_compr_open;void *mark_pm;struct dentry *debugfs_root;const char *debugfs_prefix;
};

3.4、snd_soc_component_driver结构体

struct snd_soc_component_driver {const char *name;/* Default control and setup, added after probe() is run */const struct snd_kcontrol_new *controls;unsigned int num_controls;const struct snd_soc_dapm_widget *dapm_widgets;unsigned int num_dapm_widgets;const struct snd_soc_dapm_route *dapm_routes;unsigned int num_dapm_routes;int (*probe)(struct snd_soc_component *component);void (*remove)(struct snd_soc_component *component);int (*suspend)(struct snd_soc_component *component);int (*resume)(struct snd_soc_component *component);unsigned int (*read)(struct snd_soc_component *component,unsigned int reg);int (*write)(struct snd_soc_component *component,unsigned int reg, unsigned int val);/* pcm creation and destruction */int (*pcm_construct)(struct snd_soc_component *component,struct snd_soc_pcm_runtime *rtd);void (*pcm_destruct)(struct snd_soc_component *component,struct snd_pcm *pcm);/* component wide operations */int (*set_sysclk)(struct snd_soc_component *component,int clk_id, int source, unsigned int freq, int dir);int (*set_pll)(struct snd_soc_component *component, int pll_id,int source, unsigned int freq_in, unsigned int freq_out);int (*set_jack)(struct snd_soc_component *component,struct snd_soc_jack *jack,  void *data);/* DT */int (*of_xlate_dai_name)(struct snd_soc_component *component,const struct of_phandle_args *args,const char **dai_name);int (*of_xlate_dai_id)(struct snd_soc_component *comment,struct device_node *endpoint);void (*seq_notifier)(struct snd_soc_component *component,enum snd_soc_dapm_type type, int subseq);int (*stream_event)(struct snd_soc_component *component, int event);int (*set_bias_level)(struct snd_soc_component *component,enum snd_soc_bias_level level);int (*open)(struct snd_soc_component *component,struct snd_pcm_substream *substream);int (*close)(struct snd_soc_component *component,struct snd_pcm_substream *substream);int (*ioctl)(struct snd_soc_component *component,struct snd_pcm_substream *substream,unsigned int cmd, void *arg);int (*hw_params)(struct snd_soc_component *component,struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params);int (*hw_free)(struct snd_soc_component *component,struct snd_pcm_substream *substream);int (*prepare)(struct snd_soc_component *component,struct snd_pcm_substream *substream);int (*trigger)(struct snd_soc_component *component,struct snd_pcm_substream *substream, int cmd);int (*sync_stop)(struct snd_soc_component *component,struct snd_pcm_substream *substream);snd_pcm_uframes_t (*pointer)(struct snd_soc_component *component,struct snd_pcm_substream *substream);int (*get_time_info)(struct snd_soc_component *component,struct snd_pcm_substream *substream, struct timespec64 *system_ts,struct timespec64 *audio_ts,struct snd_pcm_audio_tstamp_config *audio_tstamp_config,struct snd_pcm_audio_tstamp_report *audio_tstamp_report);int (*copy_user)(struct snd_soc_component *component,struct snd_pcm_substream *substream, int channel,unsigned long pos, void __user *buf,unsigned long bytes);struct page *(*page)(struct snd_soc_component *component,struct snd_pcm_substream *substream,unsigned long offset);int (*mmap)(struct snd_soc_component *component,struct snd_pcm_substream *substream,struct vm_area_struct *vma);int (*ack)(struct snd_soc_component *component,struct snd_pcm_substream *substream);snd_pcm_sframes_t (*delay)(struct snd_soc_component *component,struct snd_pcm_substream *substream);const struct snd_compress_ops *compress_ops;/* probe ordering - for components with runtime dependencies */int probe_order;int remove_order;/** signal if the module handling the component should not be removed* if a pcm is open. Setting this would prevent the module* refcount being incremented in probe() but allow it be incremented* when a pcm is opened and decremented when it is closed.*/unsigned int module_get_upon_open:1;/* bits */unsigned int idle_bias_on:1;unsigned int suspend_bias_off:1;unsigned int use_pmdown_time:1; /* care pmdown_time at stop *//** Indicates that the component does not care about the endianness of* PCM audio data and the core will ensure that both LE and BE variants* of each used format are present. Typically this is because the* component sits behind a bus that abstracts away the endian of the* original data, ie. one for which the transmission endian is defined* (I2S/SLIMbus/SoundWire), or the concept of endian doesn't exist (PDM,* analogue).*/unsigned int endianness:1;unsigned int non_legacy_dai_naming:1;/* this component uses topology and ignore machine driver FEs */const char *ignore_machine;const char *topology_name_prefix;int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,struct snd_pcm_hw_params *params);bool use_dai_pcm_id;  /* use DAI link PCM ID as PCM device number */int be_pcm_base;  /* base device ID for all BE PCMs */#ifdef CONFIG_DEBUG_FSconst char *debugfs_prefix;
#endif
};

module_platform_driver函数的详解请参考《module_platform_driver源码分析》

Linux ALSA驱动之Platform源码分析(wm8350.c)相关推荐

  1. Linux下USB suspend/resume源码分析【转】

    转自:http://blog.csdn.net/aaronychen/article/details/3928479 Linux下USB suspend/resume源码分析 Author:aaron ...

  2. Linux Kernel 2.6.9源码分析 -- send/recieve 报文

    Linux Kernel 2.6.9源码分析 – send/recieve 报文 可用户socket报文读写的函数有以下几对: ssize_t read(int fd, void *buf, size ...

  3. Linux kernel 3.10内核源码分析--进程上下文切换

    一.疑问 进程调度时,当被选中的next进程不是current进程时,需要进行上下文切换. 进行上下文切换时,有一些问题不太容易理解,比如: 1.进程上下文切换必然发生在内核态吗? 2.上下文切换后原 ...

  4. linux ptrace 内核源码分析,linux 3.5.4 ptrace源码分析分析(系列一)

    ptrace是linux系统中为了调试专门设立的一种系统调用.要想调试调试一个进程,有两种方式: PTRACE_TRACEME和PTRACE_ATTACH.这两种方式的主要区别可以概括为: PTRAC ...

  5. linux nat源码分析,Linux下NAT/NAPT规则源码分析

    前面有一篇文章分析了为什么在PREROUTING做DNAT对本地连接不起作用?本文再紧接着上文,深入分析一下NAT/NAPT的规则. 事情的起因要从上的那篇的文章说起,因为我的本科生毕业设计也是做P2 ...

  6. Linux 网卡驱动sk_buff内核源码随笔

    这几天在调试有关网卡驱动的东西,有很多地方不清楚.有关网卡驱动部分主要有两个很重要的结构体:struct net_device 和struct sk_buff. 驱动大部分都是围绕这两个东西进行操作的 ...

  7. Linux Kernel 3.10内核源码分析--块设备层request plug/unplug机制

    一.基本原理 Linux块设备层使用了plug/unplug(蓄流/泄流)的机制来提升IO吞吐量.基本原理为:当IO请求提交时,不知直接提交给底层驱动,而是先将其放入一个队列中(相当于水池),待一定时 ...

  8. Linux kernel 3.10内核源码分析--slab原理及相关代码

    1.基本原理 我们知道,Linux保护模式下,采用分页机制,内核中物理内存使用buddy system(伙伴系统)进行管理,管理的内存单元大小为一页,也就是说使用buddy system分配内存最少需 ...

  9. linux 2.6线程创建源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 上章讲到线程,现在对线程创建的代码流程分析下.来一步一步揭开她神秘的面纱 linux内核创建线程函数 kernel_threa ...

最新文章

  1. oracle10g总结
  2. 元素,布局方式,BFC和清除浮动
  3. 如何为javascript代码编写注释以支持智能感知
  4. 非公平锁和公平锁在reetrantlock里的实现过程是怎样的
  5. 属于HTML文档头部相关标记,下列选项中,属于HTML文档头部相关标记的是 答案:title/titlemeta /...
  6. 38 FI配置-财务会计-固定资产-组织结构-指定帐户确定
  7. Android SubsamplingScaleImageView(subsampling-scale-image-view)单双击长按事件【系列2】
  8. python如何使用geotools_Python pygeotools包_程序模块 - PyPI - Python中文网
  9. Oracle 20c 新特性:原生的 JSON 数据类型(Native JSON Datatype)
  10. GP学习(七)—Accessing raster workspaces
  11. Digix联合创始人:在接下来的12个月中 比特币将被称为真正的价值存储
  12. springboot 配置全局响应数据_spring boot 全局事务配置
  13. 多质点列车动力学模型
  14. 数据分析五、Apriori 算法之关联分析
  15. jwplayer +ffmpeg+red5 实现摄像头的直播
  16. (附源码)springboot社区疫苗接种管理系统 毕业设计 281442
  17. 怎么让Win10不显示快速访问记录
  18. OpenCv打开摄像头失败问题处理cap_msmf.cpp (677) MFVideoFormat_RGB24(codec not found)
  19. 鸿蒙os适配机vivo型,originos系统适配机型有哪些
  20. Red Hat Enterprise Linux Server 7.4 安装方法

热门文章

  1. introjs,intro.js-react 步骤指引
  2. 别让中国经济成次贷危机下的另一个“华尔街”
  3. vector中begin(),end()和front(),back()的区别
  4. Result Maps collection already contains value for com.mmall.dao.CartMapper.BaseResultMap
  5. 【元胞自动机】元胞自动机图像处理【含Matlab源码 234期 】
  6. 使用go语言编译部署最新版Yearning【v3.0.1】
  7. validform ajax 提交,调试 Validform 的辛酸,添加ajax验证机制!
  8. 详解Unity 5 全局光照系统Enlighten问题(上)
  9. python与bim_BIM轻量化之路(二)
  10. 2Python编译器的选择、安装及配置(Pycharm/jupyter)