[Linux Basics] - Linux DRM (3) RK platform DRM code analysis-Rockchip

1. Overview

I don’t know if you still remember, I quoted the introduction of DRM in the Wiki before, here we will review it again: DRM consists of two parts: one is the kernel subsystem, which provides a framework for hardware GPU operations Package. The second is to provide a libdrm library, which encapsulates a series of APIs for image display. On the whole, it is similar to the Direct Frame Buffer used on Android. Android Kernel takes the framework of FB, and abstracts a FBDEV in HAL for unified management of FB IOCTL. DRM is equivalent to direct centralized processing of graphics devices, and an extra libdrm library is added.

The overall context is as follows:

Source file

Component framework

Before talking about the startup process, let's take a brief look at the Component framework.

Because many devices are hung under drm, the startup sequence often causes various problems:

  • It is entirely possible that a driver is waiting for the preparation of another resource, and probe deferral, leading to indefinite order
  • If the sub device is not loaded, the main device is loaded, causing the device to fail to work
  • The sub-devices may have a timing relationship with each other, and the loading sequence may be uncertain. Sometimes the device can work, and sometimes it can’t work.
  • Now the kernel is compiled by multiple threads, and the order before and after compilation will also affect the loading order of the driver.

At this time, a unified management mechanism is needed to integrate all devices and load them in a unified order. Display-subsystem is officially used to solve this problem and depends on the Component driver. Through this driver, all devices can be loaded. Add them together in the form of components, and after all components are loaded, perform bind/unbind in a unified manner.

Code path: drivers/base/component.c

The following is the main logic of the Component in the rockchip drm master probe stage. In order to reduce the length, irrelevant code is removed:

static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
    for(int=0;; i++){
        /* ports 指向了 vop 的设备节点 */
        port = of_parse_phandle(np, "ports", i);
        component_match_add(dev, &match, compare_of, port->parent);
    }
    for(i=0; ; i++) {
        port = of_parse_phandle(np, "ports", i);
        component_match_add(dev, &match, compre_of, port->parent);
    }
    for(i=0;; i++) {
        port = of_parse_phandle(np, "ports", i);
        /* 搜查 port 下的各个 endpoint,将它们也加入到 match 列表 */
        rockchip_add_endpoints(dev, &match,port);
    }
    return component_master_add_with_match(dev, &rockchip_drm_ops, match);
}

static void rockchip_add_endpoints(...)
{
    for_each_child_of_node(port, ep) {
        remote = of_graph_get_remote_port_parent(ep);
        /* 这边的 remote 即为和 vop 关联的输出设备,即为 edp,mipi,或 hdmi */
        component_match_add(dev, match, compare_of, remote);
    }
}

 

Boot process

Picture from makyzq gitbook:

Based on Component mining construction, in the probe stage:

  • Parse the information of each device in dts
  • Add to the Component match list
  • After the device is loaded, the master device will bind

RK DRM Device Driver

device tree

display_subsystem: display-subsystem {
        compatible = "rockchip,display-subsystem";
        ports = <&vopl_out>, <&vopb_out>;
        status = "disabled";
};

- compatible: Should be "rockchip, display-subsystem"
- ports: Should contain a list of phandles pointing to display interface port of vop devices. vop definitions as defined in
kernel/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt

drm driver

Code path:

static struct drm_driver rockchip_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM |
DRIVER_PRIME | DRIVER_ATOMIC |
DRIVER_RENDER,
.preclose = rockchip_drm_preclose,
.lastclose = rockchip_drm_lastclose,
.get_vblank_counter = drm_vblank_no_hw_counter,
.open = rockchip_drm_open,
.postclose = rockchip_drm_postclose,
.enable_vblank = rockchip_drm_crtc_enable_vblank,
.disable_vblank = rockchip_drm_crtc_disable_vblank,
.gem_vm_ops = &rockchip_drm_vm_ops,
.gem_free_object = rockchip_gem_free_object,
.dumb_create = rockchip_gem_dumb_create,
.dumb_map_offset = rockchip_gem_dumb_map_offset,
.dumb_destroy = drm_gem_dumb_destroy,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import = drm_gem_prime_import,
.gem_prime_export = drm_gem_prime_export,
.gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
.gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table,
.gem_prime_vmap = rockchip_gem_prime_vmap,
.gem_prime_vunmap = rockchip_gem_prime_vunmap,
.gem_prime_mmap = rockchip_gem_mmap_buf,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = rockchip_drm_debugfs_init,
.debugfs_cleanup = rockchip_drm_debugfs_cleanup,
#endif
.ioctls = rockchip_ioctls,
.num_ioctls = ARRAY_SIZE(rockchip_ioctls),
.fops = &rockchip_drm_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};

vop driver

Code path:

drivers/gpu/drm/rockchip/rockchip_drm_vop.c

drivers/gpu/drm/rockchip/rockchip_vop_reg.c

Structure:

struct vop;
// vop drives the root structure, a vop corresponds to a struct vop structure

struct vop_win;
// Describe layer information, a hardware layer corresponds to a struct vop_win structure

Register read and write: In order to be compatible with different versions of vop, the vop driver uses register-level abstraction, and a structure is used to store the abstract relationship, so that the theme logic only needs to operate the abstracted function definition, and the abstract read-write interface According to the abstract relationship, it is written into the real vop hardware.

Example:

static const struct vop_win_phy rk3288_win23_data = {
        .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4),
}

static const struct vop_win_phy rk3368_win23_data = {
        .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4),
}

The address distribution of rk3368 and rk3288 layers is different, but when the structure is defined, different hardware layer bits can be mapped to the same enable function, so that when the vop driver calls VOP_WIN_SET(vop, win, enable, 1) Can operate to the real vop register.

 

2.1, device file cardX

DRM is in the kernel space, which means that the user space needs to apply for its services through system calls. However, DRM does not define its own system calls. On the contrary, it follows the principle of "Everything is file" and exposes the GPU access method under the /dev/dri/ directory through the file system. DRM will detect each GPU, generate the corresponding DRM device, and create the device file /dev/dri/cardX to connect with the GPU. X is a value from 0-15, the default is Card0.

If a user space program wants to access the GPU, it must open the file and use ioctl to communicate with DRM. Different ioctls correspond to different functions of DRM API.

2.2. Memory operations in user space

We define an external memory structure to better describe how to perform userspace drm operations. First, you need to quote drm.h when using DRM-related operations.

#include <drm.h>

struct bo {
    int fd;
    void *ptr;
    size_t size;
    size_t offset;
    size_t pitch;
    unsigned handle;
};

2.2.1, get device node

bo->fd = open("/dev/dri/card0", O_RDWR, 0);

2.2.2, allocate memory space

struct drm_mode_create_dumb arg;
int handle, size, pitch;
int ret;

memset(&arg, 0, sizeof(arg));

arg.bpp = bpp;
arg.width = width;
arg.height = height;

ret = drmIoctl(bo->fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg);

if(ret) {
    fprintf(stderr, "failed to create dumb buffer: %s\n", strerror(errno));
    return ret;
}

bo->handle = arg.handle;
bo->size = arg.size;
bo->pitch = arg.pitch;

2.2.3, mapping physical memory

struct drm_mode_map_dumb arg;
void *map;
int ret;

memset(&arg, 0, sizeof(arg));

arg.handle = bo->handle;

ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &arg);
if(ret){
    return ret;
}

map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, FD, arg.offset);
if(map == MAP_FAILED){
    return -EINVAL;
}
bo->ptr = map;

2.2.4, remove the physical memory mapping

drm_munmap(bo->ptr, bo->size);
bo->ptr = NULL;

2.2.5, release memory

struct drm_mode_destroy_dumb arg;
int ret;

memset(&arg, 0, sizeof(arg));

arg.handle = bo->hand;

ret = drmIoctl(bo->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
if(ret) {
    fprintf(stderr, "failed to destroy dumb buffer: %s\n", strerror(errno));
}

2.2.6, release gem handle

struct drm_gem_close args;

memset(&arg, 0, sizeof(args));

args.handle = bo->handle;

drmIoctl(bo->fd, DRM_IOCTL_GEM_CLOSE, &args);

2.2.7、export dmafd

int export_dmafd;

ret = drmPrimeHandleToFD(bo->fd, bo->handle, 0, &export_dmafd);

//drmPrimeHandleToFD 是会给 drm_buf 加引用计数的
//使用完 export_dmafd 后,需要使用 close(export_dmafd) 来减掉引用计数

2.2.8、import dmafd

ret = drmPrimeFDToHandle(bo->fd, import_dmafd, &bo->handle);

// drmPrimeHandleToFd 是会给 dma_buf 加引用计数的
// 使用完 bo->handle 后,需要对 handle 减引用,参看 free gem handle 部分。

2.2、DRM libdrm

libdrm was created to facilitate the connection between user space and the DRM subsystem. It only provides a package of some functions (C). These functions are written for each ioctl, bright, and structure of the DRM API. Using libdrm not only avoids directly exposing the kernel interface to user space, but also has common advantages such as code reuse.

2.3, DRM code structure

It is divided into two parts: general DRM Core and DRM Driver adapted to different types of hardware.

  1. DRM Core provides the basic framework in which different DRM drivers can be registered, and provides the user space with a minimal set of ioctls that are versatile and independent of hardware functions.
  2. DRM Driver implements the hardware-dependent part of the API. It provides the implementation of other ioctls that are not covered by DRM Core. It can also extend the API and provide additional ioctls. For example, a particular DRM Driver provides some enhanced APIs, and the user space libdrm also needs additional libdrm-driver extensions to use these additional ioctls.

2.4、DRM API

DRM Core exports multiple interfaces to user-space applications, allowing the corresponding libdrm to be packaged into functions for later use.

The interface of a specific device exported by DRM Driver can be used by user space through ioctls and sysfs.

2.5、DRM-Master 和 DRM-Auth

Several ioctls in the DRM API are limited to a single process in user space due to concurrency issues. In order to achieve this restriction, DRM devices are divided into Master and Auth. The above ioctls can only be called by the DRM-Master process. The file handle of the process that opened /dev/dri/cardX will be marked as master, especially the first process that calls the SET_MASTER ioctl. If it is not DRM-Master, the process will return an error when using these restricted ioctls. A process can also give up the role of Master through the DROP_MASTER ioctl to make other processes become Master.

X Server or other Display Server will usually be the DRM-Master process of the DRM device they manage. When the DRM device is started, these Display Servers open the device node and obtain the DRM-Master permission until the device is turned off.

For other user space processes, there is another way to obtain these restricted permissions for DRM devices, which is DRM-Auth. It is a verification method for DRM devices to prove that the process has obtained the permission of DRM-Master for them to access restricted ioctls.

step:

  • DRM Client uses GET_MAGIC ioctl to obtain a 32-bit integer token from the DRM device. And pass it to DRM-Master by any means (usually IPC).
  • The DRM-Master process uses the AUTH-MAGIC ioctl to return the token to the DRM device.
  • The device compares the token given by DRM-Master with that of Auth. If passed, special permissions are given to the process file handle.

#

The architecture of DRM in the source code (picture from Mark.Yao):

Use DRM to access Video Card (picture from Wikipedia):

Without DRM, the way user space processes access the GPU is shown in the figure above.

With DRM, the way the user space accesses the GPU is shown in the figure above.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/u014674293/article/details/105774238