前面一篇blog里面描述了命令环缓冲区机制,在命令环机制下,驱动写入PM4(不知道为何会取这样一个名字)包格式的命令对显卡进行配置。这一篇blog将详细介绍命令包的格式。
当前定义了4中命令包,分别是0型/1型/2型和3型命令包,命令包由两部分组成,第一部分是命令包头,第二部分是命令包主体,命令包头为请求GPU执行的具体操作,命令主体为执行该操作需要的数据。
- 0型命令包
0型命令包用于写连续N个寄存器。包主体部分是依次往这些寄存器写的值。包头各个部分的意义为:
位 | 域名称 | 描述 |
12:0 | BASE_INDEX | 要写的连续寄存器的第一个寄存器地址,最大地址0x7FFF |
14:13 | 保留位 | |
15 | ONE_REG_WR |
0表示将包主体的数据依次写入寄存器中 1表示所有数据写入同一个寄存器 |
29:16 | COUNT | 要写的寄存器数目N-1 |
31:30 | TYPE | 包类型,0型命令包类型名为0 |
Linux内核代码./drivers/gpu/drm/radeon/r600.c r600\_fence\_ring\_emit函数有如下语句:
radeon_ring_write(rdev, PACKET0(CP_INT_STATUS, 0));
radeon_ring_write(rdev, RB_INT_STAT);
PACKET0定义如下:
#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ 包类型 0型命令包
(((reg) >> 2) & 0xFFFF) | \ 寄存器偏移基地址
((n) & 0x3FFF) << 16) 要写的寄存器数目
所有类型的数据包31~30bit为包类型标识符,0型数据包的类型标识符为0,其30bit为PACKET_TYPE0(0x0),29~16bit为命令写的寄存器数量-1((n) & 0x3FFF) << 16),上面例子只写一个寄存器,其值为0。第14~132bit为保留位,12~0bit ((reg) >> 2) & 0xFFFF)为第一个寄存器偏移地址,由于使用0型包可以访问的所有寄存器都是4字节的,寄存器地址都是4字节对其的,所以低2位为0。
- 1型命令包
1型命令包用于写两个的寄存器,1型命令包包头定义如下:
位 | 域名称 | 描述 |
10:0 | REG_INDEX1 | 第一个寄存器的地址 |
22:11 | REG_INDEX2 | 第二个寄存器的地址 |
29:22 | RESERVED | 保留位 |
31:30 | TYPE | 1型命令包的类型为0x1 |
由于1型命令包可以用0型命令包代替而且1型命令包并不能够访问到所有寄存器,在内核radeon驱动中并没有使用1型命令包。
- 2型命令包
2型命令包是一个空命令包,用于填充对齐命令。2型命令包没有包主体,其包头最高两位为0x2,其它位无意义。
2型命令包不做任何操作,仅用于填充保证对齐用,填充ring buffer的时候有对齐要求,内核radeon驱动对齐要求是16个dword(16×4字节),在命令没有16 dword对齐的时候,就需要使用2型命令包填充。
drivers/gpu/drm/radeon/radeon_ring.c radeon_ring_commit函数用于通知GPU从ring buffer中取数据并执行,该函数包含如下代码:
count_dw_pad = (rdev->cp.align_mask + 1) - (rdev->cp.wptr & rdev->cp.align_mask);
for (i = 0; i < count_dw_pad; i++) {
radeon_ring_write(rdev, 2 << 30);
}
第一句用于计算对齐命令需要的dword数目,后面的for循环用于填充2型命令。2型命令仅有个命令头部,并且只有31~30bit有效。
-
3型命令包
3型命令包是最功能最丰富的包,图形的主要功能都是通过这类包实现的。3型命令包主体内容由包头的IT_OPCODE决定。
位 | 域名称 | 描述 |
7:0 | reserved | 保留位 |
15:8 | IT_OPCODE | 操作码 |
29:16 | COUNT | 包主题DWORDS数目-1 |
31:30 | TYPE | 3型包类型为0x3 |
3型命令是主要的命令包,涵盖了寄存器设置/绘图命令/同步等主要操作。以下是一个使用3型命令包设置寄存器的例子,这段代码来自drivers/gpu/drm/radeon/r600.c 的r600_ib_test函数:
ib->ptr[0] = PACKET3(PACKET3_SET_CONFIG_REG, 1);
ib->ptr[1] = ((scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
ib->ptr[2] = 0xDEADBEEF;
ib->ptr[3] = PACKET2(0);
ib->ptr[4] = PACKET2(0);
......
ib->ptr[15] = PACKET2(0);
ib->length_dw = 16;
这段代码使用了indirect buffer,但是填充的命令和ring buffer中填充的命令是一样的。
#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \
(((op) & 0xFF) << 8) | \
((n) & 0x3FFF) << 16)
3型命令头部包含了操作码op和数据数目(以dword计)。上面例子中PACKET3(PACKET3_SET_CONFIG_REG, 1) PACKET3_SET_CONFIG_REG表明这次命令包用于设置寄存器,1表明后面有2个dword数据,分别是(scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2(寄存器地址)和0xDEADBEEF(往寄存器中写的值)。后面是用于对齐的2型包。
下面使用一个更加复杂的命令包来说明3型包的使用,下面的这个命令包用于执行一个简单的2D操作。r600显卡是ATI推出的第一款使用统一着色器的GPU,r600及其以后的显卡不包含单独的2D单元,而是使用3D部件执行2D操作。为了简单起见,这里我们使用r500显卡上的填充矩形的命令包。
radeon_ring_write(rdev, PACKET3(PACKET3_PAINT_MULTI, 6));
radeon_ring_write(rdev,
RADEON_GMC_DST_PITCH_OFFSET_CNTL |
RADEON_GMC_DST_CLIPPING | // important
RADEON_GMC_BRUSH_SOLID_COLOR | // 13 << 4
(RADEON_COLOR_FORMAT_ARGB8888 << 8) | // << 8
RADEON_GMC_SRC_DATATYPE_COLOR | // 4 << 12
RADEON_ROP3_P | // << 16
RADEON_GMC_CLR_CMP_CNTL_DIS); // 1 << 28
radeon_ring_write(rdev, ((pitch / 64) << 22) | (fb_offset>>10));
radeon_ring_write(rdev, 0 | (0 << 16)); // SC_TOP_LEFT
radeon_ring_write(rdev, (fb_w -1) | ((fb_h -1) << 16)); // SC_BOT_RITE
radeon_ring_write(rdev, color); // this is color
radeon_ring_write(rdev, (x << 16) | y);
radeon_ring_write(rdev, (w << 16) | h);
radeon_ring_write(rdev, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0));
radeon_ring_write(rdev, RADEON_RB2D_DC_FLUSH_ALL);
radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0));
radeon_ring_write(rdev, RADEON_WAIT_2D_IDLECLEAN|
RADEON_WAIT_HOST_IDLECLEAN|
RADEON_WAIT_DMA_GUI_IDLE);
3型包根据他们IT_OPCODE的不同,其IT_BODY差别很大,如果IT_OPCODE的最高位为1(通常是2D绘图命令),那么PACKET还需要加入GUI control。R500上的2D绘图命令有如下格式:
HEADER
GUI_CONTROL
SETUP_BODY
DATA_BLOCK
其中Header部分对应3型命令包的头,GUI_CONTROL和SETUP_BODY共同构成了当前绘图环境的配置,这两部分加上DATA_BLOCK共同构成了3型包的IT_BODY部分。上面的代码第一句表明该命令包执行的是矩形绘制(PAINT_MULTI可以同时绘制多个矩形,这里我们只绘制了一个矩形)。第二句对应GUI_CONTROL,GUI_CONTROL为32bit,内容为当前绘制环境的标志,下表给出了代码中使用的一些标志(如果是blit操作,除了表中的DSTxx参数外,还需要设置对应的SRCxx参数),关于这些标志更详细的信息可以参考“R5xx Acceleration v1.5.pdf”35-36页相关内容。
位 | 域名称 | 描述 |
1 | DST_PITCH_OFFSET | 绘图目标区域的PITCH值和该区域在GPU虚拟地址空间中的偏移,如果该为被置为1,则需要在SETUP_BODY中指定该参数 |
3 | DST_CLIPPING | 设置绘图区域的裁剪参数,如果该位置为1,则需要在SETUP_BODY中设置SC_TOP_LEFT和SC_BOTTOM_RIGHT参数 |
7:4 | BRUSH_TYPE | 绘图时使用的brush类型,brush类型需要根据这里给出的类型在SETUP_BODY中填brush包,不同的BRUSH_TYPE对应的brush包不同 |
11:8 | DST_TYPE |
绘图目标区域的像素类型: 1 :- (reserved) 2 :- 8 bpp pseudocolor 3 :- 16 bpp aRGB 1555 4 :- 16 bpp RGB 565 5 :- reserved 6 :- 32 bpp aRGB 8888 7 :- 8 bpp RGB 332 8 :- Y8 greyscale 9 :- RGB8 greyscale (8 bit intensity, duplicated for all 3 channels. Green channel is used on writes) 10 :- (reserved) 11 :- YUV 422 packed (VYUY) 12 :- YUV 422 packed (YVYU) 13 :- (reserved) |
在上面示例程序中,以上标志位均被设置,并且BRUSH\_TYPE被设置为14,DST_TYPE设为32位真彩色。
根据GUI_CONTROL的设置,SETUP_BODY中需要设置以下参数:
DST_PITCH_OFFSET
SC_TOP_LEFT
SC_BOTTOM_RIGHT
BRUSH_PACKET
上面代码中的3-6行即是对这些参数的设置。更多参数的可以参考“R5xx Acceleration v1.5.pdf”37页的内容。
下面对这些参数进行介绍:
- DST_PITCH_OFFSET
包括了三部分,31:30位是和tiling相关的标志位,29:22位是以64字节为单位的pitch值,21:0位是DST绘图区域(在xorg中称为pixmap)以1KB为单位在显存中的偏移,这里提示我们,在分配内存的时候必须是1K对齐的,否则在使用的时候会出问题,后面讨论directfb的时候将会碰到这个问题。对于这个参数,上面代码填的是
radeon_ring_write(rdev, ((pitch / 64) << 22) | (fb_offset>>10));
- SC_TOP_LEFT和SC_BOTTOM_RIGHT
指定绘图区域的裁剪区域,裁剪区域是个矩形,SC_TOP_LEFT指定裁剪区域左上方坐标,2D绘图时以屏幕左上方的点为原点,从左往又为X轴正方向,从上往下为Y轴正方向。
radeon_ring_write(rdev, 0 | (0 << 16)); // SC_TOP_LEFT
radeon_ring_write(rdev, (fb_w -1) | ((fb_h -1) << 16)); // SC_BOTTOM_RIGHT
fb_w和fb_h为当前绘图区域的长和宽,这里我们指定的裁剪区域就是整个绘图区域。
- BRUSH_PACKET
在GUI_CONTROL中指定的brush type为RADEON_GMC_BRUSH_SOLID_COLOR,关于brush type 和对应的值请参考“R5xx Acceleration v1.5.pdf”38页的内容,这里指定的类型为13,对应的BRUSH\_PACKET格式为4字节,内容为绘图使用的前景色。
radeon_ring_write(rdev, color); // the foreground color
后面两句代码是DATA_BLOCK部分,对应绘图使用的参数。
radeon_ring_write(rdev, (x << 16) | y);// 矩形左上角坐标
radeon_ring_write(rdev, (w << 16) | h);// 矩形宽和高
PAINT_MULTI命令包的DATA_BLOCK部分定义如下表示:
顺序 | 域名称 | 描述 |
1 | [DST_X1 | DST_Y1] | 第1个矩形左上角的坐标,高16位为X轴坐标,低16位为Y轴坐标 |
2 | [DST_W1 | DST_H1] | 第1个矩形的宽和高 |
... | ||
2n-1 | [DST_Xn | DST_Yn] | 第n个矩形左上角的坐标 |
2n | [DST_Wn | DST_Hn] | 第n个矩形的宽和高 |
下面给出了一些代码,读者根据前面的介绍并参考“R5xx Acceleration v1.5.pdf” 是很容易理解的,如果机器上有R500核心的显卡,将这些代码添加到drivers/gpu/drm/radeon/radeon_test.c文件中并调用这些函数,在开启radeon_testing的情况下就能在启动阶段看到效果。
- 画线
POLYLINE的op_code为0x95,用于绘制折线。
1 void r5xx_draw_line_2d(struct radeon_device *rdev, uint64_t fb_location,
2 int *points, int num ,int color, int fb_w, int fb_h)
3 {
4 int r;
5 struct radeon_fence *fence = NULL;
6 int ndw = 32 + 6 + num;// ?? 32 is enough
7 int i = 0;
8
9 r = radeon_fence_create(rdev, &fence);
10 if (r) {
11 DRM_ERROR("Failed to create fence\n");
12 goto out_cleanup;
13 }
14 r = radeon_ring_lock(rdev, ndw);
15 radeon_ring_write(rdev, PACKET3(PACKET3_POLYLINE, 4 + num));
16 radeon_ring_write(rdev,
17 RADEON_GMC_DST_PITCH_OFFSET_CNTL |
18 RADEON_GMC_DST_CLIPPING | // important
19 RADEON_GMC_BRUSH_SOLID_COLOR | // 13 << 4
20 (RADEON_COLOR_FORMAT_ARGB8888 << 8) | // << 8
21 RADEON_GMC_SRC_DATATYPE_COLOR | // ?? 4 << 12
22 RADEON_ROP3_P | // << 16
23 RADEON_GMC_CLR_CMP_CNTL_DIS);
24 radeon_ring_write(rdev, ((fb_w * 4 / 64) << 22) | (fb_location >>10));
25 radeon_ring_write(rdev, 0 | (0 << 16));
26 radeon_ring_write(rdev, (fb_w -1) | ((fb_h -1) << 16));
27 radeon_ring_write(rdev, color);
28 for( i = 0; i < num; ++i){
29 radeon_ring_write(rdev, *points++);
30 }
31 radeon_ring_write(rdev, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0));
32 radeon_ring_write(rdev, RADEON_RB2D_DC_FLUSH_ALL);
33 radeon_ring_write(rdev,
34 RADEON_WAIT_2D_IDLECLEAN |
35 RADEON_WAIT_HOST_IDLECLEAN |
36 RADEON_WAIT_DMA_GUI_IDLE);
37
38 if(fence) {
39 r = radeon_fence_emit(rdev, fence);
40 }
41 radeon_ring_unlock_commit(rdev);
42 r = radeon_fence_wait(fence, false);
43 if (r) {
44 DRM_ERROR("Failed to wait for fence\n");
45 goto out_cleanup;
46 }
47
48 out_cleanup:
49 if(fence) {
50 radeon_fence_unref(&fence);
51 }
52 }
注意到这里调用了三个函数处理fence:radeon_fence_create,创建一个fence;radeon_fence_emit,在提交ring buffer之前发送fence;radeon_fence_wait,等待fence。在下一篇blog“中断机制”中会介绍。
- 画矩形
使用PAINT_MULTI可以绘制矩形,可以在一次命令中绘制多个矩形,其IT_OPCODE为0x9a。
1 void r5xx_draw_rectangl_2d(struct radeon_device *rdev, uint64_t fb_location,
2 int x, int y, int w, int h, int color, int fb_w, int fb_h)
3 {
4 int r;
5 int ndw = 32 + 6;// ?? 32 is enough
6 struct radeon_fence *fence = NULL;
7
8 r = radeon_fence_create(rdev, &fence);
......
13 r = radeon_ring_lock(rdev, ndw);
......
18 radeon_ring_write(rdev, PACKET3(PACKET3_PAINT_MULTI, 6));
19 radeon_ring_write(rdev,
20 RADEON_GMC_DST_PITCH_OFFSET_CNTL |
21 RADEON_GMC_DST_CLIPPING | // important
22 RADEON_GMC_BRUSH_SOLID_COLOR | // 13 << 4
23 (RADEON_COLOR_FORMAT_ARGB8888 << 8) | // << 8
24 RADEON_GMC_SRC_DATATYPE_COLOR | // ?? 4 << 12
25 RADEON_ROP3_P | // << 16
26 RADEON_GMC_CLR_CMP_CNTL_DIS); // 1 << 28
27
28 radeon_ring_write(rdev, ((fb_w * 4 / 64) << 22) | (fb_location >>10));
29 radeon_ring_write(rdev, 0 | (0 << 16));
30 radeon_ring_write(rdev, (fb_w -1) | ((fb_h -1) << 16));
31
32 radeon_ring_write(rdev, color); // this is color
33 radeon_ring_write(rdev, (x << 16) | y);
34 radeon_ring_write(rdev, (w << 16) | h);
35
36 radeon_ring_write(rdev, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0));
37 radeon_ring_write(rdev, RADEON_RB2D_DC_FLUSH_ALL);
38 radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0));
39 radeon_ring_write(rdev,
40 RADEON_WAIT_2D_IDLECLEAN |
41 RADEON_WAIT_HOST_IDLECLEAN |
42 RADEON_WAIT_DMA_GUI_IDLE);
43
44 r = radeon_fence_emit(rdev, fence);
......
49 radeon_ring_unlock_commit(rdev);
50 r = radeon_fence_wait(fence, false);
......
55 out_cleanup:
......
59 }
- BLT
1 void r6xx_blit_2d(struct radeon_device *rdev,
2 uint64_t src_ad, uint64_t dst_addr,
3 int src_x, int src_y, int dst_x, int dst_y,
4 int src_w, int src_h, int fb_w, int fb_h)
5 {
6 int r;
7 int ndw;
8 struct radeon_fence *fence = NULL;
9 ndw = 64 + 10;
10
......
21 radeon_ring_write(rdev, PACKET3(PACKET3_BITBLT_MULTI, 8));
22 radeon_ring_write(rdev,
23 RADEON_GMC_SRC_PITCH_OFFSET_CNTL |
24 RADEON_GMC_DST_PITCH_OFFSET_CNTL |
25 RADEON_GMC_SRC_CLIPPING |
26 RADEON_GMC_DST_CLIPPING |
27 RADEON_GMC_BRUSH_NONE |
28 (RADEON_COLOR_FORMAT_ARGB8888 << 8) |
29 RADEON_GMC_SRC_DATATYPE_COLOR |
30 RADEON_ROP3_S |
31 RADEON_DP_SRC_SOURCE_MEMORY |
32 RADEON_GMC_CLR_CMP_CNTL_DIS |
33 RADEON_GMC_WR_MSK_DIS);
34 // SRC_PITCH_OFFSET
35 radeon_ring_write(rdev, ((fb_w * 4/64) << 22) | (src_addr >> 10));
36 // DST_PITCH_OFFSET
37 radeon_ring_write(rdev, ((fb_w * 4/64) << 22) | (dst_addr >> 10));
38 // SRC_SC_BOT_RITE
39 // radeon_ring_write(rdev, (0x1fff) | (0x1fff << 16));
40 radeon_ring_write(rdev, (fb_w -1) | ((fb_h -1) << 16));
41 // SC_TOP_LEFT
42 radeon_ring_write(rdev, 0 | (0 << 16));
43 // SC_BOT_RITE
44 // radeon_ring_write(rdev, (0x1fff) | (0x1fff << 16));
45 radeon_ring_write(rdev, (fb_w -1) | ((fb_h -1) << 16));
46 // [SRC_X1 | SRC_Y1]
47 radeon_ring_write(rdev, (src_x << 16) | src_y);
48 // [DST_X1 | DST_Y1]
49 radeon_ring_write(rdev, (dst_x << 16) | dst_y);
50 // [SRC_W1 | SRC_H1]
51 radeon_ring_write(rdev, (src_w << 16) | src_h);
......
}
参考资料:
本节内容主要参考资料为“R5xx Acceleration v1.5.pdf”。