From 43985820217d2efbb1a9bc2bf91fc3a0e4d19877 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 10:17:08 +0100 Subject: [PATCH 01/16] drm/cirrus-qemu: Remove custom plane state Remove struct cirrus_primary_plane_state and its helpers, which are all unused. Use struct drm_shadow_plane_state instead. Signed-off-by: Thomas Zimmermann Acked-by: Gerd Hoffmann Link: https://lore.kernel.org/r/20250328091821.195061-5-tzimmermann@suse.de --- drivers/gpu/drm/tiny/cirrus-qemu.c | 59 +----------------------------- 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/drivers/gpu/drm/tiny/cirrus-qemu.c b/drivers/gpu/drm/tiny/cirrus-qemu.c index 76744394e2a3..ccf3f6551344 100644 --- a/drivers/gpu/drm/tiny/cirrus-qemu.c +++ b/drivers/gpu/drm/tiny/cirrus-qemu.c @@ -70,16 +70,6 @@ struct cirrus_device { #define to_cirrus(_dev) container_of(_dev, struct cirrus_device, dev) -struct cirrus_primary_plane_state { - struct drm_shadow_plane_state base; -}; - -static inline struct cirrus_primary_plane_state * -to_cirrus_primary_plane_state(struct drm_plane_state *plane_state) -{ - return container_of(plane_state, struct cirrus_primary_plane_state, base.base); -}; - /* ------------------------------------------------------------------ */ /* * The meat of this driver. The core passes us a mode and we have to program @@ -374,58 +364,11 @@ static const struct drm_plane_helper_funcs cirrus_primary_plane_helper_funcs = { .atomic_update = cirrus_primary_plane_helper_atomic_update, }; -static struct drm_plane_state * -cirrus_primary_plane_atomic_duplicate_state(struct drm_plane *plane) -{ - struct drm_plane_state *plane_state = plane->state; - struct cirrus_primary_plane_state *new_primary_plane_state; - struct drm_shadow_plane_state *new_shadow_plane_state; - - if (!plane_state) - return NULL; - - new_primary_plane_state = kzalloc(sizeof(*new_primary_plane_state), GFP_KERNEL); - if (!new_primary_plane_state) - return NULL; - new_shadow_plane_state = &new_primary_plane_state->base; - - __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); - - return &new_shadow_plane_state->base; -} - -static void cirrus_primary_plane_atomic_destroy_state(struct drm_plane *plane, - struct drm_plane_state *plane_state) -{ - struct cirrus_primary_plane_state *primary_plane_state = - to_cirrus_primary_plane_state(plane_state); - - __drm_gem_destroy_shadow_plane_state(&primary_plane_state->base); - kfree(primary_plane_state); -} - -static void cirrus_reset_primary_plane(struct drm_plane *plane) -{ - struct cirrus_primary_plane_state *primary_plane_state; - - if (plane->state) { - cirrus_primary_plane_atomic_destroy_state(plane, plane->state); - plane->state = NULL; /* must be set to NULL here */ - } - - primary_plane_state = kzalloc(sizeof(*primary_plane_state), GFP_KERNEL); - if (!primary_plane_state) - return; - __drm_gem_reset_shadow_plane(plane, &primary_plane_state->base); -} - static const struct drm_plane_funcs cirrus_primary_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, - .reset = cirrus_reset_primary_plane, - .atomic_duplicate_state = cirrus_primary_plane_atomic_duplicate_state, - .atomic_destroy_state = cirrus_primary_plane_atomic_destroy_state, + DRM_GEM_SHADOW_PLANE_FUNCS, }; static int cirrus_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) -- 2.51.0 From c46d18f98261d99711003517c444417a303c7fae Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 15:14:57 +0100 Subject: [PATCH 02/16] drm/format-helper: Move helpers for pixel conversion to header file The DRM draw helpers contain format-conversion helpers that operate on individual pixels. Move them into an internal header file and adopt them as individual API. Update the draw code accordingly. The pixel helpers will also be useful for other format conversion helpers. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20250328141709.217283-2-tzimmermann@suse.de --- drivers/gpu/drm/drm_draw.c | 100 +++------------------- drivers/gpu/drm/drm_format_internal.h | 119 ++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 89 deletions(-) create mode 100644 drivers/gpu/drm/drm_format_internal.h diff --git a/drivers/gpu/drm/drm_draw.c b/drivers/gpu/drm/drm_draw.c index cb2ad12bce57..d41f8ae1c148 100644 --- a/drivers/gpu/drm/drm_draw.c +++ b/drivers/gpu/drm/drm_draw.c @@ -11,85 +11,7 @@ #include #include "drm_draw_internal.h" - -/* - * Conversions from xrgb8888 - */ - -static u16 convert_xrgb8888_to_rgb565(u32 pix) -{ - return ((pix & 0x00F80000) >> 8) | - ((pix & 0x0000FC00) >> 5) | - ((pix & 0x000000F8) >> 3); -} - -static u16 convert_xrgb8888_to_rgba5551(u32 pix) -{ - return ((pix & 0x00f80000) >> 8) | - ((pix & 0x0000f800) >> 5) | - ((pix & 0x000000f8) >> 2) | - BIT(0); /* set alpha bit */ -} - -static u16 convert_xrgb8888_to_xrgb1555(u32 pix) -{ - return ((pix & 0x00f80000) >> 9) | - ((pix & 0x0000f800) >> 6) | - ((pix & 0x000000f8) >> 3); -} - -static u16 convert_xrgb8888_to_argb1555(u32 pix) -{ - return BIT(15) | /* set alpha bit */ - ((pix & 0x00f80000) >> 9) | - ((pix & 0x0000f800) >> 6) | - ((pix & 0x000000f8) >> 3); -} - -static u32 convert_xrgb8888_to_argb8888(u32 pix) -{ - return pix | GENMASK(31, 24); /* fill alpha bits */ -} - -static u32 convert_xrgb8888_to_xbgr8888(u32 pix) -{ - return ((pix & 0x00ff0000) >> 16) << 0 | - ((pix & 0x0000ff00) >> 8) << 8 | - ((pix & 0x000000ff) >> 0) << 16 | - ((pix & 0xff000000) >> 24) << 24; -} - -static u32 convert_xrgb8888_to_abgr8888(u32 pix) -{ - return ((pix & 0x00ff0000) >> 16) << 0 | - ((pix & 0x0000ff00) >> 8) << 8 | - ((pix & 0x000000ff) >> 0) << 16 | - GENMASK(31, 24); /* fill alpha bits */ -} - -static u32 convert_xrgb8888_to_xrgb2101010(u32 pix) -{ - pix = ((pix & 0x000000FF) << 2) | - ((pix & 0x0000FF00) << 4) | - ((pix & 0x00FF0000) << 6); - return pix | ((pix >> 8) & 0x00300C03); -} - -static u32 convert_xrgb8888_to_argb2101010(u32 pix) -{ - pix = ((pix & 0x000000FF) << 2) | - ((pix & 0x0000FF00) << 4) | - ((pix & 0x00FF0000) << 6); - return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03); -} - -static u32 convert_xrgb8888_to_abgr2101010(u32 pix) -{ - pix = ((pix & 0x00FF0000) >> 14) | - ((pix & 0x0000FF00) << 4) | - ((pix & 0x000000FF) << 22); - return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03); -} +#include "drm_format_internal.h" /** * drm_draw_color_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format @@ -104,28 +26,28 @@ u32 drm_draw_color_from_xrgb8888(u32 color, u32 format) { switch (format) { case DRM_FORMAT_RGB565: - return convert_xrgb8888_to_rgb565(color); + return drm_pixel_xrgb8888_to_rgb565(color); case DRM_FORMAT_RGBA5551: - return convert_xrgb8888_to_rgba5551(color); + return drm_pixel_xrgb8888_to_rgba5551(color); case DRM_FORMAT_XRGB1555: - return convert_xrgb8888_to_xrgb1555(color); + return drm_pixel_xrgb8888_to_xrgb1555(color); case DRM_FORMAT_ARGB1555: - return convert_xrgb8888_to_argb1555(color); + return drm_pixel_xrgb8888_to_argb1555(color); case DRM_FORMAT_RGB888: case DRM_FORMAT_XRGB8888: return color; case DRM_FORMAT_ARGB8888: - return convert_xrgb8888_to_argb8888(color); + return drm_pixel_xrgb8888_to_argb8888(color); case DRM_FORMAT_XBGR8888: - return convert_xrgb8888_to_xbgr8888(color); + return drm_pixel_xrgb8888_to_xbgr8888(color); case DRM_FORMAT_ABGR8888: - return convert_xrgb8888_to_abgr8888(color); + return drm_pixel_xrgb8888_to_abgr8888(color); case DRM_FORMAT_XRGB2101010: - return convert_xrgb8888_to_xrgb2101010(color); + return drm_pixel_xrgb8888_to_xrgb2101010(color); case DRM_FORMAT_ARGB2101010: - return convert_xrgb8888_to_argb2101010(color); + return drm_pixel_xrgb8888_to_argb2101010(color); case DRM_FORMAT_ABGR2101010: - return convert_xrgb8888_to_abgr2101010(color); + return drm_pixel_xrgb8888_to_abgr2101010(color); default: WARN_ONCE(1, "Can't convert to %p4cc\n", &format); return 0; diff --git a/drivers/gpu/drm/drm_format_internal.h b/drivers/gpu/drm/drm_format_internal.h new file mode 100644 index 000000000000..5f82f0b9c8e8 --- /dev/null +++ b/drivers/gpu/drm/drm_format_internal.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ + +#ifndef DRM_FORMAT_INTERNAL_H +#define DRM_FORMAT_INTERNAL_H + +#include +#include + +/* + * Each pixel-format conversion helper takes a raw pixel in a + * specific input format and returns a raw pixel in a specific + * output format. All pixels are in little-endian byte order. + * + * Function names are + * + * drm_pixel__to__() + * + * where and refer to pixel formats. The + * is optional and hints to the method used for the + * conversion. Helpers with no algorithm given apply pixel-bit + * shifting. + * + * The argument type is u32. We expect this to be wide enough to + * hold all conversion input from 32-bit RGB to any output format. + * The Linux kernel should avoid format conversion for anything + * but XRGB8888 input data. Converting from other format can still + * be acceptable in some cases. + * + * The return type is u32. It is wide enough to hold all conversion + * output from XRGB8888. For output formats wider than 32 bit, a + * return type of u64 would be acceptable. + */ + +/* + * Conversions from XRGB8888 + */ + +static inline u32 drm_pixel_xrgb8888_to_rgb565(u32 pix) +{ + return ((pix & 0x00f80000) >> 8) | + ((pix & 0x0000fc00) >> 5) | + ((pix & 0x000000f8) >> 3); +} + +static inline u32 drm_pixel_xrgb8888_to_rgbx5551(u32 pix) +{ + return ((pix & 0x00f80000) >> 8) | + ((pix & 0x0000f800) >> 5) | + ((pix & 0x000000f8) >> 2); +} + +static inline u32 drm_pixel_xrgb8888_to_rgba5551(u32 pix) +{ + return drm_pixel_xrgb8888_to_rgbx5551(pix) | + BIT(0); /* set alpha bit */ +} + +static inline u32 drm_pixel_xrgb8888_to_xrgb1555(u32 pix) +{ + return ((pix & 0x00f80000) >> 9) | + ((pix & 0x0000f800) >> 6) | + ((pix & 0x000000f8) >> 3); +} + +static inline u32 drm_pixel_xrgb8888_to_argb1555(u32 pix) +{ + return BIT(15) | /* set alpha bit */ + drm_pixel_xrgb8888_to_xrgb1555(pix); +} + +static inline u32 drm_pixel_xrgb8888_to_argb8888(u32 pix) +{ + return GENMASK(31, 24) | /* fill alpha bits */ + pix; +} + +static inline u32 drm_pixel_xrgb8888_to_xbgr8888(u32 pix) +{ + return ((pix & 0xff000000)) | /* also copy filler bits */ + ((pix & 0x00ff0000) >> 16) | + ((pix & 0x0000ff00)) | + ((pix & 0x000000ff) << 16); +} + +static inline u32 drm_pixel_xrgb8888_to_abgr8888(u32 pix) +{ + return GENMASK(31, 24) | /* fill alpha bits */ + drm_pixel_xrgb8888_to_xbgr8888(pix); +} + +static inline u32 drm_pixel_xrgb8888_to_xrgb2101010(u32 pix) +{ + pix = ((pix & 0x000000ff) << 2) | + ((pix & 0x0000ff00) << 4) | + ((pix & 0x00ff0000) << 6); + return pix | ((pix >> 8) & 0x00300c03); +} + +static inline u32 drm_pixel_xrgb8888_to_argb2101010(u32 pix) +{ + return GENMASK(31, 30) | /* set alpha bits */ + drm_pixel_xrgb8888_to_xrgb2101010(pix); +} + +static inline u32 drm_pixel_xrgb8888_to_xbgr2101010(u32 pix) +{ + pix = ((pix & 0x00ff0000) >> 14) | + ((pix & 0x0000ff00) << 4) | + ((pix & 0x000000ff) << 22); + return pix | ((pix >> 8) & 0x00300c03); +} + +static inline u32 drm_pixel_xrgb8888_to_abgr2101010(u32 pix) +{ + return GENMASK(31, 30) | /* set alpha bits */ + drm_pixel_xrgb8888_to_xbgr2101010(pix); +} + +#endif -- 2.51.0 From d55d0b066f4eedf030c9c1a67a2a0abffece3abc Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 15:14:58 +0100 Subject: [PATCH 03/16] drm/format-helper: Add generic conversion to 32-bit formats Add drm_fb_xfrm_line_32to32() to implement conversion from 32-bit pixels to 32-bit pixels. The pixel-conversion is specified by the given callback parameter. Mark the helper as always_inline to avoid overhead from function calls. Then implement all existing line-conversion functions with the new generic call and the respective pixel-conversion helper. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20250328141709.217283-3-tzimmermann@suse.de --- drivers/gpu/drm/drm_format_helper.c | 84 +++++++---------------------- 1 file changed, 19 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 01d3ab307ac3..abd18c23cfbb 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -20,6 +20,8 @@ #include #include +#include "drm_format_internal.h" + /** * drm_format_conv_state_init - Initialize format-conversion state * @state: The state to initialize @@ -244,6 +246,18 @@ static int drm_fb_xfrm(struct iosys_map *dst, xfrm_line); } +static __always_inline void drm_fb_xfrm_line_32to32(void *dbuf, const void *sbuf, + unsigned int pixels, + u32 (*xfrm_pixel)(u32)) +{ + __le32 *dbuf32 = dbuf; + const __le32 *sbuf32 = sbuf; + const __le32 *send32 = sbuf32 + pixels; + + while (sbuf32 < send32) + *dbuf32++ = cpu_to_le32(xfrm_pixel(le32_to_cpup(sbuf32++))); +} + /** * drm_fb_memcpy - Copy clip buffer * @dst: Array of destination buffers @@ -755,16 +769,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_bgr888); static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le32 *dbuf32 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - pix |= GENMASK(31, 24); /* fill alpha bits */ - dbuf32[x] = cpu_to_le32(pix); - } + drm_fb_xfrm_line_32to32(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_argb8888); } /** @@ -804,19 +809,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb8888); static void drm_fb_xrgb8888_to_abgr8888_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le32 *dbuf32 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - pix = ((pix & 0x00ff0000) >> 16) << 0 | - ((pix & 0x0000ff00) >> 8) << 8 | - ((pix & 0x000000ff) >> 0) << 16 | - GENMASK(31, 24); /* fill alpha bits */ - *dbuf32++ = cpu_to_le32(pix); - } + drm_fb_xfrm_line_32to32(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_abgr8888); } static void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, @@ -835,19 +828,7 @@ static void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned in static void drm_fb_xrgb8888_to_xbgr8888_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le32 *dbuf32 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - pix = ((pix & 0x00ff0000) >> 16) << 0 | - ((pix & 0x0000ff00) >> 8) << 8 | - ((pix & 0x000000ff) >> 0) << 16 | - ((pix & 0xff000000) >> 24) << 24; - *dbuf32++ = cpu_to_le32(pix); - } + drm_fb_xfrm_line_32to32(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_xbgr8888); } static void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, @@ -866,20 +847,7 @@ static void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned in static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le32 *dbuf32 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 val32; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - val32 = ((pix & 0x000000FF) << 2) | - ((pix & 0x0000FF00) << 4) | - ((pix & 0x00FF0000) << 6); - pix = val32 | ((val32 >> 8) & 0x00300C03); - *dbuf32++ = cpu_to_le32(pix); - } + drm_fb_xfrm_line_32to32(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_xrgb2101010); } /** @@ -920,21 +888,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010); static void drm_fb_xrgb8888_to_argb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le32 *dbuf32 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 val32; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - val32 = ((pix & 0x000000ff) << 2) | - ((pix & 0x0000ff00) << 4) | - ((pix & 0x00ff0000) << 6); - pix = GENMASK(31, 30) | /* set alpha bits */ - val32 | ((val32 >> 8) & 0x00300c03); - *dbuf32++ = cpu_to_le32(pix); - } + drm_fb_xfrm_line_32to32(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_argb2101010); } /** -- 2.51.0 From f46bf57299b0a8395263de0bcfcb72a600adaae9 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 15:14:59 +0100 Subject: [PATCH 04/16] drm/format-helper: Add generic conversion to 24-bit formats Add drm_fb_xfrm_line_32to24() to implement conversion from 32-bit pixels to 24-bit pixels. The pixel-conversion is specified by the given callback parameter. Mark the helper as always_inline to avoid overhead from function calls. Then implement all existing line-conversion functions with the new generic call and the respective pixel-conversion helper. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20250328141709.217283-4-tzimmermann@suse.de --- drivers/gpu/drm/drm_format_helper.c | 43 ++++++++++++--------------- drivers/gpu/drm/drm_format_internal.h | 12 ++++++++ 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index abd18c23cfbb..5a2fe3d685a2 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -246,6 +246,23 @@ static int drm_fb_xfrm(struct iosys_map *dst, xfrm_line); } +static __always_inline void drm_fb_xfrm_line_32to24(void *dbuf, const void *sbuf, + unsigned int pixels, + u32 (*xfrm_pixel)(u32)) +{ + u8 *dbuf8 = dbuf; + const __le32 *sbuf32 = sbuf; + const __le32 *send32 = sbuf32 + pixels; + + while (sbuf32 < send32) { + u32 val24 = xfrm_pixel(le32_to_cpup(sbuf32++)); + /* write output in reverse order for little endianness */ + *dbuf8++ = (val24 & 0x000000ff); + *dbuf8++ = (val24 & 0x0000ff00) >> 8; + *dbuf8++ = (val24 & 0x00ff0000) >> 16; + } +} + static __always_inline void drm_fb_xfrm_line_32to32(void *dbuf, const void *sbuf, unsigned int pixels, u32 (*xfrm_pixel)(u32)) @@ -667,18 +684,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgba5551); static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigned int pixels) { - u8 *dbuf8 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - /* write blue-green-red to output in little endianness */ - *dbuf8++ = (pix & 0x000000FF) >> 0; - *dbuf8++ = (pix & 0x0000FF00) >> 8; - *dbuf8++ = (pix & 0x00FF0000) >> 16; - } + drm_fb_xfrm_line_32to24(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_rgb888); } /** @@ -718,18 +724,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); static void drm_fb_xrgb8888_to_bgr888_line(void *dbuf, const void *sbuf, unsigned int pixels) { - u8 *dbuf8 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - /* write red-green-blue to output in little endianness */ - *dbuf8++ = (pix & 0x00ff0000) >> 16; - *dbuf8++ = (pix & 0x0000ff00) >> 8; - *dbuf8++ = (pix & 0x000000ff) >> 0; - } + drm_fb_xfrm_line_32to24(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_bgr888); } /** diff --git a/drivers/gpu/drm/drm_format_internal.h b/drivers/gpu/drm/drm_format_internal.h index 5f82f0b9c8e8..e7fcf260a914 100644 --- a/drivers/gpu/drm/drm_format_internal.h +++ b/drivers/gpu/drm/drm_format_internal.h @@ -68,6 +68,18 @@ static inline u32 drm_pixel_xrgb8888_to_argb1555(u32 pix) drm_pixel_xrgb8888_to_xrgb1555(pix); } +static inline u32 drm_pixel_xrgb8888_to_rgb888(u32 pix) +{ + return pix & GENMASK(23, 0); +} + +static inline u32 drm_pixel_xrgb8888_to_bgr888(u32 pix) +{ + return ((pix & 0x00ff0000) >> 16) | + ((pix & 0x0000ff00)) | + ((pix & 0x000000ff) << 16); +} + static inline u32 drm_pixel_xrgb8888_to_argb8888(u32 pix) { return GENMASK(31, 24) | /* fill alpha bits */ -- 2.51.0 From b39e5ba0ccb4014d94058eda21aec800c647863d Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 15:15:00 +0100 Subject: [PATCH 05/16] drm/format-helper: Add generic conversion to 16-bit formats Add drm_fb_xfrm_line_32to16() to implement conversion from 32-bit pixels to 16-bit pixels. The pixel-conversion is specified by the given callback parameter. Mark the helper as always_inline to avoid overhead from function calls. Then implement all existing line-conversion functions with the new generic call and the respective pixel-conversion helper. There's one pixel-conversion helper that swaps output bytes. It is for gud and requires refactoring, so don't move it into the header file. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20250328141709.217283-5-tzimmermann@suse.de --- drivers/gpu/drm/drm_format_helper.c | 118 +++++--------------------- drivers/gpu/drm/drm_format_internal.h | 12 +++ 2 files changed, 34 insertions(+), 96 deletions(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 5a2fe3d685a2..aecf55c1fc6b 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -246,6 +246,18 @@ static int drm_fb_xfrm(struct iosys_map *dst, xfrm_line); } +static __always_inline void drm_fb_xfrm_line_32to16(void *dbuf, const void *sbuf, + unsigned int pixels, + u32 (*xfrm_pixel)(u32)) +{ + __le16 *dbuf16 = dbuf; + const __le32 *sbuf32 = sbuf; + const __le32 *send32 = sbuf32 + pixels; + + while (sbuf32 < send32) + *dbuf16++ = cpu_to_le16(xfrm_pixel(le32_to_cpup(sbuf32++))); +} + static __always_inline void drm_fb_xfrm_line_32to24(void *dbuf, const void *sbuf, unsigned int pixels, u32 (*xfrm_pixel)(u32)) @@ -448,38 +460,19 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332); static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le16 *dbuf16 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u16 val16; - u32 pix; + drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_rgb565); +} - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - val16 = ((pix & 0x00F80000) >> 8) | - ((pix & 0x0000FC00) >> 5) | - ((pix & 0x000000F8) >> 3); - dbuf16[x] = cpu_to_le16(val16); - } +static __always_inline u32 drm_xrgb8888_to_rgb565_swab(u32 pix) +{ + return swab16(drm_pixel_xrgb8888_to_rgb565(pix)); } /* TODO: implement this helper as conversion to RGB565|BIG_ENDIAN */ static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le16 *dbuf16 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u16 val16; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - val16 = ((pix & 0x00F80000) >> 8) | - ((pix & 0x0000FC00) >> 5) | - ((pix & 0x000000F8) >> 3); - dbuf16[x] = cpu_to_le16(swab16(val16)); - } + drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_xrgb8888_to_rgb565_swab); } /** @@ -526,19 +519,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le16 *dbuf16 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u16 val16; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - val16 = ((pix & 0x00f80000) >> 9) | - ((pix & 0x0000f800) >> 6) | - ((pix & 0x000000f8) >> 3); - dbuf16[x] = cpu_to_le16(val16); - } + drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_xrgb1555); } /** @@ -578,20 +559,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb1555); static void drm_fb_xrgb8888_to_argb1555_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le16 *dbuf16 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u16 val16; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - val16 = BIT(15) | /* set alpha bit */ - ((pix & 0x00f80000) >> 9) | - ((pix & 0x0000f800) >> 6) | - ((pix & 0x000000f8) >> 3); - dbuf16[x] = cpu_to_le16(val16); - } + drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_argb1555); } /** @@ -631,20 +599,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb1555); static void drm_fb_xrgb8888_to_rgba5551_line(void *dbuf, const void *sbuf, unsigned int pixels) { - __le16 *dbuf16 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u16 val16; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - val16 = ((pix & 0x00f80000) >> 8) | - ((pix & 0x0000f800) >> 5) | - ((pix & 0x000000f8) >> 2) | - BIT(0); /* set alpha bit */ - dbuf16[x] = cpu_to_le16(val16); - } + drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_rgba5551); } /** @@ -980,36 +935,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); static void drm_fb_argb8888_to_argb4444_line(void *dbuf, const void *sbuf, unsigned int pixels) { - unsigned int pixels2 = pixels & ~GENMASK_ULL(0, 0); - __le32 *dbuf32 = dbuf; - __le16 *dbuf16 = dbuf + pixels2 * sizeof(*dbuf16); - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 val32; - u16 val16; - u32 pix[2]; - - for (x = 0; x < pixels2; x += 2, ++dbuf32) { - pix[0] = le32_to_cpu(sbuf32[x]); - pix[1] = le32_to_cpu(sbuf32[x + 1]); - val32 = ((pix[0] & 0xf0000000) >> 16) | - ((pix[0] & 0x00f00000) >> 12) | - ((pix[0] & 0x0000f000) >> 8) | - ((pix[0] & 0x000000f0) >> 4) | - ((pix[1] & 0xf0000000) >> 0) | - ((pix[1] & 0x00f00000) << 4) | - ((pix[1] & 0x0000f000) << 8) | - ((pix[1] & 0x000000f0) << 12); - *dbuf32 = cpu_to_le32(val32); - } - for (; x < pixels; x++) { - pix[0] = le32_to_cpu(sbuf32[x]); - val16 = ((pix[0] & 0xf0000000) >> 16) | - ((pix[0] & 0x00f00000) >> 12) | - ((pix[0] & 0x0000f000) >> 8) | - ((pix[0] & 0x000000f0) >> 4); - dbuf16[x] = cpu_to_le16(val16); - } + drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_argb8888_to_argb4444); } /** diff --git a/drivers/gpu/drm/drm_format_internal.h b/drivers/gpu/drm/drm_format_internal.h index e7fcf260a914..4f20b63cb6f6 100644 --- a/drivers/gpu/drm/drm_format_internal.h +++ b/drivers/gpu/drm/drm_format_internal.h @@ -128,4 +128,16 @@ static inline u32 drm_pixel_xrgb8888_to_abgr2101010(u32 pix) drm_pixel_xrgb8888_to_xbgr2101010(pix); } +/* + * Conversion from ARGB8888 + */ + +static inline u32 drm_pixel_argb8888_to_argb4444(u32 pix) +{ + return ((pix & 0xf0000000) >> 16) | + ((pix & 0x00f00000) >> 12) | + ((pix & 0x0000f000) >> 8) | + ((pix & 0x000000f0) >> 4); +} + #endif -- 2.51.0 From a376dcf49c391d06dad31a87e3cdfdf67fecc98d Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 15:15:01 +0100 Subject: [PATCH 06/16] drm/format-helper: Add generic conversion to 8-bit formats Add drm_fb_xfrm_line_32to8() to implement conversion from 32-bit pixels to 8-bit pixels. The pixel-conversion is specified by the given callback parameter. Mark the helper as always_inline to avoid overhead from function calls. Then implement all existing line-conversion functions with the new generic call and the respective pixel-conversion helper. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20250328141709.217283-6-tzimmermann@suse.de --- drivers/gpu/drm/drm_format_helper.c | 38 ++++++++++----------------- drivers/gpu/drm/drm_format_internal.h | 17 ++++++++++++ 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index aecf55c1fc6b..a926aa6671fc 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -246,6 +246,18 @@ static int drm_fb_xfrm(struct iosys_map *dst, xfrm_line); } +static __always_inline void drm_fb_xfrm_line_32to8(void *dbuf, const void *sbuf, + unsigned int pixels, + u32 (*xfrm_pixel)(u32)) +{ + u8 *dbuf8 = dbuf; + const __le32 *sbuf32 = sbuf; + const __le32 *send32 = sbuf32 + pixels; + + while (sbuf32 < send32) + *dbuf8++ = xfrm_pixel(le32_to_cpup(sbuf32++)); +} + static __always_inline void drm_fb_xfrm_line_32to16(void *dbuf, const void *sbuf, unsigned int pixels, u32 (*xfrm_pixel)(u32)) @@ -411,17 +423,7 @@ EXPORT_SYMBOL(drm_fb_swab); static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigned int pixels) { - u8 *dbuf8 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - u32 pix; - - for (x = 0; x < pixels; x++) { - pix = le32_to_cpu(sbuf32[x]); - dbuf8[x] = ((pix & 0x00e00000) >> 16) | - ((pix & 0x0000e000) >> 11) | - ((pix & 0x000000c0) >> 6); - } + drm_fb_xfrm_line_32to8(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_rgb332); } /** @@ -879,19 +881,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb2101010); static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned int pixels) { - u8 *dbuf8 = dbuf; - const __le32 *sbuf32 = sbuf; - unsigned int x; - - for (x = 0; x < pixels; x++) { - u32 pix = le32_to_cpu(sbuf32[x]); - u8 r = (pix & 0x00ff0000) >> 16; - u8 g = (pix & 0x0000ff00) >> 8; - u8 b = pix & 0x000000ff; - - /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ - *dbuf8++ = (3 * r + 6 * g + b) / 10; - } + drm_fb_xfrm_line_32to8(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_r8_bt601); } /** diff --git a/drivers/gpu/drm/drm_format_internal.h b/drivers/gpu/drm/drm_format_internal.h index 4f20b63cb6f6..9f857bfa368d 100644 --- a/drivers/gpu/drm/drm_format_internal.h +++ b/drivers/gpu/drm/drm_format_internal.h @@ -35,6 +35,23 @@ * Conversions from XRGB8888 */ +static inline u32 drm_pixel_xrgb8888_to_r8_bt601(u32 pix) +{ + u32 r = (pix & 0x00ff0000) >> 16; + u32 g = (pix & 0x0000ff00) >> 8; + u32 b = pix & 0x000000ff; + + /* ITU-R BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ + return (3 * r + 6 * g + b) / 10; +} + +static inline u32 drm_pixel_xrgb8888_to_rgb332(u32 pix) +{ + return ((pix & 0x00e00000) >> 16) | + ((pix & 0x0000e000) >> 11) | + ((pix & 0x000000c0) >> 6); +} + static inline u32 drm_pixel_xrgb8888_to_rgb565(u32 pix) { return ((pix & 0x00f80000) >> 8) | -- 2.51.0 From 58523a25cbf728695e374fd90416d9bd4a68d4c0 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 15:15:02 +0100 Subject: [PATCH 07/16] drm/format-helper: Optimize 32-to-24-bpp conversion For ease of implementation, existing line-conversion functions for 24-bit formats write each byte individually. Optimize the performance by writing 4 pixels in 3 32-bit stores. v2: - simplify address calculation (Jani) Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20250328141709.217283-7-tzimmermann@suse.de --- drivers/gpu/drm/drm_format_helper.c | 37 ++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index a926aa6671fc..daf5a6d4f283 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -246,6 +246,9 @@ static int drm_fb_xfrm(struct iosys_map *dst, xfrm_line); } +#define ALIGN_DOWN_PIXELS(end, n, a) \ + ((end) - ((n) & ((a) - 1))) + static __always_inline void drm_fb_xfrm_line_32to8(void *dbuf, const void *sbuf, unsigned int pixels, u32 (*xfrm_pixel)(u32)) @@ -274,10 +277,42 @@ static __always_inline void drm_fb_xfrm_line_32to24(void *dbuf, const void *sbuf unsigned int pixels, u32 (*xfrm_pixel)(u32)) { - u8 *dbuf8 = dbuf; + __le32 *dbuf32 = dbuf; + u8 *dbuf8; const __le32 *sbuf32 = sbuf; const __le32 *send32 = sbuf32 + pixels; + /* write pixels in chunks of 4 */ + while (sbuf32 < ALIGN_DOWN_PIXELS(send32, pixels, 4)) { + u32 val24[4] = { + xfrm_pixel(le32_to_cpup(sbuf32++)), + xfrm_pixel(le32_to_cpup(sbuf32++)), + xfrm_pixel(le32_to_cpup(sbuf32++)), + xfrm_pixel(le32_to_cpup(sbuf32++)), + }; + u32 out32[3] = { + /* write output bytes in reverse order for little endianness */ + ((val24[0] & 0x000000ff)) | + ((val24[0] & 0x0000ff00)) | + ((val24[0] & 0x00ff0000)) | + ((val24[1] & 0x000000ff) << 24), + ((val24[1] & 0x0000ff00) >> 8) | + ((val24[1] & 0x00ff0000) >> 8) | + ((val24[2] & 0x000000ff) << 16) | + ((val24[2] & 0x0000ff00) << 16), + ((val24[2] & 0x00ff0000) >> 16) | + ((val24[3] & 0x000000ff) << 8) | + ((val24[3] & 0x0000ff00) << 8) | + ((val24[3] & 0x00ff0000) << 8), + }; + + *dbuf32++ = cpu_to_le32(out32[0]); + *dbuf32++ = cpu_to_le32(out32[1]); + *dbuf32++ = cpu_to_le32(out32[2]); + } + + /* write trailing pixel */ + dbuf8 = (u8 __force *)dbuf32; while (sbuf32 < send32) { u32 val24 = xfrm_pixel(le32_to_cpup(sbuf32++)); /* write output in reverse order for little endianness */ -- 2.51.0 From 3f31a017ddbca3812247dd6332c21686caf864de Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 15:15:03 +0100 Subject: [PATCH 08/16] drm/format-helper: Optimize 32-to-16-bpp conversion For ease of implementation, existing line-conversion functions for 16-bit formats write each pixel individually. Optimize the performance by writing multiple pixels in single 64-bit and 32-bit stores. v2: - simplify address calculation (Jani) - fix typo in commit message (Jocelyn) Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20250328141709.217283-8-tzimmermann@suse.de --- drivers/gpu/drm/drm_format_helper.c | 37 ++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index daf5a6d4f283..5098a00803d2 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -265,10 +265,45 @@ static __always_inline void drm_fb_xfrm_line_32to16(void *dbuf, const void *sbuf unsigned int pixels, u32 (*xfrm_pixel)(u32)) { - __le16 *dbuf16 = dbuf; + __le64 *dbuf64 = dbuf; + __le32 *dbuf32; + __le16 *dbuf16; const __le32 *sbuf32 = sbuf; const __le32 *send32 = sbuf32 + pixels; +#if defined(CONFIG_64BIT) + /* write 4 pixels at once */ + while (sbuf32 < ALIGN_DOWN_PIXELS(send32, pixels, 4)) { + u32 pix[4] = { + le32_to_cpup(sbuf32++), + le32_to_cpup(sbuf32++), + le32_to_cpup(sbuf32++), + le32_to_cpup(sbuf32++), + }; + /* write output bytes in reverse order for little endianness */ + u64 val64 = ((u64)xfrm_pixel(pix[0])) | + ((u64)xfrm_pixel(pix[1]) << 16) | + ((u64)xfrm_pixel(pix[2]) << 32) | + ((u64)xfrm_pixel(pix[3]) << 48); + *dbuf64++ = cpu_to_le64(val64); + } +#endif + + /* write 2 pixels at once */ + dbuf32 = (__le32 __force *)dbuf64; + while (sbuf32 < ALIGN_DOWN_PIXELS(send32, pixels, 2)) { + u32 pix[2] = { + le32_to_cpup(sbuf32++), + le32_to_cpup(sbuf32++), + }; + /* write output bytes in reverse order for little endianness */ + u32 val32 = xfrm_pixel(pix[0]) | + (xfrm_pixel(pix[1]) << 16); + *dbuf32++ = cpu_to_le32(val32); + } + + /* write trailing pixel */ + dbuf16 = (__le16 __force *)dbuf32; while (sbuf32 < send32) *dbuf16++ = cpu_to_le16(xfrm_pixel(le32_to_cpup(sbuf32++))); } -- 2.51.0 From 65931bbc5177f8192a48a0aa5bdb9b95b9f6e87f Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 28 Mar 2025 15:15:04 +0100 Subject: [PATCH 09/16] drm/format-helper: Optimize 32-to-8-bpp conversion For ease of implementation, existing line-conversion functions for 8-bit formats write each pixel individually. Optimize the performance by writing multiple pixels in a single 32-bit store. v2: - simplify address calculation (Jani) - fix typo in commit message (Jocelyn) Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20250328141709.217283-9-tzimmermann@suse.de --- drivers/gpu/drm/drm_format_helper.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 5098a00803d2..fc7347caf600 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -253,10 +253,29 @@ static __always_inline void drm_fb_xfrm_line_32to8(void *dbuf, const void *sbuf, unsigned int pixels, u32 (*xfrm_pixel)(u32)) { - u8 *dbuf8 = dbuf; + __le32 *dbuf32 = dbuf; + u8 *dbuf8; const __le32 *sbuf32 = sbuf; const __le32 *send32 = sbuf32 + pixels; + /* write 4 pixels at once */ + while (sbuf32 < ALIGN_DOWN_PIXELS(send32, pixels, 4)) { + u32 pix[4] = { + le32_to_cpup(sbuf32++), + le32_to_cpup(sbuf32++), + le32_to_cpup(sbuf32++), + le32_to_cpup(sbuf32++), + }; + /* write output bytes in reverse order for little endianness */ + u32 val32 = xfrm_pixel(pix[0]) | + (xfrm_pixel(pix[1]) << 8) | + (xfrm_pixel(pix[2]) << 16) | + (xfrm_pixel(pix[3]) << 24); + *dbuf32++ = cpu_to_le32(val32); + } + + /* write trailing pixels */ + dbuf8 = (u8 __force *)dbuf32; while (sbuf32 < send32) *dbuf8++ = xfrm_pixel(le32_to_cpup(sbuf32++)); } -- 2.51.0 From ed9c594d495deb676825489032cee0a03ce29bf9 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Mon, 31 Mar 2025 11:15:25 -0400 Subject: [PATCH 10/16] drm/panel: Add new helpers for refcounted panel allocatons Introduce reference counted allocations for panels to avoid use-after-free. The patch adds the macro devm_drm_bridge_alloc() to allocate a new refcounted panel. Followed the documentation for drmm_encoder_alloc() and devm_drm_dev_alloc and other similar implementations for this purpose. Reviewed-by: Maxime Ripard Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250331-b4-panel-refcounting-v4-1-dad50c60c6c9@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_panel.c | 25 +++++++++++++++++++++++++ include/drm/drm_panel.h | 22 ++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index c627e42a7ce7..bdeab5710ee3 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -355,6 +355,31 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) } EXPORT_SYMBOL(of_drm_find_panel); +void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, + const struct drm_panel_funcs *funcs, + int connector_type) +{ + void *container; + struct drm_panel *panel; + + if (!funcs) { + dev_warn(dev, "Missing funcs pointer\n"); + return ERR_PTR(-EINVAL); + } + + container = devm_kzalloc(dev, size, GFP_KERNEL); + if (!container) + return ERR_PTR(-ENOMEM); + + panel = container + offset; + panel->funcs = funcs; + + drm_panel_init(panel, dev, funcs, connector_type); + + return container; +} +EXPORT_SYMBOL(__devm_drm_panel_alloc); + /** * of_drm_get_panel_orientation - look up the orientation of the panel through * the "rotation" binding from a device tree node diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index a9c042c8dea1..415e85e8b76a 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -268,6 +268,28 @@ struct drm_panel { bool enabled; }; +void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, + const struct drm_panel_funcs *funcs, + int connector_type); + +/** + * devm_drm_panel_alloc - Allocate and initialize a refcounted panel. + * + * @dev: struct device of the panel device + * @type: the type of the struct which contains struct &drm_panel + * @member: the name of the &drm_panel within @type + * @funcs: callbacks for this panel + * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to + * the panel interface + * + * Returns: + * Pointer to container structure embedding the panel, ERR_PTR on failure. + */ +#define devm_drm_panel_alloc(dev, type, member, funcs, connector_type) \ + ((type *)__devm_drm_panel_alloc(dev, sizeof(type), \ + offsetof(type, member), funcs, \ + connector_type)) + void drm_panel_init(struct drm_panel *panel, struct device *dev, const struct drm_panel_funcs *funcs, int connector_type); -- 2.51.0 From dcba396f69073d4ea8c04374fdd207e67e5b5a94 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Mon, 31 Mar 2025 11:15:26 -0400 Subject: [PATCH 11/16] drm/panel: Add refcount support Allocate panel via reference counting. Add _get() and _put() helper functions to ensure panel allocations are refcounted. Avoid use after free by ensuring panel pointer is valid and can be usable till the last reference is put. Reviewed-by: Luca Ceresoli Reviewed-by: Maxime Ripard Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250331-b4-panel-refcounting-v4-2-dad50c60c6c9@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_panel.c | 64 ++++++++++++++++++++++++++++++++++++- include/drm/drm_panel.h | 19 +++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index bdeab5710ee3..7b17531d85a4 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -355,24 +355,86 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) } EXPORT_SYMBOL(of_drm_find_panel); +static void __drm_panel_free(struct kref *kref) +{ + struct drm_panel *panel = container_of(kref, struct drm_panel, refcount); + + kfree(panel->container); +} + +/** + * drm_panel_get - Acquire a panel reference + * @panel: DRM panel + * + * This function increments the panel's refcount. + * Returns: + * Pointer to @panel + */ +struct drm_panel *drm_panel_get(struct drm_panel *panel) +{ + if (!panel) + return panel; + + kref_get(&panel->refcount); + + return panel; +} +EXPORT_SYMBOL(drm_panel_get); + +/** + * drm_panel_put - Release a panel reference + * @panel: DRM panel + * + * This function decrements the panel's reference count and frees the + * object if the reference count drops to zero. + */ +void drm_panel_put(struct drm_panel *panel) +{ + if (panel) + kref_put(&panel->refcount, __drm_panel_free); +} +EXPORT_SYMBOL(drm_panel_put); + +/** + * drm_panel_put_void - wrapper to drm_panel_put() taking a void pointer + * + * @data: pointer to @struct drm_panel, cast to a void pointer + * + * Wrapper of drm_panel_put() to be used when a function taking a void + * pointer is needed, for example as a devm action. + */ +static void drm_panel_put_void(void *data) +{ + struct drm_panel *panel = (struct drm_panel *)data; + + drm_panel_put(panel); +} + void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, const struct drm_panel_funcs *funcs, int connector_type) { void *container; struct drm_panel *panel; + int err; if (!funcs) { dev_warn(dev, "Missing funcs pointer\n"); return ERR_PTR(-EINVAL); } - container = devm_kzalloc(dev, size, GFP_KERNEL); + container = kzalloc(size, GFP_KERNEL); if (!container) return ERR_PTR(-ENOMEM); panel = container + offset; + panel->container = container; panel->funcs = funcs; + kref_init(&panel->refcount); + + err = devm_add_action_or_reset(dev, drm_panel_put_void, panel); + if (err) + return ERR_PTR(err); drm_panel_init(panel, dev, funcs, connector_type); diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 415e85e8b76a..31d84f901c51 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -28,6 +28,7 @@ #include #include #include +#include struct backlight_device; struct dentry; @@ -266,6 +267,17 @@ struct drm_panel { * If true then the panel has been enabled. */ bool enabled; + + /** + * @container: Pointer to the private driver struct embedding this + * @struct drm_panel. + */ + void *container; + + /** + * @refcount: reference count of users referencing this panel. + */ + struct kref refcount; }; void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, @@ -282,6 +294,10 @@ void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to * the panel interface * + * The reference count of the returned panel is initialized to 1. This + * reference will be automatically dropped via devm (by calling + * drm_panel_put()) when @dev is removed. + * * Returns: * Pointer to container structure embedding the panel, ERR_PTR on failure. */ @@ -294,6 +310,9 @@ void drm_panel_init(struct drm_panel *panel, struct device *dev, const struct drm_panel_funcs *funcs, int connector_type); +struct drm_panel *drm_panel_get(struct drm_panel *panel); +void drm_panel_put(struct drm_panel *panel); + void drm_panel_add(struct drm_panel *panel); void drm_panel_remove(struct drm_panel *panel); -- 2.51.0 From c5a635f58e4b9cc5d471c67955d02de306b6b27d Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Mon, 31 Mar 2025 11:15:27 -0400 Subject: [PATCH 12/16] drm/panel: deprecate old-style panel allocation Start moving to the new refcounted allocations using the new API devm_drm_panel_alloc(). Deprecate any other allocation. Reviewed-by: Luca Ceresoli Reviewed-by: Maxime Ripard Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250331-b4-panel-refcounting-v4-3-dad50c60c6c9@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_panel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 7b17531d85a4..870bf8d471ee 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -74,8 +74,9 @@ EXPORT_SYMBOL(drm_panel_init); * drm_panel_add - add a panel to the global registry * @panel: panel to add * - * Add a panel to the global registry so that it can be looked up by display - * drivers. + * Add a panel to the global registry so that it can be looked + * up by display drivers. The panel to be added must have been + * allocated by devm_drm_panel_alloc(). */ void drm_panel_add(struct drm_panel *panel) { -- 2.51.0 From de04bb0089a96cc00d13b12cbf66a088befe3057 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Mon, 31 Mar 2025 11:15:28 -0400 Subject: [PATCH 13/16] drm/panel/panel-simple: Use the new allocation in place of devm_kzalloc() Start using the new helper that does the refcounted allocations. Reviewed-by: Luca Ceresoli Reviewed-by: Maxime Ripard Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250331-b4-panel-refcounting-v4-4-dad50c60c6c9@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-simple.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 6ba600f97aa4..df718c4a86cb 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -579,9 +579,10 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) u32 bus_flags; int err; - panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct panel_simple, base, + &panel_simple_funcs, desc->connector_type); + if (IS_ERR(panel)) + return PTR_ERR(panel); panel->desc = desc; @@ -694,8 +695,6 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); - drm_panel_init(&panel->base, dev, &panel_simple_funcs, connector_type); - err = drm_panel_of_backlight(&panel->base); if (err) { dev_err_probe(dev, err, "Could not find backlight\n"); -- 2.51.0 From c2d3a730069545f2cc2d644bfa8b1482e6388826 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 1 Apr 2025 08:57:58 -0700 Subject: [PATCH 14/16] drm/syncobj: Extend EXPORT_SYNC_FILE for timeline syncobjs MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add support for exporting a dma_fence fd for a specific point on a timeline. This is needed for vtest/vpipe[1][2] to implement timeline syncobj support, as it needs a way to turn a point on a timeline back into a dma_fence fd. It also closes an odd omission from the syncobj UAPI. [1] https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33433 [2] https://gitlab.freedesktop.org/virgl/virglrenderer/-/merge_requests/805 v2: Add DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE v3: Add unstaged uabi header hunk v4: Also handle IMPORT_SYNC_FILE case v5: Address comments from Dmitry v6: checkpatch.pl nits Signed-off-by: Rob Clark Reviewed-by: Christian König Reviewed-by: Dmitry Osipenko Link: https://patchwork.freedesktop.org/patch/msgid/20250401155758.48855-1-robdclark@gmail.com Signed-off-by: Christian König --- drivers/gpu/drm/drm_syncobj.c | 47 +++++++++++++++++++++++++++-------- include/uapi/drm/drm.h | 4 +++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 4f2ab8a7b50f..636cd83ca29e 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -741,7 +741,7 @@ static int drm_syncobj_fd_to_handle(struct drm_file *file_private, } static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private, - int fd, int handle) + int fd, int handle, u64 point) { struct dma_fence *fence = sync_file_get_fence(fd); struct drm_syncobj *syncobj; @@ -755,14 +755,24 @@ static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private, return -ENOENT; } - drm_syncobj_replace_fence(syncobj, fence); + if (point) { + struct dma_fence_chain *chain = dma_fence_chain_alloc(); + + if (!chain) + return -ENOMEM; + + drm_syncobj_add_point(syncobj, chain, fence, point); + } else { + drm_syncobj_replace_fence(syncobj, fence); + } + dma_fence_put(fence); drm_syncobj_put(syncobj); return 0; } static int drm_syncobj_export_sync_file(struct drm_file *file_private, - int handle, int *p_fd) + int handle, u64 point, int *p_fd) { int ret; struct dma_fence *fence; @@ -772,7 +782,7 @@ static int drm_syncobj_export_sync_file(struct drm_file *file_private, if (fd < 0) return fd; - ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence); + ret = drm_syncobj_find_fence(file_private, handle, point, 0, &fence); if (ret) goto err_put_fd; @@ -869,6 +879,9 @@ drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_private) { struct drm_syncobj_handle *args = data; + unsigned int valid_flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE | + DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE; + u64 point = 0; if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) return -EOPNOTSUPP; @@ -876,13 +889,18 @@ drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data, if (args->pad) return -EINVAL; - if (args->flags != 0 && - args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE) + if (args->flags & ~valid_flags) return -EINVAL; + if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE) + point = args->point; + if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE) return drm_syncobj_export_sync_file(file_private, args->handle, - &args->fd); + point, &args->fd); + + if (args->point) + return -EINVAL; return drm_syncobj_handle_to_fd(file_private, args->handle, &args->fd); @@ -893,6 +911,9 @@ drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_private) { struct drm_syncobj_handle *args = data; + unsigned int valid_flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE | + DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE; + u64 point = 0; if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) return -EOPNOTSUPP; @@ -900,14 +921,20 @@ drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data, if (args->pad) return -EINVAL; - if (args->flags != 0 && - args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE) + if (args->flags & ~valid_flags) return -EINVAL; + if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE) + point = args->point; + if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE) return drm_syncobj_import_sync_file_fence(file_private, args->fd, - args->handle); + args->handle, + point); + + if (args->point) + return -EINVAL; return drm_syncobj_fd_to_handle(file_private, args->fd, &args->handle); diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 7fba37b94401..e63a71d3c607 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -905,13 +905,17 @@ struct drm_syncobj_destroy { }; #define DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE (1 << 0) +#define DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE (1 << 1) #define DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE (1 << 0) +#define DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE (1 << 1) struct drm_syncobj_handle { __u32 handle; __u32 flags; __s32 fd; __u32 pad; + + __u64 point; }; struct drm_syncobj_transfer { -- 2.51.0 From 6b60c282330c46954be9ae1d33cd5c7e5acb315c Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 1 Apr 2025 16:01:51 +0300 Subject: [PATCH 15/16] MAINTAINERS: Add Dmitry Osipenko as drm/virtio co-maintainer I was helping to co-maintain VirtIO-GPU driver in drm-misc with permission from Gerd Hoffmann for past 2 years and would like to receive new patches directly into my inbox. Add myself as co-maintainer. Signed-off-by: Dmitry Osipenko Reviewed-by: Gerd Hoffmann Reviewed-by: Gurchetan Singh Link: https://lore.kernel.org/r/20250401130151.2238772-1-dmitry.osipenko@collabora.com --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 24e4d90a38d1..048dcf2a3675 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25153,6 +25153,7 @@ F: include/uapi/linux/virtio_gpio.h VIRTIO GPU DRIVER M: David Airlie M: Gerd Hoffmann +M: Dmitry Osipenko R: Gurchetan Singh R: Chia-I Wu L: dri-devel@lists.freedesktop.org -- 2.51.0 From 09cdda7a60f45784cebddf1fa2109d6279f9890b Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Tue, 18 Mar 2025 14:34:35 +0800 Subject: [PATCH 16/16] drm/dp: Pull drm_dp_link_power_up/down from Tegra to common drm_dp_helper The helper functions drm_dp_link_power_up/down were moved to Tegra DRM in commit 9a42c7c647a9 ("drm/tegra: Move drm_dp_link helpers to Tegra DRM")". Now since more and more users are duplicating the same code in their own drivers, it's time to make them as DRM DP common helpers again. Signed-off-by: Andy Yan Acked-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250318063452.4983-1-andyshrk@163.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/drm_dp_helper.c | 69 +++++++++++++++++++++++++ drivers/gpu/drm/tegra/dp.c | 67 ------------------------ drivers/gpu/drm/tegra/dp.h | 2 - drivers/gpu/drm/tegra/sor.c | 4 +- include/drm/display/drm_dp_helper.h | 2 + 5 files changed, 73 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index e2439c8a7fef..57828f2b7b5a 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -831,6 +831,75 @@ int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux, } EXPORT_SYMBOL(drm_dp_dpcd_read_phy_link_status); +/** + * drm_dp_link_power_up() - power up a DisplayPort link + * @aux: DisplayPort AUX channel + * @revision: DPCD revision supported on the link + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_link_power_up(struct drm_dp_aux *aux, unsigned char revision) +{ + u8 value; + int err; + + /* DP_SET_POWER register is only available on DPCD v1.1 and later */ + if (revision < DP_DPCD_REV_11) + return 0; + + err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); + if (err < 0) + return err; + + value &= ~DP_SET_POWER_MASK; + value |= DP_SET_POWER_D0; + + err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); + if (err < 0) + return err; + + /* + * According to the DP 1.1 specification, a "Sink Device must exit the + * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink + * Control Field" (register 0x600). + */ + usleep_range(1000, 2000); + + return 0; +} +EXPORT_SYMBOL(drm_dp_link_power_up); + +/** + * drm_dp_link_power_down() - power down a DisplayPort link + * @aux: DisplayPort AUX channel + * @revision: DPCD revision supported on the link + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_link_power_down(struct drm_dp_aux *aux, unsigned char revision) +{ + u8 value; + int err; + + /* DP_SET_POWER register is only available on DPCD v1.1 and later */ + if (revision < DP_DPCD_REV_11) + return 0; + + err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); + if (err < 0) + return err; + + value &= ~DP_SET_POWER_MASK; + value |= DP_SET_POWER_D3; + + err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(drm_dp_link_power_down); + static int read_payload_update_status(struct drm_dp_aux *aux) { int ret; diff --git a/drivers/gpu/drm/tegra/dp.c b/drivers/gpu/drm/tegra/dp.c index 08fbd8f151a1..990e744b0923 100644 --- a/drivers/gpu/drm/tegra/dp.c +++ b/drivers/gpu/drm/tegra/dp.c @@ -255,73 +255,6 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) return 0; } -/** - * drm_dp_link_power_up() - power up a DisplayPort link - * @aux: DisplayPort AUX channel - * @link: pointer to a structure containing the link configuration - * - * Returns 0 on success or a negative error code on failure. - */ -int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link) -{ - u8 value; - int err; - - /* DP_SET_POWER register is only available on DPCD v1.1 and later */ - if (link->revision < 0x11) - return 0; - - err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); - if (err < 0) - return err; - - value &= ~DP_SET_POWER_MASK; - value |= DP_SET_POWER_D0; - - err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); - if (err < 0) - return err; - - /* - * According to the DP 1.1 specification, a "Sink Device must exit the - * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink - * Control Field" (register 0x600). - */ - usleep_range(1000, 2000); - - return 0; -} - -/** - * drm_dp_link_power_down() - power down a DisplayPort link - * @aux: DisplayPort AUX channel - * @link: pointer to a structure containing the link configuration - * - * Returns 0 on success or a negative error code on failure. - */ -int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link) -{ - u8 value; - int err; - - /* DP_SET_POWER register is only available on DPCD v1.1 and later */ - if (link->revision < 0x11) - return 0; - - err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); - if (err < 0) - return err; - - value &= ~DP_SET_POWER_MASK; - value |= DP_SET_POWER_D3; - - err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); - if (err < 0) - return err; - - return 0; -} - /** * drm_dp_link_configure() - configure a DisplayPort link * @aux: DisplayPort AUX channel diff --git a/drivers/gpu/drm/tegra/dp.h b/drivers/gpu/drm/tegra/dp.h index cb12ed0c54e7..695060cafac0 100644 --- a/drivers/gpu/drm/tegra/dp.h +++ b/drivers/gpu/drm/tegra/dp.h @@ -164,8 +164,6 @@ int drm_dp_link_remove_rate(struct drm_dp_link *link, unsigned long rate); void drm_dp_link_update_rates(struct drm_dp_link *link); int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link); -int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link); -int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link); int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link); int drm_dp_link_choose(struct drm_dp_link *link, const struct drm_display_mode *mode, diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index f98f70eda906..21f3dfdcc5c9 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -2666,7 +2666,7 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder) * the AUX transactions would just be timing out. */ if (output->connector.status != connector_status_disconnected) { - err = drm_dp_link_power_down(sor->aux, &sor->link); + err = drm_dp_link_power_down(sor->aux, sor->link.revision); if (err < 0) dev_err(sor->dev, "failed to power down link: %d\n", err); @@ -2882,7 +2882,7 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder) else dev_dbg(sor->dev, "link training succeeded\n"); - err = drm_dp_link_power_up(sor->aux, &sor->link); + err = drm_dp_link_power_up(sor->aux, sor->link.revision); if (err < 0) dev_err(sor->dev, "failed to power up DP link: %d\n", err); diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 21e22289d1ca..d9614e2c8939 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -654,6 +654,8 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux, enum drm_dp_phy dp_phy, u8 link_status[DP_LINK_STATUS_SIZE]); +int drm_dp_link_power_up(struct drm_dp_aux *aux, unsigned char revision); +int drm_dp_link_power_down(struct drm_dp_aux *aux, unsigned char revision); int drm_dp_dpcd_write_payload(struct drm_dp_aux *aux, int vcpid, u8 start_time_slot, u8 time_slot_count); -- 2.51.0