From: Łukasz Spintzyk <Lukasz.Spintzyk@xxxxxxxxxxxxx> 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@xxxxxxxxxxxxx> --- 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 <roberto@xxxxxxxx> * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@xxxxxxxxx> * Copyright (C) 2009 Bernie Thompson <bernie@xxxxxxxxxxxx> + * 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