diff options
Diffstat (limited to 'target/linux/brcm2708/patches-4.19/950-0655-drm-vc4-Add-support-for-margins-to-fkms.patch')
-rw-r--r-- | target/linux/brcm2708/patches-4.19/950-0655-drm-vc4-Add-support-for-margins-to-fkms.patch | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.19/950-0655-drm-vc4-Add-support-for-margins-to-fkms.patch b/target/linux/brcm2708/patches-4.19/950-0655-drm-vc4-Add-support-for-margins-to-fkms.patch new file mode 100644 index 0000000000..12f1606db0 --- /dev/null +++ b/target/linux/brcm2708/patches-4.19/950-0655-drm-vc4-Add-support-for-margins-to-fkms.patch @@ -0,0 +1,328 @@ +From a4e8051901a5d858a69732a3f9734835afc00af5 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.org> +Date: Fri, 19 Jul 2019 15:35:13 +0100 +Subject: [PATCH] drm/vc4: Add support for margins to fkms + +Allows for overscan to be configured under FKMS. +NB This is rescaling the planes, not reducing the size of the +display mode. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> +--- + drivers/gpu/drm/vc4/vc4_firmware_kms.c | 241 +++++++++++++++++++------ + 1 file changed, 190 insertions(+), 51 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c +@@ -256,6 +256,23 @@ static inline struct vc4_crtc *to_vc4_cr + return container_of(crtc, struct vc4_crtc, base); + } + ++struct vc4_crtc_state { ++ struct drm_crtc_state base; ++ ++ struct { ++ unsigned int left; ++ unsigned int right; ++ unsigned int top; ++ unsigned int bottom; ++ } margins; ++}; ++ ++static inline struct vc4_crtc_state * ++to_vc4_crtc_state(struct drm_crtc_state *crtc_state) ++{ ++ return (struct vc4_crtc_state *)crtc_state; ++} ++ + struct vc4_fkms_encoder { + struct drm_encoder base; + bool hdmi_monitor; +@@ -365,17 +382,127 @@ static int vc4_plane_set_blank(struct dr + return ret; + } + ++static void vc4_fkms_crtc_get_margins(struct drm_crtc_state *state, ++ unsigned int *left, unsigned int *right, ++ unsigned int *top, unsigned int *bottom) ++{ ++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); ++ struct drm_connector_state *conn_state; ++ struct drm_connector *conn; ++ int i; ++ ++ *left = vc4_state->margins.left; ++ *right = vc4_state->margins.right; ++ *top = vc4_state->margins.top; ++ *bottom = vc4_state->margins.bottom; ++ ++ /* We have to interate over all new connector states because ++ * vc4_fkms_crtc_get_margins() might be called before ++ * vc4_fkms_crtc_atomic_check() which means margins info in ++ * vc4_crtc_state might be outdated. ++ */ ++ for_each_new_connector_in_state(state->state, conn, conn_state, i) { ++ if (conn_state->crtc != state->crtc) ++ continue; ++ ++ *left = conn_state->tv.margins.left; ++ *right = conn_state->tv.margins.right; ++ *top = conn_state->tv.margins.top; ++ *bottom = conn_state->tv.margins.bottom; ++ break; ++ } ++} ++ ++static int vc4_fkms_margins_adj(struct drm_plane_state *pstate, ++ struct set_plane *plane) ++{ ++ unsigned int left, right, top, bottom; ++ int adjhdisplay, adjvdisplay; ++ struct drm_crtc_state *crtc_state; ++ ++ crtc_state = drm_atomic_get_new_crtc_state(pstate->state, ++ pstate->crtc); ++ ++ vc4_fkms_crtc_get_margins(crtc_state, &left, &right, &top, &bottom); ++ ++ if (!left && !right && !top && !bottom) ++ return 0; ++ ++ if (left + right >= crtc_state->mode.hdisplay || ++ top + bottom >= crtc_state->mode.vdisplay) ++ return -EINVAL; ++ ++ adjhdisplay = crtc_state->mode.hdisplay - (left + right); ++ plane->dst_x = DIV_ROUND_CLOSEST(plane->dst_x * adjhdisplay, ++ (int)crtc_state->mode.hdisplay); ++ plane->dst_x += left; ++ if (plane->dst_x > (int)(crtc_state->mode.hdisplay - left)) ++ plane->dst_x = crtc_state->mode.hdisplay - left; ++ ++ adjvdisplay = crtc_state->mode.vdisplay - (top + bottom); ++ plane->dst_y = DIV_ROUND_CLOSEST(plane->dst_y * adjvdisplay, ++ (int)crtc_state->mode.vdisplay); ++ plane->dst_y += top; ++ if (plane->dst_y > (int)(crtc_state->mode.vdisplay - top)) ++ plane->dst_y = crtc_state->mode.vdisplay - top; ++ ++ plane->dst_w = DIV_ROUND_CLOSEST(plane->dst_w * adjhdisplay, ++ crtc_state->mode.hdisplay); ++ plane->dst_h = DIV_ROUND_CLOSEST(plane->dst_h * adjvdisplay, ++ crtc_state->mode.vdisplay); ++ ++ if (!plane->dst_w || !plane->dst_h) ++ return -EINVAL; ++ ++ return 0; ++} ++ + static void vc4_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) + { + struct drm_plane_state *state = plane->state; ++ ++ /* ++ * Do NOT set now, as we haven't checked if the crtc is active or not. ++ * Set from vc4_plane_set_blank instead. ++ * ++ * If the CRTC is on (or going to be on) and we're enabled, ++ * then unblank. Otherwise, stay blank until CRTC enable. ++ */ ++ if (state->crtc->state->active) ++ vc4_plane_set_blank(plane, false); ++} ++ ++static void vc4_plane_atomic_disable(struct drm_plane *plane, ++ struct drm_plane_state *old_state) ++{ ++ struct drm_plane_state *state = plane->state; ++ struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane); ++ ++ DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane disable %dx%d@%d +%d,%d\n", ++ plane->base.id, plane->name, ++ state->crtc_w, ++ state->crtc_h, ++ vc4_plane->mb.plane.vc_image_type, ++ state->crtc_x, ++ state->crtc_y); ++ vc4_plane_set_blank(plane, true); ++} ++ ++static bool plane_enabled(struct drm_plane_state *state) ++{ ++ return state->fb && state->crtc; ++} ++ ++static int vc4_plane_to_mb(struct drm_plane *plane, ++ struct mailbox_set_plane *mb, ++ struct drm_plane_state *state) ++{ + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); + const struct drm_format_info *drm_fmt = fb->format; + const struct vc_image_format *vc_fmt = + vc4_get_vc_image_fmt(drm_fmt->format); +- struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane); +- struct mailbox_set_plane *mb = &vc4_plane->mb; + int num_planes = fb->format->num_planes; + struct drm_display_mode *mode = &state->crtc->mode; + unsigned int rotation = SUPPORTED_ROTATIONS; +@@ -417,25 +544,7 @@ static void vc4_plane_atomic_update(stru + break; + } + +- /* FIXME: If the dest rect goes off screen then clip the src rect so we +- * don't have off-screen pixels. +- */ +- if (plane->type == DRM_PLANE_TYPE_CURSOR) { +- /* There is no scaling on the cursor plane, therefore the calcs +- * to alter the source crop as the cursor goes off the screen +- * are simple. +- */ +- if (mb->plane.dst_x + mb->plane.dst_w > mode->hdisplay) { +- mb->plane.dst_w = mode->hdisplay - mb->plane.dst_x; +- mb->plane.src_w = (mode->hdisplay - mb->plane.dst_x) +- << 16; +- } +- if (mb->plane.dst_y + mb->plane.dst_h > mode->vdisplay) { +- mb->plane.dst_h = mode->vdisplay - mb->plane.dst_y; +- mb->plane.src_h = (mode->vdisplay - mb->plane.dst_y) +- << 16; +- } +- } ++ vc4_fkms_margins_adj(state, &mb->plane); + + if (num_planes > 1) { + /* Assume this must be YUV */ +@@ -525,38 +634,19 @@ static void vc4_plane_atomic_update(stru + state->alpha, + state->normalized_zpos); + +- /* +- * Do NOT set now, as we haven't checked if the crtc is active or not. +- * Set from vc4_plane_set_blank instead. +- * +- * If the CRTC is on (or going to be on) and we're enabled, +- * then unblank. Otherwise, stay blank until CRTC enable. +- */ +- if (state->crtc->state->active) +- vc4_plane_set_blank(plane, false); ++ return 0; + } + +-static void vc4_plane_atomic_disable(struct drm_plane *plane, +- struct drm_plane_state *old_state) ++static int vc4_plane_atomic_check(struct drm_plane *plane, ++ struct drm_plane_state *state) + { +- //struct vc4_dev *vc4 = to_vc4_dev(plane->dev); +- struct drm_plane_state *state = plane->state; + struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane); + +- DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane disable %dx%d@%d +%d,%d\n", +- plane->base.id, plane->name, +- state->crtc_w, +- state->crtc_h, +- vc4_plane->mb.plane.vc_image_type, +- state->crtc_x, +- state->crtc_y); +- vc4_plane_set_blank(plane, true); +-} ++ if (!plane_enabled(state)) ++ return 0; ++ ++ return vc4_plane_to_mb(plane, &vc4_plane->mb, state); + +-static int vc4_plane_atomic_check(struct drm_plane *plane, +- struct drm_plane_state *state) +-{ +- return 0; + } + + static void vc4_plane_destroy(struct drm_plane *plane) +@@ -878,8 +968,23 @@ vc4_crtc_mode_valid(struct drm_crtc *crt + static int vc4_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) + { +- DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n", +- crtc->base.id); ++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); ++ struct drm_connector *conn; ++ struct drm_connector_state *conn_state; ++ int i; ++ ++ DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n", crtc->base.id); ++ ++ for_each_new_connector_in_state(state->state, conn, conn_state, i) { ++ if (conn_state->crtc != crtc) ++ continue; ++ ++ vc4_state->margins.left = conn_state->tv.margins.left; ++ vc4_state->margins.right = conn_state->tv.margins.right; ++ vc4_state->margins.top = conn_state->tv.margins.top; ++ vc4_state->margins.bottom = conn_state->tv.margins.bottom; ++ break; ++ } + return 0; + } + +@@ -980,6 +1085,33 @@ static int vc4_page_flip(struct drm_crtc + return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx); + } + ++static struct drm_crtc_state * ++vc4_crtc_duplicate_state(struct drm_crtc *crtc) ++{ ++ struct vc4_crtc_state *vc4_state, *old_vc4_state; ++ ++ vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL); ++ if (!vc4_state) ++ return NULL; ++ ++ old_vc4_state = to_vc4_crtc_state(crtc->state); ++ vc4_state->margins = old_vc4_state->margins; ++ ++ __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base); ++ return &vc4_state->base; ++} ++ ++static void ++vc4_crtc_reset(struct drm_crtc *crtc) ++{ ++ if (crtc->state) ++ __drm_atomic_helper_crtc_destroy_state(crtc->state); ++ ++ crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); ++ if (crtc->state) ++ crtc->state->crtc = crtc; ++} ++ + static int vc4_fkms_enable_vblank(struct drm_crtc *crtc) + { + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); +@@ -1007,8 +1139,8 @@ static const struct drm_crtc_funcs vc4_c + .set_property = NULL, + .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ + .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ +- .reset = drm_atomic_helper_crtc_reset, +- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, ++ .reset = vc4_crtc_reset, ++ .atomic_duplicate_state = vc4_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = vc4_fkms_enable_vblank, + .disable_vblank = vc4_fkms_disable_vblank, +@@ -1267,6 +1399,13 @@ vc4_fkms_connector_init(struct drm_devic + connector->interlace_allowed = 0; + } + ++ /* Create and attach TV margin props to this connector. */ ++ ret = drm_mode_create_tv_margin_properties(dev); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ drm_connector_attach_tv_margin_properties(connector); ++ + connector->polled = (DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT); + |