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 区分开)。
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;
- else
- dai->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添加完成 */
- /*
- * 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结构体中。
- /*
- * 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支持的声道数,码率,数据格式等能力。
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开始时传输,结束传输,暂停传世,恢复传输的时候被回调。
- /* 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);
在编写 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继承了这个基类,然后在此基础上又添加了一些特有的成员变量。
- 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;
- };
- 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_FS
- const char *debugfs_prefix;
- #endif
- };
module_platform_driver函数的详解请参考《module_platform_driver源码分析》