[Conceptos básicos de Linux] - Análisis de código DRM de la plataforma RK de Linux DRM (3) - Rockchip

1. Información general

No sé si todavía lo recuerdan, cité la introducción de DRM en el Wiki antes, revisemos nuevamente: DRM consta de dos partes: una es el subsistema del kernel, que proporciona un marco para el paquete de operaciones de GPU de hardware. El segundo es proporcionar una biblioteca libdrm, que encapsula una serie de API para la visualización de imágenes. En general, es similar al Direct Frame Buffer utilizado en Android. Android Kernel toma el marco de FB y abstrae un FBDEV en HAL para la gestión unificada de FB IOCTL. DRM es equivalente al procesamiento centralizado directo de dispositivos gráficos y se agrega una biblioteca libdrm adicional.

El contexto general es el siguiente:

Archivo fuente

Marco de componentes

Antes de hablar sobre el proceso de inicio, echemos un vistazo breve al marco de componentes.

Debido a que muchos dispositivos se cuelgan bajo drm, la secuencia de inicio a menudo causa varios problemas:

  • Es muy posible que un conductor esté esperando la preparación de otro recurso y el aplazamiento de la investigación, lo que lleva a una orden indefinida.
  • Si el dispositivo secundario no está cargado, el dispositivo principal se carga, lo que hace que el dispositivo no funcione
  • Los subdispositivos pueden tener una relación temporal entre sí y la secuencia de carga puede ser incierta. A veces, el dispositivo puede funcionar y, a veces, no.
  • Ahora, el kernel está compilado por varios subprocesos, y el orden antes y después de la compilación también afectará el orden de carga del controlador.

En este momento, se necesita un mecanismo de administración unificado para integrar todos los dispositivos y cargarlos en un orden unificado. El subsistema de visualización se usa oficialmente para resolver este problema y depende del controlador del componente. A través de este controlador, se pueden cargar todos los dispositivos. Agregar juntos en forma de componentes, y después de que todos los componentes estén cargados, realice la vinculación / desvinculación de una manera unificada.

Ruta del código: drivers / base / component.c

La siguiente es la lógica principal del Componente en la etapa de la sonda maestra rockchip drm. Para reducir la longitud, se elimina el código irrelevante:

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);
    }
}

 

Proceso de arranque

Imagen de makyzq gitbook:

Con base en la construcción minera de componentes, en la etapa de sonda:

  • Analizar la información de cada dispositivo en dts
  • Agregar a la lista de coincidencias de componentes
  • Una vez cargado el dispositivo, el dispositivo maestro se vinculará

Controlador de dispositivo RK DRM

árbol de dispositivos

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



- compatible: debe ser "rockchip, display-subsystem"
- ports: debe contener una lista de phandles apuntando al puerto de interfaz de pantalla de los dispositivos vop. definiciones de vop como se definen en
kernel / Documentation / devicetree / bindings / display / rockchip / rockchip-vop.txt

controlador drm

Ruta de código:

estructura estática 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,
};

controlador vop

Ruta de código:

drivers / gpu / drm / rockchip / rockchip_drm_vop.c

drivers / gpu / drm / rockchip / rockchip_vop_reg.c

Estructura:

struct vop;
// vop controla la estructura raíz, un vop corresponde a una estructura struct vop

struct vop_win;
// Describe la información de la capa, una capa de hardware corresponde a una estructura struct vop_win

Registro de lectura y escritura: para ser compatible con diferentes versiones de vop, el controlador vop usa abstracción a nivel de registro, y se usa una estructura para almacenar la relación abstracta, de modo que la lógica del tema solo necesita operar la definición de función abstraída, y la interfaz abstracta de lectura y escritura Según la relación abstracta, se escribe en el hardware de voz real.

Ejemplo:

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), }

La distribución de direcciones de las capas rk3368 y rk3288 es diferente, pero cuando se define la estructura, se pueden asignar diferentes bits de la capa de hardware a la misma función de habilitación, de modo que cuando el controlador vop llama a VOP_WIN_SET (vop, win, enable, 1) Puede operar con el registro vop real.

 

2.1, tarjeta de archivo del dispositivo X

DRM está en el espacio del kernel, lo que significa que el espacio del usuario necesita solicitar sus servicios a través de llamadas al sistema. Sin embargo, DRM no define sus propias llamadas al sistema. Por el contrario, sigue el principio de "Todo es archivo" y expone el método de acceso a la GPU en el directorio / dev / dri / a través del sistema de archivos. DRM detectará cada GPU, generará el dispositivo DRM correspondiente y creará el archivo de dispositivo / dev / dri / cardX para conectarse con la GPU. X es un valor de 0 a 15, el valor predeterminado es Card0.

Si un programa de espacio de usuario desea acceder a la GPU, debe abrir el archivo y usar ioctl para comunicarse con DRM. Diferentes ioctls corresponden a diferentes funciones de la API de DRM.

2.2. Operaciones de memoria en el espacio de usuario

Definimos una estructura de memoria externa para describir mejor cómo realizar las operaciones de DRM del espacio de usuario. Primero, debe citar drm.h cuando utilice operaciones relacionadas con DRM.

#include <drm.h>

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

2.2.1, obtener el nodo del dispositivo

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

2.2.2, asignar espacio de memoria

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, mapeo de memoria física

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, eliminar la asignación de memoria física

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

2.2.5, liberar memoria

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, suelte el mango de la gema

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 、 exportar dmafd

int export_dmafd;

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

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

2.2.8 、 importar dmafd

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

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

2.2 、 DRM libdrm

libdrm se creó para facilitar la conexión entre el espacio de usuario y el subsistema DRM. Solo proporciona un paquete de algunas funciones (C). Estas funciones están escritas para cada ioctl, brillante y estructura de la API DRM. El uso de libdrm no solo evita exponer directamente la interfaz del kernel al espacio del usuario, sino que también tiene ventajas comunes como la reutilización del código.

2.3, estructura del código DRM

Se divide en dos partes: DRM Core general y DRM Driver adaptado a diferentes tipos de hardware.

  1. DRM Core proporciona el marco básico a través del cual se pueden registrar diferentes controladores DRM y proporciona al espacio de usuario un conjunto mínimo de ioctls que son universales e independientes de las funciones del hardware.
  2. DRM Driver implementa la parte dependiente del hardware de la API. Proporciona la implementación de otras ioctls que no están cubiertas por DRM Core. También puede extender la API y proporcionar ioctls adicionales. Por ejemplo, un controlador DRM específico proporciona algunas API mejoradas, y el espacio de usuario libdrm también necesita extensiones adicionales del controlador libdrm para usar estos ioctls adicionales.

2.4 、 API DRM

DRM Core exporta múltiples interfaces a aplicaciones de espacio de usuario, lo que permite empaquetar el libdrm correspondiente en funciones para su uso posterior.

La interfaz de un dispositivo específico exportado por DRM Driver puede ser utilizada por el espacio de usuario a través de ioctls y sysfs.

2.5 、 DRM-Master 和 DRM-Auth

Varias ioctls en la API DRM están limitadas a un solo proceso en el espacio del usuario debido a problemas de concurrencia. Para lograr esta restricción, los dispositivos DRM se dividen en Master y Auth. Los ioctls anteriores solo pueden ser llamados por el proceso DRM-Master. El identificador de archivo del proceso que abrió / dev / dri / cardX se marcará como maestro, especialmente el primer proceso que llama al SET_MASTER ioctl. Si no es DRM-Master, el proceso devolverá un error al usar estos ioctls restringidos. Un proceso también puede renunciar al rol de Maestro a través del ioctl DROP_MASTER para hacer que otros procesos se conviertan en Maestros.

X Server u otro servidor de pantalla generalmente será el proceso DRM-Master del dispositivo DRM que administran. Cuando se inicia el dispositivo DRM, estos servidores de pantalla abren el nodo del dispositivo y obtienen el permiso DRM-Master hasta que se apaga el dispositivo.

Para otros procesos de espacio de usuario, existe otra forma de obtener estos permisos restringidos para dispositivos DRM, que es DRM-Auth. Es un método de verificación para que los dispositivos DRM demuestren que el proceso ha obtenido el permiso de DRM-Master para que accedan a ioctls restringidas.

paso:

  • El cliente DRM utiliza el ioctl GET_MAGIC para obtener un token entero de 32 bits del dispositivo DRM. Y páselo a DRM-Master por cualquier medio (generalmente IPC).
  • El proceso DRM-Master utiliza AUTH-MAGIC ioctl para devolver el token al dispositivo DRM.
  • El dispositivo compara el token proporcionado por DRM-Master con el de Auth. Si se aprueba, se otorgan permisos especiales al identificador del archivo de proceso.

#

La arquitectura de DRM en el código fuente (imagen de Mark.Yao):

Use DRM para acceder a la tarjeta de video (imagen de Wikipedia):

Sin DRM, la forma en que los procesos de espacio de usuario acceden a la GPU se muestra en la figura anterior.

Con DRM, la forma en que el espacio de usuario accede a la GPU se muestra en la figura anterior.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/u014674293/article/details/105774238
Recomendado
Clasificación