diff mbox series

[2/4] drm/udl: Add cursor drm_plane support

Message ID 20240624071041.5087-3-lukasz.spintzyk@synaptics.com (mailing list archive)
State New, archived
Headers show
Series [1/4] drm/udl: Port CrOS cursor blending on primary plane in usb transfer | expand

Commit Message

Łukasz Spintzyk June 24, 2024, 7:10 a.m. UTC
From: Łukasz Spintzyk <Lukasz.Spintzyk@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.Spintzyk@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 mbox series

Patch

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 <roberto@unbit.it>
  * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
  * Copyright (C) 2009 Bernie Thompson <bernie@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;