Linux ALSA驱动之Platform源码分析(wm8350.c)

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源码分析》
       


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部