【原創】Linux環境下的圖形系統和AMD R600顯卡編程(6)——AMD顯卡GPU命令格式


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM