323 lines
10 KiB
Plaintext
323 lines
10 KiB
Plaintext
From 6eb43deb95d99178419b480181fd18a9b4d9f8fe Mon Sep 17 00:00:00 2001
|
|
From: Paolo Sabatino <paolo.sabatino@gmail.com>
|
|
Date: Sun, 24 Oct 2021 17:06:13 +0000
|
|
Subject: [PATCH] rk3328: hardware dedicated cursor plane
|
|
|
|
---
|
|
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 214 +++++++++++++++++++-
|
|
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 3 +
|
|
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 20 +-
|
|
3 files changed, 234 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
|
|
index f5b9028a1..9a4afba29 100644
|
|
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
|
|
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
|
|
@@ -1104,6 +1104,201 @@ static void vop_plane_atomic_async_update(struct drm_plane *plane,
|
|
}
|
|
}
|
|
|
|
+static void vop_cursor_atomic_update(struct drm_plane *plane,
|
|
+ struct drm_atomic_state *state)
|
|
+{
|
|
+
|
|
+ struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
|
|
+ plane);
|
|
+ struct drm_crtc *crtc = new_state->crtc;
|
|
+ struct vop_win *vop_win = to_vop_win(plane);
|
|
+ const struct vop_win_data *win = vop_win->data;
|
|
+ struct vop *vop = to_vop(new_state->crtc);
|
|
+ struct drm_framebuffer *fb = new_state->fb;
|
|
+ unsigned int actual_h;
|
|
+ unsigned int dsp_stx, dsp_sty;
|
|
+ uint32_t dsp_st;
|
|
+ struct drm_rect *src = &new_state->src;
|
|
+ struct drm_rect *dest = &new_state->dst;
|
|
+ struct drm_gem_object *obj;
|
|
+ struct rockchip_gem_object *rk_obj;
|
|
+ dma_addr_t dma_addr;
|
|
+ uint32_t val;
|
|
+ bool rb_swap;
|
|
+ int win_index = VOP_WIN_TO_INDEX(vop_win);
|
|
+ int format;
|
|
+
|
|
+ /*
|
|
+ * can't update plane when vop is disabled.
|
|
+ */
|
|
+ if (WARN_ON(!crtc))
|
|
+ return;
|
|
+
|
|
+ if (WARN_ON(!vop->is_enabled))
|
|
+ return;
|
|
+
|
|
+ if (!new_state->visible) {
|
|
+ vop_plane_atomic_disable(plane, state);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ obj = fb->obj[0];
|
|
+ rk_obj = to_rockchip_obj(obj);
|
|
+
|
|
+ actual_h = drm_rect_height(src) >> 16;
|
|
+
|
|
+ dsp_stx = dest->x1 + crtc->mode.htotal - crtc->mode.hsync_start;
|
|
+ dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start;
|
|
+ dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
|
|
+
|
|
+ dma_addr = rk_obj->dma_addr;
|
|
+
|
|
+ /*
|
|
+ * For y-mirroring we need to move address
|
|
+ * to the beginning of the last line.
|
|
+ */
|
|
+ if (new_state->rotation & DRM_MODE_REFLECT_Y)
|
|
+ dma_addr += (actual_h - 1) * fb->pitches[0];
|
|
+
|
|
+ spin_lock(&vop->reg_lock);
|
|
+
|
|
+ if (!(vop->win_enabled & BIT(win_index))) {
|
|
+
|
|
+ format = vop_convert_format(fb->format->format);
|
|
+
|
|
+ VOP_WIN_SET(vop, win, format, format);
|
|
+
|
|
+ rb_swap = has_rb_swapped(fb->format->format);
|
|
+ VOP_WIN_SET(vop, win, rb_swap, rb_swap);
|
|
+
|
|
+ /*
|
|
+ * Blending win0 with the background color doesn't seem to work
|
|
+ * correctly. We only get the background color, no matter the contents
|
|
+ * of the win0 framebuffer. However, blending pre-multiplied color
|
|
+ * with the default opaque black default background color is a no-op,
|
|
+ * so we can just disable blending to get the correct result.
|
|
+ */
|
|
+ if (fb->format->has_alpha && win_index > 0) {
|
|
+ VOP_WIN_SET(vop, win, dst_alpha_ctl,
|
|
+ DST_FACTOR_M0(ALPHA_SRC_INVERSE));
|
|
+ val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
|
|
+ SRC_ALPHA_M0(ALPHA_STRAIGHT) |
|
|
+ SRC_BLEND_M0(ALPHA_PER_PIX) |
|
|
+ SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
|
|
+ SRC_FACTOR_M0(ALPHA_ONE);
|
|
+ VOP_WIN_SET(vop, win, src_alpha_ctl, val);
|
|
+
|
|
+ VOP_WIN_SET(vop, win, alpha_pre_mul, ALPHA_SRC_PRE_MUL);
|
|
+ VOP_WIN_SET(vop, win, alpha_mode, ALPHA_PER_PIX);
|
|
+ VOP_WIN_SET(vop, win, alpha_en, 1);
|
|
+ } else {
|
|
+ VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0));
|
|
+ VOP_WIN_SET(vop, win, alpha_en, 0);
|
|
+ }
|
|
+
|
|
+ // 32x32 = 0, 64x64 = 1, 96x96 = 2, 128x128 = 3
|
|
+ VOP_WIN_SET(vop, win, hwc_size, (new_state->crtc_w >> 5) - 1);
|
|
+
|
|
+ VOP_WIN_SET(vop, win, enable, 1);
|
|
+ vop->win_enabled |= BIT(win_index);
|
|
+
|
|
+ }
|
|
+
|
|
+ VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
|
|
+ VOP_WIN_SET(vop, win, dsp_st, dsp_st);
|
|
+
|
|
+ spin_unlock(&vop->reg_lock);
|
|
+
|
|
+}
|
|
+
|
|
+static void vop_cursor_atomic_async_update(struct drm_plane *plane,
|
|
+ struct drm_atomic_state *state)
|
|
+{
|
|
+
|
|
+ struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
|
|
+ plane);
|
|
+ struct vop *vop = to_vop(plane->state->crtc);
|
|
+ struct drm_framebuffer *old_fb = plane->state->fb;
|
|
+
|
|
+ plane->state->crtc_x = new_state->crtc_x;
|
|
+ plane->state->crtc_y = new_state->crtc_y;
|
|
+ plane->state->crtc_h = new_state->crtc_h;
|
|
+ plane->state->crtc_w = new_state->crtc_w;
|
|
+ plane->state->src_x = new_state->src_x;
|
|
+ plane->state->src_y = new_state->src_y;
|
|
+ plane->state->src_h = new_state->src_h;
|
|
+ plane->state->src_w = new_state->src_w;
|
|
+ swap(plane->state->fb, new_state->fb);
|
|
+
|
|
+ if (vop->is_enabled) {
|
|
+ vop_cursor_atomic_update(plane, state);
|
|
+ spin_lock(&vop->reg_lock);
|
|
+ vop_cfg_done(vop);
|
|
+ spin_unlock(&vop->reg_lock);
|
|
+
|
|
+ /*
|
|
+ * A scanout can still be occurring, so we can't drop the
|
|
+ * reference to the old framebuffer. To solve this we get a
|
|
+ * reference to old_fb and set a worker to release it later.
|
|
+ * FIXME: if we perform 500 async_update calls before the
|
|
+ * vblank, then we can have 500 different framebuffers waiting
|
|
+ * to be released.
|
|
+ */
|
|
+ if (old_fb && plane->state->fb != old_fb) {
|
|
+ drm_framebuffer_get(old_fb);
|
|
+ WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0);
|
|
+ drm_flip_work_queue(&vop->fb_unref_work, old_fb);
|
|
+ set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+static int vop_cursor_atomic_check(struct drm_plane *plane,
|
|
+ struct drm_atomic_state *state)
|
|
+{
|
|
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
|
|
+ plane);
|
|
+ struct drm_crtc *crtc = new_plane_state->crtc;
|
|
+ struct drm_crtc_state *crtc_state;
|
|
+ struct drm_framebuffer *fb = new_plane_state->fb;
|
|
+ int ret;
|
|
+
|
|
+ if (!crtc || WARN_ON(!fb))
|
|
+ return 0;
|
|
+
|
|
+ crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
|
|
+ if (WARN_ON(!crtc_state))
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
|
|
+ DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING,
|
|
+ true, true);
|
|
+
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (!new_plane_state->visible)
|
|
+ return 0;
|
|
+
|
|
+ ret = vop_convert_format(fb->format->format);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ if (new_plane_state->crtc_w != new_plane_state->crtc_h)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (new_plane_state->crtc_w != 0 &&
|
|
+ new_plane_state->crtc_w != 32 &&
|
|
+ new_plane_state->crtc_w != 64 &&
|
|
+ new_plane_state->crtc_w != 96 &&
|
|
+ new_plane_state->crtc_w != 128)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
static const struct drm_plane_helper_funcs plane_helper_funcs = {
|
|
.atomic_check = vop_plane_atomic_check,
|
|
.atomic_update = vop_plane_atomic_update,
|
|
@@ -1113,6 +1308,15 @@ static const struct drm_plane_helper_funcs plane_helper_funcs = {
|
|
.prepare_fb = drm_gem_plane_helper_prepare_fb,
|
|
};
|
|
|
|
+static const struct drm_plane_helper_funcs cursor_plane_helper_funcs = {
|
|
+ .atomic_check = vop_cursor_atomic_check,
|
|
+ .atomic_update = vop_cursor_atomic_update,
|
|
+ .atomic_disable = vop_plane_atomic_disable,
|
|
+ .atomic_async_check = vop_plane_atomic_async_check,
|
|
+ .atomic_async_update = vop_cursor_atomic_async_update,
|
|
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
|
|
+};
|
|
+
|
|
static const struct drm_plane_funcs vop_plane_funcs = {
|
|
.update_plane = drm_atomic_helper_update_plane,
|
|
.disable_plane = drm_atomic_helper_disable_plane,
|
|
@@ -1772,6 +1976,7 @@ static int vop_create_crtc(struct vop *vop)
|
|
struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp;
|
|
struct drm_crtc *crtc = &vop->crtc;
|
|
struct device_node *port;
|
|
+ const struct drm_plane_helper_funcs *helper_funcs;
|
|
int ret;
|
|
int i;
|
|
|
|
@@ -1801,7 +2006,14 @@ static int vop_create_crtc(struct vop *vop)
|
|
}
|
|
|
|
plane = &vop_win->base;
|
|
- drm_plane_helper_add(plane, &plane_helper_funcs);
|
|
+ helper_funcs = &plane_helper_funcs;
|
|
+
|
|
+ if ((plane->type == DRM_PLANE_TYPE_CURSOR) && (vop_data->feature & VOP_FEATURE_SPECIAL_CURSOR_PLANE)) {
|
|
+ dev_info(dev, "using dedicated hwcursor plane\n");
|
|
+ helper_funcs = &cursor_plane_helper_funcs;
|
|
+ }
|
|
+
|
|
+ drm_plane_helper_add(plane, helper_funcs);
|
|
vop_plane_add_properties(plane, win_data);
|
|
if (plane->type == DRM_PLANE_TYPE_PRIMARY)
|
|
primary = plane;
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
|
|
index 857d97cdc..7967359c1 100644
|
|
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
|
|
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
|
|
@@ -182,6 +182,8 @@ struct vop_win_phy {
|
|
struct vop_reg alpha_mode;
|
|
struct vop_reg alpha_en;
|
|
struct vop_reg channel;
|
|
+
|
|
+ struct vop_reg hwc_size;
|
|
};
|
|
|
|
struct vop_win_yuv2yuv_data {
|
|
@@ -211,6 +213,7 @@ struct vop_data {
|
|
|
|
#define VOP_FEATURE_OUTPUT_RGB10 BIT(0)
|
|
#define VOP_FEATURE_INTERNAL_RGB BIT(1)
|
|
+#define VOP_FEATURE_SPECIAL_CURSOR_PLANE BIT(2)
|
|
u64 feature;
|
|
};
|
|
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
|
|
index ca7cc8212..56eb4f774 100644
|
|
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
|
|
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
|
|
@@ -1048,18 +1048,34 @@ static const struct vop_intr rk3328_vop_intr = {
|
|
.clear = VOP_REG_MASK_SYNC(RK3328_INTR_CLEAR0, 0xffff, 0),
|
|
};
|
|
|
|
+static const struct vop_win_phy rk3328_cursor_data = {
|
|
+ .data_formats = formats_win_lite,
|
|
+ .nformats = ARRAY_SIZE(formats_win_lite),
|
|
+ .format_modifiers = format_modifiers_win_lite,
|
|
+ .enable = VOP_REG(RK3328_HWC_CTRL0, 0x1, 0),
|
|
+ .format = VOP_REG(RK3328_HWC_CTRL0, 0x7, 1),
|
|
+ .rb_swap = VOP_REG(RK3328_HWC_CTRL0, 0x1, 12),
|
|
+ .dsp_st = VOP_REG(RK3328_HWC_DSP_ST, 0x1fff1fff, 0),
|
|
+ .yrgb_mst = VOP_REG(RK3328_HWC_MST, 0xffffffff, 0),
|
|
+ .src_alpha_ctl = VOP_REG(RK3328_HWC_SRC_ALPHA_CTRL, 0xff, 0),
|
|
+ .dst_alpha_ctl = VOP_REG(RK3328_HWC_DST_ALPHA_CTRL, 0xff, 0),
|
|
+ .hwc_size = VOP_REG(RK3328_HWC_CTRL0, 0x3, 5),
|
|
+};
|
|
+
|
|
static const struct vop_win_data rk3328_vop_win_data[] = {
|
|
{ .base = 0xd0, .phy = &rk3368_win01_data,
|
|
.type = DRM_PLANE_TYPE_PRIMARY },
|
|
{ .base = 0x1d0, .phy = &rk3368_win01_data,
|
|
.type = DRM_PLANE_TYPE_OVERLAY },
|
|
{ .base = 0x2d0, .phy = &rk3368_win01_data,
|
|
- .type = DRM_PLANE_TYPE_CURSOR },
|
|
+ .type = DRM_PLANE_TYPE_OVERLAY },
|
|
+ { .base = 0x00, .phy = &rk3328_cursor_data,
|
|
+ .type = DRM_PLANE_TYPE_CURSOR }
|
|
};
|
|
|
|
static const struct vop_data rk3328_vop = {
|
|
.version = VOP_VERSION(3, 8),
|
|
- .feature = VOP_FEATURE_OUTPUT_RGB10,
|
|
+ .feature = VOP_FEATURE_OUTPUT_RGB10 | VOP_FEATURE_SPECIAL_CURSOR_PLANE,
|
|
.intr = &rk3328_vop_intr,
|
|
.common = &rk3328_common,
|
|
.modeset = &rk3328_modeset,
|
|
--
|
|
2.30.2
|
|
|