mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
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>
This commit is contained in:
committed by
Phil Elwell
parent
1aab3cb126
commit
f51a9d2d9d
@@ -256,6 +256,23 @@ static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
|
||||
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 drm_plane *plane, bool blank)
|
||||
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(struct drm_plane *plane,
|
||||
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(struct drm_plane *plane,
|
||||
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);
|
||||
}
|
||||
|
||||
static void vc4_plane_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vc4_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
return 0;
|
||||
struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
|
||||
|
||||
if (!plane_enabled(state))
|
||||
return 0;
|
||||
|
||||
return vc4_plane_to_mb(plane, &vc4_plane->mb, state);
|
||||
|
||||
}
|
||||
|
||||
static void vc4_plane_destroy(struct drm_plane *plane)
|
||||
@@ -877,8 +967,23 @@ vc4_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -979,6 +1084,33 @@ static int vc4_page_flip(struct drm_crtc *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);
|
||||
@@ -1006,8 +1138,8 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = {
|
||||
.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,
|
||||
@@ -1266,6 +1398,13 @@ vc4_fkms_connector_init(struct drm_device *dev, struct drm_encoder *encoder,
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user