前面一篇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”。