上節講到《DRM驅動(四)之ADD_FB》調用drmModeAddFB創建drm_framebuffer。然后通過
drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE,MAP_SHARED, fd, map.offset);
將物理地址map到用戶空間后,就可以在這塊內存上繪一張自己喜歡的圖。接下來就需要把這塊內存配置到硬件上面,進行刷圖。
刷圖有很多中方式,比如:
int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, uint32_t x, uint32_t y, uint32_t *connectors, int count, drmModeModeInfoPtr mode)
fd:文件描述符
crtcId:crtc_id
bufferId:即上節創建的fb id
x,y:在屏幕上的顯示坐標
connectors:connector id
count:connector 數量
mode:包括刷新率,分辨率等timing的信息
也可以使用下面接口:
drmModeAtomicAlloc();
drmModeAtomicAddProperty(..., property_id, property_value);
drmModeAtomicCommit(...);
本節以drmModeSetCrtc為例
int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
?? ??? ? ? uint32_t x, uint32_t y, uint32_t *connectors, int count,
?? ??? ? ? drmModeModeInfoPtr mode)
{
?? ?struct drm_mode_crtc crtc;
?
?? ?memclear(crtc);
?? ?crtc.x ? ? ? ? ? ? = x;
?? ?crtc.y ? ? ? ? ? ? = y;
?? ?crtc.crtc_id ? ? ? = crtcId;
?? ?crtc.fb_id ? ? ? ? = bufferId;
?? ?crtc.set_connectors_ptr = VOID2U64(connectors);
?? ?crtc.count_connectors = count;
?? ?if (mode) {
?? ? ?memcpy(&crtc.mode, mode, sizeof(struct drm_mode_modeinfo));
?? ? ?crtc.mode_valid = 1;
?? ?}
?
?? ?return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
}
主要用于填充如下數據結構,傳給內核,drm會根據傳入的信息配置display硬件。比如crtc_id來指定顯示到哪個crtc,fb_id指定哪塊使用dumb顯存等等。
struct drm_mode_crtc {
__u64 set_connectors_ptr;
__u32 count_connectors;
__u32 crtc_id; /**< Id */
__u32 fb_id; /**< Id of framebuffer */
?
__u32 x; /**< x Position on the framebuffer */
__u32 y; /**< y Position on the framebuffer */
?
__u32 gamma_size;
__u32 mode_valid;
struct drm_mode_modeinfo mode;
}
按照之前的經驗,在內核drm_ioctl.c中找到對應函數調用
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED)
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED)
drm_mode_setcrtc和drm_mode_atomic_ioctl從函數調用來看都會調用drm_atomic_commit(state) 也就是說應用傳入的參數最終會轉換成struct drm_atomic_state;
看下struct drm_atomic_state數據結構
struct drm_atomic_state {
?? ?struct kref ref;
?
?? ?struct drm_device *dev;
?? ?bool allow_modeset : 1;
?? ?bool legacy_cursor_update : 1;
?? ?bool async_update : 1;
?? ?struct __drm_planes_state *planes;
?? ?struct __drm_crtcs_state *crtcs;
?? ?int num_connector;
?? ?struct __drm_connnectors_state *connectors;
?? ?int num_private_objs;
?? ?struct __drm_private_objs_state *private_objs;
?
?? ?struct drm_modeset_acquire_ctx *acquire_ctx;
?
? ? struct work_struct commit_work;
};
drm_atomic_state和其他組件state的繼承關系如下圖:
數據結構之間的轉換過程:
int drm_mode_setcrtc(struct drm_device *dev, void *data,
?? ??? ? ? ? struct drm_file *file_priv)
{
?? ?crtc = drm_crtc_find(dev, file_priv, crtc_req->crtc_id);
?
?? ?if (crtc_req->mode_valid) {
?
?? ??? ?fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id);
?? ??? ?mode = drm_mode_create(dev);
?? ??? ?ret = drm_mode_convert_umode(mode, &crtc_req->mode);
?? ?}
?? ?if (crtc_req->count_connectors > 0) {
?? ??? ?connector_set = kmalloc_array(crtc_req->count_connectors,
?? ??? ??? ??? ??? ? ? ? ?sizeof(struct drm_connector *),
?? ??? ??? ??? ??? ? ? ? ?GFP_KERNEL);
?
?? ??? ?for (i = 0; i < crtc_req->count_connectors; i++) {
?? ??? ??? ?connector_set[i] = NULL;
?? ??? ??? ?set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
?? ??? ??? ?connector = drm_connector_lookup(dev, file_priv, out_id);
?? ??? ??? ?connector_set[i] = connector;
?? ??? ?}
?? ?}
?
?? ?set.crtc = crtc;
?? ?set.x = crtc_req->x;
?? ?set.y = crtc_req->y;
?? ?set.mode = mode;
?? ?set.connectors = connector_set;
?? ?set.num_connectors = crtc_req->count_connectors;
?? ?set.fb = fb;
?? ?ret = __drm_mode_set_config_internal(&set, &ctx);
?
out:
?? ?if (fb)
?? ??? ?drm_framebuffer_put(fb);
?
?? ?if (connector_set) {
?? ??? ?for (i = 0; i < crtc_req->count_connectors; i++) {
?? ??? ??? ?if (connector_set[i])
?? ??? ??? ??? ?drm_connector_put(connector_set[i]);
?? ??? ?}
?? ?}
?? ?kfree(connector_set);
?? ?drm_mode_destroy(dev, mode);
?
?? ?return ret;
}
drm_mode_setcrtc的主要作用:
根據應用傳入的crtc_id找到crtc
根據應用傳入的fb_id,找到對應的drm_framebuffer
根據應用傳入的mode,創建一個drm_display_mode
根據傳入的set_connectors_ptr,找到驅動對應的connector
將以上信息轉為struct drm_mode_set并調用__drm_mode_set_config_internal
static int __drm_mode_set_config_internal(struct drm_mode_set *set,
?? ??? ??? ??? ??? ? ?struct drm_modeset_acquire_ctx *ctx)
{
?? ?struct drm_crtc *crtc = set->crtc;
?? ?struct drm_framebuffer *fb;
?? ?struct drm_crtc *tmp;
?? ?drm_for_each_crtc(tmp, crtc->dev)
?? ??? ?tmp->primary->old_fb = tmp->primary->fb;
?
?? ?fb = set->fb;
?? ?
?? ?ret = drm_atomic_helper_set_config(set, ctx);
?? ?if (ret == 0) {
?? ??? ?crtc->primary->crtc = crtc;
?? ??? ?crtc->primary->fb = fb;
?? ?}
?
?? ?return ret;
}
__drm_mode_set_config_internal的內容比較少,主要調用drm_atomic_helper_set_config
int drm_atomic_helper_set_config(struct drm_mode_set *set,
?? ??? ??? ??? ? struct drm_modeset_acquire_ctx *ctx)
{
?? ?struct drm_atomic_state *state;
?? ?struct drm_crtc *crtc = set->crtc;
?? ?int ret = 0;
?? ?state = drm_atomic_state_alloc(crtc->dev);
?
?? ?ret = __drm_atomic_helper_set_config(set, state);
?
?? ?ret = drm_atomic_commit(state);
fail:
?? ?drm_atomic_state_put(state);
?? ?return ret;
}
drm_atomic_helper_set_config的主要作用:
創建struct drm_atomic_state
將struct drm_mode_set轉換成為struct drm_atomic_state
調用drm_atomic_commit 將修改commit到硬件
int __drm_atomic_helper_set_config(struct drm_mode_set *set,
?? ??? ?struct drm_atomic_state *state)
{
?? ?struct drm_crtc_state *crtc_state;
?? ?struct drm_plane_state *primary_state;
?? ?struct drm_crtc *crtc = set->crtc;
?? ?int hdisplay, vdisplay;
?? ?int ret;
?
?? ?crtc_state = drm_atomic_get_crtc_state(state, crtc);
?
?? ?primary_state = drm_atomic_get_plane_state(state, crtc->primary);
?
?? ?ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
?
?? ?crtc_state->active = true;
?
?? ?ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
?
?? ?drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay);
?
?? ?drm_atomic_set_fb_for_plane(primary_state, set->fb);
?? ?primary_state->crtc_x = 0;
?? ?primary_state->crtc_y = 0;
?? ?primary_state->crtc_w = hdisplay;
?? ?primary_state->crtc_h = vdisplay;
?? ?primary_state->src_x = set->x << 16;
?? ?primary_state->src_y = set->y << 16;
?? ?if (drm_rotation_90_or_270(primary_state->rotation)) {
?? ??? ?primary_state->src_w = vdisplay << 16;
?? ??? ?primary_state->src_h = hdisplay << 16;
?? ?} else {
?? ??? ?primary_state->src_w = hdisplay << 16;
?? ??? ?primary_state->src_h = vdisplay << 16;
?? ?}
?
?? ?ret = update_output_state(state, set);
?
?? ?return 0;
}
這塊比較代碼有點復雜,這里簡單的用一個表格說明幾個比較重要的結構體數據之間的對應關系,不再贅述代碼里的內容。
本節介紹了應用調用drmModeSetCrtc時傳入參數轉換為struct drm_atomic_state的過程,下節將介紹drm利用drm_atomic_state中內容更新圖像的過程。
注:文中代碼僅作說明,刪除了一些錯誤處理等內容,介意可以看下drm驅動的源碼。