From: Łukasz Spintzyk <lukasz.spint...@synaptics.com>

Atomic support for cursor plane was inspired by evdi drm driver that is 
maintained on github.com/displaylink/evdi.
Also added ARGB8888 plane format as it is used by cursor plane.

Signed-off-by: Łukasz Spintzyk <lukasz.spint...@synaptics.com>
---
 drivers/gpu/drm/udl/udl_cursor.c  |  32 +++++++-
 drivers/gpu/drm/udl/udl_cursor.h  |   8 ++
 drivers/gpu/drm/udl/udl_drv.h     |   1 +
 drivers/gpu/drm/udl/udl_modeset.c | 129 +++++++++++++++++++++++++-----
 4 files changed, 150 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/udl/udl_cursor.c b/drivers/gpu/drm/udl/udl_cursor.c
index 594bb3b6b056..d60eccb704f4 100644
--- a/drivers/gpu/drm/udl/udl_cursor.c
+++ b/drivers/gpu/drm/udl/udl_cursor.c
@@ -3,6 +3,7 @@
  * udl_cursor.c
  *
  * Copyright (c) 2015 The Chromium OS Authors
+ * Copyright (c) 2024 Synaptics Incorporated. All Rights Reserved.
  *
  * This program is free software; you can redistribute  it and/or modify it
  * under  the terms of  the GNU General  Public License as published by the
@@ -69,10 +70,39 @@ int udl_cursor_download(struct udl_cursor *cursor,
        return 0;
 }
 
-
 int udl_cursor_move(struct udl_cursor *cursor, int x, int y)
 {
        cursor->x = x;
        cursor->y = y;
        return 0;
 }
+
+void udl_cursor_damage_clear(struct udl_cursor *cursor)
+{
+       cursor->damage.x1 = INT_MAX;
+       cursor->damage.y1 = INT_MAX;
+       cursor->damage.x2 = 0;
+       cursor->damage.y2 = 0;
+}
+
+void udl_rect_merge(struct drm_rect *rect, struct drm_rect *rect2)
+{
+       rect->x1 = min(rect->x1, rect2->x1);
+       rect->y1 = min(rect->y1, rect2->y1);
+       rect->x2 = max(rect->x2, rect2->x2);
+       rect->y2 = max(rect->y2, rect2->y2);
+}
+
+void udl_cursor_mark_damage_from_plane(struct udl_cursor *cursor, struct 
drm_plane_state *state)
+{
+       struct drm_rect rect;
+
+       rect.x1 = (state->crtc_x < 0) ? 0 : state->crtc_x;
+       rect.y1 = (state->crtc_y < 0) ? 0 : state->crtc_y;
+       rect.x2 = state->crtc_x + state->crtc_w;
+       rect.y2 = state->crtc_y + state->crtc_h;
+
+       udl_rect_merge(&cursor->damage, &rect);
+}
+
+
diff --git a/drivers/gpu/drm/udl/udl_cursor.h b/drivers/gpu/drm/udl/udl_cursor.h
index 6a848accc106..2375323bae55 100644
--- a/drivers/gpu/drm/udl/udl_cursor.h
+++ b/drivers/gpu/drm/udl/udl_cursor.h
@@ -3,6 +3,7 @@
  * udl_cursor.h
  *
  * Copyright (c) 2015 The Chromium OS Authors
+ * Copyright (c) 2024 Synaptics Incorporated. All Rights Reserved.
  *
  * This program is free software; you can redistribute  it and/or modify it
  * under  the terms of  the GNU General  Public License as published by the
@@ -27,12 +28,15 @@
 #define UDL_CURSOR_W 64
 #define UDL_CURSOR_H 64
 #define UDL_CURSOR_BUF (UDL_CURSOR_W * UDL_CURSOR_H)
+
 struct udl_cursor {
        uint32_t buffer[UDL_CURSOR_BUF];
+       struct drm_rect damage; // damage on primary
        bool enabled;
        int x;
        int y;
 };
+
 struct udl_cursor_hline {
        uint32_t *buffer;
        int width;
@@ -43,5 +47,9 @@ extern void udl_cursor_get_hline(struct udl_cursor *cursor, 
int x, int y,
                struct udl_cursor_hline *hline);
 extern int udl_cursor_move(struct udl_cursor *cursor, int x, int y);
 extern int udl_cursor_download(struct udl_cursor *cursor, const struct 
iosys_map *map);
+void udl_cursor_damage_clear(struct udl_cursor *cursor);
+void udl_rect_merge(struct drm_rect *rect, struct drm_rect *rect2);
+void udl_cursor_mark_damage_from_plane(struct udl_cursor *cursor,
+               struct drm_plane_state *state);
 
 #endif
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h
index ccd813bec1a9..935bcabcd593 100644
--- a/drivers/gpu/drm/udl/udl_drv.h
+++ b/drivers/gpu/drm/udl/udl_drv.h
@@ -68,6 +68,7 @@ struct udl_device {
        struct device *dmadev;
 
        struct drm_plane primary_plane;
+       struct drm_plane cursor_plane;
        struct drm_crtc crtc;
        struct drm_encoder encoder;
 
diff --git a/drivers/gpu/drm/udl/udl_modeset.c 
b/drivers/gpu/drm/udl/udl_modeset.c
index 21594144fec5..0bd4e2f02dcf 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2009 Roberto De Ioris <robe...@unbit.it>
  * Copyright (C) 2009 Jaya Kumar <jayakumar.l...@gmail.com>
  * Copyright (C) 2009 Bernie Thompson <ber...@plugable.com>
+ * Copyright (c) 2024 Synaptics Incorporated. All Rights Reserved.
  */
 
 #include <linux/bitfield.h>
@@ -202,6 +203,23 @@ static long udl_log_cpp(unsigned int cpp)
        return __ffs(cpp);
 }
 
+static void udl_trim_rect_to_framebuffer(
+                               const struct drm_framebuffer *fb,
+                               struct drm_rect *clip)
+{
+       if (clip->x1 > fb->width)
+               clip->x1 = fb->width;
+
+       if (clip->y1 > fb->height)
+               clip->y1 = fb->height;
+
+       if (clip->x2 > fb->width)
+               clip->x2 = fb->width;
+
+       if (clip->y2 > fb->height)
+               clip->y2 = fb->height;
+}
+
 static int udl_handle_damage(struct drm_framebuffer *fb,
                             const struct iosys_map *map,
                             const struct drm_rect *clip)
@@ -254,20 +272,21 @@ static int udl_handle_damage(struct drm_framebuffer *fb,
 }
 
 /*
- * Primary plane
+ * Primary and cursor planes
  */
 
-static const uint32_t udl_primary_plane_formats[] = {
+static const uint32_t udl_plane_formats[] = {
        DRM_FORMAT_RGB565,
        DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ARGB8888,
 };
 
-static const uint64_t udl_primary_plane_fmtmods[] = {
+static const uint64_t udl_plane_fmtmods[] = {
        DRM_FORMAT_MOD_LINEAR,
        DRM_FORMAT_MOD_INVALID
 };
 
-static int udl_primary_plane_helper_atomic_check(struct drm_plane *plane,
+static int udl_plane_helper_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);
@@ -280,7 +299,36 @@ static int udl_primary_plane_helper_atomic_check(struct 
drm_plane *plane,
        return drm_atomic_helper_check_plane_state(new_plane_state, 
new_crtc_state,
                                                   DRM_PLANE_NO_SCALING,
                                                   DRM_PLANE_NO_SCALING,
-                                                  false, false);
+                                                  plane->type == 
DRM_PLANE_TYPE_CURSOR, false);
+}
+
+static void
+udl_cursor_plane_helper_atomic_update(struct drm_plane *plane,
+                                                  struct drm_atomic_state 
*state)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_plane_state *plane_state = 
drm_atomic_get_new_plane_state(state, plane);
+       struct drm_shadow_plane_state *shadow_plane_state = 
to_drm_shadow_plane_state(plane_state);
+       struct drm_framebuffer *fb = plane_state->fb;
+       struct drm_plane_state *old_plane_state = 
drm_atomic_get_old_plane_state(state, plane);
+       struct udl_device *udl = to_udl(dev);
+       struct udl_cursor *cursor = &udl->cursor;
+
+       WARN_ON(old_plane_state->plane->type != DRM_PLANE_TYPE_CURSOR);
+
+       udl_cursor_move(cursor, plane_state->crtc_x, plane_state->crtc_y);
+       cursor->enabled = fb != NULL;
+
+       udl_cursor_mark_damage_from_plane(&udl->cursor, old_plane_state);
+       udl_cursor_mark_damage_from_plane(&udl->cursor, plane_state);
+
+       if (!fb)
+               return;
+
+       if (plane_state->fb == old_plane_state->fb)
+               return;
+
+       udl_cursor_download(cursor, &shadow_plane_state->data[0]);
 }
 
 static void udl_primary_plane_helper_atomic_update(struct drm_plane *plane,
@@ -291,6 +339,7 @@ static void udl_primary_plane_helper_atomic_update(struct 
drm_plane *plane,
        struct drm_shadow_plane_state *shadow_plane_state = 
to_drm_shadow_plane_state(plane_state);
        struct drm_framebuffer *fb = plane_state->fb;
        struct drm_plane_state *old_plane_state = 
drm_atomic_get_old_plane_state(state, plane);
+       struct udl_device *udl = to_udl(dev);
        struct drm_atomic_helper_damage_iter iter;
        struct drm_rect damage;
        int ret, idx;
@@ -305,24 +354,39 @@ static void udl_primary_plane_helper_atomic_update(struct 
drm_plane *plane,
        if (!drm_dev_enter(dev, &idx))
                goto out_drm_gem_fb_end_cpu_access;
 
-       drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
-       drm_atomic_for_each_plane_damage(&iter, &damage) {
-               udl_handle_damage(fb, &shadow_plane_state->data[0], &damage);
+       if (plane_state->fb != old_plane_state->fb) {
+               drm_atomic_helper_damage_iter_init(&iter, old_plane_state, 
plane_state);
+               drm_atomic_for_each_plane_damage(&iter, &damage)
+                       udl_handle_damage(fb, &shadow_plane_state->data[0], 
&damage);
        }
 
+       udl_trim_rect_to_framebuffer(fb, &udl->cursor.damage);
+       udl_handle_damage(fb, &shadow_plane_state->data[0], 
&udl->cursor.damage);
+       udl_cursor_damage_clear(&udl->cursor);
+
        drm_dev_exit(idx);
 
 out_drm_gem_fb_end_cpu_access:
        drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
 }
 
-static const struct drm_plane_helper_funcs udl_primary_plane_helper_funcs = {
+static void
+udl_plane_helper_atomic_update(struct drm_plane *plane,
+                              struct drm_atomic_state *state)
+{
+       if (plane->type == DRM_PLANE_TYPE_CURSOR)
+               udl_cursor_plane_helper_atomic_update(plane, state);
+       else
+               udl_primary_plane_helper_atomic_update(plane, state);
+}
+
+static const struct drm_plane_helper_funcs udl_plane_helper_funcs = {
        DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
-       .atomic_check = udl_primary_plane_helper_atomic_check,
-       .atomic_update = udl_primary_plane_helper_atomic_update,
+       .atomic_check = udl_plane_helper_atomic_check,
+       .atomic_update = udl_plane_helper_atomic_update,
 };
 
-static const struct drm_plane_funcs udl_primary_plane_funcs = {
+static const struct drm_plane_funcs udl_plane_funcs = {
        .update_plane = drm_atomic_helper_update_plane,
        .disable_plane = drm_atomic_helper_disable_plane,
        .destroy = drm_plane_cleanup,
@@ -393,8 +457,20 @@ static void udl_crtc_helper_atomic_disable(struct drm_crtc 
*crtc, struct drm_ato
        drm_dev_exit(idx);
 }
 
+static int udl_crtc_helper_atomic_check(struct drm_crtc *crtc,
+       struct drm_atomic_state *state)
+{
+       int ret;
+
+       ret = drm_crtc_helper_atomic_check(crtc, state);
+       if (ret)
+               return ret;
+
+       return drm_atomic_add_affected_planes(state, crtc);
+}
+
 static const struct drm_crtc_helper_funcs udl_crtc_helper_funcs = {
-       .atomic_check = drm_crtc_helper_atomic_check,
+       .atomic_check = udl_crtc_helper_atomic_check,
        .atomic_enable = udl_crtc_helper_atomic_enable,
        .atomic_disable = udl_crtc_helper_atomic_disable,
 };
@@ -573,6 +649,7 @@ int udl_modeset_init(struct drm_device *dev)
 {
        struct udl_device *udl = to_udl(dev);
        struct drm_plane *primary_plane;
+       struct drm_plane *cursor_plane;
        struct drm_crtc *crtc;
        struct drm_encoder *encoder;
        struct drm_connector *connector;
@@ -589,20 +666,34 @@ int udl_modeset_init(struct drm_device *dev)
        dev->mode_config.preferred_depth = 16;
        dev->mode_config.funcs = &udl_mode_config_funcs;
 
+       cursor_plane = &udl->cursor_plane;
+       // Add cursor plane first as this is an order of plane atomic_update 
calls
+       // That allows to gather cursor damage before primary plane update
+       ret = drm_universal_plane_init(dev, cursor_plane, 0,
+                                      &udl_plane_funcs,
+                                      udl_plane_formats,
+                                      ARRAY_SIZE(udl_plane_formats),
+                                      udl_plane_fmtmods,
+                                      DRM_PLANE_TYPE_CURSOR, NULL);
+       if (ret)
+               return ret;
+       drm_plane_helper_add(cursor_plane, &udl_plane_helper_funcs);
+
        primary_plane = &udl->primary_plane;
        ret = drm_universal_plane_init(dev, primary_plane, 0,
-                                      &udl_primary_plane_funcs,
-                                      udl_primary_plane_formats,
-                                      ARRAY_SIZE(udl_primary_plane_formats),
-                                      udl_primary_plane_fmtmods,
+                                      &udl_plane_funcs,
+                                      udl_plane_formats,
+                                      ARRAY_SIZE(udl_plane_formats),
+                                      udl_plane_fmtmods,
                                       DRM_PLANE_TYPE_PRIMARY, NULL);
        if (ret)
                return ret;
-       drm_plane_helper_add(primary_plane, &udl_primary_plane_helper_funcs);
+       drm_plane_helper_add(primary_plane, &udl_plane_helper_funcs);
        drm_plane_enable_fb_damage_clips(primary_plane);
 
+
        crtc = &udl->crtc;
-       ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
+       ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, cursor_plane,
                                        &udl_crtc_funcs, NULL);
        if (ret)
                return ret;
-- 
2.34.1

Reply via email to