From 19f4da84b695fa84b42de017ebe6018a0195425d Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 17 Feb 2025 13:22:06 +0100 Subject: [PATCH 01/16] drm/ast: cursor: Add support for ARGB4444 Add support for cursor image data in ARGB4444 format. This is the hardware's native format and requires no conversion. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://patchwork.freedesktop.org/patch/msgid/20250217122336.230067-4-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_mode.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 8ab006e6bc32..91da54a4d8a6 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -720,6 +720,7 @@ static void ast_set_cursor_enabled(struct ast_device *ast, bool enabled) } static const uint32_t ast_cursor_plane_formats[] = { + DRM_FORMAT_ARGB4444, DRM_FORMAT_ARGB8888, }; @@ -771,17 +772,28 @@ static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane, */ if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) { - u8 *argb4444 = ast_cursor_plane->argb4444; - struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = { - IOSYS_MAP_INIT_VADDR(argb4444), - }; - unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = { - AST_HWC_PITCH, - }; - - drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch, - shadow_plane_state->data, fb, &damage, - &shadow_plane_state->fmtcnv_state); + u8 *argb4444; + + switch (fb->format->format) { + case DRM_FORMAT_ARGB4444: + argb4444 = shadow_plane_state->data[0].vaddr; + break; + default: + argb4444 = ast_cursor_plane->argb4444; + { + struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = { + IOSYS_MAP_INIT_VADDR(argb4444), + }; + unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = { + AST_HWC_PITCH, + }; + + drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch, + shadow_plane_state->data, fb, &damage, + &shadow_plane_state->fmtcnv_state); + } + break; + } ast_set_cursor_image(ast, argb4444, fb->width, fb->height); ast_set_cursor_base(ast, dst_off); } -- 2.51.0 From e82e1a0c22d841f379b1c768469dcdaae650e443 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 17 Feb 2025 13:22:07 +0100 Subject: [PATCH 02/16] drm/ast: cursor: Move implementation to separate source file Move the cursor code into a separate source file for readability. No functional changes. v2: - include Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://patchwork.freedesktop.org/patch/msgid/20250217122336.230067-5-tzimmermann@suse.de --- drivers/gpu/drm/ast/Makefile | 1 + drivers/gpu/drm/ast/ast_cursor.c | 309 +++++++++++++++++++++++++++++++ drivers/gpu/drm/ast/ast_drv.h | 24 +-- drivers/gpu/drm/ast/ast_mode.c | 277 +-------------------------- 4 files changed, 330 insertions(+), 281 deletions(-) create mode 100644 drivers/gpu/drm/ast/ast_cursor.c diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile index 3107ea9c7bf5..8d09ba5d5889 100644 --- a/drivers/gpu/drm/ast/Makefile +++ b/drivers/gpu/drm/ast/Makefile @@ -4,6 +4,7 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. ast-y := \ + ast_cursor.o \ ast_ddc.o \ ast_dp501.o \ ast_dp.o \ diff --git a/drivers/gpu/drm/ast/ast_cursor.c b/drivers/gpu/drm/ast/ast_cursor.c new file mode 100644 index 000000000000..139ab00dee8f --- /dev/null +++ b/drivers/gpu/drm/ast/ast_cursor.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: MIT +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "ast_drv.h" + +/* + * Hardware cursor + */ + +/* define for signature structure */ +#define AST_HWC_SIGNATURE_CHECKSUM 0x00 +#define AST_HWC_SIGNATURE_SizeX 0x04 +#define AST_HWC_SIGNATURE_SizeY 0x08 +#define AST_HWC_SIGNATURE_X 0x0C +#define AST_HWC_SIGNATURE_Y 0x10 +#define AST_HWC_SIGNATURE_HOTSPOTX 0x14 +#define AST_HWC_SIGNATURE_HOTSPOTY 0x18 + +static u32 ast_cursor_calculate_checksum(const void *src, unsigned int width, unsigned int height) +{ + u32 csum = 0; + unsigned int one_pixel_copy = width & BIT(0); + unsigned int two_pixel_copy = width - one_pixel_copy; + unsigned int trailing_bytes = (AST_MAX_HWC_WIDTH - width) * sizeof(u16); + unsigned int x, y; + + for (y = 0; y < height; y++) { + for (x = 0; x < two_pixel_copy; x += 2) { + const u32 *src32 = (const u32 *)src; + + csum += *src32; + src += SZ_4; + } + if (one_pixel_copy) { + const u16 *src16 = (const u16 *)src; + + csum += *src16; + src += SZ_2; + } + src += trailing_bytes; + } + + return csum; +} + +static void ast_set_cursor_image(struct ast_device *ast, const u8 *src, + unsigned int width, unsigned int height) +{ + u8 __iomem *dst = ast->cursor_plane.base.vaddr; + u32 csum; + + csum = ast_cursor_calculate_checksum(src, width, height); + + /* write pixel data */ + memcpy_toio(dst, src, AST_HWC_SIZE); + + /* write checksum + signature */ + dst += AST_HWC_SIZE; + writel(csum, dst); + writel(width, dst + AST_HWC_SIGNATURE_SizeX); + writel(height, dst + AST_HWC_SIGNATURE_SizeY); + writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX); + writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY); +} + +static void ast_set_cursor_base(struct ast_device *ast, u64 address) +{ + u8 addr0 = (address >> 3) & 0xff; + u8 addr1 = (address >> 11) & 0xff; + u8 addr2 = (address >> 19) & 0xff; + + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc8, addr0); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc9, addr1); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xca, addr2); +} + +static void ast_set_cursor_location(struct ast_device *ast, u16 x, u16 y, + u8 x_offset, u8 y_offset) +{ + u8 x0 = (x & 0x00ff); + u8 x1 = (x & 0x0f00) >> 8; + u8 y0 = (y & 0x00ff); + u8 y1 = (y & 0x0700) >> 8; + + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc2, x_offset); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc3, y_offset); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc4, x0); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc5, x1); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc6, y0); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc7, y1); +} + +static void ast_set_cursor_enabled(struct ast_device *ast, bool enabled) +{ + static const u8 mask = (u8)~(AST_IO_VGACRCB_HWC_16BPP | + AST_IO_VGACRCB_HWC_ENABLED); + + u8 vgacrcb = AST_IO_VGACRCB_HWC_16BPP; + + if (enabled) + vgacrcb |= AST_IO_VGACRCB_HWC_ENABLED; + + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xcb, mask, vgacrcb); +} + +/* + * Cursor plane + */ + +static const uint32_t ast_cursor_plane_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB8888, +}; + +static int ast_cursor_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); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_crtc_state *new_crtc_state = NULL; + int ret; + + if (new_plane_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); + if (ret || !new_plane_state->visible) + return ret; + + if (new_fb->width > AST_MAX_HWC_WIDTH || new_fb->height > AST_MAX_HWC_HEIGHT) + return -EINVAL; + + return 0; +} + +static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct ast_cursor_plane *ast_cursor_plane = to_ast_cursor_plane(plane); + struct ast_plane *ast_plane = to_ast_plane(plane); + 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 ast_device *ast = to_ast_device(plane->dev); + struct drm_rect damage; + u64 dst_off = ast_plane->offset; + u8 __iomem *dst = ast_plane->vaddr; /* TODO: Use mapping abstraction properly */ + u8 __iomem *sig = dst + AST_HWC_SIZE; /* TODO: Use mapping abstraction properly */ + unsigned int offset_x, offset_y; + u16 x, y; + u8 x_offset, y_offset; + + /* + * Do data transfer to hardware buffer and point the scanout + * engine to the offset. + */ + + if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) { + u8 *argb4444; + + switch (fb->format->format) { + case DRM_FORMAT_ARGB4444: + argb4444 = shadow_plane_state->data[0].vaddr; + break; + default: + argb4444 = ast_cursor_plane->argb4444; + { + struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = { + IOSYS_MAP_INIT_VADDR(argb4444), + }; + unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = { + AST_HWC_PITCH, + }; + + drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch, + shadow_plane_state->data, fb, &damage, + &shadow_plane_state->fmtcnv_state); + } + break; + } + ast_set_cursor_image(ast, argb4444, fb->width, fb->height); + ast_set_cursor_base(ast, dst_off); + } + + /* + * Update location in HWC signature and registers. + */ + + writel(plane_state->crtc_x, sig + AST_HWC_SIGNATURE_X); + writel(plane_state->crtc_y, sig + AST_HWC_SIGNATURE_Y); + + offset_x = AST_MAX_HWC_WIDTH - fb->width; + offset_y = AST_MAX_HWC_HEIGHT - fb->height; + + if (plane_state->crtc_x < 0) { + x_offset = (-plane_state->crtc_x) + offset_x; + x = 0; + } else { + x_offset = offset_x; + x = plane_state->crtc_x; + } + if (plane_state->crtc_y < 0) { + y_offset = (-plane_state->crtc_y) + offset_y; + y = 0; + } else { + y_offset = offset_y; + y = plane_state->crtc_y; + } + + ast_set_cursor_location(ast, x, y, x_offset, y_offset); + + /* Dummy write to enable HWC and make the HW pick-up the changes. */ + ast_set_cursor_enabled(ast, true); +} + +static void ast_cursor_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct ast_device *ast = to_ast_device(plane->dev); + + ast_set_cursor_enabled(ast, false); +} + +static const struct drm_plane_helper_funcs ast_cursor_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = ast_cursor_plane_helper_atomic_check, + .atomic_update = ast_cursor_plane_helper_atomic_update, + .atomic_disable = ast_cursor_plane_helper_atomic_disable, +}; + +static const struct drm_plane_funcs ast_cursor_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, +}; + +int ast_cursor_plane_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct ast_cursor_plane *ast_cursor_plane = &ast->cursor_plane; + struct ast_plane *ast_plane = &ast_cursor_plane->base; + struct drm_plane *cursor_plane = &ast_plane->base; + size_t size; + void __iomem *vaddr; + u64 offset; + int ret; + + /* + * Allocate backing storage for cursors. The BOs are permanently + * pinned to the top end of the VRAM. + */ + + size = roundup(AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE, PAGE_SIZE); + + if (ast->vram_fb_available < size) + return -ENOMEM; + + vaddr = ast->vram + ast->vram_fb_available - size; + offset = ast->vram_fb_available - size; + + ret = ast_plane_init(dev, ast_plane, vaddr, offset, size, + 0x01, &ast_cursor_plane_funcs, + ast_cursor_plane_formats, ARRAY_SIZE(ast_cursor_plane_formats), + NULL, DRM_PLANE_TYPE_CURSOR); + if (ret) { + drm_err(dev, "ast_plane_init() failed: %d\n", ret); + return ret; + } + drm_plane_helper_add(cursor_plane, &ast_cursor_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(cursor_plane); + + ast->vram_fb_available -= size; + + return 0; +} diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 973abd0cbd42..d2c2605d2728 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -118,15 +118,6 @@ enum ast_config_mode { #define AST_HWC_SIGNATURE_SIZE 32 -/* define for signature structure */ -#define AST_HWC_SIGNATURE_CHECKSUM 0x00 -#define AST_HWC_SIGNATURE_SizeX 0x04 -#define AST_HWC_SIGNATURE_SizeY 0x08 -#define AST_HWC_SIGNATURE_X 0x0C -#define AST_HWC_SIGNATURE_Y 0x10 -#define AST_HWC_SIGNATURE_HOTSPOTX 0x14 -#define AST_HWC_SIGNATURE_HOTSPOTY 0x18 - /* * Planes */ @@ -383,8 +374,6 @@ struct ast_crtc_state { #define to_ast_crtc_state(state) container_of(state, struct ast_crtc_state, base) -int ast_mode_config_init(struct ast_device *ast); - #define AST_MM_ALIGN_SHIFT 4 #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) @@ -450,6 +439,9 @@ void ast_patch_ahb_2500(void __iomem *regs); int ast_vga_output_init(struct ast_device *ast); int ast_sil164_output_init(struct ast_device *ast); +/* ast_cursor.c */ +int ast_cursor_plane_init(struct ast_device *ast); + /* ast dp501 */ bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size); void ast_init_3rdtx(struct ast_device *ast); @@ -459,4 +451,14 @@ int ast_dp501_output_init(struct ast_device *ast); int ast_dp_launch(struct ast_device *ast); int ast_astdp_output_init(struct ast_device *ast); +/* ast_mode.c */ +int ast_mode_config_init(struct ast_device *ast); +int ast_plane_init(struct drm_device *dev, struct ast_plane *ast_plane, + void __iomem *vaddr, u64 offset, unsigned long size, + uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type); + #endif diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 91da54a4d8a6..c3b950675485 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -438,13 +438,13 @@ static void ast_wait_for_vretrace(struct ast_device *ast) * Planes */ -static int ast_plane_init(struct drm_device *dev, struct ast_plane *ast_plane, - void __iomem *vaddr, u64 offset, unsigned long size, - uint32_t possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, unsigned int format_count, - const uint64_t *format_modifiers, - enum drm_plane_type type) +int ast_plane_init(struct drm_device *dev, struct ast_plane *ast_plane, + void __iomem *vaddr, u64 offset, unsigned long size, + uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type) { struct drm_plane *plane = &ast_plane->base; @@ -628,269 +628,6 @@ static int ast_primary_plane_init(struct ast_device *ast) return 0; } -/* - * Cursor plane - */ - -static u32 ast_cursor_calculate_checksum(const void *src, unsigned int width, unsigned int height) -{ - u32 csum = 0; - unsigned int one_pixel_copy = width & BIT(0); - unsigned int two_pixel_copy = width - one_pixel_copy; - unsigned int trailing_bytes = (AST_MAX_HWC_WIDTH - width) * sizeof(u16); - unsigned int x, y; - - for (y = 0; y < height; y++) { - for (x = 0; x < two_pixel_copy; x += 2) { - const u32 *src32 = (const u32 *)src; - - csum += *src32; - src += SZ_4; - } - if (one_pixel_copy) { - const u16 *src16 = (const u16 *)src; - - csum += *src16; - src += SZ_2; - } - src += trailing_bytes; - } - - return csum; -} - -static void ast_set_cursor_image(struct ast_device *ast, const u8 *src, - unsigned int width, unsigned int height) -{ - u8 __iomem *dst = ast->cursor_plane.base.vaddr; - u32 csum; - - csum = ast_cursor_calculate_checksum(src, width, height); - - /* write pixel data */ - memcpy_toio(dst, src, AST_HWC_SIZE); - - /* write checksum + signature */ - dst += AST_HWC_SIZE; - writel(csum, dst); - writel(width, dst + AST_HWC_SIGNATURE_SizeX); - writel(height, dst + AST_HWC_SIGNATURE_SizeY); - writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX); - writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY); -} - -static void ast_set_cursor_base(struct ast_device *ast, u64 address) -{ - u8 addr0 = (address >> 3) & 0xff; - u8 addr1 = (address >> 11) & 0xff; - u8 addr2 = (address >> 19) & 0xff; - - ast_set_index_reg(ast, AST_IO_VGACRI, 0xc8, addr0); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xc9, addr1); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xca, addr2); -} - -static void ast_set_cursor_location(struct ast_device *ast, u16 x, u16 y, - u8 x_offset, u8 y_offset) -{ - u8 x0 = (x & 0x00ff); - u8 x1 = (x & 0x0f00) >> 8; - u8 y0 = (y & 0x00ff); - u8 y1 = (y & 0x0700) >> 8; - - ast_set_index_reg(ast, AST_IO_VGACRI, 0xc2, x_offset); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xc3, y_offset); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xc4, x0); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xc5, x1); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xc6, y0); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xc7, y1); -} - -static void ast_set_cursor_enabled(struct ast_device *ast, bool enabled) -{ - static const u8 mask = (u8)~(AST_IO_VGACRCB_HWC_16BPP | - AST_IO_VGACRCB_HWC_ENABLED); - - u8 vgacrcb = AST_IO_VGACRCB_HWC_16BPP; - - if (enabled) - vgacrcb |= AST_IO_VGACRCB_HWC_ENABLED; - - ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xcb, mask, vgacrcb); -} - -static const uint32_t ast_cursor_plane_formats[] = { - DRM_FORMAT_ARGB4444, - DRM_FORMAT_ARGB8888, -}; - -static int ast_cursor_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); - struct drm_framebuffer *new_fb = new_plane_state->fb; - struct drm_crtc_state *new_crtc_state = NULL; - int ret; - - if (new_plane_state->crtc) - new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); - - ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, - DRM_PLANE_NO_SCALING, - DRM_PLANE_NO_SCALING, - true, true); - if (ret || !new_plane_state->visible) - return ret; - - if (new_fb->width > AST_MAX_HWC_WIDTH || new_fb->height > AST_MAX_HWC_HEIGHT) - return -EINVAL; - - return 0; -} - -static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane, - struct drm_atomic_state *state) -{ - struct ast_cursor_plane *ast_cursor_plane = to_ast_cursor_plane(plane); - struct ast_plane *ast_plane = to_ast_plane(plane); - 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 ast_device *ast = to_ast_device(plane->dev); - struct drm_rect damage; - u64 dst_off = ast_plane->offset; - u8 __iomem *dst = ast_plane->vaddr; /* TODO: Use mapping abstraction properly */ - u8 __iomem *sig = dst + AST_HWC_SIZE; /* TODO: Use mapping abstraction properly */ - unsigned int offset_x, offset_y; - u16 x, y; - u8 x_offset, y_offset; - - /* - * Do data transfer to hardware buffer and point the scanout - * engine to the offset. - */ - - if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) { - u8 *argb4444; - - switch (fb->format->format) { - case DRM_FORMAT_ARGB4444: - argb4444 = shadow_plane_state->data[0].vaddr; - break; - default: - argb4444 = ast_cursor_plane->argb4444; - { - struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = { - IOSYS_MAP_INIT_VADDR(argb4444), - }; - unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = { - AST_HWC_PITCH, - }; - - drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch, - shadow_plane_state->data, fb, &damage, - &shadow_plane_state->fmtcnv_state); - } - break; - } - ast_set_cursor_image(ast, argb4444, fb->width, fb->height); - ast_set_cursor_base(ast, dst_off); - } - - /* - * Update location in HWC signature and registers. - */ - - writel(plane_state->crtc_x, sig + AST_HWC_SIGNATURE_X); - writel(plane_state->crtc_y, sig + AST_HWC_SIGNATURE_Y); - - offset_x = AST_MAX_HWC_WIDTH - fb->width; - offset_y = AST_MAX_HWC_HEIGHT - fb->height; - - if (plane_state->crtc_x < 0) { - x_offset = (-plane_state->crtc_x) + offset_x; - x = 0; - } else { - x_offset = offset_x; - x = plane_state->crtc_x; - } - if (plane_state->crtc_y < 0) { - y_offset = (-plane_state->crtc_y) + offset_y; - y = 0; - } else { - y_offset = offset_y; - y = plane_state->crtc_y; - } - - ast_set_cursor_location(ast, x, y, x_offset, y_offset); - - /* Dummy write to enable HWC and make the HW pick-up the changes. */ - ast_set_cursor_enabled(ast, true); -} - -static void ast_cursor_plane_helper_atomic_disable(struct drm_plane *plane, - struct drm_atomic_state *state) -{ - struct ast_device *ast = to_ast_device(plane->dev); - - ast_set_cursor_enabled(ast, false); -} - -static const struct drm_plane_helper_funcs ast_cursor_plane_helper_funcs = { - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, - .atomic_check = ast_cursor_plane_helper_atomic_check, - .atomic_update = ast_cursor_plane_helper_atomic_update, - .atomic_disable = ast_cursor_plane_helper_atomic_disable, -}; - -static const struct drm_plane_funcs ast_cursor_plane_funcs = { - .update_plane = drm_atomic_helper_update_plane, - .disable_plane = drm_atomic_helper_disable_plane, - .destroy = drm_plane_cleanup, - DRM_GEM_SHADOW_PLANE_FUNCS, -}; - -static int ast_cursor_plane_init(struct ast_device *ast) -{ - struct drm_device *dev = &ast->base; - struct ast_cursor_plane *ast_cursor_plane = &ast->cursor_plane; - struct ast_plane *ast_plane = &ast_cursor_plane->base; - struct drm_plane *cursor_plane = &ast_plane->base; - size_t size; - void __iomem *vaddr; - u64 offset; - int ret; - - /* - * Allocate backing storage for cursors. The BOs are permanently - * pinned to the top end of the VRAM. - */ - - size = roundup(AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE, PAGE_SIZE); - - if (ast->vram_fb_available < size) - return -ENOMEM; - - vaddr = ast->vram + ast->vram_fb_available - size; - offset = ast->vram_fb_available - size; - - ret = ast_plane_init(dev, ast_plane, vaddr, offset, size, - 0x01, &ast_cursor_plane_funcs, - ast_cursor_plane_formats, ARRAY_SIZE(ast_cursor_plane_formats), - NULL, DRM_PLANE_TYPE_CURSOR); - if (ret) { - drm_err(dev, "ast_plane_init() failed: %d\n", ret); - return ret; - } - drm_plane_helper_add(cursor_plane, &ast_cursor_plane_helper_funcs); - drm_plane_enable_fb_damage_clips(cursor_plane); - - ast->vram_fb_available -= size; - - return 0; -} - /* * CRTC */ -- 2.51.0 From ddd147d91d509c9d9fc6159efc5b56f61440bb9a Mon Sep 17 00:00:00 2001 From: Louis Chauvet Date: Fri, 7 Feb 2025 18:35:22 +0100 Subject: [PATCH 03/16] drm: writeback: Fix kernel doc name During the creation of drmm_ variants for writeback connector, one function was renamed, but not the kernel doc. To remove the warning, use the proper name in kernel doc. Fixes: 135d8fc7af44 ("drm: writeback: Create an helper for drm_writeback_connector initialization") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/all/20250207142201.550ce870@canb.auug.org.au/ Reviewed-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20250207-b4-fix-warning-v1-1-b4964beb60a3@bootlin.com Signed-off-by: Louis Chauvet --- drivers/gpu/drm/drm_writeback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c index f139b49af4c9..edbeab88ff2b 100644 --- a/drivers/gpu/drm/drm_writeback.c +++ b/drivers/gpu/drm/drm_writeback.c @@ -213,7 +213,7 @@ static void delete_writeback_properties(struct drm_device *dev) } /** - * drm_writeback_connector_init_with_encoder - Initialize a writeback connector with + * __drm_writeback_connector_init - Initialize a writeback connector with * a custom encoder * * @dev: DRM device -- 2.51.0 From 4ce2c7e201c265df1c62a9190a98a98803208b8f Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 17 Feb 2025 12:04:28 +0000 Subject: [PATCH 04/16] drm/panel: ilitek-ili9882t: fix GPIO name in error message This driver uses the enable-gpios property and it is confusing that the error message refers to reset-gpios. Use the correct name when the enable GPIO is not found. Fixes: e2450d32e5fb5 ("drm/panel: ili9882t: Break out as separate driver") Signed-off-by: John Keeping Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20250217120428.3779197-1-jkeeping@inmusicbrands.com --- drivers/gpu/drm/panel/panel-ilitek-ili9882t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c index 266a087fe14c..3c24a63b6be8 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c @@ -607,7 +607,7 @@ static int ili9882t_add(struct ili9882t *ili) ili->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(ili->enable_gpio)) { - dev_err(dev, "cannot get reset-gpios %ld\n", + dev_err(dev, "cannot get enable-gpios %ld\n", PTR_ERR(ili->enable_gpio)); return PTR_ERR(ili->enable_gpio); } -- 2.51.0 From 60341a6d79aa5e18a9c4ad8d7193e1ec6f8741b0 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Thu, 20 Feb 2025 15:04:06 +0100 Subject: [PATCH 05/16] drm/atomic-helper: Add a note in drm_atomic_helper_reset_crtc() kernel-doc As suggested in [0], add a note indicating that drm_atomic_helper_reset_crtc() can be a no-op in some cases. [0]:https://lore.kernel.org/all/Z7XfnPGDYspwG42y@phenom.ffwll.local/ Signed-off-by: Herve Codina Reviewed-by: Simona Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20250220140406.593314-1-herve.codina@bootlin.com Signed-off-by: Louis Chauvet --- drivers/gpu/drm/drm_atomic_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 7a25e70694ba..5302ab324898 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3409,6 +3409,10 @@ EXPORT_SYMBOL(drm_atomic_helper_disable_all); * This implies a reset of all active components available between the CRTC and * connectors. * + * NOTE: This relies on resetting &drm_crtc_state.connectors_changed. + * For drivers which optimize out unnecessary modesets this will result in + * a no-op commit, achieving nothing. + * * Returns: * 0 on success or a negative error code on failure. */ -- 2.51.0 From 27e21f22db9993769b5c983113ccffcac65d772a Mon Sep 17 00:00:00 2001 From: Jeff Hugo Date: Wed, 19 Feb 2025 14:41:12 -0700 Subject: [PATCH 06/16] MAINTAINERS: Update my email address Qualcomm is migrating away from quicinc.com email addresses towards ones with *.qualcomm.com. Signed-off-by: Jeff Hugo Reviewed-by: Bjorn Andersson Link: https://patchwork.freedesktop.org/patch/msgid/20250219214112.2168604-1-jeff.hugo@oss.qualcomm.com --- .mailmap | 3 ++- MAINTAINERS | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index ae0adc499f4a..f4b927e48ad1 100644 --- a/.mailmap +++ b/.mailmap @@ -320,7 +320,8 @@ Jeff Garzik Jeff Layton Jeff Layton Jeff Layton -Jeffrey Hugo +Jeff Hugo +Jeff Hugo Jens Axboe Jens Axboe Jens Axboe diff --git a/MAINTAINERS b/MAINTAINERS index 950e8b7c0805..815a28c7e6fc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19426,7 +19426,7 @@ F: drivers/clk/qcom/ F: include/dt-bindings/clock/qcom,* QUALCOMM CLOUD AI (QAIC) DRIVER -M: Jeffrey Hugo +M: Jeff Hugo R: Carl Vanderlip L: linux-arm-msm@vger.kernel.org L: dri-devel@lists.freedesktop.org -- 2.51.0 From acf3256160bdabcb5c07032f3bf6eb5a21f5b95f Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Fri, 14 Feb 2025 09:21:09 -0700 Subject: [PATCH 07/16] bus: mhi: host: Avoid possible uninitialized fw_load_type If mhi_fw_load_handler() bails out early because the EE is not capable of loading firmware, we may reference fw_load_type in cleanup which is uninitialized at this point. The cleanup code checks fw_load_type as a proxy for knowing if fbc_image was allocated and needs to be freed, but we can directly test for that. This avoids the possible uninitialized access and appears to be clearer code. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/e3148ac4-7bb8-422d-ae0f-18a8eb15e269@stanley.mountain/ Fixes: f88f1d0998ea ("bus: mhi: host: Add a policy to enable image transfer via BHIe in PBL") Signed-off-by: Jeffrey Hugo Acked-by: Manivannan Sadhasivam Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://patchwork.freedesktop.org/patch/msgid/20250214162109.3555300-1-quic_jhugo@quicinc.com --- drivers/bus/mhi/host/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index c8e48f621a8c..efa3b6dddf4d 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -608,7 +608,7 @@ fw_load_ready_state: return; error_ready_state: - if (fw_load_type == MHI_FW_LOAD_FBC) { + if (mhi_cntrl->fbc_image) { mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image); mhi_cntrl->fbc_image = NULL; } -- 2.51.0 From b6eb664d89e7ed1e3369fe2860fea31e6dc45e34 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Fri, 21 Feb 2025 10:50:33 +0000 Subject: [PATCH 08/16] drm/sched: Add internal job peek/pop API MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Idea is to add helpers for peeking and popping jobs from entities with the goal of decoupling the hidden assumption in the code that queue_node is the first element in struct drm_sched_job. That assumption usually comes in the form of: while ((job = to_drm_sched_job(spsc_queue_pop(&entity->job_queue)))) Which breaks if the queue_node is re-positioned due to_drm_sched_job being implemented with a container_of. This also allows us to remove duplicate definitions of to_drm_sched_job. Signed-off-by: Tvrtko Ursulin Cc: Christian König Cc: Danilo Krummrich Cc: Matthew Brost Cc: Philipp Stanner Signed-off-by: Philipp Stanner Link: https://patchwork.freedesktop.org/patch/msgid/20250221105038.79665-2-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/scheduler/sched_entity.c | 11 +++-- drivers/gpu/drm/scheduler/sched_internal.h | 48 ++++++++++++++++++++++ drivers/gpu/drm/scheduler/sched_main.c | 7 ++-- 3 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 drivers/gpu/drm/scheduler/sched_internal.h diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index 69bcf0e99d57..a171f05ad761 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -28,10 +28,9 @@ #include #include -#include "gpu_scheduler_trace.h" +#include "sched_internal.h" -#define to_drm_sched_job(sched_job) \ - container_of((sched_job), struct drm_sched_job, queue_node) +#include "gpu_scheduler_trace.h" /** * drm_sched_entity_init - Init a context entity used by scheduler when @@ -255,7 +254,7 @@ static void drm_sched_entity_kill(struct drm_sched_entity *entity) /* The entity is guaranteed to not be used by the scheduler */ prev = rcu_dereference_check(entity->last_scheduled, true); dma_fence_get(prev); - while ((job = to_drm_sched_job(spsc_queue_pop(&entity->job_queue)))) { + while ((job = drm_sched_entity_queue_pop(entity))) { struct drm_sched_fence *s_fence = job->s_fence; dma_fence_get(&s_fence->finished); @@ -477,7 +476,7 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) { struct drm_sched_job *sched_job; - sched_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); + sched_job = drm_sched_entity_queue_peek(entity); if (!sched_job) return NULL; @@ -513,7 +512,7 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) { struct drm_sched_job *next; - next = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); + next = drm_sched_entity_queue_peek(entity); if (next) { struct drm_sched_rq *rq; diff --git a/drivers/gpu/drm/scheduler/sched_internal.h b/drivers/gpu/drm/scheduler/sched_internal.h new file mode 100644 index 000000000000..bd34898911d7 --- /dev/null +++ b/drivers/gpu/drm/scheduler/sched_internal.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef _DRM_GPU_SCHEDULER_INTERNAL_H_ +#define _DRM_GPU_SCHEDULER_INTERNAL_H_ + +/** + * drm_sched_entity_queue_pop - Low level helper for popping queued jobs + * + * @entity: scheduler entity + * + * Low level helper for popping queued jobs. + * + * Returns: The job dequeued or NULL. + */ +static inline struct drm_sched_job * +drm_sched_entity_queue_pop(struct drm_sched_entity *entity) +{ + struct spsc_node *node; + + node = spsc_queue_pop(&entity->job_queue); + if (!node) + return NULL; + + return container_of(node, struct drm_sched_job, queue_node); +} + +/** + * drm_sched_entity_queue_peek - Low level helper for peeking at the job queue + * + * @entity: scheduler entity + * + * Low level helper for peeking at the job queue + * + * Returns: The job at the head of the queue or NULL. + */ +static inline struct drm_sched_job * +drm_sched_entity_queue_peek(struct drm_sched_entity *entity) +{ + struct spsc_node *node; + + node = spsc_queue_peek(&entity->job_queue); + if (!node) + return NULL; + + return container_of(node, struct drm_sched_job, queue_node); +} + +#endif diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 8c36a59afb72..c634993f1346 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -78,6 +78,8 @@ #include #include +#include "sched_internal.h" + #define CREATE_TRACE_POINTS #include "gpu_scheduler_trace.h" @@ -87,9 +89,6 @@ static struct lockdep_map drm_sched_lockdep_map = { }; #endif -#define to_drm_sched_job(sched_job) \ - container_of((sched_job), struct drm_sched_job, queue_node) - int drm_sched_policy = DRM_SCHED_POLICY_FIFO; /** @@ -123,7 +122,7 @@ static bool drm_sched_can_queue(struct drm_gpu_scheduler *sched, { struct drm_sched_job *s_job; - s_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); + s_job = drm_sched_entity_queue_peek(entity); if (!s_job) return false; -- 2.51.0 From 80b6ef8ae25ade45e6418df3ddf699a5a10a7ca4 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Fri, 21 Feb 2025 10:50:34 +0000 Subject: [PATCH 09/16] drm/amdgpu: Pop jobs from the queue more robustly MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Replace a copy of DRM scheduler's to_drm_sched_job with a copy of a newly added drm_sched_entity_queue_pop. This allows breaking the hidden dependency that queue_node has to be the first element in struct drm_sched_job. A comment is also added with a reference to the mailing list discussion explaining the copied helper will be removed when the whole broken amdgpu_job_stop_all_jobs_on_sched is removed. Signed-off-by: Tvrtko Ursulin Cc: Christian König Cc: Danilo Krummrich Cc: Matthew Brost Cc: Philipp Stanner Cc: Zhang, Hawking Reviewed-by: Christian König Signed-off-by: Philipp Stanner Link: https://patchwork.freedesktop.org/patch/msgid/20250221105038.79665-3-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_job.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index 100f04475943..1899c601c95c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -411,8 +411,24 @@ static struct dma_fence *amdgpu_job_run(struct drm_sched_job *sched_job) return fence; } -#define to_drm_sched_job(sched_job) \ - container_of((sched_job), struct drm_sched_job, queue_node) +/* + * This is a duplicate function from DRM scheduler sched_internal.h. + * Plan is to remove it when amdgpu_job_stop_all_jobs_on_sched is removed, due + * latter being incorrect and racy. + * + * See https://lore.kernel.org/amd-gfx/44edde63-7181-44fb-a4f7-94e50514f539@amd.com/ + */ +static struct drm_sched_job * +drm_sched_entity_queue_pop(struct drm_sched_entity *entity) +{ + struct spsc_node *node; + + node = spsc_queue_pop(&entity->job_queue); + if (!node) + return NULL; + + return container_of(node, struct drm_sched_job, queue_node); +} void amdgpu_job_stop_all_jobs_on_sched(struct drm_gpu_scheduler *sched) { @@ -425,7 +441,7 @@ void amdgpu_job_stop_all_jobs_on_sched(struct drm_gpu_scheduler *sched) struct drm_sched_rq *rq = sched->sched_rq[i]; spin_lock(&rq->lock); list_for_each_entry(s_entity, &rq->entities, list) { - while ((s_job = to_drm_sched_job(spsc_queue_pop(&s_entity->job_queue)))) { + while ((s_job = drm_sched_entity_queue_pop(s_entity))) { struct drm_sched_fence *s_fence = s_job->s_fence; dma_fence_signal(&s_fence->scheduled); -- 2.51.0 From b76f1467dc56fccecfdd63357e2c71542ecd96c5 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Fri, 21 Feb 2025 10:50:35 +0000 Subject: [PATCH 10/16] drm/sched: Remove a hole from struct drm_sched_job MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit We can re-order some struct members and take u32 credits outside of the pointer sandwich and also for the last_dependency member we can get away with an unsigned int since for dependency we use xa_limit_32b. Pahole report before: /* size: 160, cachelines: 3, members: 14 */ /* sum members: 156, holes: 1, sum holes: 4 */ /* last cacheline: 32 bytes */ And after: /* size: 152, cachelines: 3, members: 14 */ /* last cacheline: 24 bytes */ Signed-off-by: Tvrtko Ursulin Cc: Christian König Cc: Danilo Krummrich Cc: Matthew Brost Cc: Philipp Stanner Acked-by: Danilo Krummrich Acked-by: Christian König Signed-off-by: Philipp Stanner Link: https://patchwork.freedesktop.org/patch/msgid/20250221105038.79665-4-tvrtko.ursulin@igalia.com --- include/drm/gpu_scheduler.h | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 6bf458dbce84..1c1138308e66 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -338,8 +338,14 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * to schedule the job. */ struct drm_sched_job { - struct spsc_node queue_node; - struct list_head list; + u64 id; + + /** + * @submit_ts: + * + * When the job was pushed into the entity queue. + */ + ktime_t submit_ts; /** * @sched: @@ -349,24 +355,30 @@ struct drm_sched_job { * has finished. */ struct drm_gpu_scheduler *sched; + struct drm_sched_fence *s_fence; + struct drm_sched_entity *entity; + enum drm_sched_priority s_priority; u32 credits; + /** @last_dependency: tracks @dependencies as they signal */ + unsigned int last_dependency; + atomic_t karma; + + struct spsc_node queue_node; + struct list_head list; /* * work is used only after finish_cb has been used and will not be * accessed anymore. */ union { - struct dma_fence_cb finish_cb; - struct work_struct work; + struct dma_fence_cb finish_cb; + struct work_struct work; }; - uint64_t id; - atomic_t karma; - enum drm_sched_priority s_priority; - struct drm_sched_entity *entity; struct dma_fence_cb cb; + /** * @dependencies: * @@ -375,16 +387,6 @@ struct drm_sched_job { * drm_sched_job_add_implicit_dependencies(). */ struct xarray dependencies; - - /** @last_dependency: tracks @dependencies as they signal */ - unsigned long last_dependency; - - /** - * @submit_ts: - * - * When the job was pushed into the entity queue. - */ - ktime_t submit_ts; }; static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, -- 2.51.0 From 4b7320bfd49dcaf33ef8ab724b88f7baf6bd9978 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Fri, 21 Feb 2025 10:50:36 +0000 Subject: [PATCH 11/16] drm/sched: Move drm_sched_entity_is_ready to internal header MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Helper is for scheduler internal use so lets hide it from DRM drivers completely. At the same time we change the method of checking whethere there is anything in the queue from peeking to looking at the node count. Signed-off-by: Tvrtko Ursulin Cc: Christian König Cc: Danilo Krummrich Cc: Matthew Brost Cc: Philipp Stanner Signed-off-by: Philipp Stanner Link: https://patchwork.freedesktop.org/patch/msgid/20250221105038.79665-5-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/scheduler/sched_entity.c | 12 ------------ drivers/gpu/drm/scheduler/sched_internal.h | 13 +++++++++++++ include/drm/gpu_scheduler.h | 1 - 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index a171f05ad761..87f88259ddf6 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -151,18 +151,6 @@ static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity) return false; } -/* Return true if entity could provide a job. */ -bool drm_sched_entity_is_ready(struct drm_sched_entity *entity) -{ - if (spsc_queue_peek(&entity->job_queue) == NULL) - return false; - - if (READ_ONCE(entity->dependency)) - return false; - - return true; -} - /** * drm_sched_entity_error - return error of last scheduled job * @entity: scheduler entity to check diff --git a/drivers/gpu/drm/scheduler/sched_internal.h b/drivers/gpu/drm/scheduler/sched_internal.h index bd34898911d7..23ceda8c32e5 100644 --- a/drivers/gpu/drm/scheduler/sched_internal.h +++ b/drivers/gpu/drm/scheduler/sched_internal.h @@ -45,4 +45,17 @@ drm_sched_entity_queue_peek(struct drm_sched_entity *entity) return container_of(node, struct drm_sched_job, queue_node); } +/* Return true if entity could provide a job. */ +static inline bool +drm_sched_entity_is_ready(struct drm_sched_entity *entity) +{ + if (!spsc_queue_count(&entity->job_queue)) + return false; + + if (READ_ONCE(entity->dependency)) + return false; + + return true; +} + #endif diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 1c1138308e66..6cd0f288f6ed 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -632,7 +632,6 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity); void drm_sched_entity_push_job(struct drm_sched_job *sched_job); void drm_sched_entity_set_priority(struct drm_sched_entity *entity, enum drm_sched_priority priority); -bool drm_sched_entity_is_ready(struct drm_sched_entity *entity); int drm_sched_entity_error(struct drm_sched_entity *entity); struct drm_sched_fence *drm_sched_fence_alloc( -- 2.51.0 From 71a18f7266f301b20faf9f5fd7aee228c83bb4a3 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Fri, 21 Feb 2025 10:50:37 +0000 Subject: [PATCH 12/16] drm/sched: Move internal prototypes to internal header MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Now that we have a header file for internal scheduler interfaces we can move some more prototypes into it. By doing that we eliminate the chance of drivers trying to use something which was not intended to be used. Signed-off-by: Tvrtko Ursulin Cc: Christian König Cc: Danilo Krummrich Cc: Matthew Brost Cc: Philipp Stanner Signed-off-by: Philipp Stanner Link: https://patchwork.freedesktop.org/patch/msgid/20250221105038.79665-6-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/scheduler/sched_fence.c | 2 ++ drivers/gpu/drm/scheduler/sched_internal.h | 30 ++++++++++++++++++++++ include/drm/gpu_scheduler.h | 27 ------------------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/scheduler/sched_fence.c b/drivers/gpu/drm/scheduler/sched_fence.c index 0f35f009b9d3..e971528504a5 100644 --- a/drivers/gpu/drm/scheduler/sched_fence.c +++ b/drivers/gpu/drm/scheduler/sched_fence.c @@ -29,6 +29,8 @@ #include +#include "sched_internal.h" + static struct kmem_cache *sched_fence_slab; static int __init drm_sched_fence_slab_init(void) diff --git a/drivers/gpu/drm/scheduler/sched_internal.h b/drivers/gpu/drm/scheduler/sched_internal.h index 23ceda8c32e5..599cf6e1bb74 100644 --- a/drivers/gpu/drm/scheduler/sched_internal.h +++ b/drivers/gpu/drm/scheduler/sched_internal.h @@ -3,6 +3,36 @@ #ifndef _DRM_GPU_SCHEDULER_INTERNAL_H_ #define _DRM_GPU_SCHEDULER_INTERNAL_H_ + +/* Used to choose between FIFO and RR job-scheduling */ +extern int drm_sched_policy; + +#define DRM_SCHED_POLICY_RR 0 +#define DRM_SCHED_POLICY_FIFO 1 + +void drm_sched_wakeup(struct drm_gpu_scheduler *sched); + +void drm_sched_rq_add_entity(struct drm_sched_rq *rq, + struct drm_sched_entity *entity); +void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, + struct drm_sched_entity *entity); + +void drm_sched_rq_update_fifo_locked(struct drm_sched_entity *entity, + struct drm_sched_rq *rq, ktime_t ts); + +void drm_sched_entity_select_rq(struct drm_sched_entity *entity); +struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity); + +struct drm_sched_fence *drm_sched_fence_alloc(struct drm_sched_entity *s_entity, + void *owner); +void drm_sched_fence_init(struct drm_sched_fence *fence, + struct drm_sched_entity *entity); +void drm_sched_fence_free(struct drm_sched_fence *fence); + +void drm_sched_fence_scheduled(struct drm_sched_fence *fence, + struct dma_fence *parent); +void drm_sched_fence_finished(struct drm_sched_fence *fence, int result); + /** * drm_sched_entity_queue_pop - Low level helper for popping queued jobs * diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 6cd0f288f6ed..8cb12f6231b8 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -71,12 +71,6 @@ enum drm_sched_priority { DRM_SCHED_PRIORITY_COUNT }; -/* Used to choose between FIFO and RR job-scheduling */ -extern int drm_sched_policy; - -#define DRM_SCHED_POLICY_RR 0 -#define DRM_SCHED_POLICY_FIFO 1 - /** * struct drm_sched_entity - A wrapper around a job queue (typically * attached to the DRM file_priv). @@ -601,7 +595,6 @@ void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, void drm_sched_tdr_queue_imm(struct drm_gpu_scheduler *sched); void drm_sched_job_cleanup(struct drm_sched_job *job); -void drm_sched_wakeup(struct drm_gpu_scheduler *sched); bool drm_sched_wqueue_ready(struct drm_gpu_scheduler *sched); void drm_sched_wqueue_stop(struct drm_gpu_scheduler *sched); void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched); @@ -611,14 +604,6 @@ void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched); void drm_sched_increase_karma(struct drm_sched_job *bad); void drm_sched_fault(struct drm_gpu_scheduler *sched); -void drm_sched_rq_add_entity(struct drm_sched_rq *rq, - struct drm_sched_entity *entity); -void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, - struct drm_sched_entity *entity); - -void drm_sched_rq_update_fifo_locked(struct drm_sched_entity *entity, - struct drm_sched_rq *rq, ktime_t ts); - int drm_sched_entity_init(struct drm_sched_entity *entity, enum drm_sched_priority priority, struct drm_gpu_scheduler **sched_list, @@ -627,23 +612,11 @@ int drm_sched_entity_init(struct drm_sched_entity *entity, long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout); void drm_sched_entity_fini(struct drm_sched_entity *entity); void drm_sched_entity_destroy(struct drm_sched_entity *entity); -void drm_sched_entity_select_rq(struct drm_sched_entity *entity); -struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity); void drm_sched_entity_push_job(struct drm_sched_job *sched_job); void drm_sched_entity_set_priority(struct drm_sched_entity *entity, enum drm_sched_priority priority); int drm_sched_entity_error(struct drm_sched_entity *entity); -struct drm_sched_fence *drm_sched_fence_alloc( - struct drm_sched_entity *s_entity, void *owner); -void drm_sched_fence_init(struct drm_sched_fence *fence, - struct drm_sched_entity *entity); -void drm_sched_fence_free(struct drm_sched_fence *fence); - -void drm_sched_fence_scheduled(struct drm_sched_fence *fence, - struct dma_fence *parent); -void drm_sched_fence_finished(struct drm_sched_fence *fence, int result); - unsigned long drm_sched_suspend_timeout(struct drm_gpu_scheduler *sched); void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched, unsigned long remaining); -- 2.51.0 From 27d4815149ba0c80ef2db2a82f0512f647e76d62 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Fri, 21 Feb 2025 10:50:38 +0000 Subject: [PATCH 13/16] drm/sched: Group exported prototypes by object type MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Do a bit of house keeping in gpu_scheduler.h by grouping the API by type of object it operates on. Signed-off-by: Tvrtko Ursulin Cc: Christian König Cc: Danilo Krummrich Cc: Matthew Brost Cc: Philipp Stanner Signed-off-by: Philipp Stanner Link: https://patchwork.freedesktop.org/patch/msgid/20250221105038.79665-7-tvrtko.ursulin@igalia.com --- include/drm/gpu_scheduler.h | 60 ++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 8cb12f6231b8..50928a7ae98e 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -383,12 +383,6 @@ struct drm_sched_job { struct xarray dependencies; }; -static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, - int threshold) -{ - return s_job && atomic_inc_return(&s_job->karma) > threshold; -} - enum drm_gpu_sched_stat { DRM_GPU_SCHED_STAT_NONE, /* Reserve 0 */ DRM_GPU_SCHED_STAT_NOMINAL, @@ -566,14 +560,36 @@ struct drm_sched_init_args { struct device *dev; }; +/* Scheduler operations */ + int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_args *args); void drm_sched_fini(struct drm_gpu_scheduler *sched); + +unsigned long drm_sched_suspend_timeout(struct drm_gpu_scheduler *sched); +void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched, + unsigned long remaining); +void drm_sched_tdr_queue_imm(struct drm_gpu_scheduler *sched); +bool drm_sched_wqueue_ready(struct drm_gpu_scheduler *sched); +void drm_sched_wqueue_stop(struct drm_gpu_scheduler *sched); +void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched); +void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad); +void drm_sched_start(struct drm_gpu_scheduler *sched, int errno); +void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched); +void drm_sched_fault(struct drm_gpu_scheduler *sched); + +struct drm_gpu_scheduler * +drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, + unsigned int num_sched_list); + +/* Jobs */ + int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, u32 credits, void *owner); void drm_sched_job_arm(struct drm_sched_job *job); +void drm_sched_entity_push_job(struct drm_sched_job *sched_job); int drm_sched_job_add_dependency(struct drm_sched_job *job, struct dma_fence *fence); int drm_sched_job_add_syncobj_dependency(struct drm_sched_job *job, @@ -588,21 +604,16 @@ int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, bool write); bool drm_sched_job_has_dependency(struct drm_sched_job *job, struct dma_fence *fence); - -void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, - struct drm_gpu_scheduler **sched_list, - unsigned int num_sched_list); - -void drm_sched_tdr_queue_imm(struct drm_gpu_scheduler *sched); void drm_sched_job_cleanup(struct drm_sched_job *job); -bool drm_sched_wqueue_ready(struct drm_gpu_scheduler *sched); -void drm_sched_wqueue_stop(struct drm_gpu_scheduler *sched); -void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched); -void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad); -void drm_sched_start(struct drm_gpu_scheduler *sched, int errno); -void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched); void drm_sched_increase_karma(struct drm_sched_job *bad); -void drm_sched_fault(struct drm_gpu_scheduler *sched); + +static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, + int threshold) +{ + return s_job && atomic_inc_return(&s_job->karma) > threshold; +} + +/* Entities */ int drm_sched_entity_init(struct drm_sched_entity *entity, enum drm_sched_priority priority, @@ -612,16 +623,11 @@ int drm_sched_entity_init(struct drm_sched_entity *entity, long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout); void drm_sched_entity_fini(struct drm_sched_entity *entity); void drm_sched_entity_destroy(struct drm_sched_entity *entity); -void drm_sched_entity_push_job(struct drm_sched_job *sched_job); void drm_sched_entity_set_priority(struct drm_sched_entity *entity, enum drm_sched_priority priority); int drm_sched_entity_error(struct drm_sched_entity *entity); - -unsigned long drm_sched_suspend_timeout(struct drm_gpu_scheduler *sched); -void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched, - unsigned long remaining); -struct drm_gpu_scheduler * -drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, - unsigned int num_sched_list); +void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, + struct drm_gpu_scheduler **sched_list, + unsigned int num_sched_list); #endif -- 2.51.0 From ed531feda7852de0aa702fbe3d23a0f743ccc77b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 17 Feb 2025 18:49:36 +0100 Subject: [PATCH 14/16] drm/bridge: ti-sn65dsi86: Check for CONFIG_PWM using IS_REACHABLE() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently CONFIG_PWM is a bool but I intend to change it to tristate. If CONFIG_PWM=m in the configuration, the cpp symbol CONFIG_PWM isn't defined and so the PWM code paths in the ti-sn65dsi86 driver are not used. The correct way to check for CONFIG_PWM is using IS_REACHABLE which does the right thing for all cases CONFIG_DRM_TI_SN65DSI86 ∈ { y, m } x CONFIG_PWM ∈ { y, m, n }. There is no change until CONFIG_PWM actually becomes tristate. Signed-off-by: Uwe Kleine-König Reviewed-by: Louis Chauvet Reviewed-by: Robert Foss Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20250217174936.758420-2-u.kleine-koenig@baylibre.com --- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index ae34585e05b3..01d456b955ab 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -195,7 +195,7 @@ struct ti_sn65dsi86 { struct gpio_chip gchip; DECLARE_BITMAP(gchip_output, SN_NUM_GPIOS); #endif -#if defined(CONFIG_PWM) +#if IS_REACHABLE(CONFIG_PWM) struct pwm_chip *pchip; bool pwm_enabled; atomic_t pwm_pin_busy; @@ -1362,7 +1362,7 @@ static struct auxiliary_driver ti_sn_bridge_driver = { /* ----------------------------------------------------------------------------- * PWM Controller */ -#if defined(CONFIG_PWM) +#if IS_REACHABLE(CONFIG_PWM) static int ti_sn_pwm_pin_request(struct ti_sn65dsi86 *pdata) { return atomic_xchg(&pdata->pwm_pin_busy, 1) ? -EBUSY : 0; @@ -1956,7 +1956,7 @@ static int ti_sn65dsi86_probe(struct i2c_client *client) return ret; } - if (IS_ENABLED(CONFIG_PWM)) { + if (IS_REACHABLE(CONFIG_PWM)) { ret = ti_sn65dsi86_add_aux_device(pdata, &pdata->pwm_aux, "pwm"); if (ret) return ret; -- 2.51.0 From 4d098000ac193f359e6b8ca4801dbdbd6a27b41f Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Thu, 16 Jan 2025 05:48:01 -0800 Subject: [PATCH 15/16] drm/repaper: fix integer overflows in repeat functions There are conditions, albeit somewhat unlikely, under which right hand expressions, calculating the end of time period in functions like repaper_frame_fixed_repeat(), may overflow. For instance, if 'factor10x' in repaper_get_temperature() is high enough (170), as is 'epd->stage_time' in repaper_probe(), then the resulting value of 'end' will not fit in unsigned int expression. Mitigate this by casting 'epd->factored_stage_time' to wider type before any multiplication is done. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: 3589211e9b03 ("drm/tinydrm: Add RePaper e-ink driver") Cc: stable@vger.kernel.org Signed-off-by: Nikita Zhandarovich Signed-off-by: Alex Lanzano Link: https://patchwork.freedesktop.org/patch/msgid/20250116134801.22067-1-n.zhandarovich@fintech.ru --- drivers/gpu/drm/tiny/repaper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index 52ba6c699bc8..5c3b51eb0a97 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -456,7 +456,7 @@ static void repaper_frame_fixed_repeat(struct repaper_epd *epd, u8 fixed_value, enum repaper_stage stage) { u64 start = local_clock(); - u64 end = start + (epd->factored_stage_time * 1000 * 1000); + u64 end = start + ((u64)epd->factored_stage_time * 1000 * 1000); do { repaper_frame_fixed(epd, fixed_value, stage); @@ -467,7 +467,7 @@ static void repaper_frame_data_repeat(struct repaper_epd *epd, const u8 *image, const u8 *mask, enum repaper_stage stage) { u64 start = local_clock(); - u64 end = start + (epd->factored_stage_time * 1000 * 1000); + u64 end = start + ((u64)epd->factored_stage_time * 1000 * 1000); do { repaper_frame_data(epd, image, mask, stage); -- 2.51.0 From fb13d3497bdcf4f544be25f716b6bdf1a4f8e63a Mon Sep 17 00:00:00 2001 From: Tejas Vipin Date: Thu, 20 Feb 2025 10:27:21 +0530 Subject: [PATCH 16/16] drm/mipi-dsi: extend "multi" functions and use them in sony-td4353-jdi Removes mipi_dsi_dcs_set_tear_off and replaces it with a multi version as after replacing it in sony-td4353-jdi, it doesn't appear anywhere else. sony-td4353-jdi is converted to use multi style functions, including mipi_dsi_dcs_set_tear_off_multi. Signed-off-by: Tejas Vipin Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20250220045721.145905-1-tejasvipin76@gmail.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20250220045721.145905-1-tejasvipin76@gmail.com --- drivers/gpu/drm/drm_mipi_dsi.c | 42 +++---- drivers/gpu/drm/panel/panel-sony-td4353-jdi.c | 107 ++++-------------- include/drm/drm_mipi_dsi.h | 2 +- 3 files changed, 47 insertions(+), 104 deletions(-) diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 5e5c5f84daac..2e148753ea97 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -1265,25 +1265,6 @@ int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, } EXPORT_SYMBOL(mipi_dsi_dcs_set_page_address); -/** - * mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect - * output signal on the TE signal line - * @dsi: DSI peripheral device - * - * Return: 0 on success or a negative error code on failure - */ -int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off); - /** * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect * output signal on the TE signal line. @@ -1713,6 +1694,29 @@ void mipi_dsi_turn_on_peripheral_multi(struct mipi_dsi_multi_context *ctx) } EXPORT_SYMBOL(mipi_dsi_turn_on_peripheral_multi); +/** + * mipi_dsi_dcs_set_tear_off_multi() - turn off the display module's Tearing Effect + * output signal on the TE signal line + * @ctx: Context for multiple DSI transactions + */ +void mipi_dsi_dcs_set_tear_off_multi(struct mipi_dsi_multi_context *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + ssize_t err; + + if (ctx->accum_err) + return; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0); + if (err < 0) { + ctx->accum_err = err; + dev_err(dev, "Failed to set tear off: %d\n", + ctx->accum_err); + } +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off_multi); + /** * mipi_dsi_dcs_soft_reset_multi() - perform a software reset of the display module * @ctx: Context for multiple DSI transactions diff --git a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c index 472195d4bbbe..97f4bb4e1029 100644 --- a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c +++ b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c @@ -47,93 +47,40 @@ static inline struct sony_td4353_jdi *to_sony_td4353_jdi(struct drm_panel *panel static int sony_td4353_jdi_on(struct sony_td4353_jdi *ctx) { struct mipi_dsi_device *dsi = ctx->dsi; - struct device *dev = &dsi->dev; - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; dsi->mode_flags |= MIPI_DSI_MODE_LPM; - ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 1080 - 1); - if (ret < 0) { - dev_err(dev, "Failed to set column address: %d\n", ret); - return ret; - } - - ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 2160 - 1); - if (ret < 0) { - dev_err(dev, "Failed to set page address: %d\n", ret); - return ret; - } - - ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0); - if (ret < 0) { - dev_err(dev, "Failed to set tear scanline: %d\n", ret); - return ret; - } - - ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); - if (ret < 0) { - dev_err(dev, "Failed to set tear on: %d\n", ret); - return ret; - } + mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0x0000, 1080 - 1); + mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0x0000, 2160 - 1); + mipi_dsi_dcs_set_tear_scanline_multi(&dsi_ctx, 0); + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); - mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00); - - ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77); - if (ret < 0) { - dev_err(dev, "Failed to set pixel format: %d\n", ret); - return ret; - } - - mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_PARTIAL_ROWS, - 0x00, 0x00, 0x08, 0x6f); - - ret = mipi_dsi_dcs_exit_sleep_mode(dsi); - if (ret < 0) { - dev_err(dev, "Failed to exit sleep mode: %d\n", ret); - return ret; - } - msleep(70); + mipi_dsi_dcs_set_pixel_format_multi(&dsi_ctx, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_PARTIAL_ROWS, + 0x00, 0x00, 0x08, 0x6f); - mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 70); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_MEMORY_START); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); - ret = mipi_dsi_dcs_set_display_on(dsi); - if (ret < 0) { - dev_err(dev, "Failed to turn display on: %d\n", ret); - return ret; - } - - return 0; + return dsi_ctx.accum_err; } -static int sony_td4353_jdi_off(struct sony_td4353_jdi *ctx) +static void sony_td4353_jdi_off(struct sony_td4353_jdi *ctx) { struct mipi_dsi_device *dsi = ctx->dsi; - struct device *dev = &dsi->dev; - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; - ret = mipi_dsi_dcs_set_display_off(dsi); - if (ret < 0) { - dev_err(dev, "Failed to set display off: %d\n", ret); - return ret; - } - msleep(22); - - ret = mipi_dsi_dcs_set_tear_off(dsi); - if (ret < 0) { - dev_err(dev, "Failed to set tear off: %d\n", ret); - return ret; - } - - ret = mipi_dsi_dcs_enter_sleep_mode(dsi); - if (ret < 0) { - dev_err(dev, "Failed to enter sleep mode: %d\n", ret); - return ret; - } - msleep(80); - - return 0; + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 22); + mipi_dsi_dcs_set_tear_off_multi(&dsi_ctx); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 80); } static void sony_td4353_assert_reset_gpios(struct sony_td4353_jdi *ctx, int mode) @@ -146,14 +93,11 @@ static void sony_td4353_assert_reset_gpios(struct sony_td4353_jdi *ctx, int mode static int sony_td4353_jdi_prepare(struct drm_panel *panel) { struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel); - struct device *dev = &ctx->dsi->dev; int ret; ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); - if (ret < 0) { - dev_err(dev, "Failed to enable regulators: %d\n", ret); + if (ret < 0) return ret; - } msleep(100); @@ -161,7 +105,6 @@ static int sony_td4353_jdi_prepare(struct drm_panel *panel) ret = sony_td4353_jdi_on(ctx); if (ret < 0) { - dev_err(dev, "Failed to power on panel: %d\n", ret); sony_td4353_assert_reset_gpios(ctx, 0); regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); return ret; @@ -173,12 +116,8 @@ static int sony_td4353_jdi_prepare(struct drm_panel *panel) static int sony_td4353_jdi_unprepare(struct drm_panel *panel) { struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel); - struct device *dev = &ctx->dsi->dev; - int ret; - ret = sony_td4353_jdi_off(ctx); - if (ret < 0) - dev_err(dev, "Failed to power off panel: %d\n", ret); + sony_td4353_jdi_off(ctx); sony_td4353_assert_reset_gpios(ctx, 0); regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 94400a78031f..bd40a443385c 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -346,7 +346,6 @@ int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start, u16 end); int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, u16 end); -int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi); int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, enum mipi_dsi_dcs_tear_mode mode); int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format); @@ -379,6 +378,7 @@ void mipi_dsi_dcs_set_page_address_multi(struct mipi_dsi_multi_context *ctx, u16 start, u16 end); void mipi_dsi_dcs_set_tear_scanline_multi(struct mipi_dsi_multi_context *ctx, u16 scanline); +void mipi_dsi_dcs_set_tear_off_multi(struct mipi_dsi_multi_context *ctx); /** * mipi_dsi_generic_write_seq - transmit data using a generic write packet -- 2.51.0