Linux ALSA声卡驱动之二:Platform
1.Platform驱动在ASoC中的作用ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DAI)把音频数据传送给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音信信号。在具体实现上,ASoC有把Platform驱动分为两个部分:snd_soc_platform_drive...
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章节
更多推荐
所有评论(0)