From 4f864b4d7b292ec45901f1731e6d641588bf0042 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Wed, 24 Jun 2020 11:59:42 +0800 Subject: [PATCH 22/95] backend-drm: Support virtual screen size Support setting virtual screen size, for example: export WESTON_DRM_VIRTUAL_SIZE=1024x768 Signed-off-by: Jeffy Chen --- libweston/backend-drm/drm-internal.h | 12 +++ libweston/backend-drm/drm-kms-enums.h | 10 +++ libweston/backend-drm/drm.c | 20 ++++- libweston/backend-drm/kms.c | 111 +++++++++++++++++++++----- libweston/backend-drm/modes.c | 18 +++++ 5 files changed, 150 insertions(+), 21 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index f8f3d93..24845d2 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -280,6 +280,9 @@ struct drm_backend { drm_head_match_t *head_matches; struct drm_head *primary_head; struct wl_listener output_create_listener; + + int virtual_width; + int virtual_height; }; struct drm_mode { @@ -451,6 +454,8 @@ struct drm_plane { struct wl_list link; struct weston_drm_format_array formats; + + bool can_scale; }; struct drm_connector { @@ -611,6 +616,9 @@ struct drm_output { enum wdrm_content_type content_type; bool state_invalid; + + /* The dummy framebuffer for SET_CRTC. */ + struct drm_fb *fb_dummy; }; void @@ -744,6 +752,10 @@ uint64_t drm_property_get_value(struct drm_property_info *info, const drmModeObjectProperties *props, uint64_t def); +bool +drm_property_has_feature(struct drm_property_info *infos, + const drmModeObjectProperties *props, + enum wdrm_plane_feature feature); uint64_t * drm_property_get_range_values(struct drm_property_info *info, const drmModeObjectProperties *props); diff --git a/libweston/backend-drm/drm-kms-enums.h b/libweston/backend-drm/drm-kms-enums.h index f848ec3..e2b2bc1 100644 --- a/libweston/backend-drm/drm-kms-enums.h +++ b/libweston/backend-drm/drm-kms-enums.h @@ -56,6 +56,7 @@ enum wdrm_plane_property { WDRM_PLANE_ZPOS, WDRM_PLANE_ROTATION, WDRM_PLANE_ALPHA, + WDRM_PLANE_FEATURE, WDRM_PLANE__COUNT }; @@ -82,6 +83,15 @@ enum wdrm_plane_rotation { WDRM_PLANE_ROTATION__COUNT, }; +/** + * Possible values for the WDRM_PLANE_FEATURE property. + */ +enum wdrm_plane_feature { + WDRM_PLANE_FEATURE_SCALE = 0, + WDRM_PLANE_FEATURE_ALPHA, + WDRM_PLANE_FEATURE__COUNT +}; + /** * List of properties attached to a DRM connector */ diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 4a6e30b..20b5be8 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -366,6 +366,11 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags, drm_output_state_free(output->state_last); output->state_last = NULL; + if (output->fb_dummy) { + drm_fb_unref(output->fb_dummy); + output->fb_dummy = NULL; + } + if (output->destroy_pending) { output->destroy_pending = false; output->disable_pending = false; @@ -442,6 +447,7 @@ drm_output_render(struct drm_output_state *state) struct drm_plane *scanout_plane = output->scanout_plane; struct drm_property_info *damage_info = &scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS]; + struct drm_mode *mode; struct drm_fb *fb; pixman_region32_t damage, scanout_damage; pixman_box32_t *rects; @@ -490,10 +496,11 @@ drm_output_render(struct drm_output_state *state) scanout_state->src_w = fb->width << 16; scanout_state->src_h = fb->height << 16; + mode = to_drm_mode(output->base.current_mode); scanout_state->dest_x = 0; scanout_state->dest_y = 0; - scanout_state->dest_w = output->base.current_mode->width; - scanout_state->dest_h = output->base.current_mode->height; + scanout_state->dest_w = mode->mode_info.hdisplay; + scanout_state->dest_h = mode->mode_info.vdisplay; scanout_state->zpos = scanout_plane->zpos_min; @@ -1224,6 +1231,11 @@ drm_plane_create(struct drm_device *device, const drmModePlane *kplane) props, WDRM_PLANE_TYPE__COUNT); + plane->can_scale = + drm_property_has_feature(plane->props, + props, + WDRM_PLANE_FEATURE_SCALE); + zpos_range_values = drm_property_get_range_values(&plane->props[WDRM_PLANE_ZPOS], props); @@ -4278,6 +4290,10 @@ drm_backend_create(struct weston_compositor *compositor, b->head_matches = drm_head_matches[head_mode]; + buf = getenv("WESTON_DRM_VIRTUAL_SIZE"); + if (buf) + sscanf(buf, "%dx%d", &b->virtual_width, &b->virtual_height); + device = zalloc(sizeof *device); if (device == NULL) goto err_backend; diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 30a9519..f974c90 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -81,6 +81,15 @@ struct drm_property_enum_info plane_rotation_enums[] = { }, }; +struct drm_property_enum_info plane_feature_enums[] = { + [WDRM_PLANE_FEATURE_SCALE] = { + .name = "scale", + }, + [WDRM_PLANE_FEATURE_ALPHA] = { + .name = "alpha", + }, +}; + const struct drm_property_info plane_props[] = { [WDRM_PLANE_TYPE] = { .name = "type", @@ -107,6 +116,11 @@ const struct drm_property_info plane_props[] = { .num_enum_values = WDRM_PLANE_ROTATION__COUNT, }, [WDRM_PLANE_ALPHA] = { .name = "alpha" }, + [WDRM_PLANE_FEATURE] = { + .name = "FEATURE", + .enum_values = plane_feature_enums, + .num_enum_values = WDRM_PLANE_FEATURE__COUNT, + }, }; struct drm_property_enum_info dpms_state_enums[] = { @@ -294,6 +308,31 @@ drm_property_get_value(struct drm_property_info *info, return def; } +bool +drm_property_has_feature(struct drm_property_info *infos, + const drmModeObjectProperties *props, + enum wdrm_plane_feature feature) +{ + struct drm_property_info *info = &infos[WDRM_PLANE_FEATURE]; + unsigned int i; + + if (info->prop_id == 0 || + feature >= info->num_enum_values || + !info->enum_values[feature].valid) + return false; + + for (i = 0; i < props->count_props; i++) { + if (props->props[i] != info->prop_id) + continue; + + if (props->prop_values[i] & + (1LL << info->enum_values[feature].value)) + return true; + } + + return false; +} + /** * Get the current range values of a KMS property * @@ -481,9 +520,11 @@ drm_property_info_populate(struct drm_device *device, } if (info[j].num_enum_values == 0 && - (prop->flags & DRM_MODE_PROP_ENUM)) { + (prop->flags & DRM_MODE_PROP_ENUM || + prop->flags & DRM_MODE_PROP_BITMASK)) { weston_log("DRM: expected property %s to not be an" - " enum, but it is; ignoring\n", prop->name); + " enum or bitmask, but it is; ignoring\n", + prop->name); drmModeFreeProperty(prop); continue; } @@ -816,6 +857,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) int n_conn = 0; struct timespec now; int ret = 0; + bool scaling; wl_list_for_each(head, &output->base.head_list, base.output_link) { assert(n_conn < MAX_CLONED_CONNECTORS); @@ -863,30 +905,40 @@ drm_output_apply_state_legacy(struct drm_output_state *state) if (!scanout_state || !scanout_state->fb) return 0; - /* The legacy SetCrtc API doesn't allow us to do scaling, and the - * legacy PageFlip API doesn't allow us to do clipping either. */ - assert(scanout_state->src_x == 0); - assert(scanout_state->src_y == 0); - assert(scanout_state->src_w == - (unsigned) (output->base.current_mode->width << 16)); - assert(scanout_state->src_h == - (unsigned) (output->base.current_mode->height << 16)); - assert(scanout_state->dest_x == 0); - assert(scanout_state->dest_y == 0); - assert(scanout_state->dest_w == scanout_state->src_w >> 16); - assert(scanout_state->dest_h == scanout_state->src_h >> 16); /* The legacy SetCrtc API doesn't support fences */ assert(scanout_state->in_fence_fd == -1); mode = to_drm_mode(output->base.current_mode); + if (output->state_invalid || - !scanout_plane->state_cur->fb || - scanout_plane->state_cur->fb->strides[0] != - scanout_state->fb->strides[0]) { + !scanout_plane->state_cur->fb) { + int fb_id = scanout_state->fb->fb_id; + + if (scanout_state->dest_x || scanout_state->dest_y || + scanout_state->dest_w != mode->mode_info.hdisplay || + scanout_state->dest_h != mode->mode_info.vdisplay || + scanout_state->src_x || scanout_state->src_y || + (scanout_state->src_w >> 16) != scanout_state->dest_w || + (scanout_state->src_h >> 16) != scanout_state->dest_h) { + /* Use a dummy fb for initial mode setting */ + if (output->fb_dummy) + drm_fb_unref(output->fb_dummy); + + output->fb_dummy = + drm_fb_create_dumb(device, + mode->mode_info.hdisplay, + mode->mode_info.vdisplay, + output->format->format); + if (!output->fb_dummy) { + weston_log("failed to create fb_dummy\n"); + goto err; + } + + fb_id = output->fb_dummy->fb_id; + } ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id, - scanout_state->fb->fb_id, - 0, 0, + fb_id, 0, 0, connectors, n_conn, &mode->mode_info); if (ret) { @@ -900,6 +952,27 @@ drm_output_apply_state_legacy(struct drm_output_state *state) drm_output_reset_legacy_gamma(output); } + scaling = scanout_state->src_w >> 16 != scanout_state->dest_w || + scanout_state->src_h >> 16 != scanout_state->dest_h; + if (scaling && !output->scanout_plane->can_scale) { + weston_log("Couldn't do scaling on output %s\n", + output->base.name); + goto err; + } + + ret = drmModeSetPlane(device->drm.fd, + scanout_state->plane->plane_id, + crtc->crtc_id, + scanout_state->fb->fb_id, 0, + scanout_state->dest_x, scanout_state->dest_y, + scanout_state->dest_w, scanout_state->dest_h, + scanout_state->src_x, scanout_state->src_y, + scanout_state->src_w, scanout_state->src_h); + if (ret) { + weston_log("set plane failed: %s\n", strerror(errno)); + goto err; + } + pinfo = scanout_state->fb->format; drm_debug(backend, "\t[CRTC:%u, PLANE:%u] FORMAT: %s\n", crtc->crtc_id, scanout_state->plane->plane_id, diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index aecd406..4f17d7b 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -463,6 +463,7 @@ drm_refresh_rate_mHz(const drmModeModeInfo *info) static struct drm_mode * drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) { + struct drm_backend *b = to_drm_backend(output->base.compositor); struct drm_mode *mode; mode = malloc(sizeof *mode); @@ -473,6 +474,11 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) mode->base.width = info->hdisplay; mode->base.height = info->vdisplay; + if (b->virtual_width && b->virtual_height) { + mode->base.width = b->virtual_width; + mode->base.height = b->virtual_height; + } + mode->base.refresh = drm_refresh_rate_mHz(info); mode->mode_info = *info; mode->blob_id = 0; @@ -519,6 +525,7 @@ drm_output_print_modes(struct drm_output *output) struct weston_mode *m; struct drm_mode *dm; const char *aspect_ratio; + bool virtual_size = false; wl_list_for_each(m, &output->base.mode_list, link) { dm = to_drm_mode(m); @@ -532,7 +539,18 @@ drm_output_print_modes(struct drm_output *output) m->flags & WL_OUTPUT_MODE_CURRENT ? ", current" : "", dm->mode_info.clock / 1000.0); + + if(m->flags & WL_OUTPUT_MODE_CURRENT && + (dm->mode_info.hdisplay != m->width || + dm->mode_info.vdisplay != m->height)) + virtual_size = true; } + + if (virtual_size) + weston_log("Output %s: using virtual size %dx%d\n", + output->base.name, + output->base.current_mode->width, + output->base.current_mode->height); } -- 2.20.1