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

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

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;
    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添加完成 */

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_FS
  const char *debugfs_prefix;
#endif
};

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

相关文章
|
3月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
44 6
|
3月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
50 5
|
3月前
|
Ubuntu NoSQL Linux
Linux内核和驱动
Linux内核和驱动
29 2
|
2月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
3月前
|
Linux
【linux】【驱动】<specifier>-map-pass-thru讲解
【linux】【驱动】<specifier>-map-pass-thru讲解
22 0
|
Linux Windows
LINUX编译alsa
LINUX编译alsa
138 0
|
7天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
27 3
|
7天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
20 2
|
14天前
|
缓存 监控 Linux
|
18天前
|
Linux Shell 数据安全/隐私保护