设计说明
源码说明
本模块源代码目录结构如下:
HAL 层实现:
luban-lite/bsp/artinchip/hal/ge
├── hal_ge_normal.c // normal模式驱动
├── hal_ge_hw.c // normal模式对硬件操作的封装
├── hal_ge_hw.h // normal模式对硬件操作封装的API
├── hal_ge_cmdq.c // CMD queue模式驱动
├── hal_ge_reg.h // GE寄存器定义
└── ../include/uapi/mpp_types.h // mpp公用的类型定义
2
3
4
5
6
7
Driver 层实现:
luban-lite/bsp/artinchip/drv/ge
├── aic_drv_ge.c // GE 驱动接口
└── Kconfig
2
3
MPP 层实现:
luban-lite/packages/artinchip/mpp/ge
├── cmdq_ops.c // MPP GE 驱动命令队列封装
├── normal_ops.c // MPP GE 驱动普通模式封装
├── mpp_ge.c // MPP GE 操作封装实现
├── include/ge_ops.h // MPP GE 操作函数定义
├── include/ge_reg.h // GE寄存器定义
└── ../include/mpp_ge.h // MPP GE 操作封装定义
2
3
4
5
6
7
模块架构
GE HAL :在normal 模式下,GE 驱动具有完整的功能。在cmdq模式下,负责GE硬件资源初始化和获取等,必须依靠MPP层实现完整功能。
GE DRV :对HAL层的封装,向上提供更简洁的接口。
MPP :在normal 模式下,功能仅仅是调用GE HAL的接口。在cmdq模式下,不仅调用GE HAL的接口,而且还包含命令的准备、封装和发送。
APP :应用层调用MPP层接口,即可实现对GE的操作。
非命令队列模式
非命令队列模式,即normal模式。在GE normal模式下,GE HAL层已经实现了GE的完整功能,可以不依靠MPP层和DRV层进行运行。但在一般情况我们并不会通过直接操作HAL层操作GE,而是使用MPP层间接去操作GE。
normal模式可用hal_ge_control控制GE:
- IOC_GE_VERSION
- IOC_GE_MODE
- IOC_GE_FILLRECT
- IOC_GE_BITBLT
- IOC_GE_ROTATE
对于接口IOC_GE_FILLRECT、IOC_GE_BITBLT、IOC_GE_ROTATE在normal模式下调用是同步的,硬件执行任务完成后接口调用才会返回。
关键流程设计
在normal模式下,GE驱动各种功能都是通过hal_ge_control调用来实现,每一次hal_ge_control的调用都包括GE参数配置、GE模块中断开启, GE启动,GE等中断(阻塞等待中断服务程序通知中断到来),硬件完成任务后,关闭中断。hal_ge_control通过mutex保护。 normal模式支持多应用同时打开驱动,并调用hal_ge_control。
等待中断流程:
在调用hal_ge_init时候创建事件集: data->wait = aicos_event_create();
在hal_ge_control中调用如下函数,使当前应用在等待事件中睡眠:
ret = aicos_event_recv(data->wait, HW_RUNNING_EVENT, &recved, GE_TIMEOUT);
- 在中断服务程序中调用 aicos_event_send(data->wait, HW_RUNNING_EVENT),激活等待事件中睡眠的应用。
命令队列模式
命令队列模式,即CMD queue模式。在CMD queue模式下,GE驱动内部是以Command queue的方式执行的, GE HAL层只负责从应用接收命令队列,然后执行命令队列。GE的完整功能需要依赖MPP中间件, 在MPP中间件中会把用户设置的参数信息转换成硬件可以识别的命令队列信息。
Command Queue相关的几个概念:
Task: GE(Graphics Engine) 可以执行的最小任务单元,比如说一次blit操作、一次矩形填充
Batch:硬件以batch为单位执行,是一系列命令的集合,可以包含一个或者多个task,软件也必须以batch为单位向驱动写入命令
Command Queue:软件可以向Command Queue写入多个batch,硬件以batch为单位,按顺序执行
GE Command Queue是以ring buffer的方式实现的, 关于ring buffer的说明,请参考GE规格书
CMD queue和normal模式驱动相比增加了hal_ge_write接口,命令队列通过hal_ge_write接口,以batch为单位发送给驱动,batch中可以包含多个task的命令。 hal_ge_write操作是异步的,相应的命令只要写入驱动中的cmd queue buffer即返回,不用等待硬件执行完当前batch中的所有命令, 当应用需要等待发送的命令执行完成时可以调用IOC_GE_SYNC接口。在CMD queue模式下,通过hal_ge_write接口写入以batch为单位的命令, 硬件可以连续执行多个task。而在normal模式下,通过IOC_GE_BITBLT等接口,硬件一次只能执行一个任务。
CMD Queue模式应用可用hal_ge_control:
- IOC_GE_VERSION
- IOC_GE_MODE
- IOC_GE_CMD_BUF_SIZE
- IOC_GE_SYNC
初始化流程
GE驱动的初始化过程见hal_ge_init()函数,除了申请中断外, 还申请了存储Command Queue需要的ring buffer,以及存储batch信息的结构体。 batch结构体不存储实际的命令,batch结构体中保存指向ring buffer的一段空间, 包括相对于ring buffer起始地址的offset,以及当前batch中的命令length。batch结构体定义如下:
struct aic_ge_batch {
struct list_head list;
int offset;
int length;
int client_id;
};
2
3
4
5
6
下图ring buffer中不同的颜色代表不同的batch对应的命令:
目前驱动中定义了8个存储batch信息的结构体,ring buffer的size定义为32K, 32K的空间可以缓存超过256个task(假如都是RGB格式的task)。
#define MAX_BATCH_NUM 8
#define CMD_BUF_SIZE AIC_GE_CMDQ_BUF_LENGTH
2
AIC_GE_CMDQ_BUF_LENGTH 在Kconfig中定义,可以在menuconfig去分配CMD_BUF_SIZE 大小。
batch管理
1.每一个batch总共可能存在3中状态:
free状态,batch中没有可用信息,在free batch list中
ready状态,batch中有等待执行的cmd信息,在ready list batch中
运行状态,当前硬件正在运行的batch,即不在free list中也不在ready list中
2.在分配给应用的缓冲buffer中,应用组织好命令队列,以batch为单位,通过标准的write接口把命令拷贝到ring buffer。
3.在驱动中维护一个包含每个batch起始offset和length信息的链表,硬件以batch为单位执行命令队列。
4.如上图所示,会有两个地方启动硬件执行batch命令
当应用调用了write命令写入当前batch信息,并且硬件处于空闲状态
在中断服务判断当前的ready list batch不为空,则从列表中dequeue一个batch,送给硬件执行
多线程支持
支持多线程调用,不同应用添加的batch,按照先进先出原则运行。
在luban-lite中,应用是在线程去调用GE的。当某个线程需要等待自己batch是否完成的时候,需要调用IOC_GE_SYNC命令,等待自己所有的batch执行完成即可,如上图所示。 当线程2调用IOC_GE_SYNC命令时,只需要等待batch5、batch3、batch2、batch0完成即可,不用管后边加入的其他的batch6、batch7。
IOC_GE_SYNC的实现:
1.在hal_open创建当前应用的client,并添加到client链表中,在client中有唯一识别id。
struct aic_ge_client {
struct list_head list;
struct list_head buf_list;
int id;
int batch_num;
};
2
3
4
5
6
2.当前应用调用hal_write接口,写入一个batch命令的时候, 当前batch中的client_id会写入对应的client识别id, 并且对应的client中的batch_num引用计数会加1。
3.硬件每执行完成一个batch产生一次中断,在中断服务程序中查询当前batch中的client_id,并通过client_id从client链表中找到
aicos_event_send(data->wait_event, HW_RUNNING_EVENT);
4.应用通过接口IOC_GE_SYNC等待任务完成,只需要等待当前client中的batch_num为0即可。
static int ge_client_sync(struct aic_ge_data *data,
struct aic_ge_client *client)
{
int ret = 0;
uint32_t recved;
hal_log_debug("%s\n", __func__);
while (client->batch_num) {
ret = aicos_event_recv(data->wait_event,
BATCH_NUM_EVENT,
&recved,
GE_TIMEOUT);
if (ret < 0) {
break;
}
}
hal_log_debug("%s\n", __func__);
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
数据结构设计
enum ge_pd_rules
Porter/Duff alpha混合规则枚举
/*
* enum ge_pd_mode - graphics engine Porter/Duff alpha blending rules
*
* pixel = (source * fs + destination * fd)
* sa = source alpha
* da = destination alpha
*
* @GE_PD_NONE: fs: sa fd: 1.0-sa (defaults)
* @GE_PD_CLEAR: fs: 0.0 fd: 0.0
* @GE_PD_SRC: fs: 1.0 fd: 0.0
* @GE_PD_SRC_OVER: fs: 1.0 fd: 1.0-sa
* @GE_PD_DST_OVER: fs: 1.0-da fd: 1.0
* @GE_PD_SRC_IN: fs: da fd: 0.0
* @GE_PD_DST_IN: fs: 0.0 fd: sa
* @GE_PD_SRC_OUT: fs: 1.0-da fd: 0.0
* @GE_PD_DST_OUT: fs: 0.0 fd: 1.0-sa
* @GE_PD_SRC_ATOP: fs: da fd: 1.0-sa
* @GE_PD_DST_ATOP: fs: 1.0-da fd: sa
* @GE_PD_ADD: fs: 1.0 fd: 1.0
* @GE_PD_XOR: fs: 1.0-da fd: 1.0-sa
* @GE_PD_DST: fs: 0.0 fd: 1.0
*/
enum ge_pd_rules {
GE_PD_NONE = 0,
GE_PD_CLEAR = 1,
GE_PD_SRC = 2,
GE_PD_SRC_OVER = 3,
GE_PD_DST_OVER = 4,
GE_PD_SRC_IN = 5,
GE_PD_DST_IN = 6,
GE_PD_SRC_OUT = 7,
GE_PD_DST_OUT = 8,
GE_PD_SRC_ATOP = 9,
GE_PD_DST_ATOP = 10,
GE_PD_ADD = 11,
GE_PD_XOR = 12,
GE_PD_DST = 13,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
struct ge_ctrl
GE控制结构体
/
* struct ge_ctrl - ge ctrl functions
* @alpha_en
* 0: enable Porter/Duff alpha blending
* 1: disable Porter/Duff alpha blending
* @alpha_rules: Porter/Duff alpha blending rules
* @src_alpha_mode: source alpha mode
* 0: pixel alpha mode(src_alpha = src_pixel_alpha)
* 1: global alpha mode(src_alpha = src_global_alpha)
* 2: mixded alpha mode(src_alpha = src_pixel_alpha * src_global_alpha / 255)
* @src_global_alpha: source global alpha value (0~255)
* used by global alpha mode and mixded alpha mode
* @dst_alpha_mode: destination alpha mode
* 0: pixel alpha mode(dst_alpha = dst_pixel_alpha)
* 1: global alpha mode(dst_alpha = dst_global_alpha)
* 2: mixded alpha mode(dst_alpha = dst_pixel_alpha * dst_global_alpha / 255)
* @dst_global_alpha: destination global alpha value (0~255)
* used by global alpha mode and mixed alpha mode
* @ck_en
* 0: disable color key
* 1: enable color key
* @ck_value: rgb value of color key to match the source pixels
* bit[31:24]: reserved
* bit[23:16]: R value
* bit[15:8]: G value
* bit[7:0]: B value
* @dither_en(Not supported by IOC_GE_ROTATE)
* 0: disable dither
* 1: enable dither
* @flags: the flags of some functions, such as scan order, src H/V flip
* and src 90/180/270 degree rotation, the H flip, V flip
* and rotation can be enabled at the same time, the effect
* of flip is in front of rotation, only supported by IOC_GE_BITBLT
* the flags was defined in mpp_types.h
*/
struct ge_ctrl {
unsigned int alpha_en;
enum ge_pd_rules alpha_rules;
unsigned int src_alpha_mode;
unsigned int src_global_alpha;
unsigned int dst_alpha_mode;
unsigned int dst_global_alpha;
unsigned int ck_en;
unsigned int ck_value;
unsigned int dither_en;
unsigned int flags;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
enum ge_fillrect_type
颜色填充类型枚举
/*
* enum ge_fillrect_type - the ge fill rectangle types:
*
* GE_NO_GRADIENT: No gradient is used, only use start_color to
* fill rectangle, ignore end_color
* GE_H_LINEAR_GRADIENT: Interpolates colors between start_color
* and end_color in the horizontal direction
* form left to right
* GE_V_LINEAR_GRADIENT: Interpolates colors between start_color and
* end_color in the vertical direction from top to
* buttom
*/
enum ge_fillrect_type {
GE_NO_GRADIENT = 0,
GE_H_LINEAR_GRADIENT = 1,
GE_V_LINEAR_GRADIENT = 2,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct ge_fillrect
矩形填充结构体
/
* struct ge_fillrect - ge fill rectangle
* @type: fill rect type
* @start_color: start color(32 bits)
* bit[31:24] alpha value
* bit[23:16] r value
* bit[15:8] g value
* bit[7:0] b value
* @end_color: end color(32 bits)
* bit[31:24] alpha value
* bit[23:16] r value
* bit[15:8] g value
* bit[7:0] b value
* @dst_buf: the destination buffer
* @ctrl: ge ctrl functions
*/
struct ge_fillrect {
enum ge_fillrect_type type;
unsigned int start_color;
unsigned int end_color;
struct mpp_buf dst_buf;
struct ge_ctrl ctrl;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct ge_bitblt
位块搬移结构体
/
* struct ge_bitblt - ge bitblt
* @src_buf: the source buffer
* @dst_buf: the destination buffer
* @ctrl: ge ctrl functions
*/
struct ge_bitblt {
struct mpp_buf src_buf;
struct mpp_buf dst_buf;
struct ge_ctrl ctrl;
};
2
3
4
5
6
7
8
9
10
11
struct ge_bitblt
位块搬移结构体
/
* struct ge_rotation - ge rotation
* @src_buf: the source buffer
* @dst_buf: the destination buffer
* @src_rot_center: left-top x/y coordinate of src center
* @dst_rot_center: left-top x/y coordinate of dst center
* @angle_sin: 2.12 fixed point, the sin value of rotation angle
* @angle_cos: 2.12 fixed point, the cos value of rotation angle
* @ctrl: ge ctrl functions
*/
struct ge_rotation {
struct mpp_buf src_buf;
struct mpp_buf dst_buf;
struct mpp_point src_rot_center;
struct mpp_point dst_rot_center;
int angle_sin;
int angle_cos;
struct ge_ctrl ctrl;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum ge_mode
GE模式枚举
enum ge_mode {
GE_MODE_NORMAL,
GE_MODE_CMDQ,
};
2
3
4
接口设计
应用通过hal_ge_open打开GE驱动。
IOC_GE_VERSION
接口语法:
int hal_ge_control(struct aic_ge_client *clt, unsigned long cmd, unsinged int *pversion);
功能说明 | 获取 GE 版本 |
---|---|
参数 | cmd:IOC_GE_VERSION |
pversion: 指向 32bits 无符号版本号指针 | |
返回值 | 0:成功 |
<0:失败 | |
注意事项 | 无 |
IOC_GE_MODE
接口语法:
int hal_ge_control(struct aic_ge_client *clt, unsigned long cmd, enum ge_mode *mode);
功能说明 | 获取工作模式 |
---|---|
参数 | cmd:IOC_GE_MODE |
mode: enum ge_mode 指针 | |
返回值 | 0:成功 |
<0:失败 | |
注意事项 | 无 |
IOC_GE_FILLRECT
接口语法:
int hal_ge_control(struct aic_ge_client *clt, unsigned long cmd, struct ge_fillrect *fill);
功能说明 | 矩形填充 |
---|---|
参数 | cmd:IOC_GE_FILLRECT |
fill: 指向 struct ge_fillrect 指针 | |
返回值 | 0:成功 |
<0:失败 | |
注意事项 | 仅供 normal 模式使用, cmd queue 模式不可用 |
IOC_GE_BITBLT
接口语法:
int hal_ge_control(struct aic_ge_client *clt, unsigned long cmd, struct ge_bitblt *bitblt);
功能说明 | 位块搬移 |
---|---|
参数 | cmd:IOC_GE_BITBLT |
bitblt: 指向 struct ge_bitblt 指针 | |
返回值 | 0:成功 |
<0:失败 | |
注意事项 | 仅供 normal 模式使用, cmd queue 模式不可用 |
IOC_GE_ROTATE
接口语法:
int hal_ge_control(struct aic_ge_client *clt, unsigned long cmd, struct ge_rotation *rot);
功能说明 | 任意角度旋转 |
---|---|
参数 | cmd:IOC_GE_ROTATE |
rot: 指向 struct ge_rotation 指针 | |
返回值 | 0:成功 |
<0:失败 | |
注意事项 | 仅供 normal 模式使用, cmd queue 模式不可用 |
IOC_GE_SYNC
接口语法:
int hal_ge_control(struct aic_ge_client *clt, unsigned long cmd);
功能说明 | 等待任务完成 |
---|---|
参数 | cmd:IOC_GE_SYNC |
返回值 | 0:成功 |
<0:失败 | |
注意事项 | 无 |
IOC_GE_CMD_BUF_SIZE
接口语法:
int ioctl(int fd, unsigned long cmd, unsigned int *size);
功能说明 | 获取 cmd buffer size |
---|---|
参数 | cmd:IOC_GE_CMD_BUF_SIZE |
size: 指向 32bits 无符号数指针 | |
返回值 | 0:成功 |
<0:失败 | |
注意事项 | cmd queue 模式可用,normal 模式不可用 |
MPP对GE接口的封装
MPP对GE接口的封装请参考