当前位置: 首页 > news >正文

drm驱动学习(一)sunxi_drm初始化

平台内核版本文件系统
全志T527linux 5.15buildroot

文章目录

  • 一、从设备驱动
    • 1.1 sunxi_tcon_top_platform_driver
    • 1.2 sunxi_de_platform_driver
    • 1.3 sunxi_lvds_platform_driver
    • 1.4 sunxi_tcon_platform_driver
  • 二、 主设备驱动
  • 三、初始化代码分析
    • 3.1 __devm_drm_dev_alloc
    • 3.2 drmm_mode_config_init
    • 3.3 sunxi_drm_mode_config_init
    • 3.4 sunxi_drm_property_create
    • 3.5 get_boot_display_info
    • 3.6 component_bind_all
    • 3.7 dev_set_drvdata
    • 3.8 drm_mode_config_reset
    • 3.9 drm_kms_helper_poll_init
    • 3.10 setup_bootloader_connecting_state
    • 3.11 commit_init_connecting
    • 3.12 drm_dev_register
    • 3.13 sunxi_drm_procfs_init

现在基于全志的开发板来时讲解drm驱动。DRM 显示驱动加载使用的是Component 方法,Linux 内核中的 Component 框架 是一种用于管理复杂子系统中多个设备模块加载顺序的机制。它通过定义主设备(master)和从设备(component)之间的关系,确保在初始化过程中,所有依赖的组件都能按照正确的顺序加载和初始化。在 DRM 架构中,master 是一个显示子系统,而 component 是各个显示控制器、编码器等。在驱动文件sunxi_drm_drv.c文件的init函数中,先注册一系列的从设备驱动,也就是sunxi_drm_sub_drivers数组中的驱动,再注册主驱动。sunxi_drm_platform_driver。

一、从设备驱动

在驱动文件sunxi_drm_drv.c文件的init函数中调用sunxi_drm_register_drivers函数,在函数中使用for循环使用platform_driver_register函数注册sunxi_drm_sub_drivers数组的每一个成员,从设备驱动:

static struct platform_driver *sunxi_drm_sub_drivers[] = {DRV_PTR(sunxi_tcon_top_platform_driver,         CONFIG_AW_DRM_TCON_TOP),DRV_PTR(sunxi_de_platform_driver,               CONFIG_AW_DRM_DE),DRV_PTR(sunxi_dsi_combo_phy_platform_driver,    CONFIG_AW_DRM_DSI_COMBOPHY),DRV_PTR(sunxi_dsi_platform_driver,              CONFIG_AW_DRM_DSI),DRV_PTR(sunxi_lvds_platform_driver,             CONFIG_AW_DRM_LVDS),DRV_PTR(sunxi_rgb_platform_driver,              CONFIG_AW_DRM_RGB),DRV_PTR(sunxi_hdmi_platform_driver,             CONFIG_AW_DRM_HDMI_TX),DRV_PTR(sunxi_drm_edp_platform_driver,          CONFIG_AW_DRM_EDP),DRV_PTR(sunxi_tcon_platform_driver,             CONFIG_AW_DRM_TCON),/* TODO add tv*/
};

这里我们根据成员的CONFIG配置和实际使用情况,我们用到的驱动有sunxi_tcon_top_platform_driver、sunxi_de_platform_driver、sunxi_lvds_platform_driver、sunxi_tcon_platform_driver这几个。

1.1 sunxi_tcon_top_platform_driver

sunxi_tcon_top_platform_driver驱动在sunxi_device/sunxi_tcon_top.c文件中,他的probe函数只做了一件事,就是component_add:

struct platform_driver sunxi_tcon_top_platform_driver = {.probe = sunxi_tcon_top_probe,.remove = sunxi_tcon_top_remove,.driver = {.name = "tcon-top",.owner = THIS_MODULE,.of_match_table = sunxi_tcon_top_match,},
};
component_add(&pdev->dev, &sunxi_tcon_top_component_ops);
static const struct component_ops sunxi_tcon_top_component_ops = {.bind = sunxi_tcon_top_bind,.unbind = sunxi_tcon_top_unbind,
};

1.2 sunxi_de_platform_driver

sunxi_de_platform_driver驱动在sunxi_device/hardware/lowlevel_de/sunxi_de.c文件中:

struct platform_driver sunxi_de_platform_driver = {.probe = sunxi_de_probe,.remove = sunxi_de_remove,.driver = {.name = "sunxi-display-engine",.owner = THIS_MODULE,.of_match_table = sunxi_de_match,},
};
component_add(&pdev->dev, &sunxi_de_component_ops);
static const struct component_ops sunxi_de_component_ops = {.bind = sunxi_de_bind,.unbind = sunxi_de_unbind,
};

1.3 sunxi_lvds_platform_driver

sunxi_lvds_platform_driver驱动在sunxi_drm_lvds.c文件中

struct platform_driver sunxi_lvds_platform_driver = {.probe = sunxi_drm_lvds_probe,.remove = sunxi_drm_lvds_remove,.driver = {.name = "drm-lvds",.owner = THIS_MODULE,.of_match_table = sunxi_drm_lvds_match,},
};
component_add(&pdev->dev, &sunxi_drm_lvds_component_ops);
static const struct component_ops sunxi_drm_lvds_component_ops = {.bind = sunxi_drm_lvds_bind,.unbind = sunxi_drm_lvds_unbind,
};

1.4 sunxi_tcon_platform_driver

sunxi_tcon_platform_driver驱动在sunxi_device/sunxi_tcon.c文件中:

struct platform_driver sunxi_tcon_platform_driver = {.probe = sunxi_tcon_probe,.remove = sunxi_tcon_remove,.driver = {.name = "tcon",.owner = THIS_MODULE,.of_match_table = sunxi_tcon_match,},
};
component_add(&pdev->dev, &sunxi_tcon_component_ops);
static const struct component_ops sunxi_tcon_component_ops = {.bind = sunxi_tcon_bind,.unbind = sunxi_tcon_unbind,
};

这几个从设备驱动的probe函数,都是从设备树中获取一些信息,初始化硬件,最后调用component_add函数添加,关键是这几个bind函数。我们继续分析bind函数做了什么。

二、 主设备驱动

注册平台设备sunxi_drm_platform_driver:

static struct platform_driver sunxi_drm_platform_driver = {.probe = sunxi_drm_platform_probe,.remove = sunxi_drm_platform_remove,.driver = {.owner = THIS_MODULE,.name = "sunxi-drm",.of_match_table = sunxi_of_match,.pm = &sunxi_drm_pm_ops,},
};static int sunxi_drm_platform_probe(struct platform_device *pdev)
{struct component_match *match;DRM_INFO("%s start\n", __FUNCTION__);match = sunxi_drm_match_add(&pdev->dev);if (IS_ERR(match)) {DRM_INFO("sunxi_drm_match_add fail\n");return PTR_ERR(match);}return component_master_add_with_match(&pdev->dev, &sunxi_drm_ops,match);
}

在probe函数中然后调用函数platform_driver_register注册主驱动sunxi_drm_platform_driver。然后在probe函数中遍历sunxi_drm_sub_drivers数组,对每一个驱动通过platform_find_device_by_driver函数找到对应的device,调用函数device_link_add绑定主设备和从设备,再调用函数component_match_add把各个组件绑定到主设备中。然后调用component_master_add_with_match函数将主设备(master)注册到 Component 框架中,这里会调用master的bind函数。

static int sunxi_drm_bind(struct device *dev)
{private = __devm_drm_dev_alloc(dev, &sunxi_drm_driver,sizeof(*private) + sizeof(struct sunxi_drm_pri),offsetof(struct sunxi_drm_private, base));ret = drmm_mode_config_init(drm);sunxi_drm_mode_config_init(drm);sunxi_drm_property_create(private);get_boot_display_info(drm);ret = component_bind_all(dev, drm);dev_set_drvdata(dev, drm);drm_mode_config_reset(drm);drm_kms_helper_poll_init(drm);ret = setup_bootloader_connecting_state(drm);commit_init_connecting(drm);ret = drm_dev_register(drm, 0);return 0;
}

如上面的裁剪代码所示,在sunxi_drm_ops的bind成员sunxi_drm_bind函数中调用__devm_drm_dev_alloc函数分配并且初始化drm_dev实例,其绑定的驱动是sunxi_drm_driver。之前定义struct drm_driver sunxi_drm_driver ,包含各种func和fops,我们仔细看看:

DEFINE_DRM_GEM_CMA_FOPS(sunxi_drm_driver_fops);
#define DEFINE_DRM_GEM_CMA_FOPS(name) \static const struct file_operations name = {\.owner          = THIS_MODULE,\.open           = drm_open,\.release        = drm_release,\.unlocked_ioctl = drm_ioctl,\.compat_ioctl   = drm_compat_ioctl,\.poll           = drm_poll,\.read           = drm_read,\.llseek         = noop_llseek,\.mmap           = drm_gem_mmap,\.get_unmapped_area      = drm_gem_cma_get_unmapped_area, \}#define DRM_GEM_CMA_DRIVER_OPS_VMAP \DRM_GEM_CMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(drm_gem_cma_dumb_create)#define DRM_GEM_CMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(dumb_create_func) \.dumb_create            = dumb_create_func, \.prime_handle_to_fd     = drm_gem_prime_handle_to_fd, \.prime_fd_to_handle     = drm_gem_prime_fd_to_handle, \.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table_vmap, \.gem_prime_mmap         = drm_gem_prime_mmapstatic struct drm_driver sunxi_drm_driver = {.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,.fops = &sunxi_drm_driver_fops,.ioctls             = sunxi_drm_ioctls,.num_ioctls         = ARRAY_SIZE(sunxi_drm_ioctls),.name = DRIVER_NAME,.desc = DRIVER_DESC,.date = DRIVER_DATE,.major = DRIVER_MAJOR,.minor = DRIVER_MINOR,.gem_create_object = sunxi_gem_create_object,
#if LINUX_VERSION_CODE <= KERNEL_VERSION(6, 1, 0)DRM_GEM_CMA_DRIVER_OPS_VMAP,
#elseDRM_GEM_DMA_DRIVER_OPS_VMAP,
#endif
};

预处理后变为

static struct drm_driver sunxi_drm_driver = {.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,.fops = &sunxi_drm_driver_fops,.ioctls             = sunxi_drm_ioctls,.num_ioctls         = ARRAY_SIZE(sunxi_drm_ioctls),.name = DRIVER_NAME,.desc = DRIVER_DESC,.date = DRIVER_DATE,.major = DRIVER_MAJOR,.minor = DRIVER_MINOR,.gem_create_object = sunxi_gem_create_object,.dumb_create            = dumb_create_func, .prime_handle_to_fd     = drm_gem_prime_handle_to_fd, .prime_fd_to_handle     = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table_vmap, .gem_prime_mmap         = drm_gem_prime_mmap,
};
static const struct file_operations sunxi_drm_driver_fops = {.owner          = THIS_MODULE,.open           = drm_open,.release        = drm_release,.unlocked_ioctl = drm_ioctl,.compat_ioctl   = drm_compat_ioctl,.poll           = drm_poll,.read           = drm_read,.llseek         = noop_llseek,.mmap           = drm_gem_mmap,.get_unmapped_area      = drm_gem_cma_get_unmapped_area, 
}

分配完drm_dev数据结构后,分别调用drmm_mode_config_init、sunxi_drm_mode_config_init、sunxi_drm_property_create、get_boot_display_info、component_bind_all、dev_set_drvdata、drm_mode_config_reset、drm_kms_helper_poll_init、setup_bootloader_connecting_state、commit_init_connecting函数初始化,最后调用drm_dev_register函数注册frm字符设备。

三、初始化代码分析

3.1 __devm_drm_dev_alloc

这个函数用于分配并初始化一个新的 drm_device 实例。它是 DRM 驱动程序初始化过程中非常重要的一步,确保设备资源的生命周期与设备对象绑定,并在适当的时候自动清理资源。

3.2 drmm_mode_config_init

初始化 DRM 设备的模式配置,也就是初始化drm_device的struct drm_mode_config mode_config成员。这个成员是 记录当前drm设备当前模式配置。这里列举一些成员,因为很多成员的很重要,但是数量有太多,只能写一些比较靠前的成员:

struct drm_mode_config {struct idr object_idr;    //管理 DRM 模式配置中的对象(如 CRTC、平面、编码器、连接器等)的ID,用于高效查找struct list_head fb_list;        //fb链表struct list_head connector_list;//连接器链表struct list_head encoder_list;    //编码器链表struct list_head plane_list;    //plane链表struct list_head crtc_list;        //crtc链表struct list_head property_list;    //属性链表struct list_head property_blob_list; // 属性 blob 链表int min_width, min_height;    //最小宽度和高度int max_width, max_height;    //最大宽度和高度struct drm_mode_config_funcs *funcs;// 模式配置函数表resource_size_t fb_base;    //fb的基地址...
}

3.3 sunxi_drm_mode_config_init

继续初始化drm设备的当前配置,这里主要是设置了最大和最小宽度高度,和两个成员:

dev->mode_config.funcs = &sunxi_drm_mode_config_funcs;
dev->mode_config.helper_private = &sunxi_mode_config_helpers;static const struct drm_mode_config_funcs sunxi_drm_mode_config_funcs = {.atomic_check = drm_atomic_helper_check,.atomic_commit = sunxi_drm_atomic_helper_commit,.fb_create = sunxi_drm_gem_fb_create,
};static const struct drm_mode_config_helper_funcs sunxi_mode_config_helpers = {.atomic_commit_tail = sunxi_drm_atomic_helper_commit_tail,
};

这两个func都是很重要的回调方法,在后面会详细分析。

3.4 sunxi_drm_property_create

创建各种属性而已,这些属性存放在下面的数据中:

struct sunxi_drm_private {struct drm_device base;struct drm_property *prop_blend_mode[OVL_REMAIN];struct drm_property *prop_alpha[OVL_REMAIN];struct drm_property *prop_src_x[OVL_REMAIN], *prop_src_y[OVL_REMAIN];struct drm_property *prop_src_w[OVL_REMAIN], *prop_src_h[OVL_REMAIN];struct drm_property *prop_crtc_x[OVL_REMAIN], *prop_crtc_y[OVL_REMAIN];struct drm_property *prop_crtc_w[OVL_REMAIN], *prop_crtc_h[OVL_REMAIN];struct drm_property *prop_fb_id[OVL_REMAIN];struct drm_property *prop_color[OVL_MAX];struct drm_property *prop_layer_id;struct drm_property *prop_frontend_data;struct drm_property *prop_backend_data;struct drm_property *prop_sunxi_ctm;struct drm_property *prop_feature;struct drm_property *prop_eotf;struct drm_property *prop_color_space;struct drm_property *prop_color_format;struct drm_property *prop_color_depth;struct drm_property *prop_color_range;struct drm_property *prop_frame_rate_change;struct drm_property *prop_compressed_image_crop;struct sunxi_drm_pri *priv;
};

还会调用函数drm_mode_create_tv_properties创建TV显示的通用属性。

3.5 get_boot_display_info

获取uboot写入了的各种信息,写入到drm_device的私有数据中。猜测,看的不太懂

3.6 component_bind_all

int component_bind_all(struct device *parent, void *data)
{struct master *master;struct component *c;size_t i;int ret = 0;WARN_ON(!mutex_is_locked(&component_mutex));master = __master_find(parent, NULL);if (!master)return -EINVAL;/* Bind components in match order */for (i = 0; i < master->match->num; i++)if (!master->match->compare[i].duplicate) {c = master->match->compare[i].component;ret = component_bind(c, master, data);if (ret)break;}if (ret != 0) {for (; i > 0; i--)if (!master->match->compare[i - 1].duplicate) {c = master->match->compare[i - 1].component;component_unbind(c, master, data);}}return ret;
}

遍历之前绑定的全部组件,调用他们的bind函数。

3.7 dev_set_drvdata

设置刚刚申请的struct sunxi_drm_private为dev的私有数据。

3.8 drm_mode_config_reset

遍历drm_device下的每一个plane,调用plane->funcs->reset(plane);

遍历drm_device下的每一个crtc,调用crtc->funcs->reset(crtc);

遍历drm_device下的每一个connector,调用connector->funcs->reset(connector);

也就是把全部组件的func的reset方法全部调用一遍。

3.9 drm_kms_helper_poll_init

void drm_kms_helper_poll_init(struct drm_device *dev)
{INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);dev->mode_config.poll_enabled = true;drm_kms_helper_poll_enable(dev);
}void drm_kms_helper_poll_enable(struct drm_device *dev)
{bool poll = false;struct drm_connector *connector;struct drm_connector_list_iter conn_iter;unsigned long delay = DRM_OUTPUT_POLL_PERIOD;drm_connector_list_iter_begin(dev, &conn_iter);drm_for_each_connector_iter(connector, &conn_iter) {if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT |DRM_CONNECTOR_POLL_DISCONNECT))poll = true;}drm_connector_list_iter_end(&conn_iter);if (dev->mode_config.delayed_event) {poll = true;delay = HZ;}if (poll)schedule_delayed_work(&dev->mode_config.output_poll_work, delay);
}

drm_kms_helper_poll_init函数的作用是初始化KMS的轮询机制,以检测连接器(connector)的状态变化。如果连接器支持热插拔检测(HPD),但无法生成热插拔中断,则需要通过轮询来检测连接器的状态变化。在这个函数会初始化一个运行output_poll_execute函数的延时任务,在延时任务中探测连接状态是否发生改变,同时判断连接是否处于强制模式,如果处于强制模式,则停止轮询。目前开发板只连接lvds的显示屏,是强制连接模式的,这个函数poll初始化的delay work只会运行一次,因为检测到lvds屏的强制模式。如果是HMDI接口的connnect,这里的poll机制机会用上了。

3.10 setup_bootloader_connecting_state

遍历每一个连接器,给每一个连接器分配struct drm_connector的内存空间。然后调用函数init_connecting初始化每一个connector的struct sunxi_init_connecting数据结构,主要是查看连接器支持哪些模式,如果已经是连接状态,记录当前模式,并且加载到struct sunxi_drm_private->priv->connecting_head链表中。就是判断在bootloader阶段是否已经有连接器已经连接显示了,如果有,读取状态记录到链表中。

3.11 commit_init_connecting

commit_init_connectingsunxi_fbdev_init    //根据uboot设置的显示,初始化内核drm数据drm_fb_config    //读取bootloader显示的fb info数据sunxi_drm_get_logo_info    //读取boot模式中的fb数据和显示宽度高度drm_fb_init       //根据读取到的fb数据初始化显示 channel和drm相关结构体drm_modeset_acquire_init//初始化一个模式设置上下文。管理模式操作的锁drm_atomic_state_alloc    //分配并且初始化drm_atomic_statelist_for_each_entry {   //遍历pri->priv->connecting_head中每一个sunxi_init_connecting数据drm_atomic_get_crtc_state//获取crtc的stastdrm_atomic_set_mode_for_crtc    //根据drm_display_mode直接设置crtc模式drm_atomic_get_connector_state    //获取连接器的当前状态drm_atomic_set_crtc_for_connector    //把connect绑定到crtcdrm_atomic_get_plane_state    //获取plane的statusto_display_channel_state    //通过drm_plane_state找到display_channel_state数据drm_atomic_set_fb_for_plane    //为fb设置plane,其实就是更新fb的引用drm_atomic_set_crtc_for_plane    //将plane与crtc绑定}drm_atomic_commit//原子提交配置

commit_init_connecting函数做了很多事情,首先调用函数sunxi_fbdev_init函数根据uboot是都的显示,初始化内核drm数据,平滑显示过度;然后调用函数drm_modeset_acquire_init初始化一个模式设置上下文,用于管理模式操作;再调用函数drm_atomic_state_alloc分配并且初始化drm_atomic_state内存;接着遍历pri->priv->connecting_head中每一个sunxi_init_connecting数据,在循环中获取crtc的模式stast,获取连接器的当前状态,再把connect绑定到crtc,再获取plane的status,通过drm_plane_state找到display_channel_state数据,为fb设置plane,最后将plane与crtc绑定后进入下一个循环或者退出循环。最后调用函数drm_atomic_commit进行原子提交配置。

3.12 drm_dev_register

这个函数是注册drm字符设备的函数,首先调用函数drm_mode_config_validate校验drm模式是否ok;然后调用函数drm_minor_register创建渲染设备节点和主设备节点,由于硬件原因,只有主设备节点创建成功;然后调用函数create_compat_control_link创建sysfs目录的一些控制连接;最后调用drm_modeset_register_all函数注册所有的组件,包括plane、crtc、encoder和connector,但是并没有什么实际的作用的,因为这些组件的late_register成员都是空的。详细代码调用可以查看下面:

drm_dev_registerdrm_mode_config_validate    //应该是模式相关的一些校验drm_minor_register(dev, DRM_MINOR_RENDER);    //创建渲染设备节点/dev/dri/renderD128,全志没有这个功能drm_minor_register(dev, DRM_MINOR_PRIMARY);    //创建主设备节点/dev/dri/card0create_compat_control_link    //创建sysfs的链接节点drm_modeset_register_all    //注册所有的组件drm_plane_register_all    //注册所有的planedrm_for_each_plane(plane, dev) ret = plane->funcs->late_register(plane);drm_crtc_register_all//注册所有的crtcdrm_for_each_crtc(crtc, dev) {drm_debugfs_crtc_add(crtc);crtc->funcs->late_register(crtc);drm_encoder_register_all//注册所有的encoderdrm_for_each_encoder(encoder, dev) {encoder->funcs->late_register(encoder);drm_connector_register_all//注册所有的connectordrm_for_each_connector_iter(connector, &conn_iter) {drm_connector_registerconnector->funcs->late_register

3.13 sunxi_drm_procfs_init

proc_entry = proc_mkdir("sunxi-drm", NULL);sunxi_drm_procfs_initproc_create_single_data("debug", 444, proc_entry, sunxidrm_debug_show, NULL);proc_create_single_data("status", 444, proc_entry,sunxi_drm_procfs_status_show, drm);

从上面的代码可以看到这个函数主要是在proc的sunxi-drm目录下创建debug和status两个节点,分别用于调试和查看drm各组件的状态,仅此而已。

http://www.dtcms.com/a/285960.html

相关文章:

  • 【leetcode】3201. 找出有效子序列的最大长度(1)
  • PyCharm 高效入门指南(核心模块详解二)
  • RoboBrain 2.0(具身智能论文阅读)
  • 笔试——Day12
  • 阿里云alicloud liunux3-安装docker
  • Python编程进阶知识之第二课学习网络爬虫(selenium)
  • JavaScript进阶篇——第九章 异常、this 与性能优化全解(终)
  • 14.链路聚合技术
  • 量化交易如何查询CFD指数实时行情
  • postman接口测试,1个参数有好几个值的时候如何测试比较简单快速?
  • IP协议深入理解
  • 20250718-4-Kubernetes 应用程序生命周期管理-Pod对象:实现机制_笔记
  • 如何保证缓存数据的一致性:数据库和缓存数据一致性,本地缓存和Redis缓存怎么保证数据一致性
  • Netty封装Websocket并实现动态路由
  • Spring Boot 应用优雅停机与资源清理:深入理解关闭钩子
  • 比特币技术简史 第四章:挖矿机制 - 挖矿算法、难度调整与矿池
  • 在ajax中什么时候需要将返回值类型做转换
  • Excel基础:格式化
  • 产品需求文档(PRD)格式全解析:从 RP 到 Word 的选择与实践
  • 2023 年 3 月青少年软编等考 C 语言八级真题解析
  • 回归预测 | MATLAB实现DBO-BP蜣螂算法优化BP神经网络多输入单输出回归预测
  • 借助AI学习开源代码git0.7之三git-init-db
  • MyBatis-Flex代码生成
  • ARINC818协议详解
  • KRaft 角色状态设计模式:从状态理解 Raft
  • 美创科技建设项目被评为中国信通院医疗领域“磐安”优秀案例
  • A33-vstar报错记录:ERROR: build kernel Failed
  • 北京-4年功能测试2年空窗-报培训班学测开-第五十四天
  • (笔记+作业)第五期书生大模型实战营---L2G2000 GraphGen:训练数据合成实践
  • U盘运维笔记