Rockchip RK3399 - DRM子系统目录 从开始接触音频子系统到如今已经两个多月,说实话花费的时间的确有点长了。从今天起我们开始接触DRM,网上已经有很多优秀的关于DRM的文章了,因此我们学习直接去学习一些优秀的文章即可。后面有关DRM相关的文章我们会大量参考 一、DRM介绍1.1 DRM概述
在实际场景中,具体选择哪一种图形设备驱动框架取决于我们自己的业务需求。 1.1.1 Frambebuffer驱动
1.1.2 DRM驱动相比FB(
总之,一句话,DRM是Linux目前主流的图形显示框架,相比FB架构,DRM 有关DRM的发展历史可以参考这篇博客: ![]() 1.2 DRM框架我们来看一下DRM子系统的软件架构: ![]()
(1) 应用程序:上图中并没有画出;应用程序可以直接操纵DRM的ioctl进行显示相关操作,后来封装成了libdrm库,让用户可以更加方便的进行显示控制; (2) libdrm:lbdrm是DRM框架提供的位于用户空间操作DRM的库,提供了DRM (3) DRM core:DRM核心层,由GEM和KMS组成;
(4) HW:硬件设备; 1.2.1 KMS
![]() 以HDMI接口为例说明,Soc内部一般包含一个Display模块,通过总线连接到HDMI
1.2.2 GEM
1.2.3 元素介绍学习DRM驱动其实就是学习上面各个元素的实现及用法,如果你能掌握这些知识点,那么在编写DRM驱动的时候就能游刃有余。
1.3 目录结构
root@zhengyang:/work/sambashare/rk3399/linux-6.3# ls drivers/gpu/drm/ -I "*.o" amd drm_fbdev_generic.c drm_print.c logicvc arm drm_fb_dma_helper.c drm_privacy_screen.c Makefile armada drm_fb_helper.c drm_privacy_screen_x86.c mcde aspeed drm_file.c drm_probe_helper.c mediatek ast drm_flip_work.c drm_property.c meson atmel-hlcdc drm_format_helper.c drm_rect.c mgag200 bridge drm_fourcc.c drm_scatter.c modules.order built-in.a drm_framebuffer.c drm_self_refresh_helper.c msm display drm_gem_atomic_helper.c drm_shmem_helper.ko mxsfb drm_agpsupport.c drm_gem.c drm_shmem_helper.mod nouveau drm_aperture.c drm_gem_dma_helper.c drm_shmem_helper.mod.c omapdrm drm_atomic.c drm_gem_framebuffer_helper.c drm_simple_kms_helper.c panel drm_atomic_helper.c drm_gem_shmem_helper.c drm_syncobj.c panfrost drm_atomic_state_helper.c drm_gem_ttm_helper.c drm_sysfs.c pl111 drm_atomic_uapi.c drm_gem_vram_helper.c drm_trace.h qxl drm_auth.c drm_hashtab.c drm_trace_points.c radeon drm_blend.c drm_internal.h drm_ttm_helper.ko rcar-du drm_bridge.c drm_ioc32.c drm_ttm_helper.mod rockchip drm_bridge_connector.c drm_ioctl.c drm_ttm_helper.mod.c scheduler drm_buddy.c drm_irq.c drm_vblank.c shmobile drm_bufs.c drm_kms_helper_common.c drm_vblank_work.c solomon drm_cache.c drm_lease.c drm_vma_manager.c sprd drm_client.c drm_legacy.h drm_vm.c sti drm_client_modeset.c drm_legacy_misc.c drm_vram_helper.ko stm drm_color_mgmt.c drm_lock.c drm_vram_helper.mod sun4i drm_connector.c drm_managed.c drm_vram_helper.mod.c tegra drm_context.c drm_memory.c drm_writeback.c tests drm_crtc.c drm_mipi_dbi.c etnaviv tidss drm_crtc_helper.c drm_mipi_dsi.c exynos tilcdc drm_crtc_helper_internal.h drm_mm.c fsl-dcu tiny drm_crtc_internal.h drm_mode_config.c gma500 ttm drm_damage_helper.c drm_mode_object.c gud tve200 drm_debugfs.c drm_modes.c hisilicon udl drm_debugfs_crc.c drm_modeset_helper.c hyperv v3d drm_displayid.c drm_modeset_lock.c i2c vboxvideo drm_dma.c drm_of.c i915 vc4 drm_drv.c drm_panel.c imx vgem drm_dumb_buffers.c drm_panel_orientation_quirks.c ingenic virtio drm_edid.c drm_pci.c Kconfig vkms drm_edid_load.c drm_plane.c kmb vmwgfx drm_encoder.c drm_plane_helper.c lib xen drm_encoder_slave.c drm_prime.c lima xlnx 其中:drm_drv.c:DRM core核心实现;
其中rockchip为Rockchip官方的实现代码: root@zhengyang:/work/sambashare/rk3399/linux-6.3# ls drivers/gpu/drm/rockchip/ -I "*.o" analogix_dp-rockchip.c inno_hdmi.c rockchip_drm_drv.h rockchip_drm_vop.h built-in.a inno_hdmi.h rockchip_drm_fb.c rockchip_lvds.c cdn-dp-core.c Kconfig rockchip_drm_fb.h rockchip_lvds.h cdn-dp-core.h Makefile rockchip_drm_gem.c rockchip_rgb.c cdn-dp-reg.c modules.order rockchip_drm_gem.h rockchip_rgb.h cdn-dp-reg.h rk3066_hdmi.c rockchip_drm_vop2.c rockchip_vop2_reg.c dw_hdmi-rockchip.c rk3066_hdmi.h rockchip_drm_vop2.h rockchip_vop_reg.c dw-mipi-dsi-rockchip.c rockchip_drm_drv.c rockchip_drm_vop.c rockchip_vop_reg.h 二、硬件抽象对于初学者来说,往往让人迷惑的不是DRM中objects的概念,而是如何去建立这些objects 在学如何去抽象显示硬件到具体的DRM object之前,我们先普及一下MIPI相关的知识。
2.1 MIPI DSI 接口下图为一个典型的MIPI DSI接口屏的硬件连接框图: ![]() 它在软件架构上与DRM object的对应关系如下图: 多余的细节不做介绍,这里只说明为何如此分配drm object:
驱动参考:https://elixir./linux/latest/source/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c。 有关MIPI DSI可以参考 2.2 MIPI DPI接口
![]() 该硬件连接在软件架构上与DRM object的对应关系如下图: ![]() 多余的细节不做介绍,这里只说明为何如此分配drm object:
驱动参考:https://elixir./linux/v5.0/source/drivers/gpu/drm/panel/panel-sitronix-st7789v.c。 2.3 MIPI DBI接口
下图为一个典型的DBI接口屏的硬件连接框图: ![]() 该硬件连接在软件架构上与DRM object的对应关系如下: ![]() 上图参考kernel4.19 tinydrm软件架构。
驱动参考:https://elixir./linux/latest/source/drivers/gpu/drm/tinydrm/ili9341.c。 三、DRM Objects在编写DRM驱动程序之前,我们先对DRM内部的objects进行一番介绍,因为这些objects ![]() 上图蓝色部分则是对物理硬件的抽象,黄色部分则是对软件的抽象。虚线以上的为drm_mode_object(或者说是modset object),虚线以下为drm_gem_object(或者说是 这些objects之间的关系: 通过上图可以看到,plane是连接framebuffer和crtc的纽带,而encoder则是连接crtc 个人理解: buffer是硬件存储设备, 由gem分配和释放; framebuffer用于描述分配的显存的信息(如format、pitch、size等); plane用于描述图层信息,同一个crtc可以由多个plane组成;
需要注意的是,上图蓝色部分即使没有实际的硬件与之对应,在软件驱动中也需要实现这些 3.1 drm_pane
耦合的产生: connector的主要作用就是获取显示参数,所以会在LCD驱动中去构造connector object。但是connector初始化时需要attach上一个encoder object,而这个encoder object往往是在另一个硬件驱动中生成的,为了访问该encoder object,势必会产生一部分耦合的代码;
为了解决该耦合的问题,DRM子系统为开发人员提供了drm_panel结构体,该结构体封装了connector & encoder对LCD访问的常用接口; 于是,原来的encoder驱动和LCD驱动之间的耦合,就转变成了上图中encoder驱动与drm_panel、drm_panel与LCD驱动之间的“耦合”,从而实现了encoder驱动与LCD驱动之间的解耦合。
3.2 modeset object对于plane、crtc、encoder、connector几个对象,它们有一个公共基类struct drm_mode_object,这几个对象都由此基类扩展而来(该类作为crtc等结构体的成员)。 事实上这个基类扩展出来的子类并不是只有上面提到的几种,只不过这四种比较常见。其定义在include/drm/drm_mode_object.h: /** * struct drm_mode_object - base structure for modeset objects * @id: userspace visible identifier * @type: type of the object, one of DRM_MODE_OBJECT\_\* * @properties: properties attached to this object, including values * @refcount: reference count for objects which with dynamic lifetime * @free_cb: free function callback, only set for objects with dynamic lifetime * * Base structure for modeset objects visible to userspace. Objects can be * looked up using drm_mode_object_find(). Besides basic uapi interface * properties like @id and @type it provides two services: * * - It tracks attached properties and their values. This is used by &drm_crtc, * &drm_plane and &drm_connector. Properties are attached by calling * drm_object_attach_property() before the object is visible to userspace. * * - For objects with dynamic lifetimes (as indicated by a non-NULL @free_cb) it * provides reference counting through drm_mode_object_get() and * drm_mode_object_put(). This is used by &drm_framebuffer, &drm_connector * and &drm_property_blob. These objects provide specialized reference * counting wrappers. */ struct drm_mode_object { uint32_t id; uint32_t type; struct drm_object_properties *properties; struct kref refcount; void (*free_cb)(struct kref *kref); }; 包括以下成员:
该结构体提供了用户空间可见的modeset objects的基本结构,可以通过drm_mode_object_find函数查找对象。 为了更加清晰的了解struct drm_mode_object、struct drm_object_properties、struct snd_jack_kctl数据结构的关系,我们绘制了如下关系图: 3.2.1 对象类型
#define DRM_MODE_OBJECT_CRTC 0xcccccccc #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 #define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0 #define DRM_MODE_OBJECT_MODE 0xdededede #define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0 #define DRM_MODE_OBJECT_FB 0xfbfbfbfb #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee #define DRM_MODE_OBJECT_ANY 0 3.2.2 对象属性
/** * struct drm_object_properties - property tracking for &drm_mode_object */ struct drm_object_properties { /** * @count: number of valid properties, must be less than or equal to * DRM_OBJECT_MAX_PROPERTY. */ int count; /** * @properties: Array of pointers to &drm_property. * * NOTE: if we ever start dynamically destroying properties (ie. * not at drm_mode_config_cleanup() time), then we'd have to do * a better job of detaching property from mode objects to avoid * dangling property pointers: */ struct drm_property *properties[DRM_OBJECT_MAX_PROPERTY]; /** * @values: Array to store the property values, matching @properties. Do * not read/write values directly, but use * drm_object_property_get_value() and drm_object_property_set_value(). * * Note that atomic drivers do not store mutable properties in this * array, but only the decoded values in the corresponding state * structure. The decoding is done using the &drm_crtc.atomic_get_property and * &drm_crtc.atomic_set_property hooks for &struct drm_crtc. For * &struct drm_plane the hooks are &drm_plane_funcs.atomic_get_property and * &drm_plane_funcs.atomic_set_property. And for &struct drm_connector * the hooks are &drm_connector_funcs.atomic_get_property and * &drm_connector_funcs.atomic_set_property . * * Hence atomic drivers should not use drm_object_property_set_value() * and drm_object_property_get_value() on mutable objects, i.e. those * without the DRM_MODE_PROP_IMMUTABLE flag set. * * For atomic drivers the default value of properties is stored in this * array, so drm_object_property_get_default_value can be used to * retrieve it. */ uint64_t values[DRM_OBJECT_MAX_PROPERTY]; }; 该结构体包含以下字段:
其中struct drm_property定义在include/drm/drm_property.h: Hidden Code /** * struct drm_property - modeset object property * * This structure represent a modeset object property. It combines both the name * of the property with the set of permissible values. This means that when a * driver wants to use a property with the same name on different objects, but * with different value ranges, then it must create property for each one. An * example would be rotation of &drm_plane, when e.g. the primary plane cannot * be rotated. But if both the name and the value range match, then the same * property structure can be instantiated multiple times for the same object. * Userspace must be able to cope with this and cannot assume that the same * symbolic property will have the same modeset object ID on all modeset * objects. * * Properties are created by one of the special functions, as explained in * detail in the @flags structure member. * * To actually expose a property it must be attached to each object using * drm_object_attach_property(). Currently properties can only be attached to * &drm_connector, &drm_crtc and &drm_plane. * * Properties are also used as the generic metadatatransport for the atomic * IOCTL. Everything that was set directly in structures in the legacy modeset * IOCTLs (like the plane source or destination windows, or e.g. the links to * the CRTC) is exposed as a property with the DRM_MODE_PROP_ATOMIC flag set. */ struct drm_property { /** * @head: per-device list of properties, for cleanup. */ struct list_head head; /** * @base: base KMS object */ struct drm_mode_object base; /** * @flags: * * Property flags and type. A property needs to be one of the following * types: * * DRM_MODE_PROP_RANGE * Range properties report their minimum and maximum admissible unsigned values. * The KMS core verifies that values set by application fit in that * range. The range is unsigned. Range properties are created using * drm_property_create_range(). * * DRM_MODE_PROP_SIGNED_RANGE * Range properties report their minimum and maximum admissible unsigned values. * The KMS core verifies that values set by application fit in that * range. The range is signed. Range properties are created using * drm_property_create_signed_range(). * * DRM_MODE_PROP_ENUM * Enumerated properties take a numerical value that ranges from 0 to * the number of enumerated values defined by the property minus one, * and associate a free-formed string name to each value. Applications * can retrieve the list of defined value-name pairs and use the * numerical value to get and set property instance values. Enum * properties are created using drm_property_create_enum(). * * DRM_MODE_PROP_BITMASK * Bitmask properties are enumeration properties that additionally * restrict all enumerated values to the 0..63 range. Bitmask property * instance values combine one or more of the enumerated bits defined * by the property. Bitmask properties are created using * drm_property_create_bitmask(). * * DRM_MODE_PROP_OBJECT * Object properties are used to link modeset objects. This is used * extensively in the atomic support to create the display pipeline, * by linking &drm_framebuffer to &drm_plane, &drm_plane to * &drm_crtc and &drm_connector to &drm_crtc. An object property can * only link to a specific type of &drm_mode_object, this limit is * enforced by the core. Object properties are created using * drm_property_create_object(). * * Object properties work like blob properties, but in a more * general fashion. They are limited to atomic drivers and must have * the DRM_MODE_PROP_ATOMIC flag set. * DRM_MODE_PROP_BLOB * Blob properties store a binary blob without any format restriction. * The binary blobs are created as KMS standalone objects, and blob * property instance values store the ID of their associated blob * object. Blob properties are created by calling * drm_property_create() with DRM_MODE_PROP_BLOB as the type. * * Actual blob objects to contain blob data are created using * drm_property_create_blob(), or through the corresponding IOCTL. * * Besides the built-in limit to only accept blob objects blob * properties work exactly like object properties. The only reasons * blob properties exist is backwards compatibility with existing * userspace. * * In addition a property can have any combination of the below flags: * * DRM_MODE_PROP_ATOMIC * Set for properties which encode atomic modeset state. Such * properties are not exposed to legacy userspace. * * DRM_MODE_PROP_IMMUTABLE * Set for properties whose values cannot be changed by * userspace. The kernel is allowed to update the value of these * properties. This is generally used to expose probe state to * userspace, e.g. the EDID, or the connector path property on DP * MST sinks. Kernel can update the value of an immutable property * by calling drm_object_property_set_value(). */ uint32_t flags; /** * @name: symbolic name of the properties */ char name[DRM_PROP_NAME_LEN]; /** * @num_values: size of the @values array. */ uint32_t num_values; /** * @values: * * Array with limits and values for the property. The * interpretation of these limits is dependent upon the type per @flags. */ uint64_t *values; /** * @dev: DRM device */ struct drm_device *dev; /** * @enum_list: * * List of &drm_prop_enum_list structures with the symbolic names for * enum and bitmask values. */ struct list_head enum_list; }; 四、DRM核心数据结构学习DRM驱动,首先要了解驱动框架涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。 4.1 struct drm_device
Hidden Code /** * struct drm_device - DRM device structure * * This structure represent a complete card that * may contain multiple heads. */ struct drm_device { /** @if_version: Highest interface version set */ int if_version; /** @ref: Object ref-count */ struct kref ref; /** @dev: Device structure of bus-device */ struct device *dev; /** * @managed: * * Managed resources linked to the lifetime of this &drm_device as * tracked by @ref. */ struct { /** @managed.resources: managed resources list */ struct list_head resources; /** @managed.final_kfree: pointer for final kfree() call */ void *final_kfree; /** @managed.lock: protects @managed.resources */ spinlock_t lock; } managed; /** @driver: DRM driver managing the device */ const struct drm_driver *driver; /** * @dev_private: * * DRM driver private data. This is deprecated and should be left set to * NULL. * * Instead of using this pointer it is recommended that drivers use * devm_drm_dev_alloc() and embed struct &drm_device in their larger * per-device structure. */ void *dev_private; /** * @primary: * * Primary node. Drivers should not interact with this * directly. debugfs interfaces can be registered with * drm_debugfs_add_file(), and sysfs should be directly added on the * hardware (and not character device node) struct device @dev. */ struct drm_minor *primary; /** * @render: * * Render node. Drivers should not interact with this directly ever. * Drivers should not expose any additional interfaces in debugfs or * sysfs on this node. */ struct drm_minor *render; /** @accel: Compute Acceleration node */ struct drm_minor *accel; /** * @registered: * * Internally used by drm_dev_register() and drm_connector_register(). */ bool registered; /** * @master: * * Currently active master for this device. * Protected by &master_mutex */ struct drm_master *master; /** * @driver_features: per-device driver features * * Drivers can clear specific flags here to disallow * certain features on a per-device basis while still * sharing a single &struct drm_driver instance across * all devices. */ u32 driver_features; /** * @unplugged: * * Flag to tell if the device has been unplugged. * See drm_dev_enter() and drm_dev_is_unplugged(). */ bool unplugged; /** @anon_inode: inode for private address-space */ struct inode *anon_inode; /** @unique: Unique name of the device */ char *unique; /** * @struct_mutex: * * Lock for others (not &drm_minor.master and &drm_file.is_master) * * WARNING: * Only drivers annotated with DRIVER_LEGACY should be using this. */ struct mutex struct_mutex; /** * @master_mutex: * * Lock for &drm_minor.master and &drm_file.is_master */ struct mutex master_mutex; /** * @open_count: * * Usage counter for outstanding files open, * protected by drm_global_mutex */ atomic_t open_count; /** @filelist_mutex: Protects @filelist. */ struct mutex filelist_mutex; /** * @filelist: * * List of userspace clients, linked through &drm_file.lhead. */ struct list_head filelist; /** * @filelist_internal: * * List of open DRM files for in-kernel clients. * Protected by &filelist_mutex. */ struct list_head filelist_internal; /** * @clientlist_mutex: * * Protects &clientlist access. */ struct mutex clientlist_mutex; /** * @clientlist: * * List of in-kernel clients. Protected by &clientlist_mutex. */ struct list_head clientlist; /** * @vblank_disable_immediate: * * If true, vblank interrupt will be disabled immediately when the * refcount drops to zero, as opposed to via the vblank disable * timer. * * This can be set to true it the hardware has a working vblank counter * with high-precision timestamping (otherwise there are races) and the * driver uses drm_crtc_vblank_on() and drm_crtc_vblank_off() * appropriately. See also @max_vblank_count and * &drm_crtc_funcs.get_vblank_counter. */ bool vblank_disable_immediate; /** * @vblank: * * Array of vblank tracking structures, one per &struct drm_crtc. For * historical reasons (vblank support predates kernel modesetting) this * is free-standing and not part of &struct drm_crtc itself. It must be * initialized explicitly by calling drm_vblank_init(). */ struct drm_vblank_crtc *vblank; /** * @vblank_time_lock: * * Protects vblank count and time updates during vblank enable/disable */ spinlock_t vblank_time_lock; /** * @vbl_lock: Top-level vblank references lock, wraps the low-level * @vblank_time_lock. */ spinlock_t vbl_lock; /** * @max_vblank_count: * * Maximum value of the vblank registers. This value +1 will result in a * wrap-around of the vblank register. It is used by the vblank core to * handle wrap-arounds. * * If set to zero the vblank core will try to guess the elapsed vblanks * between times when the vblank interrupt is disabled through * high-precision timestamps. That approach is suffering from small * races and imprecision over longer time periods, hence exposing a * hardware vblank counter is always recommended. * * This is the statically configured device wide maximum. The driver * can instead choose to use a runtime configurable per-crtc value * &drm_vblank_crtc.max_vblank_count, in which case @max_vblank_count * must be left at zero. See drm_crtc_set_max_vblank_count() on how * to use the per-crtc value. * * If non-zero, &drm_crtc_funcs.get_vblank_counter must be set. */ u32 max_vblank_count; /** @vblank_event_list: List of vblank events */ struct list_head vblank_event_list; /** * @event_lock: * * Protects @vblank_event_list and event delivery in * general. See drm_send_event() and drm_send_event_locked(). */ spinlock_t event_lock; /** @num_crtcs: Number of CRTCs on this device */ unsigned int num_crtcs; /** @mode_config: Current mode config */ struct drm_mode_config mode_config; /** @object_name_lock: GEM information */ struct mutex object_name_lock; /** @object_name_idr: GEM information */ struct idr object_name_idr; /** @vma_offset_manager: GEM information */ struct drm_vma_offset_manager *vma_offset_manager; /** @vram_mm: VRAM MM memory manager */ struct drm_vram_mm *vram_mm; /** * @switch_power_state: * * Power state of the client. * Used by drivers supporting the switcheroo driver. * The state is maintained in the * &vga_switcheroo_client_ops.set_gpu_state callback */ enum switch_power_state switch_power_state; /** * @fb_helper: * * Pointer to the fbdev emulation structure. * Set by drm_fb_helper_init() and cleared by drm_fb_helper_fini(). */ struct drm_fb_helper *fb_helper; /** * @debugfs_mutex: * * Protects &debugfs_list access. */ struct mutex debugfs_mutex; /** * @debugfs_list: * * List of debugfs files to be created by the DRM device. The files * must be added during drm_dev_register(). */ struct list_head debugfs_list; /* Everything below here is for legacy driver, never use! */ /* private: */ #if IS_ENABLED(CONFIG_DRM_LEGACY) /* List of devices per driver for stealth attach cleanup */ struct list_head legacy_dev_list; #ifdef __alpha__ /** @hose: PCI hose, only used on ALPHA platforms. */ struct pci_controller *hose; #endif /* AGP data */ struct drm_agp_head *agp; /* Context handle management - linked list of context handles */ struct list_head ctxlist; /* Context handle management - mutex for &ctxlist */ struct mutex ctxlist_mutex; /* Context handle management */ struct idr ctx_idr; /* Memory management - linked list of regions */ struct list_head maplist; /* Memory management - user token hash table for maps */ struct drm_open_hash map_hash; /* Context handle management - list of vmas (for debugging) */ struct list_head vmalist; /* Optional pointer for DMA support */ struct drm_device_dma *dma; /* Context swapping flag */ __volatile__ long context_flag; /* Last current context */ int last_context; /* Lock for &buf_use and a few other things. */ spinlock_t buf_lock; /* Usage counter for buffers in use -- cannot alloc */ int buf_use; /* Buffer allocation in progress */ atomic_t buf_alloc; struct { int context; struct drm_hw_lock *lock; } sigdata; struct drm_local_map *agp_buffer_map; unsigned int agp_buffer_token; /* Scatter gather memory */ struct drm_sg_mem *sg; /* IRQs */ bool irq_enabled; int irq; #endif }; 初识这个数据结构,我们发现这个数据结构包含的字段属实有点多,如果要将每个字段的含义都搞清楚,定然不是一件容易的事情,因此我们只关注如下字段即可:
4.1.1 struct drm_minor在struct drm_device数据结构中,primary、render、accel字段都是struct drm_minor 这个数据结构和我们在ALSA中介绍的 struct snd_minor是非常相似的,其创建和注册分别通过drm_minor_alloc和drm_minor_register实现。
/* * FIXME: Not sure we want to have drm_minor here in the end, but to avoid * header include loops we need it here for now. */ /* Note that the order of this enum is ABI (it determines * /dev/dri/renderD* numbers). * * Setting DRM_MINOR_ACCEL to 32 gives enough space for more drm minors to * be implemented before we hit any future */ enum drm_minor_type { DRM_MINOR_PRIMARY, DRM_MINOR_CONTROL, DRM_MINOR_RENDER, DRM_MINOR_ACCEL = 32, }; /** * struct drm_minor - DRM device minor structure * * This structure represents a DRM minor number for device nodes in /dev. * Entirely opaque to drivers and should never be inspected directly by drivers. * Drivers instead should only interact with &struct drm_file and of course * &struct drm_device, which is also where driver-private data and resources can * be attached to. */ struct drm_minor { /* private: */ int index; /* Minor device number */ int type; /* Control or render or accel */ struct device *kdev; /* Linux device */ struct drm_device *dev; struct dentry *debugfs_root; struct list_head debugfs_list; struct mutex debugfs_lock; /* Protects debugfs_list. */ }; 其中:
/** * struct drm_debugfs_info - debugfs info list entry * * This structure represents a debugfs file to be created by the drm * core. 描述debugfs文件信息 */ struct drm_debugfs_info { /** @name: File name */ const char *name; /** * @show: * * Show callback. &seq_file->private will be set to the &struct * drm_debugfs_entry corresponding to the instance of this info * on a given &struct drm_device. */ int (*show)(struct seq_file*, void*); /** @driver_features: Required driver features for this entry. */ u32 driver_features; /** @data: Driver-private data, should not be device-specific. */ void *data; }; /** * struct drm_debugfs_entry - Per-device debugfs node structure * * This structure represents a debugfs file, as an instantiation of a &struct * drm_debugfs_info on a &struct drm_device. */ struct drm_debugfs_entry { /** @dev: &struct drm_device for this node. */ struct drm_device *dev; /** @file: Template for this node. */ struct drm_debugfs_info file; /** @list: Linked list of all device nodes. */ struct list_head list; // 链表节点,用于将当节点添加到drm设备的debugfs_list链表中 }; 4.1.2 struct drm_mode_config
目录 ----------------------------------------------------------------------------------------------- 开发板 :NanoPC-T4开发板 如果我们需要编写一个DRM驱动,我们应该怎么做呢?具体流程如下: (1) 定义struct drm_driver,并初始化成员name、desc、data、major、minor、driver_features、fops、dumb_create等; (2)调用drm_dev_alloc函数分配并初始化一个struct drm_device; (3) 调用drm_mode_config_init初始化drm_device中mode_config结构体; (4) 调用drm_xxx_init创建framebuffer、plane、crtc、encoder、connector这5个drm_mode_object; 在DRM子系统中是通过component框架完成各个功能模块的注册,比如在:CRTC驱动程序:包含了plane和crtc的初始化工作;
(5) 调用drm_dev_register注册drm_device;
一、显示子系统概述显示子系统是Rockchip平台显示输出相关软硬件系统的统称,linux内核采用component 那么问题来了,什么是显示处理器?
显示处理器可以在没有CPU参与的情况下可以做一些简单的图像处理,比如:缩放,旋转等操作;
1.1 硬件框图整个显示系统的硬件框架如下图所示: ![]() VOP 1.0显示子系统架构 ![]() VOP 2.0显示子系统架构 从上面的框图可以看到,在整个显示通路的最后端,是由RGA,GPU、VPU组成的显示图形加速模块,他们是专门针对图像处理优化设计的硬件IP,能够高效的进行图像的⽣成和进一步处理(比如GPU通过opengl功能提供图像渲染功能,RGA可以对图像数据进行缩放,旋转,合成等2D处理,VPU可以高效的进行视频解码),从而减轻CPU负担。 经过这些图像加速模块处理后的数据会存放在DDR中,然后由VOP读取,根据应用需求进行 目前Rockchip平台上存在两种VOP架构:
1.1.1RK3399
支持的显示接口:
1.1.2 NanoPC T4我们所使用的的NanoPC T4开发板,视频输出支持:
1.2 DRM加载顺序
在《
接下来我们将会以RK3399 DRM驱动为例对显示子系统的各个模块进行介绍;
可以看到驱动位于如下目录:
显示子系统各个模块驱动加载顺序如下图所示: 这里我们对驱动加载顺序图简单说明一下:
因为这些复杂的依赖关系,在DRM系统初始化的过程中,可能会出现某个资源暂时未就绪,而导致某个模块暂时无法顺利加载的情况。 为了解决这种问题,DRM驱动利用了Linux驱动中的deferred probe机制,当发现某个依赖的资源未就绪的时候,驱动返回-EPROBE_DEFER(-517), 然后退出。 二、设备树配置在RK3399上,包含两个VOP、以及1个MIPI、1个DP、1个eDP、双通道MIPI DSI 2.1 display_subsystem设备节点在每⼀个⽀持DRM显⽰功能的SoC的核⼼设备树⾥⾯,都会有display_subsystem节点:所有的子设备信息都通过设备树描述关联起来,这样系统开机后,就能统一的管理各个设备。
display_subsystem: display-subsystem { compatible = "rockchip,display-subsystem"; ports = <&vopl_out>, <&vopb_out>; }; 该节点描述的是Rockchip DRM主设备,也就是我们在component框架中介绍的aggregate_device,这是一个虚拟设备,用于列出组成图形子系统的所有vop 其中ports属性描述vop硬件资源,列出了指向各个vop设备的phandle,vopl_out、vopb_out对应着VOP_LITE、VOP_BIG。 更多属性信息可以参考:
2.2 vop设备节点
以设备节点vopb_out为例,vopb_out设备节点定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi; vopb: vop@ff900000 { compatible = "rockchip,rk3399-vop-big"; reg = <0x0 0xff900000 0x0 0x2000>, <0x0 0xff902000 0x0 0x1000>; interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>; assigned-clocks = <&cru ACLK_VOP0>, <&cru HCLK_VOP0>; assigned-clock-rates = <400000000>, <100000000>; clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>; clock-names = "aclk_vop", "dclk_vop", "hclk_vop"; iommus = <&vopb_mmu>; power-domains = <&power RK3399_PD_VOPB>; resets = <&cru SRST_A_VOP0>, <&cru SRST_H_VOP0>, <&cru SRST_D_VOP0>; reset-names = "axi", "ahb", "dclk"; status = "disabled"; vopb_out: port { #address-cells = <1>; #size-cells = <0>; vopb_out_edp: endpoint@0 { reg = <0>; remote-endpoint = <&edp_in_vopb>; }; vopb_out_mipi: endpoint@1 { reg = <1>; remote-endpoint = <&mipi_in_vopb>; }; vopb_out_hdmi: endpoint@2 { reg = <2>; remote-endpoint = <&hdmi_in_vopb>; }; vopb_out_mipi1: endpoint@3 { reg = <3>; remote-endpoint = <&mipi1_in_vopb>; }; vopb_out_dp: endpoint@4 { reg = <4>; remote-endpoint = <&dp_in_vopb>; }; }; }; 子节点port下的endpoint描述的是vop和显示接口的连接关系,vopb_out节点下有vopb_out_edp,vopb_out_mipi,vopb_out_hdmi,vopb_out_mipi1、vopb_out_dp 每个endpoint通过remote-endpoint属性和对应的显示接口组成一个连接通路,例如vopb_out_hdmi--->hdmi_in_vopb。 设备节点vopl_out同理,这里就不在介绍了; 2.3 内核配置
Device Drivers ---> Graphics support ---> <*> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) ---> <*> DRM Support for Rockchip (DRM_ROCKCHIP [=y]) [*] Rockchip VOP driver [ ] Rockchip VOP2 driver [*] Rockchip specific extensions for Analogix DP driver (ROCKCHIP_ANALOGIX_DP [=y]) [*] Rockchip cdn DP [*] Rockchip specific extensions for Synopsys DW HDMI [*] Rockchip specific extensions for Synopsys DW MIPI DSI [*] Rockchip specific extensions for Innosilicon HDMI [*] Rockchip LVDS support [ ] Rockchip RGB support 三、 DRM驱动入口
#define ADD_ROCKCHIP_SUB_DRIVER(drv, cond) { if (IS_ENABLED(cond) && !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) rockchip_sub_drivers[num_rockchip_sub_drivers++] = &drv; } static int __init rockchip_drm_init(void) { int ret; if (drm_firmware_drivers_only()) return -ENODEV; // 1. 根据配置来决定是否添加xxx_xxx_driver到数组rockchip_sub_drivers num_rockchip_sub_drivers = 0; ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP); ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2); ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver, CONFIG_ROCKCHIP_LVDS); ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver, CONFIG_ROCKCHIP_ANALOGIX_DP); ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP); ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver, CONFIG_ROCKCHIP_DW_HDMI); ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, CONFIG_ROCKCHIP_DW_MIPI_DSI); ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver, CONFIG_ROCKCHIP_RK3066_HDMI); // 2. 注册多个platform driver ret = platform_register_drivers(rockchip_sub_drivers, num_rockchip_sub_drivers); if (ret) return ret; // 3. 注册rockchip_drm_platform_driver ret = platform_driver_register(&rockchip_drm_platform_driver); if (ret) goto err_unreg_drivers; return 0; err_unreg_drivers: platform_unregister_drivers(rockchip_sub_drivers, num_rockchip_sub_drivers); return ret; } module_init(rockchip_drm_init); (1) 函数内部多次调用宏ADD_ROCKCHIP_SUB_DRIVER,完成vop、以及显示接口(lvds 咱们以hdmi如下代码为例; ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver, CONFIG_ROCKCHIP_DW_HDMI); 展开得到: if (IS_ENABLED(CONFIG_ROCKCHIP_DW_HDMI) && !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) rockchip_sub_drivers[num_rockchip_sub_drivers++] = &dw_hdmi_rockchip_pltfm_driver; 如果定义了CONFIG_ROCKCHIP_DW_HDMI,会将dw_hdmi_rockchip_pltfm_driver #define MAX_ROCKCHIP_SUB_DRIVERS 16 // 数组长度为16 static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS]; 那么宏CONFIG_ROCKCHIP_DW_HDMI到底是什么呢? root@zhengyang:/work/sambashare/rk3399/linux-6.3# grep "CONFIG_ROCKCHIP_DW_HDMI" drivers/gpu/* -nR drivers/gpu/drm/rockchip/Makefile:13:rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o 可以看到宏CONFIG_ROCKCHIP_DW_HDMI决定了是否将dw_hdmi-rockchip.c 有关vop、以及显示接口(lvds、dp、hdmi、mipi dsi)的驱动咱们在后面章节单独介绍。 (2)调用platform_register_drivers注册num_rockchip_sub_drivers个platform driver (3) 最后调用platform_driver_register注册rockchip_drm_platform_driver。 3.1 rockchip_drm_platform_driver
|
|