Linux驱动开发系列:DRM(第十部分)_drm bolb

主要负责显示buffer的分配和释放,包括dumb、prime、fence

1、Dumb:

只支持连续物理内存,基于kernel中通用CMA API实现,多用于小分辨率简单场景。主要负责一些简单的buffer显示,可以直接使用CPU渲染,GPU不会使用dumb。

2、Prime:

连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用于大内存复杂场景。

3、Fence:

buffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题。

四、部分代码介绍

st公司已经写好了DRM框架代码,位于路径:drivers\gpu\drm\stm。本次进行测试的时候,将该目录下的代码删除,参考厂家的代码重新写。设备树部分,修改compatible等代码,适合本次测试代码,仅作为学习使用。

1、struct drm_driver结构体

struct drm_driver是DRM框架的核心结构体。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图3  struct drm_driver结构体

如图3,driver_features描述的是DRM支持的相关操作。

1)、DRIVER_MODESET:表示支持modesetting 操作

2)、DRIVER_GEM:表示支持GEM 操作,用于操作对内存的分配、释放

3)、DRIVER_ATOMIC:支持 Atomic 操作,用于操作各种属性

dumb_create成员是创建dumb内存。本例中对其进行重写,其他的回调函数,使用cma api。

2、probe函数

在probe函数中,申请struct drm_device *ddev=drm_dev_alloc(&drv_driver, dev)结构体,在里面传入struct drm_driver结构体、配置KMS、注册DRM。部分代码如下:

//配置KMS信息,
static int my_modeset_init(struct drm_device *ddev)
{
	struct platform_device *pdev = to_platform_device(ddev->dev);
	struct ltdc_device *ldev;
	int ret;

DRM_DEBUG(“%s\n”, func);

ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL);
if (!ldev)
return -ENOMEM;

ddev->dev_private = (void *)ldev;

drm_mode_config_init(ddev); //初始化drm_device

/*

  • 设置最大/小宽、高值
  • 绑定framebuffer结构体,drm可以模拟fb
    */
    ddev->mode_config.min_width = 0;
    ddev->mode_config.min_height = 0;
    ddev->mode_config.max_width = MY_MAX_FB_WIDTH;
    ddev->mode_config.max_height = MY_MAX_FB_HEIGHT;
    ddev->mode_config.funcs = &drv_mode_config_funcs; //设置framebuffer的回调函数结构体

ret = ltdc_load(ddev); //初始化ltdc接口,包括初始化connector和encoder一起初始化。
//connector初始化的时候会调用drm_panel结构体离的获取屏幕参数函数

if (ret)
goto err;

drm_mode_config_reset(ddev);
drm_kms_helper_poll_init(ddev);

platform_set_drvdata(pdev, ddev);

return 0;



err:
drm_mode_config_cleanup(ddev);
return ret;
}
//驱动的probe函数
static int my_drm_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct drm_device *ddev;
int ret;

DRM_DEBUG("%s\n", __func__);

dma_set_coherent_mask(dev, DMA_BIT_MASK(32));  //设置DMA

ddev = drm_dev_alloc(&drv_driver, dev);  //分配一个drm_device结构体
if (IS_ERR(ddev))
	return PTR_ERR(ddev);

ret = my_modeset_init(ddev);  //初始化KMS
if (ret)
	goto err_put;

ret = drm_dev_register(ddev, 0);  //注册drm
if (ret)
	goto err_put;

drm_fbdev_generic_setup(ddev, 16); 

return 0;

err_put:
drm_dev_put(ddev);

return ret;

}

ddev->mode_config.funcs = &drv_mode_config_funcs;  //设置framebuffer的回调函数结构体

这个部分,是DRM用于模拟Framebuffer框架的代码,结构体如下:

static const struct drm_mode_config_funcs drv_mode_config_funcs = {
	.fb_create = drm_gem_fb_create,
	.atomic_check = drm_atomic_helper_check,
	.atomic_commit = drm_atomic_helper_commit,
};

ret = ltdc_load(ddev);代码中,对KMS中的相关功能进行完善,包括connector、encoder、plane、crtc等。代码太多了,具体的可以看后面连接放的代码。这里简单的说下大致内容。

KMS中的基本元素(CRTC,ENCODER,CONNECTOR,PLANE,Framebuffer已经在GEM中实现)均需要在DRM驱动中实现,没有硬件对应时,需要模拟出来。在DRM中,每一个部分都是使用一个结构体进行描述,需要使用对应的函数进行初始化。

注:各个Soc厂家的DRM部分设计的很多都比(互)较(相)相(抄)似(袭)。DRM框架将它们共同的代码使用xxx_funcs描述、xxx_init进行初始化,不同的部分使用xxx_helper_funcs描述、

drm_xxx_helper_add()添加。

  1. xxx_funcs 必须有,xxx_helper_funcs 可以没有。
  2. drm_xxx_init() 必须有,drm_xxx_helper_add() 可以没有。
  3. 只有当 xxx_funcs 采用 DRM 标准的 helper 函数实现时,才有可能 需要定义 xxx_helper_funcs 接口。
  4. *xxx_funcs.*destroy() 接口必须实现

例如:


static int ltdc_plane_atomic_check(struct drm_plane *plane,
				   struct drm_plane_state *state)
{
	struct drm_framebuffer *fb = state->fb;
	struct drm_crtc_state *crtc_state;
	struct drm_rect *src = &state->src;
	struct drm_rect *dst = &state->dst;

DRM_DEBUG_DRIVER(“\n”);

if (!fb)
return 0;

/* convert src from 16:16 format */
src->x1 = state->src_x >> 16;
src->y1 = state->src_y >> 16;
src->x2 = (state->src_w >> 16) + src->x1 - 1;
src->y2 = (state->src_h >> 16) + src->y1 - 1;

dst->x1 = state->crtc_x;
dst->y1 = state->crtc_y;
dst->x2 = state->crtc_w + dst->x1 - 1;
dst->y2 = state->crtc_h + dst->y1 - 1;

DRM_DEBUG_DRIVER(“plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n”,
plane->base.id, fb->base.id,
src->x2 - src->x1 + 1, src->y2 - src->y1 + 1,
src->x1, src->y1,
dst->x2 - dst->x1 + 1, dst->y2 - dst->y1 + 1,
dst->x1, dst->y1);

crtc_state = drm_atomic_get_existing_crtc_state(state->state,
state->crtc);
/* destination coordinates do not have to exceed display sizes */
if (crtc_state && (crtc_state->mode.hdisplay <= dst->x2 ||
crtc_state->mode.vdisplay <= dst->y2))
return -EINVAL;

/* source sizes do not have to exceed destination sizes */
if (dst->x2 - dst->x1 < src->x2 - src->x1 ||
dst->y2 - dst->y1 < src->y2 - src->y1)
return -EINVAL;

return 0;



}

static void ltdc_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *oldstate)
{
struct ltdc_device *ldev = plane_to_ltdc(plane);
struct drm_plane_state *state = plane->state;
struct drm_rect *src = &state->src;
struct drm_rect *dst = &state->dst;
struct drm_framebuffer *fb = state->fb;
u32 lofs = plane->index * LAY_OFS;
u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
enum ltdc_pix_fmt pf;
struct drm_rect dr;

if (!state-&gt;crtc || !fb) {
	DRM_DEBUG_DRIVER("fb or crtc NULL");
	return;
}

/* compute final coordinates of frame buffer */
dr.x1 = src-&gt;x1 + dst-&gt;x1;
dr.y1 = src-&gt;y1 + dst-&gt;y1;
dr.x2 = src-&gt;x2 + dst-&gt;x1;
dr.y2 = src-&gt;y2 + dst-&gt;y1;

bpcr = my_reg_read(ldev-&gt;regs, LTDC_BPCR);
ahbp = (bpcr &amp; BPCR_AHBP) &gt;&gt; 16;
avbp = bpcr &amp; BPCR_AVBP;

/* Configures the horizontal start and stop position */
val = ((dr.x2 + 1 + ahbp) &lt;&lt; 16) + (dr.x1 + 1 + ahbp);
my_reg_update_bits(ldev-&gt;regs, LTDC_L1WHPCR + lofs,
		LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);

/* Configures the vertical start and stop position */
val = ((dr.y2 + 1 + avbp) &lt;&lt; 16) + (dr.y1 + 1 + avbp);
my_reg_update_bits(ldev-&gt;regs, LTDC_L1WVPCR + lofs,
		LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);

/* Specifies the pixel format */
pf = to_ltdc_pixelformat(fb-&gt;format-&gt;format);
for (val = 0; val &lt; NB_PF; val++)
	if (ldev-&gt;caps.pix_fmt_hw[val] == pf)
		break;

if (val == NB_PF) {
	DRM_ERROR("Pixel format %.4s not supported\n",
		  (char *)&amp;fb-&gt;format-&gt;format);
	val = 0;	/* set by default ARGB 32 bits */
}
my_reg_update_bits(ldev-&gt;regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);

/* Configures the color frame buffer pitch in bytes &amp; line length */
pitch_in_bytes = fb-&gt;pitches[0];
line_length = fb-&gt;format-&gt;cpp[0] * (dr.x2 - dr.x1 + 1) +
	      (ldev-&gt;caps.bus_width &gt;&gt; 3) - 1;
val = ((pitch_in_bytes &lt;&lt; 16) | line_length);
my_reg_update_bits(ldev-&gt;regs, LTDC_L1CFBLR + lofs,
		LXCFBLR_CFBLL | LXCFBLR_CFBP, val);

/* Specifies the constant alpha value */
val = CONSTA_MAX;
my_reg_update_bits(ldev-&gt;regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);

/* Specifies the blending factors */
val = BF1_PAXCA | BF2_1PAXCA;
if (!fb-&gt;format-&gt;has_alpha)
	val = BF1_CA | BF2_1CA;

/* Manage hw-specific capabilities */
if (ldev-&gt;caps.non_alpha_only_l1 &amp;&amp;
    plane-&gt;type != DRM_PLANE_TYPE_PRIMARY)
	val = BF1_PAXCA | BF2_1PAXCA;

my_reg_update_bits(ldev-&gt;regs, LTDC_L1BFCR + lofs,
		LXBFCR_BF2 | LXBFCR_BF1, val);

/* Configures the frame buffer line number */
val = dr.y2 - dr.y1 + 1;
my_reg_update_bits(ldev-&gt;regs, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, val);

/* Sets the FB address */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0);

DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
my_reg_write(ldev-&gt;regs, LTDC_L1CFBAR + lofs, paddr);

/* Enable layer and CLUT if needed */
val = fb-&gt;format-&gt;format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
val |= LXCR_LEN;
my_reg_update_bits(ldev-&gt;regs, LTDC_L1CR + lofs,
		LXCR_LEN | LXCR_CLUTEN, val);

ldev-&gt;plane_fpsi[plane-&gt;index].counter++;

mutex_lock(&amp;ldev-&gt;err_lock);
if (ldev-&gt;error_status &amp; ISR_FUIF) {
	DRM_WARN("ltdc fifo underrun: please verify display mode\n");
	ldev-&gt;error_status &amp;= ~ISR_FUIF;
}
if (ldev-&gt;error_status &amp; ISR_TERRIF) {
	DRM_WARN("ltdc transfer error\n");
	ldev-&gt;error_status &amp;= ~ISR_TERRIF;
}
mutex_unlock(&amp;ldev-&gt;err_lock);

}

static void ltdc_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *oldstate)
{
struct ltdc_device *ldev = plane_to_ltdc(plane);
u32 lofs = plane->index * LAY_OFS;

/* disable layer */
my_reg_clear(ldev-&gt;regs, LTDC_L1CR + lofs, LXCR_LEN);

DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
		 oldstate-&gt;crtc-&gt;base.id, plane-&gt;base.id);

}

static void ltdc_plane_atomic_print_state(struct drm_printer *p,
const struct drm_plane_state *state)
{
struct drm_plane *plane = state->plane;
struct ltdc_device *ldev = plane_to_ltdc(plane);
struct fps_info *fpsi = &ldev->plane_fpsi[plane->index];
int ms_since_last;
ktime_t now;

now = ktime_get();
ms_since_last = ktime_to_ms(ktime_sub(now, fpsi-&gt;last_timestamp));

drm_printf(p, "\tuser_updates=%dfps\n",
	   DIV_ROUND_CLOSEST(fpsi-&gt;counter * 1000, ms_since_last));

fpsi-&gt;last_timestamp = now;
fpsi-&gt;counter = 0;

}

static bool ltdc_plane_format_mod_supported(struct drm_plane *plane,
u32 format,
u64 modifier)
{
if (modifier == DRM_FORMAT_MOD_LINEAR)
return true;

return false;

}

static const struct drm_plane_funcs ltdc_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.atomic_print_state = ltdc_plane_atomic_print_state,
.format_mod_supported = ltdc_plane_format_mod_supported,
};

static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
.prepare_fb = drm_gem_fb_prepare_fb,
.atomic_check = ltdc_plane_atomic_check,
.atomic_update = ltdc_plane_atomic_update,
.atomic_disable = ltdc_plane_atomic_disable,
};
//创建图层
static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
enum drm_plane_type type)
{
unsigned long possible_crtcs = CRTC_MASK;
struct ltdc_device *ldev = ddev->dev_private;
struct device *dev = ddev->dev;
struct drm_plane *plane;
unsigned int i, nb_fmt = 0;
u32 formats[NB_PF * 2];
u32 drm_fmt, drm_fmt_no_alpha;
const u64 *modifiers = ltdc_format_modifiers;
int ret;

/* Get supported pixel formats NB_PF个*/
for (i = 0; i &lt; NB_PF; i++) {  //添加支持的图层格式
	
	drm_fmt = to_drm_pixelformat(ldev-&gt;caps.pix_fmt_hw[i]);
	if (!drm_fmt)
		continue;
	formats[nb_fmt++] = drm_fmt;

	/* Add the no-alpha related format if any &amp; supported */
	drm_fmt_no_alpha = get_pixelformat_without_alpha(drm_fmt);
	if (!drm_fmt_no_alpha)
		continue;

	/* Manage hw-specific capabilities */
	if (ldev-&gt;caps.non_alpha_only_l1 &amp;&amp;
	    type != DRM_PLANE_TYPE_PRIMARY)
		continue;

	formats[nb_fmt++] = drm_fmt_no_alpha;
}</code></pre>

ltdc_plane_create函数用于创建plane相关部分,

drm_universal_plane_init:初始化plane结构体

drm_plane_helper_add:添加helper函数

五、测试

将代码放到drivers\gpu\drm\stm目录下进行覆盖(省心),重新编译内核(经过测试,修改设备树和stm目录下的代码之后,屏幕在uboot启动阶段能正常使用,在Image启动阶段无法使用,说明修改之后,内核中的DRM框架已经无法正常工作),下载Image和设备树。重新启动之后,可以在内核启动中看到这种输出字样。

[    1.358037] panel-simple panel-rgb: panel-rgb supply power not found, using dummy regulator

有这个部分说明DRM能正常使用。

1、查看/dev/dri

/dev/dri下是DRM生成的节点,也可以看到/dev/fb0,这个节点是DRM框架兼容FB框架产生的节点。

2、modetest

modetest是libdrm库编译出来产生的一个DRM测试程序,输出结果如图,可以看到KSM相关的输出。libdrm使用版本为libdrm-2.4.109。

root@ATK-MP157:~# modetest
trying to open device 'i915'...failed
trying to open device 'amdgpu'...failed
trying to open device 'radeon'...failed
trying to open device 'nouveau'...failed
trying to open device 'vmwgfx'...failed
trying to open device 'omapdrm'...failed
trying to open device 'exynos'...failed
trying to open device 'tilcdc'...failed
trying to open device 'msm'...failed
trying to open device 'sti'...failed
trying to open device 'tegra'...failed
trying to open device 'imx-drm'...failed
trying to open device 'rockchip'...failed
trying to open device 'atmel-hlcdc'...failed
trying to open device 'fsl-dcu-drm'...failed
trying to open device 'vc4'...failed
trying to open device 'virtio_gpu'...failed
trying to open device 'mediatek'...failed
trying to open device 'meson'...failed
trying to open device 'pl111'...failed
trying to open device 'stm'...done
Encoders:
id      crtc    type    possible crtcs  possible clones
31      35      DPI     0x00000001      0x00000000

Connectors:
id encoder status name size (mm) modes encoders
32 31 connected DPI-1 0x0 1 31
modes:
index name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot)
#0 1024x600 59.99 1024 1164 1184 1344 600 620 623 635 51200 flags: phsync, pvsync; type: preferred, driver
props:
1 EDID:
flags: immutable blob
blobs:

            value:
    2 DPMS:
            flags: enum
            enums: On=0 Standby=1 Suspend=2 Off=3
            value: 0
    5 link-status:
            flags: enum
            enums: Good=0 Bad=1
            value: 0
    6 non-desktop:
            flags: immutable range
            values: 0 1
            value: 0
    4 TILE:
            flags: immutable blob
            blobs:

            value:
    20 CRTC_ID:
            flags: object
            value: 35

CRTCs:
id fb pos size
35 38 (0,0) (1024x600)
#0 1024x600 59.99 1024 1164 1184 1344 600 620 623 635 51200 flags: phsync, pvsync; type: preferred, driver
props:
22 ACTIVE:
flags: range
values: 0 1
value: 1
23 MODE_ID:
flags: blob
blobs:

            value:
                    00c8000000048c04a004400500005802
                    6c026f027b0200003c00000005000000
                    48000000313032347836303000000000
                    00000000000000000000000000000000
                    00000000
    19 OUT_FENCE_PTR:
            flags: range
            values: 0 18446744073709551615
            value: 0
    24 VRR_ENABLED:
            flags: range
            values: 0 1
            value: 0
    28 GAMMA_LUT:
            flags: blob
            blobs:

            value:
    29 GAMMA_LUT_SIZE:
            flags: immutable range
            values: 0 4294967295
            value: 256

Planes:
id crtc fb CRTC x,y x,y gamma size possible crtcs
33 35 38 0,0 0,0 0 0x00000001
formats: AR24 XR24 RG24 RG16 AR15 XR15 AR12 XR12 C8
props:
8 type:
flags: immutable enum
enums: Overlay=0 Primary=1 Cursor=2
value: 1
17 FB_ID:
flags: object
value: 38
18 IN_FENCE_FD:
flags: signed range
values: -1 2147483647
value: -1
20 CRTC_ID:
flags: object
value: 35
13 CRTC_X:
flags: signed range
values: -2147483648 2147483647
value: 0
14 CRTC_Y:
flags: signed range
values: -2147483648 2147483647
value: 0
15 CRTC_W:
flags: range
values: 0 2147483647
value: 1024
16 CRTC_H:
flags: range
values: 0 2147483647
value: 600
9 SRC_X:
flags: range
values: 0 4294967295
value: 0
10 SRC_Y:
flags: range
values: 0 4294967295
value: 0
11 SRC_W:
flags: range
values: 0 4294967295
value: 67108864
12 SRC_H:
flags: range
values: 0 4294967295
value: 39321600
30 IN_FORMATS:
flags: immutable blob
blobs:

value: 35
13 CRTC_X:
flags: signed range
values: -2147483648 2147483647
value: 0
14 CRTC_Y:
flags: signed range
values: -2147483648 2147483647
value: 0
15 CRTC_W:
flags: range
values: 0 2147483647
value: 1024
16 CRTC_H:
flags: range
values: 0 2147483647
value: 600
9 SRC_X:
flags: range
values: 0 4294967295
value: 0
10 SRC_Y:
flags: range
values: 0 4294967295
value: 0
11 SRC_W:
flags: range
values: 0 4294967295
value: 67108864
12 SRC_H:
flags: range
values: 0 4294967295
value: 39321600
30 IN_FORMATS:
flags: immutable blob
blobs:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值