日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Linux DRM (三) RK 平臺 DRM 代碼分析 · Rockchip

 老匹夫 2019-09-26

本篇是 DRM 的第三篇文章。 在 《Linux DRM (一) Display Server》 中我們了解了 DRM 誕生的歷史。 在 《Linux DRM (二) 基本概念和特性》 中我們了解了一些基本的概念。 現(xiàn)在,我們終于要向 DRM 源碼進軍了。

+

一、概覽

不知大家是否還記得,之前我有引用 Wiki 中對 DRM 的介紹,這里我們再回顧一下: DRM 由兩個部分組成: 一是 Kernel 的子系統(tǒng),這個子系統(tǒng)對硬件 GPU 操作進行了一層框架封裝。 二是 提供了一個 libdrm 庫,里面封裝了一系列 API,用來進行圖像顯示。 整體來看和 Android 上所采用的 Direct Frame Buffer 差不多。 Android Kernel 走的是 FB 的框架,并在 HAL 抽象出一個 FBDEV,來進行 FB IOCTL 統(tǒng)一管理。 DRM 就相當(dāng)于直接對圖形設(shè)備集中處理,并且多出了一個 libdrm 庫。

+

其整體脈絡(luò)如下:

+

源碼文件

component framework

在講述啟動過程之前,先簡單了解一下 component framework。

+

因為 drm下掛了許多的設(shè)備, 啟動順序經(jīng)常會引發(fā)各種問題:

+
  1. 一個驅(qū)動完全有可能因為等另一個資源的準備, 而probe deferral, 導(dǎo)致順序不定

  2. 子設(shè)備沒有加載好, 主設(shè)備就加載了, 導(dǎo)致設(shè)備無法工作

  3. 子設(shè)備相互之間可能有時序關(guān)系,不定的加載順序,可能帶來有些時候設(shè)備能工作,有些時候又不能工作

  4. 現(xiàn)在編kernel是多線程編譯的,編譯的前后順序也會影響驅(qū)動的加載順序.

這時就需要有一個統(tǒng)一管理的機制, 將所有設(shè)備統(tǒng)合起來, 按照一個統(tǒng)一的順序加載, Display-subsystem正是用來解決這個問題的, 依賴于component的驅(qū)動, 通過這個驅(qū)動, 可以把所有的設(shè)備以組件的形式加在一起, 等所有的組件加載完畢后, 統(tǒng)一進行bind/unbind.

+

代碼路徑 drivers/base/component.c

+

以下為rockchip drm master probe階段component 主要邏輯, 為了減小篇幅, 去掉了無關(guān)的代碼:

+
static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
    for (i = 0;; i++) {
        /* ports指向了vop的設(shè)備節(jié)點 */
        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);
        /* 搜查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關(guān)聯(lián)的輸出設(shè)備, 即為edp, mipi或hdmi */
        component_match_add(dev, match, compare_of, remote);
    }
}

啟動過程

圖自 markyzq gitbook:

+

基于 component 框架,在 probe 階段

+
  1. 解析 dts 中各個設(shè)備的信息

  2. 加到 component match 列表中

  3. 設(shè)備加載完畢后,master 設(shè)備進行 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

代碼路徑

drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
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

代碼路徑:

+
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_vop_reg.c

結(jié)構(gòu)體:

struct vop;
// vop 驅(qū)動根結(jié)構(gòu), 一個vop對應(yīng)一個struct vop結(jié)構(gòu)

struct vop_win;
// 描述圖層信息, 一個硬件圖層對應(yīng)一個struct vop_win結(jié)構(gòu)

寄存器讀寫: 為了兼容各種不同版本的vop, vop驅(qū)動里面使用了寄存器級的抽象, 由一個結(jié)構(gòu)體來保存抽象關(guān)系, 這樣主體邏輯只需要操作抽象后的功能定義, 由抽象的讀寫接口根據(jù)抽象關(guān)系寫到真實的vop硬件中.

+

示例:

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

rk3368和rk3288圖層的地址分布不同, 但在結(jié)構(gòu)定義的時候, 可以將不同的硬件圖層bit 映射到同一個enable功能上, 這樣vop驅(qū)動主體調(diào)用VOP_WIN_SET(vop, win, enable, 1);時就能操作到真實的vop寄存器了.

+

2.1 設(shè)備文件 cardX

DRM 處于內(nèi)核空間,這意味著用戶空間需要通過系統(tǒng)調(diào)用來申請它的服務(wù)。 不過 DRM 并沒有定義它自己的系統(tǒng)調(diào)用。相反,它遵循“Everything is file”的原則,通過文件系統(tǒng),在 /dev/dri/目錄下暴露了 GPU 的訪問方式。 DRM 會檢測每個 GPU,并生成對應(yīng)的 DRM 設(shè)備,創(chuàng)建設(shè)備文件 /dev/dri/cardX與 GPU 相接。X 為 0-15 的數(shù)值,默認是 Card0。

+

用戶空間的程序如果希望訪問 GPU 則必須打開該文件,并使用 ioctl 與 DRM 通信。不同的 ioctl 對應(yīng) DRM API 的不同功能。

+

2.2 用戶空間的內(nèi)存操作

我們定義一個外部的內(nèi)存結(jié)構(gòu)來更好的描述如何進行 userspace 的 drm 操作。 首先使用到 DRM 相關(guān)操作的時候需要引用 drm.h。

+
#include <drm.h>
struct bo {
   int fd;
   void *ptr;
   size_t size;
   size_t offset;
   size_t pitch;
   unsigned handle;
};

2.2.1 獲取設(shè)備節(jié)點

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

2.2.2 分配內(nèi)存空間

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 映射物理內(nèi)存

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 解除物理內(nèi)存映射

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

2.2.5 釋放內(nèi)存

struct drm_mode_destroy_dumb arg;
int ret;

memset(&arg, 0, sizeof(arg));
arg.handle = bo->handle;
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 釋放 gem handle

struct drm_gem_close args;
memset(&args, 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是會給dma_buf加引用計數(shù)的
// 使用完export_dmafd后, 需要使用  close(export_dmafd)來減掉引用計數(shù)

2.2.8 import dmafd

ret = drmPrimeFDToHandle(bo->fd, import_dmafd, &bo->handle);
// drmPrimeHandleToFD是會給dma_buf加引用計數(shù)的
// 使用完bo->handle后, 需要對handle減引用, 參看free gem handle部分.

2.2 DRM libdrm

libdrm 被創(chuàng)建以用于方便用戶空間和 DRM 子系統(tǒng)的聯(lián)系。它僅僅只提供了一些函數(shù)的包裝(C),這些函數(shù)是為 DRM API 的每一個 ioctl、常量、結(jié)構(gòu)體 而寫。 使用 libdrm 這個庫不僅僅避免了將內(nèi)核接口直接暴露給用戶空間,也有代碼復(fù)用等常見優(yōu)點。

+

2.3 DRM 代碼結(jié)構(gòu)

分為兩個部分:通用的 DRM Core 和適配于不同類型硬件的 DRM Driver。 DRM Core 提供了不同 DRM 驅(qū)動程序可以注冊的基本框架, 并且為用戶空間提供了具有通用,獨立于硬件功能的最小 ioctl 集合。 DRM Driver 實現(xiàn)了 API 的硬件依賴部分。 它提供了沒有被 DRM core 覆蓋的其余 ioctl 的實現(xiàn),它也可以拓展 API,提供額外的 ioctl。比如某個特定的 DRM Driver 提供了一個增強的 API ,用戶空間的 libdrm 也需要以額外的 libdrm-driver 拓展,用來使用這些額外的 ioctl。

+

2.4 DRM API

DRM Core 向用戶空間應(yīng)用程序?qū)С隽硕鄠€接口,讓相應(yīng)的 libdrm 包裝成函數(shù)后來使用。 DRM Driver 導(dǎo)出的特定設(shè)備的接口,可以通過 ioctls 和 sysfs 來供用戶空間使用。

+

2.5 DRM-Master 和 DRM-Auth

DRM API 中有幾個 ioctl 由于并發(fā)問題僅限于用戶空間的單個進程使用。 為了實現(xiàn)這種限制,將 DRM 設(shè)備分為 Master 和 Auth。 上述的那些 ioctl 只能被 DRM-Master 的進程調(diào)用。 打開了 /dev/dri/cardX的進程的文件句柄會被標(biāo)志為 master,特別是第一個掉喲該 SET_MASTERioctl 的進程。如果不是 DRM-Master 的進程在使用這些限制 ioctl 的時候會返回錯誤。進程也可以通過 DROP_MASTERioctl 放棄 Master 角色,來讓其他進程變成 Master。

+

X Server 或者其他的 Display Server 通常會是他們所管理的 DRM 設(shè)備的 DRM-Master 進程。當(dāng) DRM 設(shè)備啟動的時候哦,這些 Display Server 打開設(shè)備節(jié)點,獲取 DRM-Master 權(quán)限,直到關(guān)閉設(shè)備。

+

對于其他的用戶空間進程,還有一種辦法可以獲得 DRM 設(shè)備的這些 受限權(quán)限,這就是 DRM-Auth。它是一種針對 DRM 設(shè)備的驗證方式,用來證明該進程已經(jīng)獲得了 DRM-Master 對于他們?nèi)ピL問受限 ioctls 的許可。

+

步驟:

  1. DRM Client 使用 GET_MAGIC ioctl 從 DRM 設(shè)備獲取一個 32bit整型的 token。并通過任何方式(通常是 IPC)傳遞給 DRM-Master。

  2. DRM-Master 進程使用 AUTH_MAGIC ioctl 返回 token 給 DRM 設(shè)備。

  3. 設(shè)備將 DRM-Master 所給的 token 和 Auth 的進行對比。通過的話就賦予進程文件句柄特殊的權(quán)限。

#

DRM 在源碼中的架構(gòu) (圖自 Mark.Yao):

+

使用 DRM 訪問 Video Card (圖自 wikipedia):

+

沒有 DRM 時,用戶空間進程訪問 GPU 的方式有 DRM 后,用戶空間訪問 GPU 的方式

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多