ALSA声卡驱动:

           1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介

            2.Linux ALSA声卡驱动之二:Platform

            3. Linux ALSA声卡驱动之三:Platform之Cpu_dai

            4. Linux ALSA声卡驱动之四:Codec 以及Codec_dai

            5.Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册

            6.Linux ALSA声卡驱动之六:PCM的注册流程

            7.Linux ALSA声卡驱动之七:录音(Capture) 调用流程

 

 

1.Platform驱动在ASoC中的作用

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

2.snd_soc_register_platform注册

  • snd_soc_register_platform时序图

 

 

  •  snd_soc_register_platform相关的结构体关系图

 

2.2分析snd_soc_register_platform的执行流程和相关结构体

     2.2.1   mtk-soc-pcm-capture.c      mtk_capture_probe函数  

static int mtk_capture_probe(struct platform_device *pdev)
{
	pr_warn("mtk_capture_probe\n");

	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
	if (pdev->dev.dma_mask == NULL)
		pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;

	if (pdev->dev.of_node)
		dev_set_name(&pdev->dev, "%s", MT_SOC_UL1_PCM);

	pr_warn("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
//platform的驱动注册
	return snd_soc_register_platform(&pdev->dev,
					 &mtk_soc_platform);
}
  •     struct device结构体
struct platform_device {
	const char	*name; //  平台设备的名字
	int		id;//   ID 是用来区分如果设备名字相同的时候(通过在后面添加一个数字来代表不同的设备,因为有时候有这种需求)
	bool		id_auto;
	struct device	dev;//   内置的device结构体
	u32		num_resources;//   资源结构体数量
	struct resource	*resource;

	const struct platform_device_id	*id_entry; //用来进行与设备驱动匹配用的id_table表
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};
static struct snd_soc_platform_driver mtk_soc_platform = {
	.ops        = &mtk_afe_capture_ops,
	.probe      = mtk_afe_capture_probe,
};

//驱动层的open close  ioctl 相关方法的具体实现
static struct snd_pcm_ops mtk_afe_capture_ops = {
	.open =     mtk_capture_pcm_open, //上层pcm_open  最终会调用这个函数
	.close =    mtk_capture_pcm_close,
	.ioctl =    snd_pcm_lib_ioctl,
	.hw_params =    mtk_capture_pcm_hw_params,
	.hw_free =  mtk_capture_pcm_hw_free,
	.prepare =  mtk_capture_pcm_prepare,
	.trigger =  mtk_capture_pcm_trigger,
	.pointer =  mtk_capture_pcm_pointer,
	.copy =     mtk_capture_pcm_copy,
	.silence =  mtk_capture_pcm_silence,
	.page =     mtk_capture_pcm_page,
	.mmap =     mtk_pcm_mmap,
};

      2.2.2  snd_soc_register_platform函数

int snd_soc_register_platform(struct device *dev,
		const struct snd_soc_platform_driver *platform_drv)
{
	struct snd_soc_platform *platform;
	int ret;

	dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));

	platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);//申请内存
	if (platform == NULL)
		return -ENOMEM;

	ret = snd_soc_add_platform(dev, platform, platform_drv);
	if (ret)
		kfree(platform);

	return ret;
}

  

       2.2.3  snd_soc_add_platform 函数做了如下事情:

               1.初始化platform->component和platform结构体:platform_drv和dev的值赋值给platform->component
              2.把platform->component添加到component_list,利用platform->list   把之前初始化完成的platform   添加到全局变量platform_list中,以供soc_bind_dai_link使用。

    


int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
		const struct snd_soc_platform_driver *platform_drv)
{
	int ret;
     //初始化platform->component
	ret = snd_soc_component_initialize(&platform->component,
			&platform_drv->component_driver, dev);
	if (ret)
		return ret;

	platform->dev = dev;//snd_soc_platform 与device关联
	platform->driver = platform_drv;

	if (platform_drv->probe)
		platform->component.probe = snd_soc_platform_drv_probe;//与platform_drv->probe 的一样
	if (platform_drv->remove)
		platform->component.remove = snd_soc_platform_drv_remove;

#ifdef CONFIG_DEBUG_FS
	platform->component.debugfs_prefix = "platform";
#endif

	mutex_lock(&client_mutex);
//通过list_add(&component->list, &component_list)把platform->component添加到component_list
	snd_soc_component_add_unlocked(&platform->component);
	list_add(&platform->list, &platform_list);//把platform加到platform_list列队中
	mutex_unlock(&client_mutex);

	dev_dbg(dev, "ASoC: Registered platform '%s'\n",
		platform->component.name);

	return 0;
}
static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
{
	if (!component->write && !component->read) {
		if (!component->regmap)
			component->regmap = dev_get_regmap(component->dev, NULL);
		if (component->regmap)
			snd_soc_component_setup_regmap(component);
	}

	list_add(&component->list, &component_list);
	INIT_LIST_HEAD(&component->dobj_list);
}
  •    snd_soc_component结构体

//当底层驱动注册platform、codec+codec dai、cpu dai时, 核心层都会创建一个对应的snd_soc_component,并且会挂到component_list 链表中
struct snd_soc_component {
	const char *name;  //这个跟device_driver->name 和snd_soc_component_driver->id有关,
	int id;
	const char *name_prefix;
	struct device *dev;
	struct snd_soc_card *card;

	unsigned int active;

	unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
	unsigned int registered_as_component:1;//在snd_soc_register_component时修改为1

	struct list_head list;//用于把自己挂载到全局链表component_list下 ,component_list 实在soc-core 中保持的全局变量

	struct snd_soc_dai_driver *dai_drv;//dai  有可能是cpu dai 也有可能是 codec dai  是一个数组
	int num_dai;//dai 的数量

	const struct snd_soc_component_driver *driver;//指向下属的snd_soc_component_driver, 该结构体一般由底层平台驱动实现

	struct list_head dai_list;//链表头, 挂接snd_soc_dai->list   list_add(&dai->list, &component->dai_list)

	int (*read)(struct snd_soc_component *, unsigned int, unsigned int *);
	int (*write)(struct snd_soc_component *, unsigned int, unsigned int);

	struct regmap *regmap;
	int val_bytes;

	struct mutex io_mutex;

	/* attached dynamic objects */
	struct list_head dobj_list;

#ifdef CONFIG_DEBUG_FS
	struct dentry *debugfs_root;
#endif

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

	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;
	struct snd_soc_codec *codec;

	int (*probe)(struct snd_soc_component *);
	void (*remove)(struct snd_soc_component *);

#ifdef CONFIG_DEBUG_FS
	void (*init_debugfs)(struct snd_soc_component *component);
	const char *debugfs_prefix;
#endif
};
  • snd_soc_platform结构体
struct snd_soc_platform {
	struct device *dev;
	const struct snd_soc_platform_driver *driver;

	struct list_head list;//添加到全局变量platform_list

	struct snd_soc_component component;
};

   总结:结合上面的时序图和结构体关系图,snd_soc_register_platform  把mtk_soc_platform和pdev->dev 的值赋值给snd_soc_platform(platform)和snd_soc_component(platform->component) 。并且通过list_add(&platform->list, &platform_list)  和list_add(&component->list, &component_list)把platform添加到platform_list全局链表中,platform->component添加到component_list全局链表。这两个全局链表在函数soc_bind_dai_link中被调用,soc_bind_dai_link是绑定cpu_dai 、codec_dai 、codec、platform。具体实现看machine章节

Logo

更多推荐