From 6133cf70725049344a679f85df27d146a2d995ea Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Thu, 22 Aug 2024 09:33:54 +0200 Subject: drm/panic: Add integer scaling to blit() Add a parameter to the blit function, to upscale the image. This is necessary to draw a QR code, otherwise, the pixels are usually too small to be readable by most QR code reader. It can also be used later for drawing fonts on high DPI display. Signed-off-by: Jocelyn Falempe Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20240822073852.562286-2-jfalempe@redhat.com --- drivers/gpu/drm/drm_panic.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index eff4598e2fc7..0a047152f88b 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -257,20 +257,20 @@ static bool drm_panic_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, int x, i static void drm_panic_blit16(struct iosys_map *dmap, unsigned int dpitch, const u8 *sbuf8, unsigned int spitch, unsigned int height, unsigned int width, - u16 fg16) + unsigned int scale, u16 fg16) { unsigned int y, x; for (y = 0; y < height; y++) for (x = 0; x < width; x++) - if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) + if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, fg16); } static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch, const u8 *sbuf8, unsigned int spitch, unsigned int height, unsigned int width, - u32 fg32) + unsigned int scale, u32 fg32) { unsigned int y, x; @@ -278,7 +278,7 @@ static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch, for (x = 0; x < width; x++) { u32 off = y * dpitch + x * 3; - if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) { + if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) { /* write blue-green-red to output in little endianness */ iosys_map_wr(dmap, off, u8, (fg32 & 0x000000FF) >> 0); iosys_map_wr(dmap, off + 1, u8, (fg32 & 0x0000FF00) >> 8); @@ -291,24 +291,25 @@ static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch, static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch, const u8 *sbuf8, unsigned int spitch, unsigned int height, unsigned int width, - u32 fg32) + unsigned int scale, u32 fg32) { unsigned int y, x; for (y = 0; y < height; y++) for (x = 0; x < width; x++) - if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) + if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, fg32); } static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect *clip, - const u8 *sbuf8, unsigned int spitch, u32 fg_color) + const u8 *sbuf8, unsigned int spitch, unsigned int scale, + u32 fg_color) { unsigned int y, x; for (y = 0; y < drm_rect_height(clip); y++) for (x = 0; x < drm_rect_width(clip); x++) - if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) + if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color); } @@ -318,18 +319,22 @@ static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect * @clip: destination rectangle * @sbuf8: source buffer, in monochrome format, 8 pixels per byte. * @spitch: source pitch in bytes + * @scale: integer scale, source buffer is scale time smaller than destination + * rectangle * @fg_color: foreground color, in destination format * * This can be used to draw a font character, which is a monochrome image, to a * framebuffer in other supported format. */ static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip, - const u8 *sbuf8, unsigned int spitch, u32 fg_color) + const u8 *sbuf8, unsigned int spitch, + unsigned int scale, u32 fg_color) + { struct iosys_map map; if (sb->set_pixel) - return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, fg_color); + return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, scale, fg_color); map = sb->map[0]; iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]); @@ -337,15 +342,15 @@ static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip, switch (sb->format->cpp[0]) { case 2: drm_panic_blit16(&map, sb->pitch[0], sbuf8, spitch, - drm_rect_height(clip), drm_rect_width(clip), fg_color); + drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); break; case 3: drm_panic_blit24(&map, sb->pitch[0], sbuf8, spitch, - drm_rect_height(clip), drm_rect_width(clip), fg_color); + drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); break; case 4: drm_panic_blit32(&map, sb->pitch[0], sbuf8, spitch, - drm_rect_height(clip), drm_rect_width(clip), fg_color); + drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); break; default: WARN_ONCE(1, "Can't blit with pixel width %d\n", sb->format->cpp[0]); @@ -485,7 +490,7 @@ static void draw_txt_rectangle(struct drm_scanout_buffer *sb, for (j = 0; j < line_len; j++) { src = get_char_bitmap(font, msg[i].txt[j], font_pitch); rec.x2 = rec.x1 + font->width; - drm_panic_blit(sb, &rec, src, font_pitch, color); + drm_panic_blit(sb, &rec, src, font_pitch, 1, color); rec.x1 += font->width; } } -- cgit v1.2.3 From 4b570ac2eb54f66ff64f2864be6303b8d67cc7f9 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Thu, 22 Aug 2024 09:33:55 +0200 Subject: drm/rect: Add drm_rect_overlap() Check if two rectangles overlap. It's a bit similar to drm_rect_intersect() but this won't modify the rectangle. Simplifies a bit drm_panic. Signed-off-by: Jocelyn Falempe Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20240822073852.562286-3-jfalempe@redhat.com --- drivers/gpu/drm/drm_panic.c | 3 +-- include/drm/drm_rect.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index 0a047152f88b..59fba23e5fd7 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -529,8 +529,7 @@ static void draw_panic_static_user(struct drm_scanout_buffer *sb) /* Fill with the background color, and draw text on top */ drm_panic_fill(sb, &r_screen, bg_color); - if ((r_msg.x1 >= logo_width || r_msg.y1 >= logo_height) && - logo_width <= sb->width && logo_height <= sb->height) { + if (!drm_rect_overlap(&r_logo, &r_msg)) { if (logo_mono) drm_panic_blit(sb, &r_logo, logo_mono->data, DIV_ROUND_UP(logo_width, 8), fg_color); diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index 73fcb899a01d..46f09cf68458 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -238,6 +238,21 @@ static inline void drm_rect_fp_to_int(struct drm_rect *dst, drm_rect_height(src) >> 16); } +/** + * drm_rect_overlap - Check if two rectangles overlap + * @a: first rectangle + * @b: second rectangle + * + * RETURNS: + * %true if the rectangles overlap, %false otherwise. + */ +static inline bool drm_rect_overlap(const struct drm_rect *a, + const struct drm_rect *b) +{ + return (a->x2 > b->x1 && b->x2 > a->x1 && + a->y2 > b->y1 && b->y2 > a->y1); +} + bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip); bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, const struct drm_rect *clip); -- cgit v1.2.3 From 8f4eca6ac52a72181b4f054d4ef6289a5d8cfa5d Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Thu, 22 Aug 2024 09:33:56 +0200 Subject: drm/panic: Simplify logo handling Move logo rectangle initialisation, and logo drawing in separate functions, so they can be re-used by different panic screens. It prepares the introduction of the QR code panic screen. Signed-off-by: Jocelyn Falempe Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20240822073852.562286-4-jfalempe@redhat.com --- drivers/gpu/drm/drm_panic.c | 58 ++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 25 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index 59fba23e5fd7..79a3c902a98b 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -85,7 +85,7 @@ static struct drm_panic_line panic_msg[] = { PANIC_LINE(""), /* will be replaced by the panic description */ }; -#define PANIC_MSG_LINES ARRAY_SIZE(panic_msg) +static const size_t panic_msg_lines = ARRAY_SIZE(panic_msg); static const struct drm_panic_line logo_ascii[] = { PANIC_LINE(" .--. _"), @@ -97,7 +97,7 @@ static const struct drm_panic_line logo_ascii[] = { PANIC_LINE(" \\___)=(___/"), }; -#define PANIC_LOGO_LINES ARRAY_SIZE(logo_ascii) +static const size_t logo_ascii_lines = ARRAY_SIZE(logo_ascii); #if defined(CONFIG_LOGO) && !defined(MODULE) static const struct linux_logo *logo_mono; @@ -496,31 +496,44 @@ static void draw_txt_rectangle(struct drm_scanout_buffer *sb, } } +static void drm_panic_logo_rect(struct drm_rect *rect, const struct font_desc *font) +{ + if (logo_mono) { + drm_rect_init(rect, 0, 0, logo_mono->width, logo_mono->height); + } else { + int logo_width = get_max_line_len(logo_ascii, logo_ascii_lines) * font->width; + + drm_rect_init(rect, 0, 0, logo_width, logo_ascii_lines * font->height); + } +} + +static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *rect, + const struct font_desc *font, u32 fg_color) +{ + if (logo_mono) + drm_panic_blit(sb, rect, logo_mono->data, + DIV_ROUND_UP(drm_rect_width(rect), 8), 1, fg_color); + else + draw_txt_rectangle(sb, font, logo_ascii, logo_ascii_lines, false, rect, + fg_color); +} + static void draw_panic_static_user(struct drm_scanout_buffer *sb) { u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format); u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format); const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); struct drm_rect r_screen, r_logo, r_msg; - unsigned int logo_width, logo_height; unsigned int msg_width, msg_height; if (!font) return; r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height); + drm_panic_logo_rect(&r_logo, font); - if (logo_mono) { - logo_width = logo_mono->width; - logo_height = logo_mono->height; - } else { - logo_width = get_max_line_len(logo_ascii, PANIC_LOGO_LINES) * font->width; - logo_height = PANIC_LOGO_LINES * font->height; - } - r_logo = DRM_RECT_INIT(0, 0, logo_width, logo_height); - - msg_width = min(get_max_line_len(panic_msg, PANIC_MSG_LINES) * font->width, sb->width); - msg_height = min(PANIC_MSG_LINES * font->height, sb->height); + msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width); + msg_height = min(panic_msg_lines * font->height, sb->height); r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height); /* Center the panic message */ @@ -529,15 +542,10 @@ static void draw_panic_static_user(struct drm_scanout_buffer *sb) /* Fill with the background color, and draw text on top */ drm_panic_fill(sb, &r_screen, bg_color); - if (!drm_rect_overlap(&r_logo, &r_msg)) { - if (logo_mono) - drm_panic_blit(sb, &r_logo, logo_mono->data, DIV_ROUND_UP(logo_width, 8), - fg_color); - else - draw_txt_rectangle(sb, font, logo_ascii, PANIC_LOGO_LINES, false, &r_logo, - fg_color); - } - draw_txt_rectangle(sb, font, panic_msg, PANIC_MSG_LINES, true, &r_msg, fg_color); + if (!drm_rect_overlap(&r_logo, &r_msg)) + drm_panic_logo_draw(sb, &r_logo, font, fg_color); + + draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color); } /* @@ -647,7 +655,7 @@ static void drm_panic_set_description(const char *description) u32 len; if (description) { - struct drm_panic_line *desc_line = &panic_msg[PANIC_MSG_LINES - 1]; + struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1]; desc_line->txt = description; len = strlen(description); @@ -660,7 +668,7 @@ static void drm_panic_set_description(const char *description) static void drm_panic_clear_description(void) { - struct drm_panic_line *desc_line = &panic_msg[PANIC_MSG_LINES - 1]; + struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1]; desc_line->len = 0; desc_line->txt = NULL; -- cgit v1.2.3 From cb5164ac43d0fc37ac6b45cabbc4d244068289ef Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Thu, 22 Aug 2024 09:33:57 +0200 Subject: drm/panic: Add a QR code panic screen This patch adds a new panic screen, with a QR code and the kmsg data embedded. If DRM_PANIC_SCREEN_QR_CODE_URL is set, then the kmsg data will be compressed with zlib and encoded as a numerical segment, and appended to the URL as a URL parameter. This allows to save space, and put about ~7500 bytes of kmsg data, in a V40 QR code. Linux distributions can customize the URL, and put a web frontend to directly open a bug report with the kmsg data. Otherwise the kmsg data will be encoded as a binary segment (ie raw ascii) and only a maximum of 2953 bytes of kmsg data will be available in the QR code. You can also limit the QR code size with DRM_PANIC_SCREEN_QR_VERSION. Signed-off-by: Jocelyn Falempe Reviewed-by: Alice Ryhl Link: https://patchwork.freedesktop.org/patch/msgid/20240822073852.562286-5-jfalempe@redhat.com --- drivers/gpu/drm/Kconfig | 31 ++ drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_crtc_internal.h | 4 + drivers/gpu/drm/drm_drv.c | 3 + drivers/gpu/drm/drm_panic.c | 248 +++++++++ drivers/gpu/drm/drm_panic_qr.rs | 1003 +++++++++++++++++++++++++++++++++++ 6 files changed, 1290 insertions(+) create mode 100644 drivers/gpu/drm/drm_panic_qr.rs (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0387143bbb39..1cb5a4f19293 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -149,6 +149,37 @@ config DRM_PANIC_SCREEN or by writing to /sys/module/drm/parameters/panic_screen sysfs entry Default is "user" +config DRM_PANIC_SCREEN_QR_CODE + bool "Add a panic screen with a QR code" + depends on DRM_PANIC && RUST + help + This option adds a QR code generator, and a panic screen with a QR + code. The QR code will contain the last lines of kmsg and other debug + information. This should be easier for the user to report a kernel + panic, with all debug information available. + To use this panic screen, also set DRM_PANIC_SCREEN to "qr_code" + +config DRM_PANIC_SCREEN_QR_CODE_URL + string "Base URL of the QR code in the panic screen" + depends on DRM_PANIC_SCREEN_QR_CODE + help + This option sets the base URL to report the kernel panic. If it's set + the QR code will contain the URL and the kmsg compressed with zlib as + a URL parameter. If it's empty, the QR code will contain the kmsg as + uncompressed text only. + There is a demo code in javascript, to decode and uncompress the kmsg + data from the URL parameter at https://github.com/kdj0c/panic_report + +config DRM_PANIC_SCREEN_QR_VERSION + int "Maximum version (size) of the QR code." + depends on DRM_PANIC_SCREEN_QR_CODE + default 40 + help + This option limits the version (or size) of the QR code. QR code + version ranges from Version 1 (21x21) to Version 40 (177x177). + Smaller QR code are easier to read, but will contain less debugging + data. Default is 40. + config DRM_DEBUG_DP_MST_TOPOLOGY_REFS bool "Enable refcount backtrace history in the DP MST helpers" depends on STACKTRACE_SUPPORT diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 68cc9258ffc4..c62339b89d46 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -89,6 +89,7 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \ drm_privacy_screen_x86.o drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o drm-$(CONFIG_DRM_PANIC) += drm_panic.o +drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o obj-$(CONFIG_DRM) += drm.o obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 251ff7bba40d..89706aa8232f 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -320,10 +320,14 @@ drm_edid_load_firmware(struct drm_connector *connector) bool drm_panic_is_enabled(struct drm_device *dev); void drm_panic_register(struct drm_device *dev); void drm_panic_unregister(struct drm_device *dev); +void drm_panic_init(void); +void drm_panic_exit(void); #else static inline bool drm_panic_is_enabled(struct drm_device *dev) { return false; } static inline void drm_panic_register(struct drm_device *dev) {} static inline void drm_panic_unregister(struct drm_device *dev) {} +static inline void drm_panic_init(void) {} +static inline void drm_panic_exit(void) {} #endif #endif /* __DRM_CRTC_INTERNAL_H__ */ diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 93543071a500..27007b53a8c8 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -1067,6 +1067,7 @@ static const struct file_operations drm_stub_fops = { static void drm_core_exit(void) { drm_privacy_screen_lookup_exit(); + drm_panic_exit(); accel_core_exit(); unregister_chrdev(DRM_MAJOR, "drm"); debugfs_remove(drm_debugfs_root); @@ -1099,6 +1100,8 @@ static int __init drm_core_init(void) if (ret < 0) goto error; + drm_panic_init(); + drm_privacy_screen_lookup_init(); drm_core_init_complete = true; diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index 79a3c902a98b..74412b7bf936 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -26,6 +28,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" @@ -627,6 +630,233 @@ static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb) } } +#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE) +/* + * It is unwise to allocate memory in the panic callback, so the buffers are + * pre-allocated. Only 2 buffers and the zlib workspace are needed. + * Two buffers are enough, using the following buffer usage: + * 1) kmsg messages are dumped in buffer1 + * 2) kmsg is zlib-compressed into buffer2 + * 3) compressed kmsg is encoded as QR-code Numeric stream in buffer1 + * 4) QR-code image is generated in buffer2 + * The Max QR code size is V40, 177x177, 4071 bytes for image, 2956 bytes for + * data segments. + * + * Typically, ~7500 bytes of kmsg, are compressed into 2800 bytes, which fits in + * a V40 QR-code (177x177). + * + * If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data will be put + * directly in the QR code. + * 1) kmsg messages are dumped in buffer1 + * 2) kmsg message is encoded as byte stream in buffer2 + * 3) QR-code image is generated in buffer1 + */ + +static uint panic_qr_version = CONFIG_DRM_PANIC_SCREEN_QR_VERSION; +module_param(panic_qr_version, uint, 0644); +MODULE_PARM_DESC(panic_qr_version, "maximum version (size) of the QR code"); + +#define MAX_QR_DATA 2956 +#define MAX_ZLIB_RATIO 3 +#define QR_BUFFER1_SIZE (MAX_ZLIB_RATIO * MAX_QR_DATA) /* Must also be > 4071 */ +#define QR_BUFFER2_SIZE 4096 +#define QR_MARGIN 4 /* 4 modules of foreground color around the qr code */ + +/* Compression parameters */ +#define COMPR_LEVEL 6 +#define WINDOW_BITS 12 +#define MEM_LEVEL 4 + +static char *qrbuf1; +static char *qrbuf2; +static struct z_stream_s stream; + +static void __init drm_panic_qr_init(void) +{ + qrbuf1 = kmalloc(QR_BUFFER1_SIZE, GFP_KERNEL); + qrbuf2 = kmalloc(QR_BUFFER2_SIZE, GFP_KERNEL); + stream.workspace = kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL), + GFP_KERNEL); +} + +static void drm_panic_qr_exit(void) +{ + kfree(qrbuf1); + qrbuf1 = NULL; + kfree(qrbuf2); + qrbuf2 = NULL; + kfree(stream.workspace); + stream.workspace = NULL; +} + +extern size_t drm_panic_qr_max_data_size(u8 version, size_t url_len); + +extern u8 drm_panic_qr_generate(const char *url, u8 *data, size_t data_len, size_t data_size, + u8 *tmp, size_t tmp_size); + +static int drm_panic_get_qr_code_url(u8 **qr_image) +{ + struct kmsg_dump_iter iter; + char url[256]; + size_t kmsg_len, max_kmsg_size; + char *kmsg; + int max_qr_data_size, url_len; + + url_len = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&zl=", + utsname()->machine, utsname()->release); + + max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len); + max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, QR_BUFFER1_SIZE); + + /* get kmsg to buffer 1 */ + kmsg_dump_rewind(&iter); + kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len); + + if (!kmsg_len) + return -ENODATA; + kmsg = qrbuf1; + +try_again: + if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, + MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) + return -EINVAL; + + stream.next_in = kmsg; + stream.avail_in = kmsg_len; + stream.total_in = 0; + stream.next_out = qrbuf2; + stream.avail_out = QR_BUFFER2_SIZE; + stream.total_out = 0; + + if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END) + return -EINVAL; + + if (zlib_deflateEnd(&stream) != Z_OK) + return -EINVAL; + + if (stream.total_out > max_qr_data_size) { + /* too much data for the QR code, so skip the first line and try again */ + kmsg = strchr(kmsg, '\n'); + if (!kmsg) + return -EINVAL; + /* skip the first \n */ + kmsg += 1; + kmsg_len = strlen(kmsg); + goto try_again; + } + *qr_image = qrbuf2; + + /* generate qr code image in buffer2 */ + return drm_panic_qr_generate(url, qrbuf2, stream.total_out, QR_BUFFER2_SIZE, + qrbuf1, QR_BUFFER1_SIZE); +} + +static int drm_panic_get_qr_code_raw(u8 **qr_image) +{ + struct kmsg_dump_iter iter; + size_t kmsg_len; + size_t max_kmsg_size = min(drm_panic_qr_max_data_size(panic_qr_version, 0), + QR_BUFFER1_SIZE); + + kmsg_dump_rewind(&iter); + kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len); + if (!kmsg_len) + return -ENODATA; + + *qr_image = qrbuf1; + return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, QR_BUFFER1_SIZE, + qrbuf2, QR_BUFFER2_SIZE); +} + +static int drm_panic_get_qr_code(u8 **qr_image) +{ + if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0) + return drm_panic_get_qr_code_url(qr_image); + else + return drm_panic_get_qr_code_raw(qr_image); +} + +/* + * Draw the panic message at the center of the screen, with a QR Code + */ +static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb) +{ + u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format); + u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format); + const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); + struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas; + unsigned int max_qr_size, scale; + unsigned int msg_width, msg_height; + int qr_width, qr_canvas_width, qr_pitch, v_margin; + u8 *qr_image; + + if (!font || !qrbuf1 || !qrbuf2 || !stream.workspace) + return -ENOMEM; + + r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height); + + drm_panic_logo_rect(&r_logo, font); + + msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width); + msg_height = min(panic_msg_lines * font->height, sb->height); + r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height); + + max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4); + + qr_width = drm_panic_get_qr_code(&qr_image); + if (qr_width <= 0) + return -ENOSPC; + + qr_canvas_width = qr_width + QR_MARGIN * 2; + scale = max_qr_size / qr_canvas_width; + /* QR code is not readable if not scaled at least by 2 */ + if (scale < 2) + return -ENOSPC; + + pr_debug("QR width %d and scale %d\n", qr_width, scale); + r_qr_canvas = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_width * scale); + + v_margin = (sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg)) / 5; + + drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_margin); + r_qr = DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, r_qr_canvas.y1 + QR_MARGIN * scale, + qr_width * scale, qr_width * scale); + + /* Center the panic message */ + drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, + 3 * v_margin + drm_rect_height(&r_qr_canvas)); + + /* Fill with the background color, and draw text on top */ + drm_panic_fill(sb, &r_screen, bg_color); + + if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr)) + drm_panic_logo_draw(sb, &r_logo, font, fg_color); + + draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color); + + /* Draw the qr code */ + qr_pitch = DIV_ROUND_UP(qr_width, 8); + drm_panic_fill(sb, &r_qr_canvas, fg_color); + drm_panic_fill(sb, &r_qr, bg_color); + drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color); + return 0; +} + +static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb) +{ + if (_draw_panic_static_qr_code(sb)) + draw_panic_static_user(sb); +} +#else +static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb) +{ + draw_panic_static_user(sb); +} + +static void drm_panic_qr_init(void) {}; +static void drm_panic_qr_exit(void) {}; +#endif + /* * drm_panic_is_format_supported() * @format: a fourcc color code @@ -645,6 +875,8 @@ static void draw_panic_dispatch(struct drm_scanout_buffer *sb) { if (!strcmp(drm_panic_screen, "kmsg")) { draw_panic_static_kmsg(sb); + } else if (!strcmp(drm_panic_screen, "qr_code")) { + draw_panic_static_qr_code(sb); } else { draw_panic_static_user(sb); } @@ -814,3 +1046,19 @@ void drm_panic_unregister(struct drm_device *dev) kmsg_dump_unregister(&plane->kmsg_panic); } } + +/** + * drm_panic_init() - initialize DRM panic. + */ +void __init drm_panic_init(void) +{ + drm_panic_qr_init(); +} + +/** + * drm_panic_exit() - Free the resources taken by drm_panic_exit() + */ +void drm_panic_exit(void) +{ + drm_panic_qr_exit(); +} diff --git a/drivers/gpu/drm/drm_panic_qr.rs b/drivers/gpu/drm/drm_panic_qr.rs new file mode 100644 index 000000000000..1ef56cb07dfb --- /dev/null +++ b/drivers/gpu/drm/drm_panic_qr.rs @@ -0,0 +1,1003 @@ +// SPDX-License-Identifier: MIT + +//! This is a simple QR encoder for DRM panic. +//! +//! It is called from a panic handler, so it should't allocate memory and +//! does all the work on the stack or on the provided buffers. For +//! simplification, it only supports low error correction, and applies the +//! first mask (checkerboard). It will draw the smallest QRcode that can +//! contain the string passed as parameter. To get the most compact +//! QR code, the start of the URL is encoded as binary, and the +//! compressed kmsg is encoded as numeric. +//! +//! The binary data must be a valid URL parameter, so the easiest way is +//! to use base64 encoding. But this wastes 25% of data space, so the +//! whole stack trace won't fit in the QR code. So instead it encodes +//! every 13bits of input into 4 decimal digits, and then uses the +//! efficient numeric encoding, that encode 3 decimal digits into +//! 10bits. This makes 39bits of compressed data into 12 decimal digits, +//! into 40bits in the QR code, so wasting only 2.5%. And the numbers are +//! valid URL parameter, so the website can do the reverse, to get the +//! binary data. +//! +//! Inspired by these 3 projects, all under MIT license: +//! +//! * +//! * +//! * + +use core::cmp; +use kernel::str::CStr; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] +struct Version(usize); + +// Generator polynomials for ECC, only those that are needed for low quality. +const P7: [u8; 7] = [87, 229, 146, 149, 238, 102, 21]; +const P10: [u8; 10] = [251, 67, 46, 61, 118, 70, 64, 94, 32, 45]; +const P15: [u8; 15] = [ + 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105, +]; +const P18: [u8; 18] = [ + 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 96, 153, +]; +const P20: [u8; 20] = [ + 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 212, 212, 188, 190, +]; +const P22: [u8; 22] = [ + 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80, 219, 134, 160, 105, + 165, 231, +]; +const P24: [u8; 24] = [ + 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 111, 0, 117, + 232, 87, 96, 227, 21, +]; +const P26: [u8; 26] = [ + 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, 21, 245, 142, 13, 102, + 48, 227, 153, 145, 218, 70, +]; +const P28: [u8; 28] = [ + 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, 21, 43, 245, 87, + 42, 195, 212, 119, 242, 37, 9, 123, +]; +const P30: [u8; 30] = [ + 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 42, 173, 226, 193, + 224, 130, 156, 37, 251, 216, 238, 40, 192, 180, +]; + +/// QR Code parameters for Low quality ECC: +/// - Error Correction polynomial. +/// - Number of blocks in group 1. +/// - Number of blocks in group 2. +/// - Block size in group 1. +/// +/// (Block size in group 2 is one more than group 1). +struct VersionParameter(&'static [u8], u8, u8, u8); +const VPARAM: [VersionParameter; 40] = [ + VersionParameter(&P7, 1, 0, 19), // V1 + VersionParameter(&P10, 1, 0, 34), // V2 + VersionParameter(&P15, 1, 0, 55), // V3 + VersionParameter(&P20, 1, 0, 80), // V4 + VersionParameter(&P26, 1, 0, 108), // V5 + VersionParameter(&P18, 2, 0, 68), // V6 + VersionParameter(&P20, 2, 0, 78), // V7 + VersionParameter(&P24, 2, 0, 97), // V8 + VersionParameter(&P30, 2, 0, 116), // V9 + VersionParameter(&P18, 2, 2, 68), // V10 + VersionParameter(&P20, 4, 0, 81), // V11 + VersionParameter(&P24, 2, 2, 92), // V12 + VersionParameter(&P26, 4, 0, 107), // V13 + VersionParameter(&P30, 3, 1, 115), // V14 + VersionParameter(&P22, 5, 1, 87), // V15 + VersionParameter(&P24, 5, 1, 98), // V16 + VersionParameter(&P28, 1, 5, 107), // V17 + VersionParameter(&P30, 5, 1, 120), // V18 + VersionParameter(&P28, 3, 4, 113), // V19 + VersionParameter(&P28, 3, 5, 107), // V20 + VersionParameter(&P28, 4, 4, 116), // V21 + VersionParameter(&P28, 2, 7, 111), // V22 + VersionParameter(&P30, 4, 5, 121), // V23 + VersionParameter(&P30, 6, 4, 117), // V24 + VersionParameter(&P26, 8, 4, 106), // V25 + VersionParameter(&P28, 10, 2, 114), // V26 + VersionParameter(&P30, 8, 4, 122), // V27 + VersionParameter(&P30, 3, 10, 117), // V28 + VersionParameter(&P30, 7, 7, 116), // V29 + VersionParameter(&P30, 5, 10, 115), // V30 + VersionParameter(&P30, 13, 3, 115), // V31 + VersionParameter(&P30, 17, 0, 115), // V32 + VersionParameter(&P30, 17, 1, 115), // V33 + VersionParameter(&P30, 13, 6, 115), // V34 + VersionParameter(&P30, 12, 7, 121), // V35 + VersionParameter(&P30, 6, 14, 121), // V36 + VersionParameter(&P30, 17, 4, 122), // V37 + VersionParameter(&P30, 4, 18, 122), // V38 + VersionParameter(&P30, 20, 4, 117), // V39 + VersionParameter(&P30, 19, 6, 118), // V40 +]; + +const MAX_EC_SIZE: usize = 30; +const MAX_BLK_SIZE: usize = 123; + +/// Position of the alignment pattern grid. +const ALIGNMENT_PATTERNS: [&[u8]; 40] = [ + &[], + &[6, 18], + &[6, 22], + &[6, 26], + &[6, 30], + &[6, 34], + &[6, 22, 38], + &[6, 24, 42], + &[6, 26, 46], + &[6, 28, 50], + &[6, 30, 54], + &[6, 32, 58], + &[6, 34, 62], + &[6, 26, 46, 66], + &[6, 26, 48, 70], + &[6, 26, 50, 74], + &[6, 30, 54, 78], + &[6, 30, 56, 82], + &[6, 30, 58, 86], + &[6, 34, 62, 90], + &[6, 28, 50, 72, 94], + &[6, 26, 50, 74, 98], + &[6, 30, 54, 78, 102], + &[6, 28, 54, 80, 106], + &[6, 32, 58, 84, 110], + &[6, 30, 58, 86, 114], + &[6, 34, 62, 90, 118], + &[6, 26, 50, 74, 98, 122], + &[6, 30, 54, 78, 102, 126], + &[6, 26, 52, 78, 104, 130], + &[6, 30, 56, 82, 108, 134], + &[6, 34, 60, 86, 112, 138], + &[6, 30, 58, 86, 114, 142], + &[6, 34, 62, 90, 118, 146], + &[6, 30, 54, 78, 102, 126, 150], + &[6, 24, 50, 76, 102, 128, 154], + &[6, 28, 54, 80, 106, 132, 158], + &[6, 32, 58, 84, 110, 136, 162], + &[6, 26, 54, 82, 110, 138, 166], + &[6, 30, 58, 86, 114, 142, 170], +]; + +/// Version information for format V7-V40. +const VERSION_INFORMATION: [u32; 34] = [ + 0b00_0111_1100_1001_0100, + 0b00_1000_0101_1011_1100, + 0b00_1001_1010_1001_1001, + 0b00_1010_0100_1101_0011, + 0b00_1011_1011_1111_0110, + 0b00_1100_0111_0110_0010, + 0b00_1101_1000_0100_0111, + 0b00_1110_0110_0000_1101, + 0b00_1111_1001_0010_1000, + 0b01_0000_1011_0111_1000, + 0b01_0001_0100_0101_1101, + 0b01_0010_1010_0001_0111, + 0b01_0011_0101_0011_0010, + 0b01_0100_1001_1010_0110, + 0b01_0101_0110_1000_0011, + 0b01_0110_1000_1100_1001, + 0b01_0111_0111_1110_1100, + 0b01_1000_1110_1100_0100, + 0b01_1001_0001_1110_0001, + 0b01_1010_1111_1010_1011, + 0b01_1011_0000_1000_1110, + 0b01_1100_1100_0001_1010, + 0b01_1101_0011_0011_1111, + 0b01_1110_1101_0111_0101, + 0b01_1111_0010_0101_0000, + 0b10_0000_1001_1101_0101, + 0b10_0001_0110_1111_0000, + 0b10_0010_1000_1011_1010, + 0b10_0011_0111_1001_1111, + 0b10_0100_1011_0000_1011, + 0b10_0101_0100_0010_1110, + 0b10_0110_1010_0110_0100, + 0b10_0111_0101_0100_0001, + 0b10_1000_1100_0110_1001, +]; + +/// Format info for low quality ECC. +const FORMAT_INFOS_QR_L: [u16; 8] = [ + 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, +]; + +impl Version { + /// Returns the smallest QR version than can hold these segments. + fn from_segments(segments: &[&Segment<'_>]) -> Option { + for v in (1..=40).map(|k| Version(k)) { + if v.max_data() * 8 >= segments.iter().map(|s| s.total_size_bits(v)).sum() { + return Some(v); + } + } + None + } + + fn width(&self) -> u8 { + (self.0 as u8) * 4 + 17 + } + + fn max_data(&self) -> usize { + self.g1_blk_size() * self.g1_blocks() + (self.g1_blk_size() + 1) * self.g2_blocks() + } + + fn ec_size(&self) -> usize { + VPARAM[self.0 - 1].0.len() + } + + fn g1_blocks(&self) -> usize { + VPARAM[self.0 - 1].1 as usize + } + + fn g2_blocks(&self) -> usize { + VPARAM[self.0 - 1].2 as usize + } + + fn g1_blk_size(&self) -> usize { + VPARAM[self.0 - 1].3 as usize + } + + fn alignment_pattern(&self) -> &'static [u8] { + &ALIGNMENT_PATTERNS[self.0 - 1] + } + + fn poly(&self) -> &'static [u8] { + VPARAM[self.0 - 1].0 + } + + fn version_info(&self) -> u32 { + if *self >= Version(7) { + VERSION_INFORMATION[self.0 - 7] + } else { + 0 + } + } +} + +/// Exponential table for Galois Field GF(256). +const EXP_TABLE: [u8; 256] = [ + 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, + 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, + 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, + 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, + 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, + 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, + 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, + 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, + 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, + 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, + 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, + 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, + 131, 27, 54, 108, 216, 173, 71, 142, 1, +]; + +/// Reverse exponential table for Galois Field GF(256). +const LOG_TABLE: [u8; 256] = [ + 175, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, + 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, + 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, + 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, + 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, + 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, + 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, + 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, + 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, + 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, + 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, + 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, + 232, 116, 214, 244, 234, 168, 80, 88, 175, +]; + +// 4 bits segment header. +const MODE_STOP: u16 = 0; +const MODE_NUMERIC: u16 = 1; +const MODE_BINARY: u16 = 4; +/// Padding bytes. +const PADDING: [u8; 2] = [236, 17]; + +/// Get the next 13 bits of data, starting at specified offset (in bits). +fn get_next_13b(data: &[u8], offset: usize) -> Option<(u16, usize)> { + if offset < data.len() * 8 { + let size = cmp::min(13, data.len() * 8 - offset); + let byte_off = offset / 8; + let bit_off = offset % 8; + // `b` is 20 at max (`bit_off` <= 7 and `size` <= 13). + let b = (bit_off + size) as u16; + + let first_byte = (data[byte_off] << bit_off >> bit_off) as u16; + + let number = match b { + 0..=8 => first_byte >> (8 - b), + 9..=16 => (first_byte << (b - 8)) + (data[byte_off + 1] >> (16 - b)) as u16, + _ => { + (first_byte << (b - 8)) + + ((data[byte_off + 1] as u16) << (b - 16)) + + (data[byte_off + 2] >> (24 - b)) as u16 + } + }; + Some((number, size)) + } else { + None + } +} + +/// Number of bits to encode characters in numeric mode. +const NUM_CHARS_BITS: [usize; 4] = [0, 4, 7, 10]; +const POW10: [u16; 4] = [1, 10, 100, 1000]; + +enum Segment<'a> { + Numeric(&'a [u8]), + Binary(&'a [u8]), +} + +impl Segment<'_> { + fn get_header(&self) -> (u16, usize) { + match self { + Segment::Binary(_) => (MODE_BINARY, 4), + Segment::Numeric(_) => (MODE_NUMERIC, 4), + } + } + + // Returns the size of the length field in bits, depending on QR Version. + fn length_bits_count(&self, version: Version) -> usize { + let Version(v) = version; + match self { + Segment::Binary(_) => match v { + 1..=9 => 8, + _ => 16, + }, + Segment::Numeric(_) => match v { + 1..=9 => 10, + 10..=26 => 12, + _ => 14, + }, + } + } + + // Number of characters in the segment. + fn character_count(&self) -> usize { + match self { + Segment::Binary(data) => data.len(), + Segment::Numeric(data) => { + let data_bits = data.len() * 8; + let last_chars = match data_bits % 13 { + 1 => 1, + k => (k + 1) / 3, + }; + // 4 decimal numbers per 13bits + remainder. + 4 * (data_bits / 13) + last_chars + } + } + } + + fn get_length_field(&self, version: Version) -> (u16, usize) { + ( + self.character_count() as u16, + self.length_bits_count(version), + ) + } + + fn total_size_bits(&self, version: Version) -> usize { + let data_size = match self { + Segment::Binary(data) => data.len() * 8, + Segment::Numeric(_) => { + let digits = self.character_count(); + 10 * (digits / 3) + NUM_CHARS_BITS[digits % 3] + } + }; + // header + length + data. + 4 + self.length_bits_count(version) + data_size + } + + fn iter(&self) -> SegmentIterator<'_> { + SegmentIterator { + segment: self, + offset: 0, + carry: 0, + carry_len: 0, + } + } +} + +struct SegmentIterator<'a> { + segment: &'a Segment<'a>, + offset: usize, + carry: u16, + carry_len: usize, +} + +impl Iterator for SegmentIterator<'_> { + type Item = (u16, usize); + + fn next(&mut self) -> Option { + match self.segment { + Segment::Binary(data) => { + if self.offset < data.len() { + let byte = data[self.offset] as u16; + self.offset += 1; + Some((byte, 8)) + } else { + None + } + } + Segment::Numeric(data) => { + if self.carry_len == 3 { + let out = (self.carry, NUM_CHARS_BITS[self.carry_len]); + self.carry_len = 0; + self.carry = 0; + Some(out) + } else if let Some((bits, size)) = get_next_13b(data, self.offset) { + self.offset += size; + let new_chars = match size { + 1 => 1, + k => (k + 1) / 3, + }; + if self.carry_len + new_chars > 3 { + self.carry_len = new_chars + self.carry_len - 3; + let out = ( + self.carry * POW10[new_chars - self.carry_len] + + bits / POW10[self.carry_len], + NUM_CHARS_BITS[3], + ); + self.carry = bits % POW10[self.carry_len]; + Some(out) + } else { + let out = ( + self.carry * POW10[new_chars] + bits, + NUM_CHARS_BITS[self.carry_len + new_chars], + ); + self.carry_len = 0; + Some(out) + } + } else if self.carry_len > 0 { + let out = (self.carry, NUM_CHARS_BITS[self.carry_len]); + self.carry_len = 0; + Some(out) + } else { + None + } + } + } + } +} + +struct EncodedMsg<'a> { + data: &'a mut [u8], + ec_size: usize, + g1_blocks: usize, + g2_blocks: usize, + g1_blk_size: usize, + g2_blk_size: usize, + poly: &'static [u8], + version: Version, +} + +/// Data to be put in the QR code, with correct segment encoding, padding, and +/// Error Code Correction. +impl EncodedMsg<'_> { + fn new<'a, 'b>(segments: &[&Segment<'b>], data: &'a mut [u8]) -> Option> { + let version = Version::from_segments(segments)?; + let ec_size = version.ec_size(); + let g1_blocks = version.g1_blocks(); + let g2_blocks = version.g2_blocks(); + let g1_blk_size = version.g1_blk_size(); + let g2_blk_size = g1_blk_size + 1; + let poly = version.poly(); + + // clear the output. + data.fill(0); + + let mut em = EncodedMsg { + data: data, + ec_size, + g1_blocks, + g2_blocks, + g1_blk_size, + g2_blk_size, + poly, + version, + }; + em.encode(segments); + Some(em) + } + + /// Push bits of data at an offset (in bits). + fn push(&mut self, offset: &mut usize, bits: (u16, usize)) { + let (number, len_bits) = bits; + let byte_off = *offset / 8; + let bit_off = *offset % 8; + let b = bit_off + len_bits; + + match (bit_off, b) { + (0, 0..=8) => { + self.data[byte_off] = (number << (8 - b)) as u8; + } + (0, _) => { + self.data[byte_off] = (number >> (b - 8)) as u8; + self.data[byte_off + 1] = (number << (16 - b)) as u8; + } + (_, 0..=8) => { + self.data[byte_off] |= (number << (8 - b)) as u8; + } + (_, 9..=16) => { + self.data[byte_off] |= (number >> (b - 8)) as u8; + self.data[byte_off + 1] = (number << (16 - b)) as u8; + } + _ => { + self.data[byte_off] |= (number >> (b - 8)) as u8; + self.data[byte_off + 1] = (number >> (b - 16)) as u8; + self.data[byte_off + 2] = (number << (24 - b)) as u8; + } + } + *offset += len_bits; + } + + fn add_segments(&mut self, segments: &[&Segment<'_>]) { + let mut offset: usize = 0; + + for s in segments.iter() { + self.push(&mut offset, s.get_header()); + self.push(&mut offset, s.get_length_field(self.version)); + for bits in s.iter() { + self.push(&mut offset, bits); + } + } + self.push(&mut offset, (MODE_STOP, 4)); + + let pad_offset = (offset + 7) / 8; + for i in pad_offset..self.version.max_data() { + self.data[i] = PADDING[(i & 1) ^ (pad_offset & 1)]; + } + } + + fn error_code_for_blocks(&mut self, offset: usize, size: usize, ec_offset: usize) { + let mut tmp: [u8; MAX_BLK_SIZE + MAX_EC_SIZE] = [0; MAX_BLK_SIZE + MAX_EC_SIZE]; + + tmp[0..size].copy_from_slice(&self.data[offset..offset + size]); + for i in 0..size { + let lead_coeff = tmp[i] as usize; + if lead_coeff == 0 { + continue; + } + let log_lead_coeff = usize::from(LOG_TABLE[lead_coeff]); + for (u, &v) in tmp[i + 1..].iter_mut().zip(self.poly.iter()) { + *u ^= EXP_TABLE[(usize::from(v) + log_lead_coeff) % 255]; + } + } + self.data[ec_offset..ec_offset + self.ec_size] + .copy_from_slice(&tmp[size..size + self.ec_size]); + } + + fn compute_error_code(&mut self) { + let mut offset = 0; + let mut ec_offset = self.g1_blocks * self.g1_blk_size + self.g2_blocks * self.g2_blk_size; + + for _ in 0..self.g1_blocks { + self.error_code_for_blocks(offset, self.g1_blk_size, ec_offset); + offset += self.g1_blk_size; + ec_offset += self.ec_size; + } + for _ in 0..self.g2_blocks { + self.error_code_for_blocks(offset, self.g2_blk_size, ec_offset); + offset += self.g2_blk_size; + ec_offset += self.ec_size; + } + } + + fn encode(&mut self, segments: &[&Segment<'_>]) { + self.add_segments(segments); + self.compute_error_code(); + } + + fn iter(&self) -> EncodedMsgIterator<'_> { + EncodedMsgIterator { + em: self, + offset: 0, + } + } +} + +/// Iterator, to retrieve the data in the interleaved order needed by QR code. +struct EncodedMsgIterator<'a> { + em: &'a EncodedMsg<'a>, + offset: usize, +} + +impl Iterator for EncodedMsgIterator<'_> { + type Item = u8; + + // Send the bytes in interleaved mode, first byte of first block of group1, + // then first byte of second block of group1, ... + fn next(&mut self) -> Option { + let em = self.em; + let blocks = em.g1_blocks + em.g2_blocks; + let g1_end = em.g1_blocks * em.g1_blk_size; + let g2_end = g1_end + em.g2_blocks * em.g2_blk_size; + let ec_end = g2_end + em.ec_size * blocks; + + if self.offset >= ec_end { + return None; + } + + let offset = if self.offset < em.g1_blk_size * blocks { + // group1 and group2 interleaved + let blk = self.offset % blocks; + let blk_off = self.offset / blocks; + if blk < em.g1_blocks { + blk * em.g1_blk_size + blk_off + } else { + g1_end + em.g2_blk_size * (blk - em.g1_blocks) + blk_off + } + } else if self.offset < g2_end { + // last byte of group2 blocks + let blk2 = self.offset - blocks * em.g1_blk_size; + em.g1_blk_size * em.g1_blocks + blk2 * em.g2_blk_size + em.g2_blk_size - 1 + } else { + // EC blocks + let ec_offset = self.offset - g2_end; + let blk = ec_offset % blocks; + let blk_off = ec_offset / blocks; + + g2_end + blk * em.ec_size + blk_off + }; + self.offset += 1; + Some(em.data[offset]) + } +} + +/// A QR code image, encoded as a linear binary framebuffer. +/// 1 bit per module (pixel), each new line start at next byte boundary. +/// Max width is 177 for V40 QR code, so `u8` is enough for coordinate. +struct QrImage<'a> { + data: &'a mut [u8], + width: u8, + stride: u8, + version: Version, +} + +impl QrImage<'_> { + fn new<'a, 'b>(em: &'b EncodedMsg<'b>, qrdata: &'a mut [u8]) -> QrImage<'a> { + let width = em.version.width(); + let stride = (width + 7) / 8; + let data = qrdata; + + let mut qr_image = QrImage { + data, + width, + stride, + version: em.version, + }; + qr_image.draw_all(em.iter()); + qr_image + } + + fn clear(&mut self) { + self.data.fill(0); + } + + // Set pixel to light color. + fn set(&mut self, x: u8, y: u8) { + let off = y as usize * self.stride as usize + x as usize / 8; + let mut v = self.data[off]; + v |= 0x80 >> (x % 8); + self.data[off] = v; + } + + // Invert a module color. + fn xor(&mut self, x: u8, y: u8) { + let off = y as usize * self.stride as usize + x as usize / 8; + self.data[off] ^= 0x80 >> (x % 8); + } + + // Draw a light square at (x, y) top left corner. + fn draw_square(&mut self, x: u8, y: u8, size: u8) { + for k in 0..size { + self.set(x + k, y); + self.set(x, y + k + 1); + self.set(x + size, y + k); + self.set(x + k + 1, y + size); + } + } + + // Finder pattern: 3 8x8 square at the corners. + fn draw_finders(&mut self) { + self.draw_square(1, 1, 4); + self.draw_square(self.width - 6, 1, 4); + self.draw_square(1, self.width - 6, 4); + for k in 0..8 { + self.set(k, 7); + self.set(self.width - k - 1, 7); + self.set(k, self.width - 8); + } + for k in 0..7 { + self.set(7, k); + self.set(self.width - 8, k); + self.set(7, self.width - 1 - k); + } + } + + fn is_finder(&self, x: u8, y: u8) -> bool { + let end = self.width - 8; + (x < 8 && y < 8) || (x < 8 && y >= end) || (x >= end && y < 8) + } + + // Alignment pattern: 5x5 squares in a grid. + fn draw_alignments(&mut self) { + let positions = self.version.alignment_pattern(); + for &x in positions.iter() { + for &y in positions.iter() { + if !self.is_finder(x, y) { + self.draw_square(x - 1, y - 1, 2); + } + } + } + } + + fn is_alignment(&self, x: u8, y: u8) -> bool { + let positions = self.version.alignment_pattern(); + for &ax in positions.iter() { + for &ay in positions.iter() { + if self.is_finder(ax, ay) { + continue; + } + if x >= ax - 2 && x <= ax + 2 && y >= ay - 2 && y <= ay + 2 { + return true; + } + } + } + false + } + + // Timing pattern: 2 dotted line between the finder patterns. + fn draw_timing_patterns(&mut self) { + let end = self.width - 8; + + for x in (9..end).step_by(2) { + self.set(x, 6); + self.set(6, x); + } + } + + fn is_timing(&self, x: u8, y: u8) -> bool { + x == 6 || y == 6 + } + + // Mask info: 15 bits around the finders, written twice for redundancy. + fn draw_maskinfo(&mut self) { + let info: u16 = FORMAT_INFOS_QR_L[0]; + let mut skip = 0; + + for k in 0..7 { + if k == 6 { + skip = 1; + } + if info & (1 << (14 - k)) == 0 { + self.set(k + skip, 8); + self.set(8, self.width - 1 - k); + } + } + skip = 0; + for k in 0..8 { + if k == 2 { + skip = 1; + } + if info & (1 << (7 - k)) == 0 { + self.set(8, 8 - skip - k); + self.set(self.width - 8 + k, 8); + } + } + } + + fn is_maskinfo(&self, x: u8, y: u8) -> bool { + let end = self.width - 8; + // Count the dark module as mask info. + (x <= 8 && y == 8) || (y <= 8 && x == 8) || (x == 8 && y >= end) || (x >= end && y == 8) + } + + // Version info: 18bits written twice, close to the finders. + fn draw_version_info(&mut self) { + let vinfo = self.version.version_info(); + let pos = self.width - 11; + + if vinfo != 0 { + for x in 0..3 { + for y in 0..6 { + if vinfo & (1 << (x + y * 3)) == 0 { + self.set(x + pos, y); + self.set(y, x + pos); + } + } + } + } + } + + fn is_version_info(&self, x: u8, y: u8) -> bool { + let vinfo = self.version.version_info(); + let pos = self.width - 11; + + vinfo != 0 && ((x >= pos && x < pos + 3 && y < 6) || (y >= pos && y < pos + 3 && x < 6)) + } + + // Returns true if the module is reserved (Not usable for data and EC). + fn is_reserved(&self, x: u8, y: u8) -> bool { + self.is_alignment(x, y) + || self.is_finder(x, y) + || self.is_timing(x, y) + || self.is_maskinfo(x, y) + || self.is_version_info(x, y) + } + + // Last module to draw, at bottom left corner. + fn is_last(&self, x: u8, y: u8) -> bool { + x == 0 && y == self.width - 1 + } + + // Move to the next module according to QR code order. + // From bottom right corner, to bottom left corner. + fn next(&self, x: u8, y: u8) -> (u8, u8) { + let x_adj = if x <= 6 { x + 1 } else { x }; + let column_type = (self.width - x_adj) % 4; + + match column_type { + 2 if y > 0 => (x + 1, y - 1), + 0 if y < self.width - 1 => (x + 1, y + 1), + 0 | 2 if x == 7 => (x - 2, y), + _ => (x - 1, y), + } + } + + // Find next module that can hold data. + fn next_available(&self, x: u8, y: u8) -> (u8, u8) { + let (mut x, mut y) = self.next(x, y); + while self.is_reserved(x, y) && !self.is_last(x, y) { + (x, y) = self.next(x, y); + } + (x, y) + } + + fn draw_data(&mut self, data: impl Iterator) { + let (mut x, mut y) = (self.width - 1, self.width - 1); + for byte in data { + for s in 0..8 { + if byte & (0x80 >> s) == 0 { + self.set(x, y); + } + (x, y) = self.next_available(x, y); + } + } + // Set the remaining modules (0, 3 or 7 depending on version). + // because 0 correspond to a light module. + while !self.is_last(x, y) { + if !self.is_reserved(x, y) { + self.set(x, y); + } + (x, y) = self.next(x, y); + } + } + + // Apply checkerboard mask to all non-reserved modules. + fn apply_mask(&mut self) { + for x in 0..self.width { + for y in 0..self.width { + if (x ^ y) % 2 == 0 && !self.is_reserved(x, y) { + self.xor(x, y); + } + } + } + } + + // Draw the QR code with the provided data iterator. + fn draw_all(&mut self, data: impl Iterator) { + // First clear the table, as it may have already some data. + self.clear(); + self.draw_finders(); + self.draw_alignments(); + self.draw_timing_patterns(); + self.draw_version_info(); + self.draw_data(data); + self.draw_maskinfo(); + self.apply_mask(); + } +} + +/// C entry point for the rust QR Code generator. +/// +/// Write the QR code image in the data buffer, and return the QR code width, +/// or 0, if the data doesn't fit in a QR code. +/// +/// * `url`: The base URL of the QR code. It will be encoded as Binary segment. +/// * `data`: A pointer to the binary data, to be encoded. if URL is NULL, it +/// will be encoded as binary segment, otherwise it will be encoded +/// efficiently as a numeric segment, and appended to the URL. +/// * `data_len`: Length of the data, that needs to be encoded, must be less +/// than data_size. +/// * `data_size`: Size of data buffer, it should be at least 4071 bytes to hold +/// a V40 QR code. It will then be overwritten with the QR code image. +/// * `tmp`: A temporary buffer that the QR code encoder will use, to write the +/// segments and ECC. +/// * `tmp_size`: Size of the temporary buffer, it must be at least 3706 bytes +/// long for V40. +/// +/// # Safety +/// +/// * `url` must be null or point at a nul-terminated string. +/// * `data` must be valid for reading and writing for `data_size` bytes. +/// * `tmp` must be valid for reading and writing for `tmp_size` bytes. +/// +/// They must remain valid for the duration of the function call. + +#[no_mangle] +pub unsafe extern "C" fn drm_panic_qr_generate( + url: *const i8, + data: *mut u8, + data_len: usize, + data_size: usize, + tmp: *mut u8, + tmp_size: usize, +) -> u8 { + if data_size < 4071 || tmp_size < 3706 || data_len > data_size { + return 0; + } + // SAFETY: The caller ensures that `data` is a valid pointer for reading and + // writing `data_size` bytes. + let data_slice: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(data, data_size) }; + // SAFETY: The caller ensures that `tmp` is a valid pointer for reading and + // writing `tmp_size` bytes. + let tmp_slice: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(tmp, tmp_size) }; + if url.is_null() { + match EncodedMsg::new(&[&Segment::Binary(&data_slice[0..data_len])], tmp_slice) { + None => 0, + Some(em) => { + let qr_image = QrImage::new(&em, data_slice); + qr_image.width + } + } + } else { + // SAFETY: The caller ensures that `url` is a valid pointer to a + // nul-terminated string. + let url_cstr: &CStr = unsafe { CStr::from_char_ptr(url) }; + let segments = &[ + &Segment::Binary(url_cstr.as_bytes()), + &Segment::Numeric(&data_slice[0..data_len]), + ]; + match EncodedMsg::new(segments, tmp_slice) { + None => 0, + Some(em) => { + let qr_image = QrImage::new(&em, data_slice); + qr_image.width + } + } + } +} + +/// Returns the maximum data size that can fit in a QR code of this version. +/// * `version`: QR code version, between 1-40. +/// * `url_len`: Length of the URL. +/// +/// * If `url_len` > 0, remove the 2 segments header/length and also count the +/// conversion to numeric segments. +/// * If `url_len` = 0, only removes 3 bytes for 1 binary segment. +#[no_mangle] +pub extern "C" fn drm_panic_qr_max_data_size(version: u8, url_len: usize) -> usize { + if version < 1 || version > 40 { + return 0; + } + let max_data = Version(version as usize).max_data(); + + if url_len > 0 { + // Binary segment (URL) 4 + 16 bits, numeric segment (kmsg) 4 + 12 bits => 5 bytes. + if url_len + 5 >= max_data { + 0 + } else { + let max = max_data - url_len - 5; + (max * 39) / 40 + } + } else { + // Remove 3 bytes for the binary segment (header 4 bits, length 16 bits, stop 4bits). + max_data - 3 + } +} -- cgit v1.2.3 From 537ef0f8889761ffe3a6cb4a7bda4de47fe2d69b Mon Sep 17 00:00:00 2001 From: Hamza Mahfooz Date: Thu, 15 Aug 2024 14:37:27 -0400 Subject: drm/amd/display: use new vblank enable policy for DCN35+ Hook up drm_crtc_vblank_on_config() in amdgpu_dm. So, that we can enable PSR and other static screen optimizations more quickly, while avoiding stuttering issues that are accompanied by the following dmesg error: [drm:dc_dmub_srv_wait_idle [amdgpu]] *ERROR* Error waiting for DMUB idle: status=3 This also allows us to mimic how vblanking is handled by the Windows amdgpu driver. Specifically, we wait two idle frames before disabling the vblank timer there. Reviewed-by: Harry Wentland Signed-off-by: Hamza Mahfooz Link: https://patchwork.freedesktop.org/patch/msgid/20240822161856.174600-2-hamza.mahfooz@amd.com --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 35 ++++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 7e7929f24ae4..032a0ea4b066 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4934,12 +4934,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) if (psr_feature_enabled) amdgpu_dm_set_psr_caps(link); - - /* TODO: Fix vblank control helpers to delay PSR entry to allow this when - * PSR is also supported. - */ - if (link->psr_settings.psr_feature_enabled) - adev_to_drm(adev)->vblank_disable_immediate = false; } } amdgpu_set_panel_orientation(&aconnector->base); @@ -8232,7 +8226,7 @@ static int amdgpu_dm_encoder_init(struct drm_device *dev, static void manage_dm_interrupts(struct amdgpu_device *adev, struct amdgpu_crtc *acrtc, - bool enable) + struct dm_crtc_state *acrtc_state) { /* * We have no guarantee that the frontend index maps to the same @@ -8244,9 +8238,28 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, amdgpu_display_crtc_idx_to_irq_type( adev, acrtc->crtc_id); + struct drm_vblank_crtc_config config = {0}; + struct dc_crtc_timing *timing; + int offdelay; + + if (acrtc_state) { + if (amdgpu_ip_version(adev, DCE_HWIP, 0) < + IP_VERSION(3, 5, 0)) { + drm_crtc_vblank_on(&acrtc->base); + } else { + timing = &acrtc_state->stream->timing; + + /* at least 2 frames */ + offdelay = DIV64_U64_ROUND_UP((u64)20 * + timing->v_total * + timing->h_total, + timing->pix_clk_100hz); + + config.offdelay_ms = offdelay ?: 30; + drm_crtc_vblank_on_config(&acrtc->base, + &config); + } - if (enable) { - drm_crtc_vblank_on(&acrtc->base); amdgpu_irq_get( adev, &adev->pageflip_irq, @@ -9320,7 +9333,7 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_state *state, if (old_crtc_state->active && (!new_crtc_state->active || drm_atomic_crtc_needs_modeset(new_crtc_state))) { - manage_dm_interrupts(adev, acrtc, false); + manage_dm_interrupts(adev, acrtc, NULL); dc_stream_release(dm_old_crtc_state->stream); } } @@ -9835,7 +9848,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_crtc_needs_modeset(new_crtc_state))) { dc_stream_retain(dm_new_crtc_state->stream); acrtc->dm_irq_params.stream = dm_new_crtc_state->stream; - manage_dm_interrupts(adev, acrtc, true); + manage_dm_interrupts(adev, acrtc, dm_new_crtc_state); } /* Handle vrr on->off / off->on transitions */ amdgpu_dm_handle_vrr_transition(dm_old_crtc_state, dm_new_crtc_state); -- cgit v1.2.3 From e45b6716de4bf06b628a9f3559f7fc8dd5e94d58 Mon Sep 17 00:00:00 2001 From: Hamza Mahfooz Date: Tue, 20 Aug 2024 13:53:23 -0400 Subject: drm/amd/display: use a more lax vblank enable policy for DCN35+ Ideally, we want to enable immediate vblank disable, when possible and we should be able to do so on DCN35+, if PSR isn't supported by a given CRTC. Suggested-by: Leo Li Reviewed-by: Harry Wentland Signed-off-by: Hamza Mahfooz Link: https://patchwork.freedesktop.org/patch/msgid/20240822161856.174600-3-hamza.mahfooz@amd.com --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 032a0ea4b066..6323a097d0e2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -8246,7 +8246,8 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0)) { drm_crtc_vblank_on(&acrtc->base); - } else { + } else if (acrtc_state->stream->link->psr_settings.psr_version < + DC_PSR_VERSION_UNSUPPORTED) { timing = &acrtc_state->stream->timing; /* at least 2 frames */ @@ -8258,6 +8259,10 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, config.offdelay_ms = offdelay ?: 30; drm_crtc_vblank_on_config(&acrtc->base, &config); + } else { + config.disable_immediate = true; + drm_crtc_vblank_on_config(&acrtc->base, + &config); } amdgpu_irq_get( -- cgit v1.2.3 From 58a261bfc96763a851cb48b203ed57da37e157b8 Mon Sep 17 00:00:00 2001 From: Hamza Mahfooz Date: Thu, 22 Aug 2024 11:58:22 -0400 Subject: drm/amd/display: use a more lax vblank enable policy for older ASICs Ideally, we want to drop the legacy vblank enable for older ASICs. This should be possible now, since we can now specify how many frames we need to wait before disabling vblanking instead of being forced to either choose between no delay (which can still be buggy) and drm_vblank_offdelay (which is much longer by default than is required on AMD hardware). Suggested-by: Leo Li Reviewed-by: Harry Wentland Signed-off-by: Hamza Mahfooz Link: https://patchwork.freedesktop.org/patch/msgid/20240822161856.174600-4-hamza.mahfooz@amd.com --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 6323a097d0e2..91490d5d106b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -8244,10 +8244,9 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, if (acrtc_state) { if (amdgpu_ip_version(adev, DCE_HWIP, 0) < - IP_VERSION(3, 5, 0)) { - drm_crtc_vblank_on(&acrtc->base); - } else if (acrtc_state->stream->link->psr_settings.psr_version < - DC_PSR_VERSION_UNSUPPORTED) { + IP_VERSION(3, 5, 0) || + acrtc_state->stream->link->psr_settings.psr_version < + DC_PSR_VERSION_UNSUPPORTED) { timing = &acrtc_state->stream->timing; /* at least 2 frames */ @@ -8257,14 +8256,13 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, timing->pix_clk_100hz); config.offdelay_ms = offdelay ?: 30; - drm_crtc_vblank_on_config(&acrtc->base, - &config); } else { config.disable_immediate = true; - drm_crtc_vblank_on_config(&acrtc->base, - &config); } + drm_crtc_vblank_on_config(&acrtc->base, + &config); + amdgpu_irq_get( adev, &adev->pageflip_irq, -- cgit v1.2.3 From e794b7b9b92977365c693760a259f8eef940c536 Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Thu, 8 Aug 2024 14:13:36 +0800 Subject: drm: omapdrm: Add missing check for alloc_ordered_workqueue As it may return NULL pointer and cause NULL pointer dereference. Add check for the return value of alloc_ordered_workqueue. Cc: stable@vger.kernel.org Fixes: 2f95bc6d324a ("drm: omapdrm: Perform initialization/cleanup at probe/remove time") Signed-off-by: Ma Ke Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240808061336.2796729-1-make24@iscas.ac.cn --- drivers/gpu/drm/omapdrm/omap_drv.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 6598c9c08ba1..d3eac4817d76 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -695,6 +695,10 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev) soc = soc_device_match(omapdrm_soc_devices); priv->omaprev = soc ? (uintptr_t)soc->data : 0; priv->wq = alloc_ordered_workqueue("omapdrm", 0); + if (!priv->wq) { + ret = -ENOMEM; + goto err_alloc_workqueue; + } mutex_init(&priv->list_lock); INIT_LIST_HEAD(&priv->obj_list); @@ -753,6 +757,7 @@ err_gem_deinit: drm_mode_config_cleanup(ddev); omap_gem_deinit(ddev); destroy_workqueue(priv->wq); +err_alloc_workqueue: omap_disconnect_pipelines(ddev); drm_dev_put(ddev); return ret; -- cgit v1.2.3 From 1b5dfd1881dbe303536d4167500b94549ff2f6a7 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 25 Jun 2024 13:32:44 +0100 Subject: drm: renesas: Move RZ/G2L MIPI DSI driver to rz-du All the RZ/G2L DU specific components are located under the rz-du folder, so it makes sense to move the RZ/G2L MIPI DSI driver there instead of keeping it in the rcar-du folder. This change improves the organization and modularity of the driver configuration by grouping related settings together. Signed-off-by: Lad Prabhakar Acked-by: Biju Das Reviewed-by: Laurent Pinchart Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240625123244.200533-1-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/gpu/drm/renesas/rcar-du/Kconfig | 8 - drivers/gpu/drm/renesas/rcar-du/Makefile | 2 - drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c | 813 --------------------- .../gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h | 151 ---- drivers/gpu/drm/renesas/rz-du/Kconfig | 8 + drivers/gpu/drm/renesas/rz-du/Makefile | 2 + drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 813 +++++++++++++++++++++ .../gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h | 151 ++++ 8 files changed, 974 insertions(+), 974 deletions(-) delete mode 100644 drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c delete mode 100644 drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/renesas/rcar-du/Kconfig b/drivers/gpu/drm/renesas/rcar-du/Kconfig index c17e7c50492c..be86ecb9f559 100644 --- a/drivers/gpu/drm/renesas/rcar-du/Kconfig +++ b/drivers/gpu/drm/renesas/rcar-du/Kconfig @@ -60,14 +60,6 @@ config DRM_RCAR_MIPI_DSI select DRM_MIPI_DSI select RESET_CONTROLLER -config DRM_RZG2L_MIPI_DSI - tristate "RZ/G2L MIPI DSI Encoder Support" - depends on DRM && DRM_BRIDGE && OF - depends on ARCH_RENESAS || COMPILE_TEST - select DRM_MIPI_DSI - help - Enable support for the RZ/G2L Display Unit embedded MIPI DSI encoders. - config DRM_RCAR_VSP bool "R-Car DU VSP Compositor Support" if ARM default y if ARM64 diff --git a/drivers/gpu/drm/renesas/rcar-du/Makefile b/drivers/gpu/drm/renesas/rcar-du/Makefile index b8f2c82651d9..6f132325c8b7 100644 --- a/drivers/gpu/drm/renesas/rcar-du/Makefile +++ b/drivers/gpu/drm/renesas/rcar-du/Makefile @@ -14,5 +14,3 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o obj-$(CONFIG_DRM_RCAR_MIPI_DSI) += rcar_mipi_dsi.o - -obj-$(CONFIG_DRM_RZG2L_MIPI_DSI) += rzg2l_mipi_dsi.o diff --git a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c deleted file mode 100644 index 10febea473cd..000000000000 --- a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c +++ /dev/null @@ -1,813 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * RZ/G2L MIPI DSI Encoder Driver - * - * Copyright (C) 2022 Renesas Electronics Corporation - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "rzg2l_mipi_dsi_regs.h" - -struct rzg2l_mipi_dsi { - struct device *dev; - void __iomem *mmio; - - struct reset_control *rstc; - struct reset_control *arstc; - struct reset_control *prstc; - - struct mipi_dsi_host host; - struct drm_bridge bridge; - struct drm_bridge *next_bridge; - - struct clk *vclk; - - enum mipi_dsi_pixel_format format; - unsigned int num_data_lanes; - unsigned int lanes; - unsigned long mode_flags; -}; - -static inline struct rzg2l_mipi_dsi * -bridge_to_rzg2l_mipi_dsi(struct drm_bridge *bridge) -{ - return container_of(bridge, struct rzg2l_mipi_dsi, bridge); -} - -static inline struct rzg2l_mipi_dsi * -host_to_rzg2l_mipi_dsi(struct mipi_dsi_host *host) -{ - return container_of(host, struct rzg2l_mipi_dsi, host); -} - -struct rzg2l_mipi_dsi_timings { - unsigned long hsfreq_max; - u32 t_init; - u32 tclk_prepare; - u32 ths_prepare; - u32 tclk_zero; - u32 tclk_pre; - u32 tclk_post; - u32 tclk_trail; - u32 ths_zero; - u32 ths_trail; - u32 ths_exit; - u32 tlpx; -}; - -static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { - { - .hsfreq_max = 80000, - .t_init = 79801, - .tclk_prepare = 8, - .ths_prepare = 13, - .tclk_zero = 33, - .tclk_pre = 24, - .tclk_post = 94, - .tclk_trail = 10, - .ths_zero = 23, - .ths_trail = 17, - .ths_exit = 13, - .tlpx = 6, - }, - { - .hsfreq_max = 125000, - .t_init = 79801, - .tclk_prepare = 8, - .ths_prepare = 12, - .tclk_zero = 33, - .tclk_pre = 15, - .tclk_post = 94, - .tclk_trail = 10, - .ths_zero = 23, - .ths_trail = 17, - .ths_exit = 13, - .tlpx = 6, - }, - { - .hsfreq_max = 250000, - .t_init = 79801, - .tclk_prepare = 8, - .ths_prepare = 12, - .tclk_zero = 33, - .tclk_pre = 13, - .tclk_post = 94, - .tclk_trail = 10, - .ths_zero = 23, - .ths_trail = 16, - .ths_exit = 13, - .tlpx = 6, - }, - { - .hsfreq_max = 360000, - .t_init = 79801, - .tclk_prepare = 8, - .ths_prepare = 10, - .tclk_zero = 33, - .tclk_pre = 4, - .tclk_post = 35, - .tclk_trail = 7, - .ths_zero = 16, - .ths_trail = 9, - .ths_exit = 13, - .tlpx = 6, - }, - { - .hsfreq_max = 720000, - .t_init = 79801, - .tclk_prepare = 8, - .ths_prepare = 9, - .tclk_zero = 33, - .tclk_pre = 4, - .tclk_post = 35, - .tclk_trail = 7, - .ths_zero = 16, - .ths_trail = 9, - .ths_exit = 13, - .tlpx = 6, - }, - { - .hsfreq_max = 1500000, - .t_init = 79801, - .tclk_prepare = 8, - .ths_prepare = 9, - .tclk_zero = 33, - .tclk_pre = 4, - .tclk_post = 35, - .tclk_trail = 7, - .ths_zero = 16, - .ths_trail = 9, - .ths_exit = 13, - .tlpx = 6, - }, -}; - -static void rzg2l_mipi_dsi_phy_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data) -{ - iowrite32(data, dsi->mmio + reg); -} - -static void rzg2l_mipi_dsi_link_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data) -{ - iowrite32(data, dsi->mmio + LINK_REG_OFFSET + reg); -} - -static u32 rzg2l_mipi_dsi_phy_read(struct rzg2l_mipi_dsi *dsi, u32 reg) -{ - return ioread32(dsi->mmio + reg); -} - -static u32 rzg2l_mipi_dsi_link_read(struct rzg2l_mipi_dsi *dsi, u32 reg) -{ - return ioread32(dsi->mmio + LINK_REG_OFFSET + reg); -} - -/* ----------------------------------------------------------------------------- - * Hardware Setup - */ - -static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi, - unsigned long hsfreq) -{ - const struct rzg2l_mipi_dsi_timings *dphy_timings; - unsigned int i; - u32 dphyctrl0; - u32 dphytim0; - u32 dphytim1; - u32 dphytim2; - u32 dphytim3; - int ret; - - /* All DSI global operation timings are set with recommended setting */ - for (i = 0; i < ARRAY_SIZE(rzg2l_mipi_dsi_global_timings); ++i) { - dphy_timings = &rzg2l_mipi_dsi_global_timings[i]; - if (hsfreq <= dphy_timings->hsfreq_max) - break; - } - - /* Initializing DPHY before accessing LINK */ - dphyctrl0 = DSIDPHYCTRL0_CAL_EN_HSRX_OFS | DSIDPHYCTRL0_CMN_MASTER_EN | - DSIDPHYCTRL0_RE_VDD_DETVCCQLV18 | DSIDPHYCTRL0_EN_BGR; - - rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0); - usleep_range(20, 30); - - dphyctrl0 |= DSIDPHYCTRL0_EN_LDO1200; - rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0); - usleep_range(10, 20); - - dphytim0 = DSIDPHYTIM0_TCLK_MISS(0) | - DSIDPHYTIM0_T_INIT(dphy_timings->t_init); - dphytim1 = DSIDPHYTIM1_THS_PREPARE(dphy_timings->ths_prepare) | - DSIDPHYTIM1_TCLK_PREPARE(dphy_timings->tclk_prepare) | - DSIDPHYTIM1_THS_SETTLE(0) | - DSIDPHYTIM1_TCLK_SETTLE(0); - dphytim2 = DSIDPHYTIM2_TCLK_TRAIL(dphy_timings->tclk_trail) | - DSIDPHYTIM2_TCLK_POST(dphy_timings->tclk_post) | - DSIDPHYTIM2_TCLK_PRE(dphy_timings->tclk_pre) | - DSIDPHYTIM2_TCLK_ZERO(dphy_timings->tclk_zero); - dphytim3 = DSIDPHYTIM3_TLPX(dphy_timings->tlpx) | - DSIDPHYTIM3_THS_EXIT(dphy_timings->ths_exit) | - DSIDPHYTIM3_THS_TRAIL(dphy_timings->ths_trail) | - DSIDPHYTIM3_THS_ZERO(dphy_timings->ths_zero); - - rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM0, dphytim0); - rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM1, dphytim1); - rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM2, dphytim2); - rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM3, dphytim3); - - ret = reset_control_deassert(dsi->rstc); - if (ret < 0) - return ret; - - udelay(1); - - return 0; -} - -static void rzg2l_mipi_dsi_dphy_exit(struct rzg2l_mipi_dsi *dsi) -{ - u32 dphyctrl0; - - dphyctrl0 = rzg2l_mipi_dsi_phy_read(dsi, DSIDPHYCTRL0); - - dphyctrl0 &= ~(DSIDPHYCTRL0_EN_LDO1200 | DSIDPHYCTRL0_EN_BGR); - rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0); - - reset_control_assert(dsi->rstc); -} - -static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, - const struct drm_display_mode *mode) -{ - unsigned long hsfreq; - unsigned int bpp; - u32 txsetr; - u32 clstptsetr; - u32 lptrnstsetr; - u32 clkkpt; - u32 clkbfht; - u32 clkstpt; - u32 golpbkt; - int ret; - - /* - * Relationship between hsclk and vclk must follow - * vclk * bpp = hsclk * 8 * lanes - * where vclk: video clock (Hz) - * bpp: video pixel bit depth - * hsclk: DSI HS Byte clock frequency (Hz) - * lanes: number of data lanes - * - * hsclk(bit) = hsclk(byte) * 8 - */ - bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); - hsfreq = (mode->clock * bpp * 8) / (8 * dsi->lanes); - - ret = pm_runtime_resume_and_get(dsi->dev); - if (ret < 0) - return ret; - - clk_set_rate(dsi->vclk, mode->clock * 1000); - - ret = rzg2l_mipi_dsi_dphy_init(dsi, hsfreq); - if (ret < 0) - goto err_phy; - - /* Enable Data lanes and Clock lanes */ - txsetr = TXSETR_DLEN | TXSETR_NUMLANEUSE(dsi->lanes - 1) | TXSETR_CLEN; - rzg2l_mipi_dsi_link_write(dsi, TXSETR, txsetr); - - /* - * Global timings characteristic depends on high speed Clock Frequency - * Currently MIPI DSI-IF just supports maximum FHD@60 with: - * - videoclock = 148.5 (MHz) - * - bpp: maximum 24bpp - * - data lanes: maximum 4 lanes - * Therefore maximum hsclk will be 891 Mbps. - */ - if (hsfreq > 445500) { - clkkpt = 12; - clkbfht = 15; - clkstpt = 48; - golpbkt = 75; - } else if (hsfreq > 250000) { - clkkpt = 7; - clkbfht = 8; - clkstpt = 27; - golpbkt = 40; - } else { - clkkpt = 8; - clkbfht = 6; - clkstpt = 24; - golpbkt = 29; - } - - clstptsetr = CLSTPTSETR_CLKKPT(clkkpt) | CLSTPTSETR_CLKBFHT(clkbfht) | - CLSTPTSETR_CLKSTPT(clkstpt); - rzg2l_mipi_dsi_link_write(dsi, CLSTPTSETR, clstptsetr); - - lptrnstsetr = LPTRNSTSETR_GOLPBKT(golpbkt); - rzg2l_mipi_dsi_link_write(dsi, LPTRNSTSETR, lptrnstsetr); - - return 0; - -err_phy: - rzg2l_mipi_dsi_dphy_exit(dsi); - pm_runtime_put(dsi->dev); - - return ret; -} - -static void rzg2l_mipi_dsi_stop(struct rzg2l_mipi_dsi *dsi) -{ - rzg2l_mipi_dsi_dphy_exit(dsi); - pm_runtime_put(dsi->dev); -} - -static void rzg2l_mipi_dsi_set_display_timing(struct rzg2l_mipi_dsi *dsi, - const struct drm_display_mode *mode) -{ - u32 vich1ppsetr; - u32 vich1vssetr; - u32 vich1vpsetr; - u32 vich1hssetr; - u32 vich1hpsetr; - int dsi_format; - u32 delay[2]; - u8 index; - - /* Configuration for Pixel Packet */ - dsi_format = mipi_dsi_pixel_format_to_bpp(dsi->format); - switch (dsi_format) { - case 24: - vich1ppsetr = VICH1PPSETR_DT_RGB24; - break; - case 18: - vich1ppsetr = VICH1PPSETR_DT_RGB18; - break; - } - - if ((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) && - !(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)) - vich1ppsetr |= VICH1PPSETR_TXESYNC_PULSE; - - rzg2l_mipi_dsi_link_write(dsi, VICH1PPSETR, vich1ppsetr); - - /* Configuration for Video Parameters */ - vich1vssetr = VICH1VSSETR_VACTIVE(mode->vdisplay) | - VICH1VSSETR_VSA(mode->vsync_end - mode->vsync_start); - vich1vssetr |= (mode->flags & DRM_MODE_FLAG_PVSYNC) ? - VICH1VSSETR_VSPOL_HIGH : VICH1VSSETR_VSPOL_LOW; - - vich1vpsetr = VICH1VPSETR_VFP(mode->vsync_start - mode->vdisplay) | - VICH1VPSETR_VBP(mode->vtotal - mode->vsync_end); - - vich1hssetr = VICH1HSSETR_HACTIVE(mode->hdisplay) | - VICH1HSSETR_HSA(mode->hsync_end - mode->hsync_start); - vich1hssetr |= (mode->flags & DRM_MODE_FLAG_PHSYNC) ? - VICH1HSSETR_HSPOL_HIGH : VICH1HSSETR_HSPOL_LOW; - - vich1hpsetr = VICH1HPSETR_HFP(mode->hsync_start - mode->hdisplay) | - VICH1HPSETR_HBP(mode->htotal - mode->hsync_end); - - rzg2l_mipi_dsi_link_write(dsi, VICH1VSSETR, vich1vssetr); - rzg2l_mipi_dsi_link_write(dsi, VICH1VPSETR, vich1vpsetr); - rzg2l_mipi_dsi_link_write(dsi, VICH1HSSETR, vich1hssetr); - rzg2l_mipi_dsi_link_write(dsi, VICH1HPSETR, vich1hpsetr); - - /* - * Configuration for Delay Value - * Delay value based on 2 ranges of video clock. - * 74.25MHz is videoclock of HD@60p or FHD@30p - */ - if (mode->clock > 74250) { - delay[0] = 231; - delay[1] = 216; - } else { - delay[0] = 220; - delay[1] = 212; - } - - if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) - index = 0; - else - index = 1; - - rzg2l_mipi_dsi_link_write(dsi, VICH1SET1R, - VICH1SET1R_DLY(delay[index])); -} - -static int rzg2l_mipi_dsi_start_hs_clock(struct rzg2l_mipi_dsi *dsi) -{ - bool is_clk_cont; - u32 hsclksetr; - u32 status; - int ret; - - is_clk_cont = !(dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS); - - /* Start HS clock */ - hsclksetr = HSCLKSETR_HSCLKRUN_HS | (is_clk_cont ? - HSCLKSETR_HSCLKMODE_CONT : - HSCLKSETR_HSCLKMODE_NON_CONT); - rzg2l_mipi_dsi_link_write(dsi, HSCLKSETR, hsclksetr); - - if (is_clk_cont) { - ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, - status & PLSR_CLLP2HS, - 2000, 20000, false, dsi, PLSR); - if (ret < 0) { - dev_err(dsi->dev, "failed to start HS clock\n"); - return ret; - } - } - - dev_dbg(dsi->dev, "Start High Speed Clock with %s clock mode", - is_clk_cont ? "continuous" : "non-continuous"); - - return 0; -} - -static int rzg2l_mipi_dsi_stop_hs_clock(struct rzg2l_mipi_dsi *dsi) -{ - bool is_clk_cont; - u32 status; - int ret; - - is_clk_cont = !(dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS); - - /* Stop HS clock */ - rzg2l_mipi_dsi_link_write(dsi, HSCLKSETR, - is_clk_cont ? HSCLKSETR_HSCLKMODE_CONT : - HSCLKSETR_HSCLKMODE_NON_CONT); - - if (is_clk_cont) { - ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, - status & PLSR_CLHS2LP, - 2000, 20000, false, dsi, PLSR); - if (ret < 0) { - dev_err(dsi->dev, "failed to stop HS clock\n"); - return ret; - } - } - - return 0; -} - -static int rzg2l_mipi_dsi_start_video(struct rzg2l_mipi_dsi *dsi) -{ - u32 vich1set0r; - u32 status; - int ret; - - /* Configuration for Blanking sequence and start video input*/ - vich1set0r = VICH1SET0R_HFPNOLP | VICH1SET0R_HBPNOLP | - VICH1SET0R_HSANOLP | VICH1SET0R_VSTART; - rzg2l_mipi_dsi_link_write(dsi, VICH1SET0R, vich1set0r); - - ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, - status & VICH1SR_VIRDY, - 2000, 20000, false, dsi, VICH1SR); - if (ret < 0) - dev_err(dsi->dev, "Failed to start video signal input\n"); - - return ret; -} - -static int rzg2l_mipi_dsi_stop_video(struct rzg2l_mipi_dsi *dsi) -{ - u32 status; - int ret; - - rzg2l_mipi_dsi_link_write(dsi, VICH1SET0R, VICH1SET0R_VSTPAFT); - ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, - (status & VICH1SR_STOP) && (!(status & VICH1SR_RUNNING)), - 2000, 20000, false, dsi, VICH1SR); - if (ret < 0) - goto err; - - ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, - !(status & LINKSR_HSBUSY), - 2000, 20000, false, dsi, LINKSR); - if (ret < 0) - goto err; - - return 0; - -err: - dev_err(dsi->dev, "Failed to stop video signal input\n"); - return ret; -} - -/* ----------------------------------------------------------------------------- - * Bridge - */ - -static int rzg2l_mipi_dsi_attach(struct drm_bridge *bridge, - enum drm_bridge_attach_flags flags) -{ - struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); - - return drm_bridge_attach(bridge->encoder, dsi->next_bridge, bridge, - flags); -} - -static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) -{ - struct drm_atomic_state *state = old_bridge_state->base.state; - struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); - const struct drm_display_mode *mode; - struct drm_connector *connector; - struct drm_crtc *crtc; - int ret; - - connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); - crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; - mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode; - - ret = rzg2l_mipi_dsi_startup(dsi, mode); - if (ret < 0) - return; - - rzg2l_mipi_dsi_set_display_timing(dsi, mode); - - ret = rzg2l_mipi_dsi_start_hs_clock(dsi); - if (ret < 0) - goto err_stop; - - ret = rzg2l_mipi_dsi_start_video(dsi); - if (ret < 0) - goto err_stop_clock; - - return; - -err_stop_clock: - rzg2l_mipi_dsi_stop_hs_clock(dsi); -err_stop: - rzg2l_mipi_dsi_stop(dsi); -} - -static void rzg2l_mipi_dsi_atomic_disable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) -{ - struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); - - rzg2l_mipi_dsi_stop_video(dsi); - rzg2l_mipi_dsi_stop_hs_clock(dsi); - rzg2l_mipi_dsi_stop(dsi); -} - -static enum drm_mode_status -rzg2l_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_info *info, - const struct drm_display_mode *mode) -{ - if (mode->clock > 148500) - return MODE_CLOCK_HIGH; - - return MODE_OK; -} - -static const struct drm_bridge_funcs rzg2l_mipi_dsi_bridge_ops = { - .attach = rzg2l_mipi_dsi_attach, - .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, - .atomic_reset = drm_atomic_helper_bridge_reset, - .atomic_enable = rzg2l_mipi_dsi_atomic_enable, - .atomic_disable = rzg2l_mipi_dsi_atomic_disable, - .mode_valid = rzg2l_mipi_dsi_bridge_mode_valid, -}; - -/* ----------------------------------------------------------------------------- - * Host setting - */ - -static int rzg2l_mipi_dsi_host_attach(struct mipi_dsi_host *host, - struct mipi_dsi_device *device) -{ - struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host); - int ret; - - if (device->lanes > dsi->num_data_lanes) { - dev_err(dsi->dev, - "Number of lines of device (%u) exceeds host (%u)\n", - device->lanes, dsi->num_data_lanes); - return -EINVAL; - } - - switch (mipi_dsi_pixel_format_to_bpp(device->format)) { - case 24: - case 18: - break; - default: - dev_err(dsi->dev, "Unsupported format 0x%04x\n", device->format); - return -EINVAL; - } - - dsi->lanes = device->lanes; - dsi->format = device->format; - dsi->mode_flags = device->mode_flags; - - dsi->next_bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node, - 1, 0); - if (IS_ERR(dsi->next_bridge)) { - ret = PTR_ERR(dsi->next_bridge); - dev_err(dsi->dev, "failed to get next bridge: %d\n", ret); - return ret; - } - - drm_bridge_add(&dsi->bridge); - - return 0; -} - -static int rzg2l_mipi_dsi_host_detach(struct mipi_dsi_host *host, - struct mipi_dsi_device *device) -{ - struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host); - - drm_bridge_remove(&dsi->bridge); - - return 0; -} - -static const struct mipi_dsi_host_ops rzg2l_mipi_dsi_host_ops = { - .attach = rzg2l_mipi_dsi_host_attach, - .detach = rzg2l_mipi_dsi_host_detach, -}; - -/* ----------------------------------------------------------------------------- - * Power Management - */ - -static int __maybe_unused rzg2l_mipi_pm_runtime_suspend(struct device *dev) -{ - struct rzg2l_mipi_dsi *dsi = dev_get_drvdata(dev); - - reset_control_assert(dsi->prstc); - reset_control_assert(dsi->arstc); - - return 0; -} - -static int __maybe_unused rzg2l_mipi_pm_runtime_resume(struct device *dev) -{ - struct rzg2l_mipi_dsi *dsi = dev_get_drvdata(dev); - int ret; - - ret = reset_control_deassert(dsi->arstc); - if (ret < 0) - return ret; - - ret = reset_control_deassert(dsi->prstc); - if (ret < 0) - reset_control_assert(dsi->arstc); - - return ret; -} - -static const struct dev_pm_ops rzg2l_mipi_pm_ops = { - SET_RUNTIME_PM_OPS(rzg2l_mipi_pm_runtime_suspend, rzg2l_mipi_pm_runtime_resume, NULL) -}; - -/* ----------------------------------------------------------------------------- - * Probe & Remove - */ - -static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) -{ - unsigned int num_data_lanes; - struct rzg2l_mipi_dsi *dsi; - u32 txsetr; - int ret; - - dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; - - platform_set_drvdata(pdev, dsi); - dsi->dev = &pdev->dev; - - ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4); - if (ret < 0) - return dev_err_probe(dsi->dev, ret, - "missing or invalid data-lanes property\n"); - - num_data_lanes = ret; - - dsi->mmio = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dsi->mmio)) - return PTR_ERR(dsi->mmio); - - dsi->vclk = devm_clk_get(dsi->dev, "vclk"); - if (IS_ERR(dsi->vclk)) - return PTR_ERR(dsi->vclk); - - dsi->rstc = devm_reset_control_get_exclusive(dsi->dev, "rst"); - if (IS_ERR(dsi->rstc)) - return dev_err_probe(dsi->dev, PTR_ERR(dsi->rstc), - "failed to get rst\n"); - - dsi->arstc = devm_reset_control_get_exclusive(dsi->dev, "arst"); - if (IS_ERR(dsi->arstc)) - return dev_err_probe(&pdev->dev, PTR_ERR(dsi->arstc), - "failed to get arst\n"); - - dsi->prstc = devm_reset_control_get_exclusive(dsi->dev, "prst"); - if (IS_ERR(dsi->prstc)) - return dev_err_probe(dsi->dev, PTR_ERR(dsi->prstc), - "failed to get prst\n"); - - platform_set_drvdata(pdev, dsi); - - pm_runtime_enable(dsi->dev); - - ret = pm_runtime_resume_and_get(dsi->dev); - if (ret < 0) - goto err_pm_disable; - - /* - * TXSETR register can be read only after DPHY init. But during probe - * mode->clock and format are not available. So initialize DPHY with - * timing parameters for 80Mbps. - */ - ret = rzg2l_mipi_dsi_dphy_init(dsi, 80000); - if (ret < 0) - goto err_phy; - - txsetr = rzg2l_mipi_dsi_link_read(dsi, TXSETR); - dsi->num_data_lanes = min(((txsetr >> 16) & 3) + 1, num_data_lanes); - rzg2l_mipi_dsi_dphy_exit(dsi); - pm_runtime_put(dsi->dev); - - /* Initialize the DRM bridge. */ - dsi->bridge.funcs = &rzg2l_mipi_dsi_bridge_ops; - dsi->bridge.of_node = dsi->dev->of_node; - - /* Init host device */ - dsi->host.dev = dsi->dev; - dsi->host.ops = &rzg2l_mipi_dsi_host_ops; - ret = mipi_dsi_host_register(&dsi->host); - if (ret < 0) - goto err_pm_disable; - - return 0; - -err_phy: - rzg2l_mipi_dsi_dphy_exit(dsi); - pm_runtime_put(dsi->dev); -err_pm_disable: - pm_runtime_disable(dsi->dev); - return ret; -} - -static void rzg2l_mipi_dsi_remove(struct platform_device *pdev) -{ - struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev); - - mipi_dsi_host_unregister(&dsi->host); - pm_runtime_disable(&pdev->dev); -} - -static const struct of_device_id rzg2l_mipi_dsi_of_table[] = { - { .compatible = "renesas,rzg2l-mipi-dsi" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, rzg2l_mipi_dsi_of_table); - -static struct platform_driver rzg2l_mipi_dsi_platform_driver = { - .probe = rzg2l_mipi_dsi_probe, - .remove_new = rzg2l_mipi_dsi_remove, - .driver = { - .name = "rzg2l-mipi-dsi", - .pm = &rzg2l_mipi_pm_ops, - .of_match_table = rzg2l_mipi_dsi_of_table, - }, -}; - -module_platform_driver(rzg2l_mipi_dsi_platform_driver); - -MODULE_AUTHOR("Biju Das "); -MODULE_DESCRIPTION("Renesas RZ/G2L MIPI DSI Encoder Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h b/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h deleted file mode 100644 index 1dbc16ec64a4..000000000000 --- a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h +++ /dev/null @@ -1,151 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * RZ/G2L MIPI DSI Interface Registers Definitions - * - * Copyright (C) 2022 Renesas Electronics Corporation - */ - -#ifndef __RZG2L_MIPI_DSI_REGS_H__ -#define __RZG2L_MIPI_DSI_REGS_H__ - -#include - -/* DPHY Registers */ -#define DSIDPHYCTRL0 0x00 -#define DSIDPHYCTRL0_CAL_EN_HSRX_OFS BIT(16) -#define DSIDPHYCTRL0_CMN_MASTER_EN BIT(8) -#define DSIDPHYCTRL0_RE_VDD_DETVCCQLV18 BIT(2) -#define DSIDPHYCTRL0_EN_LDO1200 BIT(1) -#define DSIDPHYCTRL0_EN_BGR BIT(0) - -#define DSIDPHYTIM0 0x04 -#define DSIDPHYTIM0_TCLK_MISS(x) ((x) << 24) -#define DSIDPHYTIM0_T_INIT(x) ((x) << 0) - -#define DSIDPHYTIM1 0x08 -#define DSIDPHYTIM1_THS_PREPARE(x) ((x) << 24) -#define DSIDPHYTIM1_TCLK_PREPARE(x) ((x) << 16) -#define DSIDPHYTIM1_THS_SETTLE(x) ((x) << 8) -#define DSIDPHYTIM1_TCLK_SETTLE(x) ((x) << 0) - -#define DSIDPHYTIM2 0x0c -#define DSIDPHYTIM2_TCLK_TRAIL(x) ((x) << 24) -#define DSIDPHYTIM2_TCLK_POST(x) ((x) << 16) -#define DSIDPHYTIM2_TCLK_PRE(x) ((x) << 8) -#define DSIDPHYTIM2_TCLK_ZERO(x) ((x) << 0) - -#define DSIDPHYTIM3 0x10 -#define DSIDPHYTIM3_TLPX(x) ((x) << 24) -#define DSIDPHYTIM3_THS_EXIT(x) ((x) << 16) -#define DSIDPHYTIM3_THS_TRAIL(x) ((x) << 8) -#define DSIDPHYTIM3_THS_ZERO(x) ((x) << 0) - -/* --------------------------------------------------------*/ -/* Link Registers */ -#define LINK_REG_OFFSET 0x10000 - -/* Link Status Register */ -#define LINKSR 0x10 -#define LINKSR_LPBUSY BIT(13) -#define LINKSR_HSBUSY BIT(12) -#define LINKSR_VICHRUN1 BIT(8) -#define LINKSR_SQCHRUN1 BIT(4) -#define LINKSR_SQCHRUN0 BIT(0) - -/* Tx Set Register */ -#define TXSETR 0x100 -#define TXSETR_NUMLANECAP (0x3 << 16) -#define TXSETR_DLEN (1 << 9) -#define TXSETR_CLEN (1 << 8) -#define TXSETR_NUMLANEUSE(x) (((x) & 0x3) << 0) - -/* HS Clock Set Register */ -#define HSCLKSETR 0x104 -#define HSCLKSETR_HSCLKMODE_CONT (1 << 1) -#define HSCLKSETR_HSCLKMODE_NON_CONT (0 << 1) -#define HSCLKSETR_HSCLKRUN_HS (1 << 0) -#define HSCLKSETR_HSCLKRUN_LP (0 << 0) - -/* Reset Control Register */ -#define RSTCR 0x110 -#define RSTCR_SWRST BIT(0) -#define RSTCR_FCETXSTP BIT(16) - -/* Reset Status Register */ -#define RSTSR 0x114 -#define RSTSR_DL0DIR (1 << 15) -#define RSTSR_DLSTPST (0xf << 8) -#define RSTSR_SWRSTV1 (1 << 4) -#define RSTSR_SWRSTIB (1 << 3) -#define RSTSR_SWRSTAPB (1 << 2) -#define RSTSR_SWRSTLP (1 << 1) -#define RSTSR_SWRSTHS (1 << 0) - -/* Clock Lane Stop Time Set Register */ -#define CLSTPTSETR 0x314 -#define CLSTPTSETR_CLKKPT(x) ((x) << 24) -#define CLSTPTSETR_CLKBFHT(x) ((x) << 16) -#define CLSTPTSETR_CLKSTPT(x) ((x) << 2) - -/* LP Transition Time Set Register */ -#define LPTRNSTSETR 0x318 -#define LPTRNSTSETR_GOLPBKT(x) ((x) << 0) - -/* Physical Lane Status Register */ -#define PLSR 0x320 -#define PLSR_CLHS2LP BIT(27) -#define PLSR_CLLP2HS BIT(26) - -/* Video-Input Channel 1 Set 0 Register */ -#define VICH1SET0R 0x400 -#define VICH1SET0R_VSEN BIT(12) -#define VICH1SET0R_HFPNOLP BIT(10) -#define VICH1SET0R_HBPNOLP BIT(9) -#define VICH1SET0R_HSANOLP BIT(8) -#define VICH1SET0R_VSTPAFT BIT(1) -#define VICH1SET0R_VSTART BIT(0) - -/* Video-Input Channel 1 Set 1 Register */ -#define VICH1SET1R 0x404 -#define VICH1SET1R_DLY(x) (((x) & 0xfff) << 2) - -/* Video-Input Channel 1 Status Register */ -#define VICH1SR 0x410 -#define VICH1SR_VIRDY BIT(3) -#define VICH1SR_RUNNING BIT(2) -#define VICH1SR_STOP BIT(1) -#define VICH1SR_START BIT(0) - -/* Video-Input Channel 1 Pixel Packet Set Register */ -#define VICH1PPSETR 0x420 -#define VICH1PPSETR_DT_RGB18 (0x1e << 16) -#define VICH1PPSETR_DT_RGB18_LS (0x2e << 16) -#define VICH1PPSETR_DT_RGB24 (0x3e << 16) -#define VICH1PPSETR_TXESYNC_PULSE (1 << 15) -#define VICH1PPSETR_VC(x) ((x) << 22) - -/* Video-Input Channel 1 Vertical Size Set Register */ -#define VICH1VSSETR 0x428 -#define VICH1VSSETR_VACTIVE(x) (((x) & 0x7fff) << 16) -#define VICH1VSSETR_VSPOL_LOW (1 << 15) -#define VICH1VSSETR_VSPOL_HIGH (0 << 15) -#define VICH1VSSETR_VSA(x) (((x) & 0xfff) << 0) - -/* Video-Input Channel 1 Vertical Porch Set Register */ -#define VICH1VPSETR 0x42c -#define VICH1VPSETR_VFP(x) (((x) & 0x1fff) << 16) -#define VICH1VPSETR_VBP(x) (((x) & 0x1fff) << 0) - -/* Video-Input Channel 1 Horizontal Size Set Register */ -#define VICH1HSSETR 0x430 -#define VICH1HSSETR_HACTIVE(x) (((x) & 0x7fff) << 16) -#define VICH1HSSETR_HSPOL_LOW (1 << 15) -#define VICH1HSSETR_HSPOL_HIGH (0 << 15) -#define VICH1HSSETR_HSA(x) (((x) & 0xfff) << 0) - -/* Video-Input Channel 1 Horizontal Porch Set Register */ -#define VICH1HPSETR 0x434 -#define VICH1HPSETR_HFP(x) (((x) & 0x1fff) << 16) -#define VICH1HPSETR_HBP(x) (((x) & 0x1fff) << 0) - -#endif /* __RZG2L_MIPI_DSI_REGS_H__ */ diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig index 5f0db2c5fee6..8ec14271ebba 100644 --- a/drivers/gpu/drm/renesas/rz-du/Kconfig +++ b/drivers/gpu/drm/renesas/rz-du/Kconfig @@ -10,3 +10,11 @@ config DRM_RZG2L_DU help Choose this option if you have an RZ/G2L alike chipset. If M is selected the module will be called rzg2l-du-drm. + +config DRM_RZG2L_MIPI_DSI + tristate "RZ/G2L MIPI DSI Encoder Support" + depends on DRM && DRM_BRIDGE && OF + depends on ARCH_RENESAS || COMPILE_TEST + select DRM_MIPI_DSI + help + Enable support for the RZ/G2L Display Unit embedded MIPI DSI encoders. diff --git a/drivers/gpu/drm/renesas/rz-du/Makefile b/drivers/gpu/drm/renesas/rz-du/Makefile index 663b82a2577f..2987900ea6b6 100644 --- a/drivers/gpu/drm/renesas/rz-du/Makefile +++ b/drivers/gpu/drm/renesas/rz-du/Makefile @@ -6,3 +6,5 @@ rzg2l-du-drm-y := rzg2l_du_crtc.o \ rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1) += rzg2l_du_vsp.o obj-$(CONFIG_DRM_RZG2L_DU) += rzg2l-du-drm.o + +obj-$(CONFIG_DRM_RZG2L_MIPI_DSI) += rzg2l_mipi_dsi.o diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c new file mode 100644 index 000000000000..10febea473cd --- /dev/null +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G2L MIPI DSI Encoder Driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "rzg2l_mipi_dsi_regs.h" + +struct rzg2l_mipi_dsi { + struct device *dev; + void __iomem *mmio; + + struct reset_control *rstc; + struct reset_control *arstc; + struct reset_control *prstc; + + struct mipi_dsi_host host; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + + struct clk *vclk; + + enum mipi_dsi_pixel_format format; + unsigned int num_data_lanes; + unsigned int lanes; + unsigned long mode_flags; +}; + +static inline struct rzg2l_mipi_dsi * +bridge_to_rzg2l_mipi_dsi(struct drm_bridge *bridge) +{ + return container_of(bridge, struct rzg2l_mipi_dsi, bridge); +} + +static inline struct rzg2l_mipi_dsi * +host_to_rzg2l_mipi_dsi(struct mipi_dsi_host *host) +{ + return container_of(host, struct rzg2l_mipi_dsi, host); +} + +struct rzg2l_mipi_dsi_timings { + unsigned long hsfreq_max; + u32 t_init; + u32 tclk_prepare; + u32 ths_prepare; + u32 tclk_zero; + u32 tclk_pre; + u32 tclk_post; + u32 tclk_trail; + u32 ths_zero; + u32 ths_trail; + u32 ths_exit; + u32 tlpx; +}; + +static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { + { + .hsfreq_max = 80000, + .t_init = 79801, + .tclk_prepare = 8, + .ths_prepare = 13, + .tclk_zero = 33, + .tclk_pre = 24, + .tclk_post = 94, + .tclk_trail = 10, + .ths_zero = 23, + .ths_trail = 17, + .ths_exit = 13, + .tlpx = 6, + }, + { + .hsfreq_max = 125000, + .t_init = 79801, + .tclk_prepare = 8, + .ths_prepare = 12, + .tclk_zero = 33, + .tclk_pre = 15, + .tclk_post = 94, + .tclk_trail = 10, + .ths_zero = 23, + .ths_trail = 17, + .ths_exit = 13, + .tlpx = 6, + }, + { + .hsfreq_max = 250000, + .t_init = 79801, + .tclk_prepare = 8, + .ths_prepare = 12, + .tclk_zero = 33, + .tclk_pre = 13, + .tclk_post = 94, + .tclk_trail = 10, + .ths_zero = 23, + .ths_trail = 16, + .ths_exit = 13, + .tlpx = 6, + }, + { + .hsfreq_max = 360000, + .t_init = 79801, + .tclk_prepare = 8, + .ths_prepare = 10, + .tclk_zero = 33, + .tclk_pre = 4, + .tclk_post = 35, + .tclk_trail = 7, + .ths_zero = 16, + .ths_trail = 9, + .ths_exit = 13, + .tlpx = 6, + }, + { + .hsfreq_max = 720000, + .t_init = 79801, + .tclk_prepare = 8, + .ths_prepare = 9, + .tclk_zero = 33, + .tclk_pre = 4, + .tclk_post = 35, + .tclk_trail = 7, + .ths_zero = 16, + .ths_trail = 9, + .ths_exit = 13, + .tlpx = 6, + }, + { + .hsfreq_max = 1500000, + .t_init = 79801, + .tclk_prepare = 8, + .ths_prepare = 9, + .tclk_zero = 33, + .tclk_pre = 4, + .tclk_post = 35, + .tclk_trail = 7, + .ths_zero = 16, + .ths_trail = 9, + .ths_exit = 13, + .tlpx = 6, + }, +}; + +static void rzg2l_mipi_dsi_phy_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data) +{ + iowrite32(data, dsi->mmio + reg); +} + +static void rzg2l_mipi_dsi_link_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data) +{ + iowrite32(data, dsi->mmio + LINK_REG_OFFSET + reg); +} + +static u32 rzg2l_mipi_dsi_phy_read(struct rzg2l_mipi_dsi *dsi, u32 reg) +{ + return ioread32(dsi->mmio + reg); +} + +static u32 rzg2l_mipi_dsi_link_read(struct rzg2l_mipi_dsi *dsi, u32 reg) +{ + return ioread32(dsi->mmio + LINK_REG_OFFSET + reg); +} + +/* ----------------------------------------------------------------------------- + * Hardware Setup + */ + +static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi, + unsigned long hsfreq) +{ + const struct rzg2l_mipi_dsi_timings *dphy_timings; + unsigned int i; + u32 dphyctrl0; + u32 dphytim0; + u32 dphytim1; + u32 dphytim2; + u32 dphytim3; + int ret; + + /* All DSI global operation timings are set with recommended setting */ + for (i = 0; i < ARRAY_SIZE(rzg2l_mipi_dsi_global_timings); ++i) { + dphy_timings = &rzg2l_mipi_dsi_global_timings[i]; + if (hsfreq <= dphy_timings->hsfreq_max) + break; + } + + /* Initializing DPHY before accessing LINK */ + dphyctrl0 = DSIDPHYCTRL0_CAL_EN_HSRX_OFS | DSIDPHYCTRL0_CMN_MASTER_EN | + DSIDPHYCTRL0_RE_VDD_DETVCCQLV18 | DSIDPHYCTRL0_EN_BGR; + + rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0); + usleep_range(20, 30); + + dphyctrl0 |= DSIDPHYCTRL0_EN_LDO1200; + rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0); + usleep_range(10, 20); + + dphytim0 = DSIDPHYTIM0_TCLK_MISS(0) | + DSIDPHYTIM0_T_INIT(dphy_timings->t_init); + dphytim1 = DSIDPHYTIM1_THS_PREPARE(dphy_timings->ths_prepare) | + DSIDPHYTIM1_TCLK_PREPARE(dphy_timings->tclk_prepare) | + DSIDPHYTIM1_THS_SETTLE(0) | + DSIDPHYTIM1_TCLK_SETTLE(0); + dphytim2 = DSIDPHYTIM2_TCLK_TRAIL(dphy_timings->tclk_trail) | + DSIDPHYTIM2_TCLK_POST(dphy_timings->tclk_post) | + DSIDPHYTIM2_TCLK_PRE(dphy_timings->tclk_pre) | + DSIDPHYTIM2_TCLK_ZERO(dphy_timings->tclk_zero); + dphytim3 = DSIDPHYTIM3_TLPX(dphy_timings->tlpx) | + DSIDPHYTIM3_THS_EXIT(dphy_timings->ths_exit) | + DSIDPHYTIM3_THS_TRAIL(dphy_timings->ths_trail) | + DSIDPHYTIM3_THS_ZERO(dphy_timings->ths_zero); + + rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM0, dphytim0); + rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM1, dphytim1); + rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM2, dphytim2); + rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM3, dphytim3); + + ret = reset_control_deassert(dsi->rstc); + if (ret < 0) + return ret; + + udelay(1); + + return 0; +} + +static void rzg2l_mipi_dsi_dphy_exit(struct rzg2l_mipi_dsi *dsi) +{ + u32 dphyctrl0; + + dphyctrl0 = rzg2l_mipi_dsi_phy_read(dsi, DSIDPHYCTRL0); + + dphyctrl0 &= ~(DSIDPHYCTRL0_EN_LDO1200 | DSIDPHYCTRL0_EN_BGR); + rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0); + + reset_control_assert(dsi->rstc); +} + +static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, + const struct drm_display_mode *mode) +{ + unsigned long hsfreq; + unsigned int bpp; + u32 txsetr; + u32 clstptsetr; + u32 lptrnstsetr; + u32 clkkpt; + u32 clkbfht; + u32 clkstpt; + u32 golpbkt; + int ret; + + /* + * Relationship between hsclk and vclk must follow + * vclk * bpp = hsclk * 8 * lanes + * where vclk: video clock (Hz) + * bpp: video pixel bit depth + * hsclk: DSI HS Byte clock frequency (Hz) + * lanes: number of data lanes + * + * hsclk(bit) = hsclk(byte) * 8 + */ + bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + hsfreq = (mode->clock * bpp * 8) / (8 * dsi->lanes); + + ret = pm_runtime_resume_and_get(dsi->dev); + if (ret < 0) + return ret; + + clk_set_rate(dsi->vclk, mode->clock * 1000); + + ret = rzg2l_mipi_dsi_dphy_init(dsi, hsfreq); + if (ret < 0) + goto err_phy; + + /* Enable Data lanes and Clock lanes */ + txsetr = TXSETR_DLEN | TXSETR_NUMLANEUSE(dsi->lanes - 1) | TXSETR_CLEN; + rzg2l_mipi_dsi_link_write(dsi, TXSETR, txsetr); + + /* + * Global timings characteristic depends on high speed Clock Frequency + * Currently MIPI DSI-IF just supports maximum FHD@60 with: + * - videoclock = 148.5 (MHz) + * - bpp: maximum 24bpp + * - data lanes: maximum 4 lanes + * Therefore maximum hsclk will be 891 Mbps. + */ + if (hsfreq > 445500) { + clkkpt = 12; + clkbfht = 15; + clkstpt = 48; + golpbkt = 75; + } else if (hsfreq > 250000) { + clkkpt = 7; + clkbfht = 8; + clkstpt = 27; + golpbkt = 40; + } else { + clkkpt = 8; + clkbfht = 6; + clkstpt = 24; + golpbkt = 29; + } + + clstptsetr = CLSTPTSETR_CLKKPT(clkkpt) | CLSTPTSETR_CLKBFHT(clkbfht) | + CLSTPTSETR_CLKSTPT(clkstpt); + rzg2l_mipi_dsi_link_write(dsi, CLSTPTSETR, clstptsetr); + + lptrnstsetr = LPTRNSTSETR_GOLPBKT(golpbkt); + rzg2l_mipi_dsi_link_write(dsi, LPTRNSTSETR, lptrnstsetr); + + return 0; + +err_phy: + rzg2l_mipi_dsi_dphy_exit(dsi); + pm_runtime_put(dsi->dev); + + return ret; +} + +static void rzg2l_mipi_dsi_stop(struct rzg2l_mipi_dsi *dsi) +{ + rzg2l_mipi_dsi_dphy_exit(dsi); + pm_runtime_put(dsi->dev); +} + +static void rzg2l_mipi_dsi_set_display_timing(struct rzg2l_mipi_dsi *dsi, + const struct drm_display_mode *mode) +{ + u32 vich1ppsetr; + u32 vich1vssetr; + u32 vich1vpsetr; + u32 vich1hssetr; + u32 vich1hpsetr; + int dsi_format; + u32 delay[2]; + u8 index; + + /* Configuration for Pixel Packet */ + dsi_format = mipi_dsi_pixel_format_to_bpp(dsi->format); + switch (dsi_format) { + case 24: + vich1ppsetr = VICH1PPSETR_DT_RGB24; + break; + case 18: + vich1ppsetr = VICH1PPSETR_DT_RGB18; + break; + } + + if ((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) && + !(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)) + vich1ppsetr |= VICH1PPSETR_TXESYNC_PULSE; + + rzg2l_mipi_dsi_link_write(dsi, VICH1PPSETR, vich1ppsetr); + + /* Configuration for Video Parameters */ + vich1vssetr = VICH1VSSETR_VACTIVE(mode->vdisplay) | + VICH1VSSETR_VSA(mode->vsync_end - mode->vsync_start); + vich1vssetr |= (mode->flags & DRM_MODE_FLAG_PVSYNC) ? + VICH1VSSETR_VSPOL_HIGH : VICH1VSSETR_VSPOL_LOW; + + vich1vpsetr = VICH1VPSETR_VFP(mode->vsync_start - mode->vdisplay) | + VICH1VPSETR_VBP(mode->vtotal - mode->vsync_end); + + vich1hssetr = VICH1HSSETR_HACTIVE(mode->hdisplay) | + VICH1HSSETR_HSA(mode->hsync_end - mode->hsync_start); + vich1hssetr |= (mode->flags & DRM_MODE_FLAG_PHSYNC) ? + VICH1HSSETR_HSPOL_HIGH : VICH1HSSETR_HSPOL_LOW; + + vich1hpsetr = VICH1HPSETR_HFP(mode->hsync_start - mode->hdisplay) | + VICH1HPSETR_HBP(mode->htotal - mode->hsync_end); + + rzg2l_mipi_dsi_link_write(dsi, VICH1VSSETR, vich1vssetr); + rzg2l_mipi_dsi_link_write(dsi, VICH1VPSETR, vich1vpsetr); + rzg2l_mipi_dsi_link_write(dsi, VICH1HSSETR, vich1hssetr); + rzg2l_mipi_dsi_link_write(dsi, VICH1HPSETR, vich1hpsetr); + + /* + * Configuration for Delay Value + * Delay value based on 2 ranges of video clock. + * 74.25MHz is videoclock of HD@60p or FHD@30p + */ + if (mode->clock > 74250) { + delay[0] = 231; + delay[1] = 216; + } else { + delay[0] = 220; + delay[1] = 212; + } + + if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) + index = 0; + else + index = 1; + + rzg2l_mipi_dsi_link_write(dsi, VICH1SET1R, + VICH1SET1R_DLY(delay[index])); +} + +static int rzg2l_mipi_dsi_start_hs_clock(struct rzg2l_mipi_dsi *dsi) +{ + bool is_clk_cont; + u32 hsclksetr; + u32 status; + int ret; + + is_clk_cont = !(dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS); + + /* Start HS clock */ + hsclksetr = HSCLKSETR_HSCLKRUN_HS | (is_clk_cont ? + HSCLKSETR_HSCLKMODE_CONT : + HSCLKSETR_HSCLKMODE_NON_CONT); + rzg2l_mipi_dsi_link_write(dsi, HSCLKSETR, hsclksetr); + + if (is_clk_cont) { + ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, + status & PLSR_CLLP2HS, + 2000, 20000, false, dsi, PLSR); + if (ret < 0) { + dev_err(dsi->dev, "failed to start HS clock\n"); + return ret; + } + } + + dev_dbg(dsi->dev, "Start High Speed Clock with %s clock mode", + is_clk_cont ? "continuous" : "non-continuous"); + + return 0; +} + +static int rzg2l_mipi_dsi_stop_hs_clock(struct rzg2l_mipi_dsi *dsi) +{ + bool is_clk_cont; + u32 status; + int ret; + + is_clk_cont = !(dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS); + + /* Stop HS clock */ + rzg2l_mipi_dsi_link_write(dsi, HSCLKSETR, + is_clk_cont ? HSCLKSETR_HSCLKMODE_CONT : + HSCLKSETR_HSCLKMODE_NON_CONT); + + if (is_clk_cont) { + ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, + status & PLSR_CLHS2LP, + 2000, 20000, false, dsi, PLSR); + if (ret < 0) { + dev_err(dsi->dev, "failed to stop HS clock\n"); + return ret; + } + } + + return 0; +} + +static int rzg2l_mipi_dsi_start_video(struct rzg2l_mipi_dsi *dsi) +{ + u32 vich1set0r; + u32 status; + int ret; + + /* Configuration for Blanking sequence and start video input*/ + vich1set0r = VICH1SET0R_HFPNOLP | VICH1SET0R_HBPNOLP | + VICH1SET0R_HSANOLP | VICH1SET0R_VSTART; + rzg2l_mipi_dsi_link_write(dsi, VICH1SET0R, vich1set0r); + + ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, + status & VICH1SR_VIRDY, + 2000, 20000, false, dsi, VICH1SR); + if (ret < 0) + dev_err(dsi->dev, "Failed to start video signal input\n"); + + return ret; +} + +static int rzg2l_mipi_dsi_stop_video(struct rzg2l_mipi_dsi *dsi) +{ + u32 status; + int ret; + + rzg2l_mipi_dsi_link_write(dsi, VICH1SET0R, VICH1SET0R_VSTPAFT); + ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, + (status & VICH1SR_STOP) && (!(status & VICH1SR_RUNNING)), + 2000, 20000, false, dsi, VICH1SR); + if (ret < 0) + goto err; + + ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status, + !(status & LINKSR_HSBUSY), + 2000, 20000, false, dsi, LINKSR); + if (ret < 0) + goto err; + + return 0; + +err: + dev_err(dsi->dev, "Failed to stop video signal input\n"); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Bridge + */ + +static int rzg2l_mipi_dsi_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); + + return drm_bridge_attach(bridge->encoder, dsi->next_bridge, bridge, + flags); +} + +static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct drm_atomic_state *state = old_bridge_state->base.state; + struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); + const struct drm_display_mode *mode; + struct drm_connector *connector; + struct drm_crtc *crtc; + int ret; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; + mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode; + + ret = rzg2l_mipi_dsi_startup(dsi, mode); + if (ret < 0) + return; + + rzg2l_mipi_dsi_set_display_timing(dsi, mode); + + ret = rzg2l_mipi_dsi_start_hs_clock(dsi); + if (ret < 0) + goto err_stop; + + ret = rzg2l_mipi_dsi_start_video(dsi); + if (ret < 0) + goto err_stop_clock; + + return; + +err_stop_clock: + rzg2l_mipi_dsi_stop_hs_clock(dsi); +err_stop: + rzg2l_mipi_dsi_stop(dsi); +} + +static void rzg2l_mipi_dsi_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); + + rzg2l_mipi_dsi_stop_video(dsi); + rzg2l_mipi_dsi_stop_hs_clock(dsi); + rzg2l_mipi_dsi_stop(dsi); +} + +static enum drm_mode_status +rzg2l_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + if (mode->clock > 148500) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static const struct drm_bridge_funcs rzg2l_mipi_dsi_bridge_ops = { + .attach = rzg2l_mipi_dsi_attach, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_enable = rzg2l_mipi_dsi_atomic_enable, + .atomic_disable = rzg2l_mipi_dsi_atomic_disable, + .mode_valid = rzg2l_mipi_dsi_bridge_mode_valid, +}; + +/* ----------------------------------------------------------------------------- + * Host setting + */ + +static int rzg2l_mipi_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host); + int ret; + + if (device->lanes > dsi->num_data_lanes) { + dev_err(dsi->dev, + "Number of lines of device (%u) exceeds host (%u)\n", + device->lanes, dsi->num_data_lanes); + return -EINVAL; + } + + switch (mipi_dsi_pixel_format_to_bpp(device->format)) { + case 24: + case 18: + break; + default: + dev_err(dsi->dev, "Unsupported format 0x%04x\n", device->format); + return -EINVAL; + } + + dsi->lanes = device->lanes; + dsi->format = device->format; + dsi->mode_flags = device->mode_flags; + + dsi->next_bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node, + 1, 0); + if (IS_ERR(dsi->next_bridge)) { + ret = PTR_ERR(dsi->next_bridge); + dev_err(dsi->dev, "failed to get next bridge: %d\n", ret); + return ret; + } + + drm_bridge_add(&dsi->bridge); + + return 0; +} + +static int rzg2l_mipi_dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host); + + drm_bridge_remove(&dsi->bridge); + + return 0; +} + +static const struct mipi_dsi_host_ops rzg2l_mipi_dsi_host_ops = { + .attach = rzg2l_mipi_dsi_host_attach, + .detach = rzg2l_mipi_dsi_host_detach, +}; + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static int __maybe_unused rzg2l_mipi_pm_runtime_suspend(struct device *dev) +{ + struct rzg2l_mipi_dsi *dsi = dev_get_drvdata(dev); + + reset_control_assert(dsi->prstc); + reset_control_assert(dsi->arstc); + + return 0; +} + +static int __maybe_unused rzg2l_mipi_pm_runtime_resume(struct device *dev) +{ + struct rzg2l_mipi_dsi *dsi = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(dsi->arstc); + if (ret < 0) + return ret; + + ret = reset_control_deassert(dsi->prstc); + if (ret < 0) + reset_control_assert(dsi->arstc); + + return ret; +} + +static const struct dev_pm_ops rzg2l_mipi_pm_ops = { + SET_RUNTIME_PM_OPS(rzg2l_mipi_pm_runtime_suspend, rzg2l_mipi_pm_runtime_resume, NULL) +}; + +/* ----------------------------------------------------------------------------- + * Probe & Remove + */ + +static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) +{ + unsigned int num_data_lanes; + struct rzg2l_mipi_dsi *dsi; + u32 txsetr; + int ret; + + dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + platform_set_drvdata(pdev, dsi); + dsi->dev = &pdev->dev; + + ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4); + if (ret < 0) + return dev_err_probe(dsi->dev, ret, + "missing or invalid data-lanes property\n"); + + num_data_lanes = ret; + + dsi->mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dsi->mmio)) + return PTR_ERR(dsi->mmio); + + dsi->vclk = devm_clk_get(dsi->dev, "vclk"); + if (IS_ERR(dsi->vclk)) + return PTR_ERR(dsi->vclk); + + dsi->rstc = devm_reset_control_get_exclusive(dsi->dev, "rst"); + if (IS_ERR(dsi->rstc)) + return dev_err_probe(dsi->dev, PTR_ERR(dsi->rstc), + "failed to get rst\n"); + + dsi->arstc = devm_reset_control_get_exclusive(dsi->dev, "arst"); + if (IS_ERR(dsi->arstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(dsi->arstc), + "failed to get arst\n"); + + dsi->prstc = devm_reset_control_get_exclusive(dsi->dev, "prst"); + if (IS_ERR(dsi->prstc)) + return dev_err_probe(dsi->dev, PTR_ERR(dsi->prstc), + "failed to get prst\n"); + + platform_set_drvdata(pdev, dsi); + + pm_runtime_enable(dsi->dev); + + ret = pm_runtime_resume_and_get(dsi->dev); + if (ret < 0) + goto err_pm_disable; + + /* + * TXSETR register can be read only after DPHY init. But during probe + * mode->clock and format are not available. So initialize DPHY with + * timing parameters for 80Mbps. + */ + ret = rzg2l_mipi_dsi_dphy_init(dsi, 80000); + if (ret < 0) + goto err_phy; + + txsetr = rzg2l_mipi_dsi_link_read(dsi, TXSETR); + dsi->num_data_lanes = min(((txsetr >> 16) & 3) + 1, num_data_lanes); + rzg2l_mipi_dsi_dphy_exit(dsi); + pm_runtime_put(dsi->dev); + + /* Initialize the DRM bridge. */ + dsi->bridge.funcs = &rzg2l_mipi_dsi_bridge_ops; + dsi->bridge.of_node = dsi->dev->of_node; + + /* Init host device */ + dsi->host.dev = dsi->dev; + dsi->host.ops = &rzg2l_mipi_dsi_host_ops; + ret = mipi_dsi_host_register(&dsi->host); + if (ret < 0) + goto err_pm_disable; + + return 0; + +err_phy: + rzg2l_mipi_dsi_dphy_exit(dsi); + pm_runtime_put(dsi->dev); +err_pm_disable: + pm_runtime_disable(dsi->dev); + return ret; +} + +static void rzg2l_mipi_dsi_remove(struct platform_device *pdev) +{ + struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev); + + mipi_dsi_host_unregister(&dsi->host); + pm_runtime_disable(&pdev->dev); +} + +static const struct of_device_id rzg2l_mipi_dsi_of_table[] = { + { .compatible = "renesas,rzg2l-mipi-dsi" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, rzg2l_mipi_dsi_of_table); + +static struct platform_driver rzg2l_mipi_dsi_platform_driver = { + .probe = rzg2l_mipi_dsi_probe, + .remove_new = rzg2l_mipi_dsi_remove, + .driver = { + .name = "rzg2l-mipi-dsi", + .pm = &rzg2l_mipi_pm_ops, + .of_match_table = rzg2l_mipi_dsi_of_table, + }, +}; + +module_platform_driver(rzg2l_mipi_dsi_platform_driver); + +MODULE_AUTHOR("Biju Das "); +MODULE_DESCRIPTION("Renesas RZ/G2L MIPI DSI Encoder Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h new file mode 100644 index 000000000000..1dbc16ec64a4 --- /dev/null +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RZ/G2L MIPI DSI Interface Registers Definitions + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#ifndef __RZG2L_MIPI_DSI_REGS_H__ +#define __RZG2L_MIPI_DSI_REGS_H__ + +#include + +/* DPHY Registers */ +#define DSIDPHYCTRL0 0x00 +#define DSIDPHYCTRL0_CAL_EN_HSRX_OFS BIT(16) +#define DSIDPHYCTRL0_CMN_MASTER_EN BIT(8) +#define DSIDPHYCTRL0_RE_VDD_DETVCCQLV18 BIT(2) +#define DSIDPHYCTRL0_EN_LDO1200 BIT(1) +#define DSIDPHYCTRL0_EN_BGR BIT(0) + +#define DSIDPHYTIM0 0x04 +#define DSIDPHYTIM0_TCLK_MISS(x) ((x) << 24) +#define DSIDPHYTIM0_T_INIT(x) ((x) << 0) + +#define DSIDPHYTIM1 0x08 +#define DSIDPHYTIM1_THS_PREPARE(x) ((x) << 24) +#define DSIDPHYTIM1_TCLK_PREPARE(x) ((x) << 16) +#define DSIDPHYTIM1_THS_SETTLE(x) ((x) << 8) +#define DSIDPHYTIM1_TCLK_SETTLE(x) ((x) << 0) + +#define DSIDPHYTIM2 0x0c +#define DSIDPHYTIM2_TCLK_TRAIL(x) ((x) << 24) +#define DSIDPHYTIM2_TCLK_POST(x) ((x) << 16) +#define DSIDPHYTIM2_TCLK_PRE(x) ((x) << 8) +#define DSIDPHYTIM2_TCLK_ZERO(x) ((x) << 0) + +#define DSIDPHYTIM3 0x10 +#define DSIDPHYTIM3_TLPX(x) ((x) << 24) +#define DSIDPHYTIM3_THS_EXIT(x) ((x) << 16) +#define DSIDPHYTIM3_THS_TRAIL(x) ((x) << 8) +#define DSIDPHYTIM3_THS_ZERO(x) ((x) << 0) + +/* --------------------------------------------------------*/ +/* Link Registers */ +#define LINK_REG_OFFSET 0x10000 + +/* Link Status Register */ +#define LINKSR 0x10 +#define LINKSR_LPBUSY BIT(13) +#define LINKSR_HSBUSY BIT(12) +#define LINKSR_VICHRUN1 BIT(8) +#define LINKSR_SQCHRUN1 BIT(4) +#define LINKSR_SQCHRUN0 BIT(0) + +/* Tx Set Register */ +#define TXSETR 0x100 +#define TXSETR_NUMLANECAP (0x3 << 16) +#define TXSETR_DLEN (1 << 9) +#define TXSETR_CLEN (1 << 8) +#define TXSETR_NUMLANEUSE(x) (((x) & 0x3) << 0) + +/* HS Clock Set Register */ +#define HSCLKSETR 0x104 +#define HSCLKSETR_HSCLKMODE_CONT (1 << 1) +#define HSCLKSETR_HSCLKMODE_NON_CONT (0 << 1) +#define HSCLKSETR_HSCLKRUN_HS (1 << 0) +#define HSCLKSETR_HSCLKRUN_LP (0 << 0) + +/* Reset Control Register */ +#define RSTCR 0x110 +#define RSTCR_SWRST BIT(0) +#define RSTCR_FCETXSTP BIT(16) + +/* Reset Status Register */ +#define RSTSR 0x114 +#define RSTSR_DL0DIR (1 << 15) +#define RSTSR_DLSTPST (0xf << 8) +#define RSTSR_SWRSTV1 (1 << 4) +#define RSTSR_SWRSTIB (1 << 3) +#define RSTSR_SWRSTAPB (1 << 2) +#define RSTSR_SWRSTLP (1 << 1) +#define RSTSR_SWRSTHS (1 << 0) + +/* Clock Lane Stop Time Set Register */ +#define CLSTPTSETR 0x314 +#define CLSTPTSETR_CLKKPT(x) ((x) << 24) +#define CLSTPTSETR_CLKBFHT(x) ((x) << 16) +#define CLSTPTSETR_CLKSTPT(x) ((x) << 2) + +/* LP Transition Time Set Register */ +#define LPTRNSTSETR 0x318 +#define LPTRNSTSETR_GOLPBKT(x) ((x) << 0) + +/* Physical Lane Status Register */ +#define PLSR 0x320 +#define PLSR_CLHS2LP BIT(27) +#define PLSR_CLLP2HS BIT(26) + +/* Video-Input Channel 1 Set 0 Register */ +#define VICH1SET0R 0x400 +#define VICH1SET0R_VSEN BIT(12) +#define VICH1SET0R_HFPNOLP BIT(10) +#define VICH1SET0R_HBPNOLP BIT(9) +#define VICH1SET0R_HSANOLP BIT(8) +#define VICH1SET0R_VSTPAFT BIT(1) +#define VICH1SET0R_VSTART BIT(0) + +/* Video-Input Channel 1 Set 1 Register */ +#define VICH1SET1R 0x404 +#define VICH1SET1R_DLY(x) (((x) & 0xfff) << 2) + +/* Video-Input Channel 1 Status Register */ +#define VICH1SR 0x410 +#define VICH1SR_VIRDY BIT(3) +#define VICH1SR_RUNNING BIT(2) +#define VICH1SR_STOP BIT(1) +#define VICH1SR_START BIT(0) + +/* Video-Input Channel 1 Pixel Packet Set Register */ +#define VICH1PPSETR 0x420 +#define VICH1PPSETR_DT_RGB18 (0x1e << 16) +#define VICH1PPSETR_DT_RGB18_LS (0x2e << 16) +#define VICH1PPSETR_DT_RGB24 (0x3e << 16) +#define VICH1PPSETR_TXESYNC_PULSE (1 << 15) +#define VICH1PPSETR_VC(x) ((x) << 22) + +/* Video-Input Channel 1 Vertical Size Set Register */ +#define VICH1VSSETR 0x428 +#define VICH1VSSETR_VACTIVE(x) (((x) & 0x7fff) << 16) +#define VICH1VSSETR_VSPOL_LOW (1 << 15) +#define VICH1VSSETR_VSPOL_HIGH (0 << 15) +#define VICH1VSSETR_VSA(x) (((x) & 0xfff) << 0) + +/* Video-Input Channel 1 Vertical Porch Set Register */ +#define VICH1VPSETR 0x42c +#define VICH1VPSETR_VFP(x) (((x) & 0x1fff) << 16) +#define VICH1VPSETR_VBP(x) (((x) & 0x1fff) << 0) + +/* Video-Input Channel 1 Horizontal Size Set Register */ +#define VICH1HSSETR 0x430 +#define VICH1HSSETR_HACTIVE(x) (((x) & 0x7fff) << 16) +#define VICH1HSSETR_HSPOL_LOW (1 << 15) +#define VICH1HSSETR_HSPOL_HIGH (0 << 15) +#define VICH1HSSETR_HSA(x) (((x) & 0xfff) << 0) + +/* Video-Input Channel 1 Horizontal Porch Set Register */ +#define VICH1HPSETR 0x434 +#define VICH1HPSETR_HFP(x) (((x) & 0x1fff) << 16) +#define VICH1HPSETR_HBP(x) (((x) & 0x1fff) << 0) + +#endif /* __RZG2L_MIPI_DSI_REGS_H__ */ -- cgit v1.2.3 From b330f148017251810fc1f0c297f51b3039b796e0 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 22 Aug 2024 17:23:15 +0100 Subject: drm: renesas: rz-du: Add RZ/G2UL DU Support The LCD controller is composed of Frame Compression Processor (FCPVD), Video Signal Processor (VSPD), and Display Unit (DU). It has DPI interface and supports a maximum resolution of WXGA along with 2 RPFs to support the blending of two picture layers and raster operations (ROPs). The DU module is connected to VSPD. Add RZ/G2UL DU support. Signed-off-by: Biju Das Reviewed-by: Laurent Pinchart Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240822162320.5084-3-biju.das.jz@bp.renesas.com --- drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c | 8 +++++++- drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c | 11 +++++++++++ drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c | 3 ++- 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c index 6e7aac6219be..c4c1474d487e 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c @@ -28,6 +28,7 @@ #include "rzg2l_du_vsp.h" #define DU_MCR0 0x00 +#define DU_MCR0_DPI_OE BIT(0) #define DU_MCR0_DI_EN BIT(8) #define DU_DITR0 0x10 @@ -216,9 +217,14 @@ static void rzg2l_du_crtc_put(struct rzg2l_du_crtc *rcrtc) static void rzg2l_du_start_stop(struct rzg2l_du_crtc *rcrtc, bool start) { + struct rzg2l_du_crtc_state *rstate = to_rzg2l_crtc_state(rcrtc->crtc.state); struct rzg2l_du_device *rcdu = rcrtc->dev; + u32 val = DU_MCR0_DI_EN; - writel(start ? DU_MCR0_DI_EN : 0, rcdu->mmio + DU_MCR0); + if (rstate->outputs & BIT(RZG2L_DU_OUTPUT_DPAD0)) + val |= DU_MCR0_DPI_OE; + + writel(start ? val : 0, rcdu->mmio + DU_MCR0); } static void rzg2l_du_crtc_start(struct rzg2l_du_crtc *rcrtc) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c index e5eca8691a33..bc7c381f92ac 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c @@ -25,6 +25,16 @@ * Device Information */ +static const struct rzg2l_du_device_info rzg2l_du_r9a07g043u_info = { + .channels_mask = BIT(0), + .routes = { + [RZG2L_DU_OUTPUT_DPAD0] = { + .possible_outputs = BIT(0), + .port = 0, + }, + }, +}; + static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = { .channels_mask = BIT(0), .routes = { @@ -40,6 +50,7 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = { }; static const struct of_device_id rzg2l_du_of_table[] = { + { .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info }, { .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c index 07b312b6f81e..b99217b4e05d 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c @@ -183,7 +183,8 @@ static int rzg2l_du_encoders_init(struct rzg2l_du_device *rcdu) /* Find the output route corresponding to the port number. */ for (i = 0; i < RZG2L_DU_OUTPUT_MAX; ++i) { - if (rcdu->info->routes[i].port == ep.port) { + if (rcdu->info->routes[i].possible_outputs && + rcdu->info->routes[i].port == ep.port) { output = i; break; } -- cgit v1.2.3 From 51394119f640423858a2f04076d6f1c3e83fa715 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 26 Aug 2024 15:38:46 +0300 Subject: drm/panel-edp: add BOE NE140WUM-N6G panel entry Add an eDP panel entry for BOE NE140WUM-N6G. Due to lack of documentation, use the delay_200_500_e80 timings like some other BOE entries for now. The raw edid of the panel is: 00 ff ff ff ff ff ff 00 09 e5 66 0b 00 00 00 00 1a 20 01 04 a5 1e 13 78 07 01 5f a7 54 4c 9b 24 11 51 56 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 9c 3e 80 c8 70 b0 3c 40 30 20 36 00 2e bc 10 00 00 1a 16 32 80 c8 70 b0 3c 40 30 20 36 00 2e bc 10 00 00 1a 00 00 00 fd 00 1e 3c 4c 4c 10 01 0a 20 20 20 20 20 20 00 00 00 fe 00 4e 45 31 34 30 57 55 4d 2d 4e 36 47 0a 00 dc Reviewed-by: Douglas Anderson Signed-off-by: Abel Vesa Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20240826-drm-panel-edp-add-boe-ne140wum-n6g-v2-1-2758e8574842@linaro.org --- drivers/gpu/drm/panel/panel-edp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 7183df267777..f6102ceaf0a7 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1911,6 +1911,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b34, &delay_200_500_e80, "NV122WUM-N41"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b43, &delay_200_500_e200, "NV140FHM-T09"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b56, &delay_200_500_e80, "NT140FHM-N47"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b66, &delay_200_500_e80, "NE140WUM-N6G"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c20, &delay_200_500_e80, "NT140FHM-N47"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cb6, &delay_200_500_e200, "NT116WHM-N44"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cfa, &delay_200_500_e50, "NV116WHM-A4D"), -- cgit v1.2.3 From 22bc22ccf95bfa6eb6288ba4bc33d7fc0078381e Mon Sep 17 00:00:00 2001 From: renjun wang Date: Sat, 24 Aug 2024 16:36:34 +0800 Subject: drm: Fix kerneldoc for "Returns" section The blank line between title "Returns:" and detail description is not allowed, otherwise the title will goes under the description block in generated .html file after running `make htmldocs`. There are a few examples for current kerneldoc at [1][2][3]. v2: - use Link tag with stable URLs Signed-off-by: renjun wang Link: https://www.kernel.org/doc/html/v6.10/gpu/drm-kms.html#c.drm_crtc_commit_wait # 1 Link: https://www.kernel.org/doc/html/v6.10/gpu/drm-kms.html#c.drm_atomic_get_crtc_state # 2 Link: https://www.kernel.org/doc/html/v6.10/gpu/i915.html#c.i915_vma_pin_fence # 3 Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/tencent_37A873672B5CD20DECAF99DEDAC5E45C3106@qq.com --- drivers/gpu/drm/display/drm_dp_mst_topology.c | 4 ---- drivers/gpu/drm/drm_atomic.c | 6 ------ drivers/gpu/drm/drm_atomic_helper.c | 2 -- drivers/gpu/drm/drm_file.c | 7 ------- drivers/gpu/drm/drm_gem.c | 7 ++----- drivers/gpu/drm/drm_modes.c | 1 - drivers/gpu/drm/drm_rect.c | 1 - drivers/gpu/drm/drm_vblank.c | 2 -- drivers/gpu/drm/i915/gem/i915_gem_object.h | 1 - drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c | 1 - drivers/gpu/drm/i915/i915_vma.h | 1 - 11 files changed, 2 insertions(+), 31 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 65c1700f3e40..f54d0cbfb94d 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -5569,7 +5569,6 @@ EXPORT_SYMBOL(drm_dp_mst_atomic_check_mgr); * drm_dp_atomic_release_time_slots() * * Returns: - * * 0 if the new state is valid, negative error code otherwise. */ int drm_dp_mst_atomic_check(struct drm_atomic_state *state) @@ -5606,7 +5605,6 @@ EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs); * topology object. * * RETURNS: - * * The MST topology state or error pointer. */ struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state, @@ -5626,7 +5624,6 @@ EXPORT_SYMBOL(drm_atomic_get_mst_topology_state); * topology object. * * Returns: - * * The old MST topology state, or NULL if there's no topology state for this MST mgr * in the global atomic state */ @@ -5651,7 +5648,6 @@ EXPORT_SYMBOL(drm_atomic_get_old_mst_topology_state); * topology object. * * Returns: - * * The new MST topology state, or NULL if there's no topology state for this MST mgr * in the global atomic state */ diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 6e516c39a372..0fc99da93afe 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -63,7 +63,6 @@ EXPORT_SYMBOL(__drm_crtc_commit_free); * hardware and flipped to. * * Returns: - * * 0 on success, a negative error code otherwise. */ int drm_crtc_commit_wait(struct drm_crtc_commit *commit) @@ -337,7 +336,6 @@ EXPORT_SYMBOL(__drm_atomic_state_free); * not created by userspace through an IOCTL call. * * Returns: - * * Either the allocated state or the error code encoded into the pointer. When * the error is EDEADLK then the w/w mutex code has detected a deadlock and the * entire atomic sequence must be restarted. All other errors are fatal. @@ -518,7 +516,6 @@ static int drm_atomic_connector_check(struct drm_connector *connector, * is consistent. * * Returns: - * * Either the allocated state or the error code encoded into the pointer. When * the error is EDEADLK then the w/w mutex code has detected a deadlock and the * entire atomic sequence must be restarted. All other errors are fatal. @@ -828,7 +825,6 @@ EXPORT_SYMBOL(drm_atomic_private_obj_fini); * object lock to make sure that the state is consistent. * * RETURNS: - * * Either the allocated state or the error code encoded into a pointer. */ struct drm_private_state * @@ -1061,7 +1057,6 @@ EXPORT_SYMBOL(drm_atomic_get_new_crtc_for_encoder); * make sure that the state is consistent. * * Returns: - * * Either the allocated state or the error code encoded into the pointer. When * the error is EDEADLK then the w/w mutex code has detected a deadlock and the * entire atomic sequence must be restarted. All other errors are fatal. @@ -1169,7 +1164,6 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, * state is consistent. * * Returns: - * * Either the allocated state or the error code encoded into the pointer. When * the error is EDEADLK then the w/w mutex code has detected a deadlock and the * entire atomic sequence must be restarted. diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index fb97b51b38f1..43cdf39019a4 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2266,7 +2266,6 @@ crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc) * automatically. * * Returns: - * * 0 on success. -EBUSY when userspace schedules nonblocking commits too fast, * -ENOMEM on allocation failures and -EINTR when a signal is pending. */ @@ -3009,7 +3008,6 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); * don't pass the right state structures to the callbacks. * * Returns: - * * Returns 0 on success. Can return -ERESTARTSYS when @stall is true and the * waiting for the previous commits has been interrupted. */ diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index e8a841e70934..351591f3a27a 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -347,7 +347,6 @@ int drm_open_helper(struct file *filp, struct drm_minor *minor) * resources for it. It also calls the &drm_driver.open driver callback. * * RETURNS: - * * 0 on success or negative errno value on failure. */ int drm_open(struct inode *inode, struct file *filp) @@ -406,7 +405,6 @@ static void drm_lastclose(struct drm_device *dev) * in-kernel DRM client. * * RETURNS: - * * Always succeeds and returns 0. */ int drm_release(struct inode *inode, struct file *filp) @@ -477,7 +475,6 @@ void drm_file_update_pid(struct drm_file *filp) * then restores the active in-kernel DRM client. * * RETURNS: - * * Always succeeds and returns 0. */ int drm_release_noglobal(struct inode *inode, struct file *filp) @@ -520,7 +517,6 @@ EXPORT_SYMBOL(drm_release_noglobal); * safety. * * RETURNS: - * * Number of bytes read (always aligned to full events, and can be 0) or a * negative error code on failure. */ @@ -606,7 +602,6 @@ EXPORT_SYMBOL(drm_read); * See also drm_read(). * * RETURNS: - * * Mask of POLL flags indicating the current status of the file. */ __poll_t drm_poll(struct file *filp, struct poll_table_struct *wait) @@ -644,7 +639,6 @@ EXPORT_SYMBOL(drm_poll); * already hold &drm_device.event_lock. * * RETURNS: - * * 0 on success or a negative error code on failure. */ int drm_event_reserve_init_locked(struct drm_device *dev, @@ -686,7 +680,6 @@ EXPORT_SYMBOL(drm_event_reserve_init_locked); * drm_event_reserve_init_locked() instead. * * RETURNS: - * * 0 on success or a negative error code on failure. */ int drm_event_reserve_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index d4bbc5d109c8..149b8e25da5b 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -689,7 +689,6 @@ static int objects_lookup(struct drm_file *filp, u32 *handle, int count, * For a single handle lookup, use drm_gem_object_lookup(). * * Returns: - * * @objs filled in with GEM object pointers. Returned GEM objects need to be * released with drm_gem_object_put(). -ENOENT is returned on a lookup * failure. 0 is returned on success. @@ -737,12 +736,11 @@ EXPORT_SYMBOL(drm_gem_objects_lookup); * @filp: DRM file private date * @handle: userspace handle * - * Returns: + * If looking up an array of handles, use drm_gem_objects_lookup(). * + * Returns: * A reference to the object named by the handle if such exists on @filp, NULL * otherwise. - * - * If looking up an array of handles, use drm_gem_objects_lookup(). */ struct drm_gem_object * drm_gem_object_lookup(struct drm_file *filp, u32 handle) @@ -763,7 +761,6 @@ EXPORT_SYMBOL(drm_gem_object_lookup); * @timeout: timeout value in jiffies or zero to return immediately * * Returns: - * * Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or * greater than 0 on success. */ diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 1a0890083aee..6ba167a33461 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -539,7 +539,6 @@ static int fill_analog_mode(struct drm_device *dev, * to reach those resolutions. * * Returns: - * * A pointer to the mode, allocated with drm_mode_create(). Returns NULL * on error. */ diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index 85c79a38c13a..492acce0516f 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -85,7 +85,6 @@ static u32 clip_scaled(int src, int dst, int *clip) * factors from @src to @dst. * * RETURNS: - * * %true if rectangle @dst is still visible after being clipped, * %false otherwise. */ diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index c6b4cd77df72..94e45ed6869d 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -686,7 +686,6 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); * drm_atomic_helper_calc_timestamping_constants(). * * Returns: - * * Returns true on success, and false on failure, i.e. when no accurate * timestamp could be acquired. */ @@ -831,7 +830,6 @@ EXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_internal); * drm_atomic_helper_calc_timestamping_constants(). * * Returns: - * * Returns true on success, and false on failure, i.e. when no accurate * timestamp could be acquired. */ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h index 5d7446a48ae7..3dc61cbd2e11 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h @@ -89,7 +89,6 @@ __i915_gem_object_unset_pages(struct drm_i915_gem_object *obj); * @handle: userspace handle * * Returns: - * * A pointer to the object named by the handle if such exists on @filp, NULL * otherwise. This object is only valid whilst under the RCU read lock, and * note carefully the object may be in the process of being destroyed. diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c index 93bc1cc1ee7e..0ffba50981e3 100644 --- a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c +++ b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c @@ -418,7 +418,6 @@ out_unpin: * For an untiled surface, this removes any existing fence. * * Returns: - * * 0 on success, negative error code on failure. */ int i915_vma_pin_fence(struct i915_vma *vma) diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h index e356dfb883d3..6a6be8048aa8 100644 --- a/drivers/gpu/drm/i915/i915_vma.h +++ b/drivers/gpu/drm/i915/i915_vma.h @@ -389,7 +389,6 @@ void i915_vma_unpin_iomap(struct i915_vma *vma); * i915_vma_unpin_fence(). * * Returns: - * * True if the vma has a fence, false otherwise. */ int __must_check i915_vma_pin_fence(struct i915_vma *vma); -- cgit v1.2.3 From 5fbca8b48b3050ae7fb611a8b09af60012ed6de1 Mon Sep 17 00:00:00 2001 From: Michał Winiarski Date: Fri, 23 Aug 2024 18:30:46 +0200 Subject: drm: Use XArray instead of IDR for minors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IDR is deprecated, and since XArray manages its own state with internal locking, it simplifies the locking on DRM side. Additionally, don't use the IRQ-safe variant, since operating on drm minor is not done in IRQ context. Suggested-by: Matthew Wilcox Signed-off-by: Michał Winiarski Acked-by: James Zhu Acked-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20240823163048.2676257-2-michal.winiarski@intel.com Signed-off-by: Christian König --- drivers/gpu/drm/drm_drv.c | 63 +++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 38 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 27007b53a8c8..c760dc0f814a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -54,8 +55,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl"); MODULE_DESCRIPTION("DRM shared core routines"); MODULE_LICENSE("GPL and additional rights"); -static DEFINE_SPINLOCK(drm_minor_lock); -static struct idr drm_minors_idr; +static DEFINE_XARRAY_ALLOC(drm_minors_xa); /* * If the drm core fails to init for whatever reason, @@ -101,26 +101,23 @@ static struct drm_minor **drm_minor_get_slot(struct drm_device *dev, static void drm_minor_alloc_release(struct drm_device *dev, void *data) { struct drm_minor *minor = data; - unsigned long flags; WARN_ON(dev != minor->dev); put_device(minor->kdev); - if (minor->type == DRM_MINOR_ACCEL) { + if (minor->type == DRM_MINOR_ACCEL) accel_minor_remove(minor->index); - } else { - spin_lock_irqsave(&drm_minor_lock, flags); - idr_remove(&drm_minors_idr, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); - } + else + xa_erase(&drm_minors_xa, minor->index); } +#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * _t + 63); }) + static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type) { struct drm_minor *minor; - unsigned long flags; - int r; + int index, r; minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL); if (!minor) @@ -129,24 +126,17 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type) minor->type = type; minor->dev = dev; - idr_preload(GFP_KERNEL); if (type == DRM_MINOR_ACCEL) { r = accel_minor_alloc(); + index = r; } else { - spin_lock_irqsave(&drm_minor_lock, flags); - r = idr_alloc(&drm_minors_idr, - NULL, - 64 * type, - 64 * (type + 1), - GFP_NOWAIT); - spin_unlock_irqrestore(&drm_minor_lock, flags); + r = xa_alloc(&drm_minors_xa, &index, NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL); } - idr_preload_end(); if (r < 0) return r; - minor->index = r; + minor->index = index; r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor); if (r) @@ -163,7 +153,7 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type) static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type) { struct drm_minor *minor; - unsigned long flags; + void *entry; int ret; DRM_DEBUG("\n"); @@ -189,9 +179,12 @@ static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type) if (minor->type == DRM_MINOR_ACCEL) { accel_minor_replace(minor, minor->index); } else { - spin_lock_irqsave(&drm_minor_lock, flags); - idr_replace(&drm_minors_idr, minor, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); + entry = xa_store(&drm_minors_xa, minor->index, minor, GFP_KERNEL); + if (xa_is_err(entry)) { + ret = xa_err(entry); + goto err_debugfs; + } + WARN_ON(entry); } DRM_DEBUG("new minor registered %d\n", minor->index); @@ -205,20 +198,16 @@ err_debugfs: static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type type) { struct drm_minor *minor; - unsigned long flags; minor = *drm_minor_get_slot(dev, type); if (!minor || !device_is_registered(minor->kdev)) return; /* replace @minor with NULL so lookups will fail from now on */ - if (minor->type == DRM_MINOR_ACCEL) { + if (minor->type == DRM_MINOR_ACCEL) accel_minor_replace(NULL, minor->index); - } else { - spin_lock_irqsave(&drm_minor_lock, flags); - idr_replace(&drm_minors_idr, NULL, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); - } + else + xa_store(&drm_minors_xa, minor->index, NULL, GFP_KERNEL); device_del(minor->kdev); dev_set_drvdata(minor->kdev, NULL); /* safety belt */ @@ -237,13 +226,12 @@ static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type typ struct drm_minor *drm_minor_acquire(unsigned int minor_id) { struct drm_minor *minor; - unsigned long flags; - spin_lock_irqsave(&drm_minor_lock, flags); - minor = idr_find(&drm_minors_idr, minor_id); + xa_lock(&drm_minors_xa); + minor = xa_load(&drm_minors_xa, minor_id); if (minor) drm_dev_get(minor->dev); - spin_unlock_irqrestore(&drm_minor_lock, flags); + xa_unlock(&drm_minors_xa); if (!minor) { return ERR_PTR(-ENODEV); @@ -1072,7 +1060,7 @@ static void drm_core_exit(void) unregister_chrdev(DRM_MAJOR, "drm"); debugfs_remove(drm_debugfs_root); drm_sysfs_destroy(); - idr_destroy(&drm_minors_idr); + WARN_ON(!xa_empty(&drm_minors_xa)); drm_connector_ida_destroy(); } @@ -1081,7 +1069,6 @@ static int __init drm_core_init(void) int ret; drm_connector_ida_init(); - idr_init(&drm_minors_idr); drm_memcpy_init_early(); ret = drm_sysfs_init(); -- cgit v1.2.3 From 45c4d994b82b08f0ce5eb50f8da29379c92a391e Mon Sep 17 00:00:00 2001 From: Michał Winiarski Date: Fri, 23 Aug 2024 18:30:47 +0200 Subject: accel: Use XArray instead of IDR for minors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accel minor management is based on DRM (and is also using struct drm_minor internally), since DRM is using XArray for minors, it makes sense to also convert accel. As the two implementations are identical (only difference being the underlying xarray), move the accel_minor_* functionality to DRM. Signed-off-by: Michał Winiarski Acked-by: James Zhu Acked-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20240823163048.2676257-3-michal.winiarski@intel.com Signed-off-by: Christian König --- drivers/accel/drm_accel.c | 110 +++-------------------------------------- drivers/gpu/drm/drm_drv.c | 66 ++++++++++++------------- drivers/gpu/drm/drm_file.c | 2 +- drivers/gpu/drm/drm_internal.h | 4 -- include/drm/drm_accel.h | 18 +------ include/drm/drm_file.h | 5 ++ 6 files changed, 47 insertions(+), 158 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/accel/drm_accel.c b/drivers/accel/drm_accel.c index 16c3edb8c46e..aa826033b0ce 100644 --- a/drivers/accel/drm_accel.c +++ b/drivers/accel/drm_accel.c @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -18,8 +18,7 @@ #include #include -static DEFINE_SPINLOCK(accel_minor_lock); -static struct idr accel_minors_idr; +DEFINE_XARRAY_ALLOC(accel_minors_xa); static struct dentry *accel_debugfs_root; @@ -117,99 +116,6 @@ void accel_set_device_instance_params(struct device *kdev, int index) kdev->type = &accel_sysfs_device_minor; } -/** - * accel_minor_alloc() - Allocates a new accel minor - * - * This function access the accel minors idr and allocates from it - * a new id to represent a new accel minor - * - * Return: A new id on success or error code in case idr_alloc failed - */ -int accel_minor_alloc(void) -{ - unsigned long flags; - int r; - - spin_lock_irqsave(&accel_minor_lock, flags); - r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT); - spin_unlock_irqrestore(&accel_minor_lock, flags); - - return r; -} - -/** - * accel_minor_remove() - Remove an accel minor - * @index: The minor id to remove. - * - * This function access the accel minors idr and removes from - * it the member with the id that is passed to this function. - */ -void accel_minor_remove(int index) -{ - unsigned long flags; - - spin_lock_irqsave(&accel_minor_lock, flags); - idr_remove(&accel_minors_idr, index); - spin_unlock_irqrestore(&accel_minor_lock, flags); -} - -/** - * accel_minor_replace() - Replace minor pointer in accel minors idr. - * @minor: Pointer to the new minor. - * @index: The minor id to replace. - * - * This function access the accel minors idr structure and replaces the pointer - * that is associated with an existing id. Because the minor pointer can be - * NULL, we need to explicitly pass the index. - * - * Return: 0 for success, negative value for error - */ -void accel_minor_replace(struct drm_minor *minor, int index) -{ - unsigned long flags; - - spin_lock_irqsave(&accel_minor_lock, flags); - idr_replace(&accel_minors_idr, minor, index); - spin_unlock_irqrestore(&accel_minor_lock, flags); -} - -/* - * Looks up the given minor-ID and returns the respective DRM-minor object. The - * refence-count of the underlying device is increased so you must release this - * object with accel_minor_release(). - * - * The object can be only a drm_minor that represents an accel device. - * - * As long as you hold this minor, it is guaranteed that the object and the - * minor->dev pointer will stay valid! However, the device may get unplugged and - * unregistered while you hold the minor. - */ -static struct drm_minor *accel_minor_acquire(unsigned int minor_id) -{ - struct drm_minor *minor; - unsigned long flags; - - spin_lock_irqsave(&accel_minor_lock, flags); - minor = idr_find(&accel_minors_idr, minor_id); - if (minor) - drm_dev_get(minor->dev); - spin_unlock_irqrestore(&accel_minor_lock, flags); - - if (!minor) { - return ERR_PTR(-ENODEV); - } else if (drm_dev_is_unplugged(minor->dev)) { - drm_dev_put(minor->dev); - return ERR_PTR(-ENODEV); - } - - return minor; -} - -static void accel_minor_release(struct drm_minor *minor) -{ - drm_dev_put(minor->dev); -} - /** * accel_open - open method for ACCEL file * @inode: device inode @@ -227,7 +133,7 @@ int accel_open(struct inode *inode, struct file *filp) struct drm_minor *minor; int retcode; - minor = accel_minor_acquire(iminor(inode)); + minor = drm_minor_acquire(&accel_minors_xa, iminor(inode)); if (IS_ERR(minor)) return PTR_ERR(minor); @@ -246,7 +152,7 @@ int accel_open(struct inode *inode, struct file *filp) err_undo: atomic_dec(&dev->open_count); - accel_minor_release(minor); + drm_minor_release(minor); return retcode; } EXPORT_SYMBOL_GPL(accel_open); @@ -257,7 +163,7 @@ static int accel_stub_open(struct inode *inode, struct file *filp) struct drm_minor *minor; int err; - minor = accel_minor_acquire(iminor(inode)); + minor = drm_minor_acquire(&accel_minors_xa, iminor(inode)); if (IS_ERR(minor)) return PTR_ERR(minor); @@ -274,7 +180,7 @@ static int accel_stub_open(struct inode *inode, struct file *filp) err = 0; out: - accel_minor_release(minor); + drm_minor_release(minor); return err; } @@ -290,15 +196,13 @@ void accel_core_exit(void) unregister_chrdev(ACCEL_MAJOR, "accel"); debugfs_remove(accel_debugfs_root); accel_sysfs_destroy(); - idr_destroy(&accel_minors_idr); + WARN_ON(!xa_empty(&accel_minors_xa)); } int __init accel_core_init(void) { int ret; - idr_init(&accel_minors_idr); - ret = accel_sysfs_init(); if (ret < 0) { DRM_ERROR("Cannot create ACCEL class: %d\n", ret); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index c760dc0f814a..0feaaf74abc3 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -55,7 +55,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl"); MODULE_DESCRIPTION("DRM shared core routines"); MODULE_LICENSE("GPL and additional rights"); -static DEFINE_XARRAY_ALLOC(drm_minors_xa); +DEFINE_XARRAY_ALLOC(drm_minors_xa); /* * If the drm core fails to init for whatever reason, @@ -83,6 +83,18 @@ DEFINE_STATIC_SRCU(drm_unplug_srcu); * registered and unregistered dynamically according to device-state. */ +static struct xarray *drm_minor_get_xa(enum drm_minor_type type) +{ + if (type == DRM_MINOR_PRIMARY || type == DRM_MINOR_RENDER) + return &drm_minors_xa; +#if IS_ENABLED(CONFIG_DRM_ACCEL) + else if (type == DRM_MINOR_ACCEL) + return &accel_minors_xa; +#endif + else + return ERR_PTR(-EOPNOTSUPP); +} + static struct drm_minor **drm_minor_get_slot(struct drm_device *dev, enum drm_minor_type type) { @@ -106,18 +118,18 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data) put_device(minor->kdev); - if (minor->type == DRM_MINOR_ACCEL) - accel_minor_remove(minor->index); - else - xa_erase(&drm_minors_xa, minor->index); + xa_erase(drm_minor_get_xa(minor->type), minor->index); } -#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * _t + 63); }) +#define DRM_MINOR_LIMIT(t) ({ \ + typeof(t) _t = (t); \ + _t == DRM_MINOR_ACCEL ? XA_LIMIT(0, ACCEL_MAX_MINORS) : XA_LIMIT(64 * _t, 64 * _t + 63); \ +}) static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type) { struct drm_minor *minor; - int index, r; + int r; minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL); if (!minor) @@ -126,18 +138,11 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type) minor->type = type; minor->dev = dev; - if (type == DRM_MINOR_ACCEL) { - r = accel_minor_alloc(); - index = r; - } else { - r = xa_alloc(&drm_minors_xa, &index, NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL); - } - + r = xa_alloc(drm_minor_get_xa(type), &minor->index, + NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL); if (r < 0) return r; - minor->index = index; - r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor); if (r) return r; @@ -176,16 +181,12 @@ static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type) goto err_debugfs; /* replace NULL with @minor so lookups will succeed from now on */ - if (minor->type == DRM_MINOR_ACCEL) { - accel_minor_replace(minor, minor->index); - } else { - entry = xa_store(&drm_minors_xa, minor->index, minor, GFP_KERNEL); - if (xa_is_err(entry)) { - ret = xa_err(entry); - goto err_debugfs; - } - WARN_ON(entry); + entry = xa_store(drm_minor_get_xa(type), minor->index, minor, GFP_KERNEL); + if (xa_is_err(entry)) { + ret = xa_err(entry); + goto err_debugfs; } + WARN_ON(entry); DRM_DEBUG("new minor registered %d\n", minor->index); return 0; @@ -204,10 +205,7 @@ static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type typ return; /* replace @minor with NULL so lookups will fail from now on */ - if (minor->type == DRM_MINOR_ACCEL) - accel_minor_replace(NULL, minor->index); - else - xa_store(&drm_minors_xa, minor->index, NULL, GFP_KERNEL); + xa_store(drm_minor_get_xa(type), minor->index, NULL, GFP_KERNEL); device_del(minor->kdev); dev_set_drvdata(minor->kdev, NULL); /* safety belt */ @@ -223,15 +221,15 @@ static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type typ * minor->dev pointer will stay valid! However, the device may get unplugged and * unregistered while you hold the minor. */ -struct drm_minor *drm_minor_acquire(unsigned int minor_id) +struct drm_minor *drm_minor_acquire(struct xarray *minor_xa, unsigned int minor_id) { struct drm_minor *minor; - xa_lock(&drm_minors_xa); - minor = xa_load(&drm_minors_xa, minor_id); + xa_lock(minor_xa); + minor = xa_load(minor_xa, minor_id); if (minor) drm_dev_get(minor->dev); - xa_unlock(&drm_minors_xa); + xa_unlock(minor_xa); if (!minor) { return ERR_PTR(-ENODEV); @@ -1024,7 +1022,7 @@ static int drm_stub_open(struct inode *inode, struct file *filp) DRM_DEBUG("\n"); - minor = drm_minor_acquire(iminor(inode)); + minor = drm_minor_acquire(&drm_minors_xa, iminor(inode)); if (IS_ERR(minor)) return PTR_ERR(minor); diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 351591f3a27a..01fde94fe2a9 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -355,7 +355,7 @@ int drm_open(struct inode *inode, struct file *filp) struct drm_minor *minor; int retcode; - minor = drm_minor_acquire(iminor(inode)); + minor = drm_minor_acquire(&drm_minors_xa, iminor(inode)); if (IS_ERR(minor)) return PTR_ERR(minor); diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 23c99803af44..1705bfc90b1e 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -80,10 +80,6 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, uint32_t handle); -/* drm_drv.c */ -struct drm_minor *drm_minor_acquire(unsigned int minor_id); -void drm_minor_release(struct drm_minor *minor); - /* drm_managed.c */ void drm_managed_release(struct drm_device *dev); void drmm_add_final_kfree(struct drm_device *dev, void *container); diff --git a/include/drm/drm_accel.h b/include/drm/drm_accel.h index f4d3784b1dce..8867ce0be94c 100644 --- a/include/drm/drm_accel.h +++ b/include/drm/drm_accel.h @@ -51,11 +51,10 @@ #if IS_ENABLED(CONFIG_DRM_ACCEL) +extern struct xarray accel_minors_xa; + void accel_core_exit(void); int accel_core_init(void); -void accel_minor_remove(int index); -int accel_minor_alloc(void); -void accel_minor_replace(struct drm_minor *minor, int index); void accel_set_device_instance_params(struct device *kdev, int index); int accel_open(struct inode *inode, struct file *filp); void accel_debugfs_init(struct drm_device *dev); @@ -73,19 +72,6 @@ static inline int __init accel_core_init(void) return 0; } -static inline void accel_minor_remove(int index) -{ -} - -static inline int accel_minor_alloc(void) -{ - return -EOPNOTSUPP; -} - -static inline void accel_minor_replace(struct drm_minor *minor, int index) -{ -} - static inline void accel_set_device_instance_params(struct device *kdev, int index) { } diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index ab230d3af138..8c0030c77308 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -45,6 +45,8 @@ struct drm_printer; struct device; struct file; +extern struct xarray drm_minors_xa; + /* * FIXME: Not sure we want to have drm_minor here in the end, but to avoid * header include loops we need it here for now. @@ -434,6 +436,9 @@ static inline bool drm_is_accel_client(const struct drm_file *file_priv) void drm_file_update_pid(struct drm_file *); +struct drm_minor *drm_minor_acquire(struct xarray *minors_xa, unsigned int minor_id); +void drm_minor_release(struct drm_minor *minor); + int drm_open(struct inode *inode, struct file *filp); int drm_open_helper(struct file *filp, struct drm_minor *minor); ssize_t drm_read(struct file *filp, char __user *buffer, -- cgit v1.2.3 From 071d583e01c88272f6ff216d4f867f8f35e94d7d Mon Sep 17 00:00:00 2001 From: Michał Winiarski Date: Fri, 23 Aug 2024 18:30:48 +0200 Subject: drm: Expand max DRM device number to full MINORBITS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having a limit of 64 DRM devices is not good enough for modern world where we have multi-GPU servers, SR-IOV virtual functions and virtual devices used for testing. Let's utilize full minor range for DRM devices. To avoid regressing the existing userspace, we're still maintaining the numbering scheme where 0-63 is used for primary, 64-127 is reserved (formerly for control) and 128-191 is used for render. For minors >= 192, we're allocating minors dynamically on a first-come, first-served basis. Signed-off-by: Michał Winiarski Link: https://patchwork.freedesktop.org/patch/msgid/20240823163048.2676257-4-michal.winiarski@intel.com Acked-by: James Zhu Acked-by: Christian König Signed-off-by: Christian König --- drivers/gpu/drm/drm_drv.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 0feaaf74abc3..ac30b0ec9d93 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -121,10 +121,19 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data) xa_erase(drm_minor_get_xa(minor->type), minor->index); } +/* + * DRM used to support 64 devices, for backwards compatibility we need to maintain the + * minor allocation scheme where minors 0-63 are primary nodes, 64-127 are control nodes, + * and 128-191 are render nodes. + * After reaching the limit, we're allocating minors dynamically - first-come, first-serve. + * Accel nodes are using a distinct major, so the minors are allocated in continuous 0-MAX + * range. + */ #define DRM_MINOR_LIMIT(t) ({ \ typeof(t) _t = (t); \ _t == DRM_MINOR_ACCEL ? XA_LIMIT(0, ACCEL_MAX_MINORS) : XA_LIMIT(64 * _t, 64 * _t + 63); \ }) +#define DRM_EXTENDED_MINOR_LIMIT XA_LIMIT(192, (1 << MINORBITS) - 1) static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type) { @@ -140,6 +149,9 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type) r = xa_alloc(drm_minor_get_xa(type), &minor->index, NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL); + if (r == -EBUSY && (type == DRM_MINOR_PRIMARY || type == DRM_MINOR_RENDER)) + r = xa_alloc(&drm_minors_xa, &minor->index, + NULL, DRM_EXTENDED_MINOR_LIMIT, GFP_KERNEL); if (r < 0) return r; -- cgit v1.2.3 From 01cc7b2e8a59fcae0c4493720561e5b33a195fe7 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 15 Jul 2024 14:15:38 +0200 Subject: Revert "drm/panel-edp: Add SDC ATNA45AF01" This reverts commit 8ebb1fc2e69ab8b89a425e402c7bd85e053b7b01. The panel should be handled through the samsung-atna33xc20 driver for correct power up timings. Otherwise the backlight does not work correctly. We have existing users of this panel through the generic "edp-panel" compatible (e.g. the Qualcomm X1E80100 CRD), but the screen works only partially in that configuration: It works after boot but once the screen gets disabled it does not turn on again until after reboot. It behaves the same way with the default "conservative" timings, so we might as well drop the configuration from the panel-edp driver. That way, users with old DTBs will get a warning and can move to the new driver. Reviewed-by: Douglas Anderson Signed-off-by: Stephan Gerhold Reviewed-by: Johan Hovold Tested-by: Johan Hovold Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20240715-x1e80100-crd-backlight-v2-2-31b7f2f658a3@linaro.org --- drivers/gpu/drm/panel/panel-edp.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index f6102ceaf0a7..767e47a2b0c1 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1978,8 +1978,6 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('L', 'G', 'D', 0x05af, &delay_200_500_e200_d200, "Unknown"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x05f1, &delay_200_500_e200_d200, "Unknown"), - EDP_PANEL_ENTRY('S', 'D', 'C', 0x416d, &delay_100_500_e200, "ATNA45AF01"), - EDP_PANEL_ENTRY('S', 'H', 'P', 0x1511, &delay_200_500_e50, "LQ140M1JW48"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x1523, &delay_80_500_e50, "LQ140M1JW46"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x153a, &delay_200_500_e50, "LQ140T1JH01"), -- cgit v1.2.3 From aec8485f226c36eb4eea1d489772cd6f2c40144d Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 28 Aug 2024 17:04:20 +0200 Subject: drm/panel: add BOE tv101wum-ll2 panel driver Add support for the 1200x1920 BOE TV101WUM-LL2 DSI Display Panel found in the Lenovo Smart Tab M10 tablet. The controller is unknown. Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20240828-topic-sdm450-upstream-tbx605f-panel-v3-2-b792f93e1d6b@linaro.org Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20240828-topic-sdm450-upstream-tbx605f-panel-v3-2-b792f93e1d6b@linaro.org --- drivers/gpu/drm/panel/Kconfig | 9 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c | 241 +++++++++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 5d83ddc06ece..d3a9a9fafe4e 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -87,6 +87,15 @@ config DRM_PANEL_BOE_TV101WUM_NL6 Say Y here if you want to support for BOE TV101WUM and AUO KD101N80 45NA WUXGA PANEL DSI Video Mode panel +config DRM_PANEL_BOE_TV101WUM_LL2 + tristate "BOE TV101WUM LL2 1200x1920 panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to support for BOE TV101WUM-LL2 + WUXGA PANEL DSI Video Mode panel + config DRM_PANEL_EBBG_FT8719 tristate "EBBG FT8719 panel driver" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 5581387707c6..987a08702410 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.o obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o +obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c new file mode 100644 index 000000000000..50e4a5341bc6 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: +// Copyright (c) 2013, The Linux Foundation. All rights reserved. +// Copyright (c) 2024, Neil Armstrong + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct boe_tv101wum_ll2 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; +}; + +static const struct regulator_bulk_data boe_tv101wum_ll2_supplies[] = { + { .supply = "vsp" }, + { .supply = "vsn" }, +}; + +static inline struct boe_tv101wum_ll2 *to_boe_tv101wum_ll2(struct drm_panel *panel) +{ + return container_of(panel, struct boe_tv101wum_ll2, panel); +} + +static void boe_tv101wum_ll2_reset(struct boe_tv101wum_ll2 *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(5000, 6000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(5000, 6000); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + + msleep(120); +} + +static int boe_tv101wum_ll2_on(struct boe_tv101wum_ll2 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x0e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0xff, 0x81, 0x68, 0x6c, 0x22, + 0x6d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x23); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x94, 0x2c, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x19); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa2, 0x38); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x0c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x80, 0xfd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x50, 0x00); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 20); + + return dsi_ctx.accum_err; +} + +static void boe_tv101wum_ll2_off(struct boe_tv101wum_ll2 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 70); + + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 20); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x5a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x5a); + + mipi_dsi_msleep(&dsi_ctx, 150); +} + +static int boe_tv101wum_ll2_prepare(struct drm_panel *panel) +{ + struct boe_tv101wum_ll2 *ctx = to_boe_tv101wum_ll2(panel); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(boe_tv101wum_ll2_supplies), + ctx->supplies); + if (ret < 0) + return ret; + + boe_tv101wum_ll2_reset(ctx); + + ret = boe_tv101wum_ll2_on(ctx); + if (ret < 0) { + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(boe_tv101wum_ll2_supplies), + ctx->supplies); + return ret; + } + + return 0; +} + +static int boe_tv101wum_ll2_unprepare(struct drm_panel *panel) +{ + struct boe_tv101wum_ll2 *ctx = to_boe_tv101wum_ll2(panel); + + /* Ignore errors on failure, in any case set gpio and disable regulators */ + boe_tv101wum_ll2_off(ctx); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(boe_tv101wum_ll2_supplies), + ctx->supplies); + + return 0; +} + +static const struct drm_display_mode boe_tv101wum_ll2_mode = { + .clock = (1200 + 27 + 8 + 12) * (1920 + 155 + 8 + 32) * 60 / 1000, + .hdisplay = 1200, + .hsync_start = 1200 + 27, + .hsync_end = 1200 + 27 + 8, + .htotal = 1200 + 27 + 8 + 12, + .vdisplay = 1920, + .vsync_start = 1920 + 155, + .vsync_end = 1920 + 155 + 8, + .vtotal = 1920 + 155 + 8 + 32, + .width_mm = 136, + .height_mm = 217, + .type = DRM_MODE_TYPE_DRIVER, +}; + +static int boe_tv101wum_ll2_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + /* We do not set display_info.bpc since unset value is bpc=8 by default */ + return drm_connector_helper_get_modes_fixed(connector, &boe_tv101wum_ll2_mode); +} + +static const struct drm_panel_funcs boe_tv101wum_ll2_panel_funcs = { + .prepare = boe_tv101wum_ll2_prepare, + .unprepare = boe_tv101wum_ll2_unprepare, + .get_modes = boe_tv101wum_ll2_get_modes, +}; + +static int boe_tv101wum_ll2_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct boe_tv101wum_ll2 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ret = devm_regulator_bulk_get_const(&dsi->dev, + ARRAY_SIZE(boe_tv101wum_ll2_supplies), + boe_tv101wum_ll2_supplies, + &ctx->supplies); + if (ret < 0) + return ret; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_VIDEO_HSE; + + drm_panel_init(&ctx->panel, dev, &boe_tv101wum_ll2_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + ctx->panel.prepare_prev_first = true; + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + drm_panel_remove(&ctx->panel); + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); + } + + return 0; +} + +static void boe_tv101wum_ll2_remove(struct mipi_dsi_device *dsi) +{ + struct boe_tv101wum_ll2 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); +} + +static const struct of_device_id boe_tv101wum_ll2_of_match[] = { + { .compatible = "boe,tv101wum-ll2" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, boe_tv101wum_ll2_of_match); + +static struct mipi_dsi_driver boe_tv101wum_ll2_driver = { + .probe = boe_tv101wum_ll2_probe, + .remove = boe_tv101wum_ll2_remove, + .driver = { + .name = "panel-boe-tv101wum_ll2", + .of_match_table = boe_tv101wum_ll2_of_match, + }, +}; +module_mipi_dsi_driver(boe_tv101wum_ll2_driver); + +MODULE_DESCRIPTION("DRM driver for BOE TV101WUM-LL2 Panel"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From bad928d2cae5d4d5ec1474ae7428a797fb34c111 Mon Sep 17 00:00:00 2001 From: Vignesh Raman Date: Tue, 20 Aug 2024 12:38:16 +0530 Subject: drm/ci: increase timeout for all jobs Set the timeout of all drm-ci jobs to 1h30m since some jobs takes more than 1 hour to complete. Signed-off-by: Vignesh Raman Acked-by: Helen Koike Acked-by: Rob Clark Signed-off-by: Helen Koike Link: https://patchwork.freedesktop.org/patch/msgid/20240820070818.1124403-1-vignesh.raman@collabora.com --- drivers/gpu/drm/ci/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml index b6f428cdaf94..09d8447840e9 100644 --- a/drivers/gpu/drm/ci/test.yml +++ b/drivers/gpu/drm/ci/test.yml @@ -10,6 +10,7 @@ .lava-test: extends: - .test-rules + timeout: "1h30m" script: # Note: Build dir (and thus install) may be dirty due to GIT_STRATEGY - rm -rf install @@ -71,6 +72,7 @@ - .baremetal-test-arm64 - .use-debian/baremetal_arm64_test - .test-rules + timeout: "1h30m" variables: FDO_CI_CONCURRENT: 10 HWCI_TEST_SCRIPT: "/install/igt_runner.sh" @@ -215,7 +217,6 @@ panfrost:rk3399: extends: - .lava-igt:x86_64 stage: i915 - timeout: "1h30m" variables: DRIVER_NAME: i915 DTB: "" @@ -414,6 +415,7 @@ panfrost:g12b: virtio_gpu:none: stage: software-driver + timeout: "1h30m" variables: CROSVM_GALLIUM_DRIVER: llvmpipe DRIVER_NAME: virtio_gpu @@ -436,6 +438,7 @@ virtio_gpu:none: vkms:none: stage: software-driver + timeout: "1h30m" variables: DRIVER_NAME: vkms GPU_VERSION: none -- cgit v1.2.3 From f790b5c09665cab0d51dfcc84832d79d2b1e6c0e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 23 Jul 2024 16:46:07 +0200 Subject: drm/tegra: gr3d: Convert into dev_pm_domain_attach|detach_list() Rather than hooking up the PM domains through devm_pm_opp_attach_genpd() and manage the device-link, let's avoid the boilerplate-code by converting into dev_pm_domain_attach|detach_list. Signed-off-by: Ulf Hansson Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20240723144610.564273-2-ulf.hansson@linaro.org --- drivers/gpu/drm/tegra/gr3d.c | 46 +++++++++++++------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 00c8564520e7..4de1ea0fc7c0 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -46,6 +46,7 @@ struct gr3d { unsigned int nclocks; struct reset_control_bulk_data resets[RST_GR3D_MAX]; unsigned int nresets; + struct dev_pm_domain_list *pd_list; DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS); }; @@ -369,18 +370,12 @@ static int gr3d_power_up_legacy_domain(struct device *dev, const char *name, return 0; } -static void gr3d_del_link(void *link) -{ - device_link_del(link); -} - static int gr3d_init_power(struct device *dev, struct gr3d *gr3d) { - static const char * const opp_genpd_names[] = { "3d0", "3d1", NULL }; - const u32 link_flags = DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME; - struct device **opp_virt_devs, *pd_dev; - struct device_link *link; - unsigned int i; + struct dev_pm_domain_attach_data pd_data = { + .pd_names = (const char *[]) { "3d0", "3d1" }, + .num_pd_names = 2, + }; int err; err = of_count_phandle_with_args(dev->of_node, "power-domains", @@ -414,29 +409,10 @@ static int gr3d_init_power(struct device *dev, struct gr3d *gr3d) if (dev->pm_domain) return 0; - err = devm_pm_opp_attach_genpd(dev, opp_genpd_names, &opp_virt_devs); - if (err) + err = dev_pm_domain_attach_list(dev, &pd_data, &gr3d->pd_list); + if (err < 0) return err; - for (i = 0; opp_genpd_names[i]; i++) { - pd_dev = opp_virt_devs[i]; - if (!pd_dev) { - dev_err(dev, "failed to get %s power domain\n", - opp_genpd_names[i]); - return -EINVAL; - } - - link = device_link_add(dev, pd_dev, link_flags); - if (!link) { - dev_err(dev, "failed to link to %s\n", dev_name(pd_dev)); - return -EINVAL; - } - - err = devm_add_action_or_reset(dev, gr3d_del_link, link); - if (err) - return err; - } - return 0; } @@ -527,13 +503,13 @@ static int gr3d_probe(struct platform_device *pdev) err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); if (err) - return err; + goto err; err = host1x_client_register(&gr3d->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); - return err; + goto err; } /* initialize address register map */ @@ -541,6 +517,9 @@ static int gr3d_probe(struct platform_device *pdev) set_bit(gr3d_addr_regs[i], gr3d->addr_regs); return 0; +err: + dev_pm_domain_detach_list(gr3d->pd_list); + return err; } static void gr3d_remove(struct platform_device *pdev) @@ -549,6 +528,7 @@ static void gr3d_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); host1x_client_unregister(&gr3d->client.base); + dev_pm_domain_detach_list(gr3d->pd_list); } static int __maybe_unused gr3d_runtime_suspend(struct device *dev) -- cgit v1.2.3 From b290af0500f09577ad40b9f716d551fd65ceff25 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 10 Jul 2024 23:00:35 +0200 Subject: drm/tegra: hub: Use fn parameter directly to fix Coccinelle warning The function parameter out can be used directly instead of assigning it to a temporary u64 variable first. Remove the local variable tmp2 and use the parameter out directly as the divisor in do_div() to remove the following Coccinelle/coccicheck warning reported by do_div.cocci: WARNING: do_div() does a 64-by-32 division, please consider using div64_u64 instead Signed-off-by: Thorsten Blum Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20240710210034.796032-2-thorsten.blum@toblux.com --- drivers/gpu/drm/tegra/hub.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index f21e57e8599e..e0c2019a591b 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -521,12 +521,11 @@ static void tegra_shared_plane_atomic_disable(struct drm_plane *plane, static inline u32 compute_phase_incr(fixed20_12 in, unsigned int out) { - u64 tmp, tmp1, tmp2; + u64 tmp, tmp1; tmp = (u64)dfixed_trunc(in); - tmp2 = (u64)out; - tmp1 = (tmp << NFB) + (tmp2 >> 1); - do_div(tmp1, tmp2); + tmp1 = (tmp << NFB) + ((u64)out >> 1); + do_div(tmp1, out); return lower_32_bits(tmp1); } -- cgit v1.2.3 From 33929707b808ba7839c40c15d3e68cbc51070b31 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 12 Aug 2024 15:23:10 +0300 Subject: drm/mst: switch to guid_t type for GUID The kernel has a guid_t type for GUIDs. Switch to using it, but avoid any functional changes here. Reviewed-by: Daniel Vetter Acked-by: Harry Wentland Acked-by: Alex Deucher Acked-by: Hamza Mahfooz Link: https://patchwork.freedesktop.org/patch/msgid/20240812122312.1567046-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 +- drivers/gpu/drm/display/drm_dp_mst_topology.c | 67 +++++++++++++---------- include/drm/display/drm_dp_mst_helper.h | 12 ++-- 3 files changed, 45 insertions(+), 36 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 91490d5d106b..f3f74c6d0e1a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2610,7 +2610,7 @@ static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr) } } - memcpy(mgr->mst_primary->guid, guid, 16); + import_guid(&mgr->mst_primary->guid, guid); out_fail: mutex_unlock(&mgr->lock); diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index f54d0cbfb94d..382e05cb18d7 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -89,7 +89,7 @@ static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *port); static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, - u8 *guid); + guid_t *guid); static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port); static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port); @@ -801,7 +801,7 @@ static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_ int idx = 1; int i; - memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16); + import_guid(&repmsg->u.link_addr.guid, &raw->msg[idx]); idx += 16; repmsg->u.link_addr.nports = raw->msg[idx] & 0xf; idx++; @@ -829,7 +829,7 @@ static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_ idx++; if (idx > raw->curlen) goto fail_len; - memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16); + import_guid(&repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx]); idx += 16; if (idx > raw->curlen) goto fail_len; @@ -1029,7 +1029,7 @@ static bool drm_dp_sideband_parse_reply(const struct drm_dp_mst_topology_mgr *mg msg->req_type = (raw->msg[0] & 0x7f); if (msg->reply_type == DP_SIDEBAND_REPLY_NAK) { - memcpy(msg->u.nak.guid, &raw->msg[1], 16); + import_guid(&msg->u.nak.guid, &raw->msg[1]); msg->u.nak.reason = raw->msg[17]; msg->u.nak.nak_data = raw->msg[18]; return false; @@ -1078,7 +1078,7 @@ drm_dp_sideband_parse_connection_status_notify(const struct drm_dp_mst_topology_ if (idx > raw->curlen) goto fail_len; - memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16); + import_guid(&msg->u.conn_stat.guid, &raw->msg[idx]); idx += 16; if (idx > raw->curlen) goto fail_len; @@ -1107,7 +1107,7 @@ static bool drm_dp_sideband_parse_resource_status_notify(const struct drm_dp_mst if (idx > raw->curlen) goto fail_len; - memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16); + import_guid(&msg->u.resource_stat.guid, &raw->msg[idx]); idx += 16; if (idx > raw->curlen) goto fail_len; @@ -2174,20 +2174,24 @@ ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux, offset, size, buffer); } -static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid) +static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, guid_t *guid) { int ret = 0; - memcpy(mstb->guid, guid, 16); + guid_copy(&mstb->guid, guid); + + if (!drm_dp_validate_guid(mstb->mgr, &mstb->guid)) { + u8 buf[UUID_SIZE]; + + export_guid(buf, &mstb->guid); - if (!drm_dp_validate_guid(mstb->mgr, mstb->guid)) { if (mstb->port_parent) { ret = drm_dp_send_dpcd_write(mstb->mgr, mstb->port_parent, - DP_GUID, 16, mstb->guid); + DP_GUID, sizeof(buf), buf); } else { ret = drm_dp_dpcd_write(mstb->mgr->aux, - DP_GUID, mstb->guid, 16); + DP_GUID, buf, sizeof(buf)); } } @@ -2570,9 +2574,9 @@ out: return mstb; } -static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper( - struct drm_dp_mst_branch *mstb, - const uint8_t *guid) +static struct drm_dp_mst_branch * +get_mst_branch_device_by_guid_helper(struct drm_dp_mst_branch *mstb, + const guid_t *guid) { struct drm_dp_mst_branch *found_mstb; struct drm_dp_mst_port *port; @@ -2580,10 +2584,9 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper( if (!mstb) return NULL; - if (memcmp(mstb->guid, guid, 16) == 0) + if (guid_equal(&mstb->guid, guid)) return mstb; - list_for_each_entry(port, &mstb->ports, next) { found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid); @@ -2596,7 +2599,7 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper( static struct drm_dp_mst_branch * drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr, - const uint8_t *guid) + const guid_t *guid) { struct drm_dp_mst_branch *mstb; int ret; @@ -2693,17 +2696,20 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work) } static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, - u8 *guid) + guid_t *guid) { u64 salt; + u8 buf[UUID_SIZE]; - if (memchr_inv(guid, 0, 16)) + if (!guid_is_null(guid)) return true; salt = get_jiffies_64(); - memcpy(&guid[0], &salt, sizeof(u64)); - memcpy(&guid[8], &salt, sizeof(u64)); + memcpy(&buf[0], &salt, sizeof(u64)); + memcpy(&buf[8], &salt, sizeof(u64)); + + import_guid(guid, buf); return false; } @@ -2943,7 +2949,7 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, drm_dbg_kms(mgr->dev, "link address reply: %d\n", reply->nports); drm_dp_dump_link_address(mgr, reply); - ret = drm_dp_check_mstb_guid(mstb, reply->guid); + ret = drm_dp_check_mstb_guid(mstb, &reply->guid); if (ret) { char buf[64]; @@ -3770,8 +3776,9 @@ EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend); int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr, bool sync) { + u8 buf[UUID_SIZE]; + guid_t guid; int ret; - u8 guid[16]; mutex_lock(&mgr->lock); if (!mgr->mst_primary) @@ -3792,13 +3799,15 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr, } /* Some hubs forget their guids after they resume */ - ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16); - if (ret != 16) { + ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, buf, sizeof(buf)); + if (ret != sizeof(buf)) { drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n"); goto out_fail; } - ret = drm_dp_check_mstb_guid(mgr->mst_primary, guid); + import_guid(&guid, buf); + + ret = drm_dp_check_mstb_guid(mgr->mst_primary, &guid); if (ret) { drm_dbg_kms(mgr->dev, "check mstb failed - undocked during suspend?\n"); goto out_fail; @@ -3976,12 +3985,12 @@ drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr, bool hotplug = false, dowork = false; if (hdr->broadcast) { - const u8 *guid = NULL; + const guid_t *guid = NULL; if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY) - guid = msg->u.conn_stat.guid; + guid = &msg->u.conn_stat.guid; else if (msg->req_type == DP_RESOURCE_STATUS_NOTIFY) - guid = msg->u.resource_stat.guid; + guid = &msg->u.resource_stat.guid; if (guid) mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid); diff --git a/include/drm/display/drm_dp_mst_helper.h b/include/drm/display/drm_dp_mst_helper.h index cfe096389d94..dd466631f174 100644 --- a/include/drm/display/drm_dp_mst_helper.h +++ b/include/drm/display/drm_dp_mst_helper.h @@ -244,18 +244,18 @@ struct drm_dp_mst_branch { bool link_address_sent; /* global unique identifier to identify branch devices */ - u8 guid[16]; + guid_t guid; }; struct drm_dp_nak_reply { - u8 guid[16]; + guid_t guid; u8 reason; u8 nak_data; }; struct drm_dp_link_address_ack_reply { - u8 guid[16]; + guid_t guid; u8 nports; struct drm_dp_link_addr_reply_port { bool input_port; @@ -265,7 +265,7 @@ struct drm_dp_link_address_ack_reply { bool ddps; bool legacy_device_plug_status; u8 dpcd_revision; - u8 peer_guid[16]; + guid_t peer_guid; u8 num_sdp_streams; u8 num_sdp_stream_sinks; } ports[16]; @@ -348,7 +348,7 @@ struct drm_dp_allocate_payload_ack_reply { }; struct drm_dp_connection_status_notify { - u8 guid[16]; + guid_t guid; u8 port_number; bool legacy_device_plug_status; bool displayport_device_plug_status; @@ -425,7 +425,7 @@ struct drm_dp_query_payload { struct drm_dp_resource_status_notify { u8 port_number; - u8 guid[16]; + guid_t guid; u16 available_pbn; }; -- cgit v1.2.3 From 4548f10bf4c67b569b7c9fbc6746340a558faab9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 12 Aug 2024 15:23:11 +0300 Subject: drm/mst: switch to guid_gen() to generate valid GUIDs Instead of just smashing jiffies into a GUID, use guid_gen() to generate RFC 4122 compliant GUIDs. Reviewed-by: Daniel Vetter Acked-by: Harry Wentland Acked-by: Alex Deucher Acked-by: Hamza Mahfooz Link: https://patchwork.freedesktop.org/patch/msgid/20240812122312.1567046-2-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/display/drm_dp_mst_topology.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 382e05cb18d7..e8695e390686 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -2698,18 +2698,10 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work) static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, guid_t *guid) { - u64 salt; - u8 buf[UUID_SIZE]; - if (!guid_is_null(guid)) return true; - salt = get_jiffies_64(); - - memcpy(&buf[0], &salt, sizeof(u64)); - memcpy(&buf[8], &salt, sizeof(u64)); - - import_guid(guid, buf); + guid_gen(guid); return false; } -- cgit v1.2.3 From b71ccff68ef1a5bd1c02d0fca01ddb3d9088329a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 12 Aug 2024 15:23:12 +0300 Subject: drm/amd/display: switch to guid_gen() to generate valid GUIDs Instead of just smashing jiffies into a GUID, use guid_gen() to generate RFC 4122 compliant GUIDs. Reviewed-by: Daniel Vetter Acked-by: Harry Wentland Acked-by: Alex Deucher Acked-by: Hamza Mahfooz Link: https://patchwork.freedesktop.org/patch/msgid/20240812122312.1567046-3-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index f3f74c6d0e1a..0ed6ce40bec8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2568,9 +2568,9 @@ static int dm_late_init(void *handle) static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr) { + u8 buf[UUID_SIZE]; + guid_t guid; int ret; - u8 guid[16]; - u64 tmp64; mutex_lock(&mgr->lock); if (!mgr->mst_primary) @@ -2591,26 +2591,27 @@ static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr) } /* Some hubs forget their guids after they resume */ - ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16); - if (ret != 16) { + ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, buf, sizeof(buf)); + if (ret != sizeof(buf)) { drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n"); goto out_fail; } - if (memchr_inv(guid, 0, 16) == NULL) { - tmp64 = get_jiffies_64(); - memcpy(&guid[0], &tmp64, sizeof(u64)); - memcpy(&guid[8], &tmp64, sizeof(u64)); + import_guid(&guid, buf); - ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, guid, 16); + if (guid_is_null(&guid)) { + guid_gen(&guid); + export_guid(buf, &guid); - if (ret != 16) { + ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, buf, sizeof(buf)); + + if (ret != sizeof(buf)) { drm_dbg_kms(mgr->dev, "check mstb guid failed - undocked during suspend?\n"); goto out_fail; } } - import_guid(&mgr->mst_primary->guid, guid); + guid_copy(&mgr->mst_primary->guid, &guid); out_fail: mutex_unlock(&mgr->lock); -- cgit v1.2.3 From 175d648be98bae22eb22444377dd9a0c497c9d46 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 28 Aug 2024 18:03:39 +0200 Subject: drm/panel: visionox-vtdr6130: switch to mipi_dsi wrapped functions Make usage of the new _multi() mipi_dsi functions instead of the deprecated macros, improving error handling and printing. bloat-o-meter gives a 12% gain on arm64: Function old new delta visionox_vtdr6130_unprepare 208 204 -4 visionox_vtdr6130_prepare 1192 896 -296 Total: Before=2348, After=2048, chg -12.78% Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20240828-topic-sm8x50-upstream-vtdr6130-multi-v1-1-0cae20d4c55d@linaro.org Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20240828-topic-sm8x50-upstream-vtdr6130-multi-v1-1-0cae20d4c55d@linaro.org --- drivers/gpu/drm/panel/panel-visionox-vtdr6130.c | 186 +++++++++++------------- 1 file changed, 82 insertions(+), 104 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index 540099253e1b..ebe92871dbb6 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -40,120 +40,103 @@ static void visionox_vtdr6130_reset(struct visionox_vtdr6130 *ctx) static int visionox_vtdr6130_on(struct visionox_vtdr6130 *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_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); - if (ret) - return ret; - - mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); - mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0x00, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x59, 0x09); - mipi_dsi_dcs_write_seq(dsi, 0x6c, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0x6d, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x6f, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0x70, - 0x12, 0x00, 0x00, 0xab, 0x30, 0x80, 0x09, 0x60, 0x04, - 0x38, 0x00, 0x28, 0x02, 0x1c, 0x02, 0x1c, 0x02, 0x00, - 0x02, 0x0e, 0x00, 0x20, 0x03, 0xdd, 0x00, 0x07, 0x00, - 0x0c, 0x02, 0x77, 0x02, 0x8b, 0x18, 0x00, 0x10, 0xf0, - 0x07, 0x10, 0x20, 0x00, 0x06, 0x0f, 0x0f, 0x33, 0x0e, - 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, - 0x79, 0x7b, 0x7d, 0x7e, 0x02, 0x02, 0x22, 0x00, 0x2a, - 0x40, 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, - 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0xb6, 0x4b, 0xb6, 0x4b, - 0xf4, 0x4b, 0xf4, 0x6c, 0x34, 0x84, 0x74, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xaa, 0x10); - mipi_dsi_dcs_write_seq(dsi, 0xb1, - 0x01, 0x38, 0x00, 0x14, 0x00, 0x1c, 0x00, 0x01, 0x66, - 0x00, 0x14, 0x00, 0x14, 0x00, 0x01, 0x66, 0x00, 0x14, - 0x05, 0xcc, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xaa, 0x13); - mipi_dsi_dcs_write_seq(dsi, 0xce, - 0x09, 0x11, 0x09, 0x11, 0x08, 0xc1, 0x07, 0xfa, 0x05, - 0xa4, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x24, 0x00, 0x0c, - 0x00, 0x0c, 0x04, 0x00, 0x35); - mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xaa, 0x14); - mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x03, 0x33); - mipi_dsi_dcs_write_seq(dsi, 0xb4, - 0x00, 0x33, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xb5, - 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x06, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0x00, 0x08, 0x09, 0x09, 0x09); - mipi_dsi_dcs_write_seq(dsi, 0xbc, - 0x10, 0x00, 0x00, 0x06, 0x11, 0x09, 0x3b, 0x09, 0x47, - 0x09, 0x47, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xbe, - 0x10, 0x10, 0x00, 0x08, 0x22, 0x09, 0x19, 0x09, 0x25, - 0x09, 0x25, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x80); - mipi_dsi_dcs_write_seq(dsi, 0x65, 0x14); - mipi_dsi_dcs_write_seq(dsi, 0xfa, 0x08, 0x08, 0x08); - mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x81); - mipi_dsi_dcs_write_seq(dsi, 0x65, 0x05); - mipi_dsi_dcs_write_seq(dsi, 0xf3, 0x0f); - mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xaa, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x82); - mipi_dsi_dcs_write_seq(dsi, 0xf9, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xff, 0x51, 0x83); - mipi_dsi_dcs_write_seq(dsi, 0x65, 0x04); - mipi_dsi_dcs_write_seq(dsi, 0xf8, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x65, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0xf4, 0x9a); - mipi_dsi_dcs_write_seq(dsi, 0xff, 0x5a, 0x00); - - 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(120); - - ret = mipi_dsi_dcs_set_display_on(dsi); - if (ret < 0) { - dev_err(dev, "Failed to set display on: %d\n", ret); - return ret; - } - msleep(20); - - return 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_WRITE_CONTROL_DISPLAY, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, + MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0x00, + 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x12, 0x00, 0x00, 0xab, + 0x30, 0x80, 0x09, 0x60, 0x04, 0x38, 0x00, + 0x28, 0x02, 0x1c, 0x02, 0x1c, 0x02, 0x00, + 0x02, 0x0e, 0x00, 0x20, 0x03, 0xdd, 0x00, + 0x07, 0x00, 0x0c, 0x02, 0x77, 0x02, 0x8b, + 0x18, 0x00, 0x10, 0xf0, 0x07, 0x10, 0x20, + 0x00, 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, + 0x2a, 0x38, 0x46, 0x54, 0x62, 0x69, 0x70, + 0x77, 0x79, 0x7b, 0x7d, 0x7e, 0x02, 0x02, + 0x22, 0x00, 0x2a, 0x40, 0x2a, 0xbe, 0x3a, + 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, 0x3b, 0x38, + 0x3b, 0x78, 0x3b, 0xb6, 0x4b, 0xb6, 0x4b, + 0xf4, 0x4b, 0xf4, 0x6c, 0x34, 0x84, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x01, 0x38, 0x00, 0x14, + 0x00, 0x1c, 0x00, 0x01, 0x66, 0x00, 0x14, + 0x00, 0x14, 0x00, 0x01, 0x66, 0x00, 0x14, + 0x05, 0xcc, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x13); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xce, 0x09, 0x11, 0x09, 0x11, + 0x08, 0xc1, 0x07, 0xfa, 0x05, 0xa4, 0x00, + 0x3c, 0x00, 0x34, 0x00, 0x24, 0x00, 0x0c, + 0x00, 0x0c, 0x04, 0x00, 0x35); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x03, 0x33); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x00, 0x33, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3e, 0x00, + 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x00, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x06, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x00, 0x00, 0x08, 0x09, + 0x09, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbc, 0x10, 0x00, 0x00, 0x06, + 0x11, 0x09, 0x3b, 0x09, 0x47, 0x09, 0x47, + 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x10, 0x10, 0x00, 0x08, + 0x22, 0x09, 0x19, 0x09, 0x25, 0x09, 0x25, + 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x80); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x08, 0x08, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x81); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf3, 0x0f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x82); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf9, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x51, 0x83); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x9a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x00); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 20); + + return dsi_ctx.accum_err; } -static int visionox_vtdr6130_off(struct visionox_vtdr6130 *ctx) +static void visionox_vtdr6130_off(struct visionox_vtdr6130 *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(20); - - 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(120); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 20); - return 0; + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); } static int visionox_vtdr6130_prepare(struct drm_panel *panel) { struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel); - struct device *dev = &ctx->dsi->dev; int ret; ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), @@ -165,7 +148,6 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel) ret = visionox_vtdr6130_on(ctx); if (ret < 0) { - dev_err(dev, "Failed to initialize panel: %d\n", ret); gpiod_set_value_cansleep(ctx->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); return ret; @@ -177,12 +159,8 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel) static int visionox_vtdr6130_unprepare(struct drm_panel *panel) { struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel); - struct device *dev = &ctx->dsi->dev; - int ret; - ret = visionox_vtdr6130_off(ctx); - if (ret < 0) - dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + visionox_vtdr6130_off(ctx); gpiod_set_value_cansleep(ctx->reset_gpio, 1); -- cgit v1.2.3 From e0cb0c785250d0565c19ba17fc95bd7ab97670ba Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 28 Aug 2024 18:03:40 +0200 Subject: drm/panel: visionox-vtdr6130: switch to devm_regulator_bulk_get_const Switch to devm_regulator_bulk_get_const() to stop setting the supplies list in probe(), and move the regulator_bulk_data struct in static const. Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20240828-topic-sm8x50-upstream-vtdr6130-multi-v1-2-0cae20d4c55d@linaro.org Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20240828-topic-sm8x50-upstream-vtdr6130-multi-v1-2-0cae20d4c55d@linaro.org --- drivers/gpu/drm/panel/panel-visionox-vtdr6130.c | 26 +++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index ebe92871dbb6..17b8defe79c1 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -19,7 +19,13 @@ struct visionox_vtdr6130 { struct drm_panel panel; struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; - struct regulator_bulk_data supplies[3]; + struct regulator_bulk_data *supplies; +}; + +static const struct regulator_bulk_data visionox_vtdr6130_supplies[] = { + { .supply = "vddio" }, + { .supply = "vci" }, + { .supply = "vdd" }, }; static inline struct visionox_vtdr6130 *to_visionox_vtdr6130(struct drm_panel *panel) @@ -139,7 +145,7 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel) struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel); int ret; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), + ret = regulator_bulk_enable(ARRAY_SIZE(visionox_vtdr6130_supplies), ctx->supplies); if (ret < 0) return ret; @@ -149,7 +155,8 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel) ret = visionox_vtdr6130_on(ctx); if (ret < 0) { gpiod_set_value_cansleep(ctx->reset_gpio, 1); - regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + regulator_bulk_disable(ARRAY_SIZE(visionox_vtdr6130_supplies), + ctx->supplies); return ret; } @@ -164,7 +171,8 @@ static int visionox_vtdr6130_unprepare(struct drm_panel *panel) gpiod_set_value_cansleep(ctx->reset_gpio, 1); - regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + regulator_bulk_disable(ARRAY_SIZE(visionox_vtdr6130_supplies), + ctx->supplies); return 0; } @@ -244,12 +252,10 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) if (!ctx) return -ENOMEM; - ctx->supplies[0].supply = "vddio"; - ctx->supplies[1].supply = "vci"; - ctx->supplies[2].supply = "vdd"; - - ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies), - ctx->supplies); + ret = devm_regulator_bulk_get_const(&dsi->dev, + ARRAY_SIZE(visionox_vtdr6130_supplies), + visionox_vtdr6130_supplies, + &ctx->supplies); if (ret < 0) return ret; -- cgit v1.2.3 From 076403a69837c1f8454ceced3ed6f0465835f4d1 Mon Sep 17 00:00:00 2001 From: Tejas Vipin Date: Wed, 28 Aug 2024 23:52:10 +0530 Subject: drm/panel: novatek-nt35950: transition to mipi_dsi wrapped functions Changes the novatek-nt35950 panel to use multi style functions for improved error handling. Reviewed-by: Neil Armstrong Signed-off-by: Tejas Vipin Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20240828182210.565861-1-tejasvipin76@gmail.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20240828182210.565861-1-tejasvipin76@gmail.com --- drivers/gpu/drm/panel/panel-novatek-nt35950.c | 211 ++++++++------------------ 1 file changed, 66 insertions(+), 145 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35950.c b/drivers/gpu/drm/panel/panel-novatek-nt35950.c index 028fdac293f7..b036208f9356 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35950.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35950.c @@ -100,106 +100,87 @@ static void nt35950_reset(struct nt35950 *nt) /* * nt35950_set_cmd2_page - Select manufacturer control (CMD2) page + * @dsi_ctx: context for mipi_dsi functions * @nt: Main driver structure * @page: Page number (0-7) - * - * Return: Number of transferred bytes or negative number on error */ -static int nt35950_set_cmd2_page(struct nt35950 *nt, u8 page) +static void nt35950_set_cmd2_page(struct mipi_dsi_multi_context *dsi_ctx, + struct nt35950 *nt, u8 page) { const u8 mauc_cmd2_page[] = { MCS_CMD_MAUCCTR, 0x55, 0xaa, 0x52, 0x08, page }; - int ret; - ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], mauc_cmd2_page, + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, mauc_cmd2_page, ARRAY_SIZE(mauc_cmd2_page)); - if (ret < 0) - return ret; - - nt->last_page = page; - return 0; + if (!dsi_ctx->accum_err) + nt->last_page = page; } /* * nt35950_set_data_compression - Set data compression mode + * @dsi_ctx: context for mipi_dsi functions * @nt: Main driver structure * @comp_mode: Compression mode - * - * Return: Number of transferred bytes or negative number on error */ -static int nt35950_set_data_compression(struct nt35950 *nt, u8 comp_mode) +static void nt35950_set_data_compression(struct mipi_dsi_multi_context *dsi_ctx, + struct nt35950 *nt, u8 comp_mode) { u8 cmd_data_compression[] = { MCS_PARAM_DATA_COMPRESSION, comp_mode }; u8 cmd_vesa_dsc_on[] = { MCS_PARAM_VESA_DSC_ON, !!comp_mode }; u8 cmd_vesa_dsc_setting[] = { MCS_PARAM_VESA_DSC_SETTING, 0x03 }; u8 last_page = nt->last_page; - int ret; /* Set CMD2 Page 0 if we're not there yet */ - if (last_page != 0) { - ret = nt35950_set_cmd2_page(nt, 0); - if (ret < 0) - return ret; - } + if (last_page != 0) + nt35950_set_cmd2_page(dsi_ctx, nt, 0); - ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_data_compression, + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_data_compression, ARRAY_SIZE(cmd_data_compression)); - if (ret < 0) - return ret; - - ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_on, + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_vesa_dsc_on, ARRAY_SIZE(cmd_vesa_dsc_on)); - if (ret < 0) - return ret; /* Set the vesa dsc setting on Page 4 */ - ret = nt35950_set_cmd2_page(nt, 4); - if (ret < 0) - return ret; + nt35950_set_cmd2_page(dsi_ctx, nt, 4); /* Display Stream Compression setting, always 0x03 */ - ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_setting, + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_vesa_dsc_setting, ARRAY_SIZE(cmd_vesa_dsc_setting)); - if (ret < 0) - return ret; /* Get back to the previously set page */ - return nt35950_set_cmd2_page(nt, last_page); + nt35950_set_cmd2_page(dsi_ctx, nt, last_page); } /* * nt35950_set_scaler - Enable/disable resolution upscaling - * @nt: Main driver structure + * @dsi_ctx: context for mipi_dsi functions * @scale_up: Scale up function control - * - * Return: Number of transferred bytes or negative number on error */ -static int nt35950_set_scaler(struct nt35950 *nt, u8 scale_up) +static void nt35950_set_scaler(struct mipi_dsi_multi_context *dsi_ctx, + u8 scale_up) { u8 cmd_scaler[] = { MCS_PARAM_SCALER_FUNCTION, scale_up }; - return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler, - ARRAY_SIZE(cmd_scaler)); + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_scaler, + ARRAY_SIZE(cmd_scaler)); } /* * nt35950_set_scale_mode - Resolution upscaling mode - * @nt: Main driver structure + * @dsi_ctx: context for mipi_dsi functions * @mode: Scaler mode (MCS_DATA_COMPRESSION_*) - * - * Return: Number of transferred bytes or negative number on error */ -static int nt35950_set_scale_mode(struct nt35950 *nt, u8 mode) +static void nt35950_set_scale_mode(struct mipi_dsi_multi_context *dsi_ctx, + u8 mode) { u8 cmd_scaler[] = { MCS_PARAM_SCALEUP_MODE, mode }; - return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler, - ARRAY_SIZE(cmd_scaler)); + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_scaler, + ARRAY_SIZE(cmd_scaler)); } /* * nt35950_inject_black_image - Display a completely black image - * @nt: Main driver structure + * @dsi_ctx: context for mipi_dsi functions * * After IC setup, the attached panel may show random data * due to driveric behavior changes (resolution, compression, @@ -208,43 +189,34 @@ static int nt35950_set_scale_mode(struct nt35950 *nt, u8 mode) * the display. * It makes sense to push a black image before sending the sleep-out * and display-on commands. - * - * Return: Number of transferred bytes or negative number on error */ -static int nt35950_inject_black_image(struct nt35950 *nt) +static void nt35950_inject_black_image(struct mipi_dsi_multi_context *dsi_ctx) { const u8 cmd0_black_img[] = { 0x6f, 0x01 }; const u8 cmd1_black_img[] = { 0xf3, 0x10 }; u8 cmd_test[] = { 0xff, 0xaa, 0x55, 0xa5, 0x80 }; - int ret; /* Enable test command */ - ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test)); - if (ret < 0) - return ret; + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_test, ARRAY_SIZE(cmd_test)); /* Send a black image */ - ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd0_black_img, + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd0_black_img, ARRAY_SIZE(cmd0_black_img)); - if (ret < 0) - return ret; - ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd1_black_img, + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd1_black_img, ARRAY_SIZE(cmd1_black_img)); - if (ret < 0) - return ret; /* Disable test command */ cmd_test[ARRAY_SIZE(cmd_test) - 1] = 0x00; - return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test)); + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_test, ARRAY_SIZE(cmd_test)); } /* * nt35950_set_dispout - Set Display Output register parameters * @nt: Main driver structure - * - * Return: Number of transferred bytes or negative number on error + * @dsi_ctx: context for mipi_dsi functions */ -static int nt35950_set_dispout(struct nt35950 *nt) +static void nt35950_set_dispout(struct mipi_dsi_multi_context *dsi_ctx, + struct nt35950 *nt) { u8 cmd_dispout[] = { MCS_PARAM_DISP_OUTPUT_CTRL, 0x00 }; const struct nt35950_panel_mode *mode_data = nt->desc->mode_data; @@ -254,8 +226,8 @@ static int nt35950_set_dispout(struct nt35950 *nt) if (mode_data[nt->cur_mode].enable_sram) cmd_dispout[1] |= MCS_DISP_OUT_SRAM_EN; - return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_dispout, - ARRAY_SIZE(cmd_dispout)); + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_dispout, + ARRAY_SIZE(cmd_dispout)); } static int nt35950_get_current_mode(struct nt35950 *nt) @@ -284,78 +256,47 @@ static int nt35950_on(struct nt35950 *nt) { const struct nt35950_panel_mode *mode_data = nt->desc->mode_data; struct mipi_dsi_device *dsi = nt->dsi[0]; - struct device *dev = &dsi->dev; - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; nt->cur_mode = nt35950_get_current_mode(nt); nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM; nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM; - ret = nt35950_set_cmd2_page(nt, 0); - if (ret < 0) - return ret; + nt35950_set_cmd2_page(&dsi_ctx, nt, 0); + nt35950_set_data_compression(&dsi_ctx, nt, mode_data[nt->cur_mode].compression); + nt35950_set_scale_mode(&dsi_ctx, mode_data[nt->cur_mode].scaler_mode); + nt35950_set_scaler(&dsi_ctx, mode_data[nt->cur_mode].scaler_on); + nt35950_set_dispout(&dsi_ctx, nt); - ret = nt35950_set_data_compression(nt, mode_data[nt->cur_mode].compression); - if (ret < 0) - return ret; - - ret = nt35950_set_scale_mode(nt, mode_data[nt->cur_mode].scaler_mode); - if (ret < 0) - return ret; - - ret = nt35950_set_scaler(nt, mode_data[nt->cur_mode].scaler_on); - if (ret < 0) - return ret; - - ret = nt35950_set_dispout(nt); - if (ret < 0) - 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; - } - - 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; - } + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + mipi_dsi_dcs_set_tear_scanline_multi(&dsi_ctx, 0); /* CMD2 Page 1 */ - ret = nt35950_set_cmd2_page(nt, 1); - if (ret < 0) - return ret; + nt35950_set_cmd2_page(&dsi_ctx, nt, 1); /* Unknown command */ - mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x88, 0x88); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x88, 0x88); /* CMD2 Page 7 */ - ret = nt35950_set_cmd2_page(nt, 7); - if (ret < 0) - return ret; + nt35950_set_cmd2_page(&dsi_ctx, nt, 7); /* Enable SubPixel Rendering */ - mipi_dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_EN, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PARAM_SPR_EN, 0x01); /* SPR Mode: YYG Rainbow-RGB */ - mipi_dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_MODE, MCS_SPR_MODE_YYG_RAINBOW_RGB); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PARAM_SPR_MODE, + MCS_SPR_MODE_YYG_RAINBOW_RGB); /* CMD3 */ - ret = nt35950_inject_black_image(nt); - if (ret < 0) - return ret; + nt35950_inject_black_image(&dsi_ctx); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); - ret = mipi_dsi_dcs_exit_sleep_mode(dsi); - if (ret < 0) - return ret; - msleep(120); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); - ret = mipi_dsi_dcs_set_display_on(dsi); - if (ret < 0) - return ret; - msleep(120); + if (dsi_ctx.accum_err) + return dsi_ctx.accum_err; nt->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM; nt->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM; @@ -363,30 +304,19 @@ static int nt35950_on(struct nt35950 *nt) return 0; } -static int nt35950_off(struct nt35950 *nt) +static void nt35950_off(struct nt35950 *nt) { - struct device *dev = &nt->dsi[0]->dev; - int ret; + struct mipi_dsi_device *dsi = nt->dsi[0]; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; - ret = mipi_dsi_dcs_set_display_off(nt->dsi[0]); - if (ret < 0) { - dev_err(dev, "Failed to set display off: %d\n", ret); - goto set_lpm; - } - usleep_range(10000, 11000); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); - ret = mipi_dsi_dcs_enter_sleep_mode(nt->dsi[0]); - if (ret < 0) { - dev_err(dev, "Failed to enter sleep mode: %d\n", ret); - goto set_lpm; - } - msleep(150); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 150); -set_lpm: nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM; nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM; - - return 0; } static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev) @@ -427,7 +357,6 @@ static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev) static int nt35950_prepare(struct drm_panel *panel) { struct nt35950 *nt = to_nt35950(panel); - struct device *dev = &nt->dsi[0]->dev; int ret; ret = regulator_enable(nt->vregs[0].consumer); @@ -452,10 +381,6 @@ static int nt35950_prepare(struct drm_panel *panel) nt35950_reset(nt); ret = nt35950_on(nt); - if (ret < 0) { - dev_err(dev, "Failed to initialize panel: %d\n", ret); - goto end; - } end: if (ret < 0) { @@ -469,12 +394,8 @@ end: static int nt35950_unprepare(struct drm_panel *panel) { struct nt35950 *nt = to_nt35950(panel); - struct device *dev = &nt->dsi[0]->dev; - int ret; - ret = nt35950_off(nt); - if (ret < 0) - dev_err(dev, "Failed to deinitialize panel: %d\n", ret); + nt35950_off(nt); gpiod_set_value_cansleep(nt->reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs); -- cgit v1.2.3 From bfbdf72d6c145e008805f0745ff79f9b8e301e0e Mon Sep 17 00:00:00 2001 From: Abhishek Tamboli Date: Tue, 27 Aug 2024 20:55:04 +0530 Subject: drm/panel: nv3051d: Transition to mipi_dsi_dcs_write_seq_multi Replace deprecated 'mipi_dsi_dcs_write_seq()' macro to 'mipi_dsi_dcs_write_seq_multi' macro in panel_nv3051d_init_sequence function. Signed-off-by: Abhishek Tamboli Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20240827152504.30586-1-abhishektamboli9@gmail.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20240827152504.30586-1-abhishektamboli9@gmail.com --- drivers/gpu/drm/panel/panel-newvision-nv3051d.c | 367 ++++++++++++------------ 1 file changed, 184 insertions(+), 183 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c index 94d89ffd596b..5d115ecd5dd4 100644 --- a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c +++ b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c @@ -47,195 +47,196 @@ static inline struct panel_nv3051d *panel_to_panelnv3051d(struct drm_panel *pane static int panel_nv3051d_init_sequence(struct panel_nv3051d *ctx) { struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi}; /* * Init sequence was supplied by device vendor with no * documentation. */ - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0xE3, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x03, 0x40); - mipi_dsi_dcs_write_seq(dsi, 0x04, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x05, 0x03); - mipi_dsi_dcs_write_seq(dsi, 0x24, 0x12); - mipi_dsi_dcs_write_seq(dsi, 0x25, 0x1E); - mipi_dsi_dcs_write_seq(dsi, 0x26, 0x28); - mipi_dsi_dcs_write_seq(dsi, 0x27, 0x52); - mipi_dsi_dcs_write_seq(dsi, 0x28, 0x57); - mipi_dsi_dcs_write_seq(dsi, 0x29, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0x2A, 0xDF); - mipi_dsi_dcs_write_seq(dsi, 0x38, 0x9C); - mipi_dsi_dcs_write_seq(dsi, 0x39, 0xA7); - mipi_dsi_dcs_write_seq(dsi, 0x3A, 0x53); - mipi_dsi_dcs_write_seq(dsi, 0x44, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x49, 0x3C); - mipi_dsi_dcs_write_seq(dsi, 0x59, 0xFE); - mipi_dsi_dcs_write_seq(dsi, 0x5C, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x91, 0x77); - mipi_dsi_dcs_write_seq(dsi, 0x92, 0x77); - mipi_dsi_dcs_write_seq(dsi, 0xA0, 0x55); - mipi_dsi_dcs_write_seq(dsi, 0xA1, 0x50); - mipi_dsi_dcs_write_seq(dsi, 0xA4, 0x9C); - mipi_dsi_dcs_write_seq(dsi, 0xA7, 0x02); - mipi_dsi_dcs_write_seq(dsi, 0xA8, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0xA9, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0xAA, 0xFC); - mipi_dsi_dcs_write_seq(dsi, 0xAB, 0x28); - mipi_dsi_dcs_write_seq(dsi, 0xAC, 0x06); - mipi_dsi_dcs_write_seq(dsi, 0xAD, 0x06); - mipi_dsi_dcs_write_seq(dsi, 0xAE, 0x06); - mipi_dsi_dcs_write_seq(dsi, 0xAF, 0x03); - mipi_dsi_dcs_write_seq(dsi, 0xB0, 0x08); - mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x26); - mipi_dsi_dcs_write_seq(dsi, 0xB2, 0x28); - mipi_dsi_dcs_write_seq(dsi, 0xB3, 0x28); - mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x33); - mipi_dsi_dcs_write_seq(dsi, 0xB5, 0x08); - mipi_dsi_dcs_write_seq(dsi, 0xB6, 0x26); - mipi_dsi_dcs_write_seq(dsi, 0xB7, 0x08); - mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x26); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x02); - mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x0E); - mipi_dsi_dcs_write_seq(dsi, 0xD1, 0x0E); - mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x29); - mipi_dsi_dcs_write_seq(dsi, 0xD4, 0x2B); - mipi_dsi_dcs_write_seq(dsi, 0xB2, 0x0C); - mipi_dsi_dcs_write_seq(dsi, 0xD2, 0x0A); - mipi_dsi_dcs_write_seq(dsi, 0xB3, 0x28); - mipi_dsi_dcs_write_seq(dsi, 0xD3, 0x28); - mipi_dsi_dcs_write_seq(dsi, 0xB6, 0x11); - mipi_dsi_dcs_write_seq(dsi, 0xD6, 0x0D); - mipi_dsi_dcs_write_seq(dsi, 0xB7, 0x32); - mipi_dsi_dcs_write_seq(dsi, 0xD7, 0x30); - mipi_dsi_dcs_write_seq(dsi, 0xC1, 0x04); - mipi_dsi_dcs_write_seq(dsi, 0xE1, 0x06); - mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x0A); - mipi_dsi_dcs_write_seq(dsi, 0xD8, 0x0A); - mipi_dsi_dcs_write_seq(dsi, 0xB9, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0xD9, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0xBD, 0x13); - mipi_dsi_dcs_write_seq(dsi, 0xDD, 0x13); - mipi_dsi_dcs_write_seq(dsi, 0xBC, 0x11); - mipi_dsi_dcs_write_seq(dsi, 0xDC, 0x11); - mipi_dsi_dcs_write_seq(dsi, 0xBB, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xDB, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xBA, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xDA, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xBE, 0x18); - mipi_dsi_dcs_write_seq(dsi, 0xDE, 0x18); - mipi_dsi_dcs_write_seq(dsi, 0xBF, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xDF, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xC0, 0x17); - mipi_dsi_dcs_write_seq(dsi, 0xE0, 0x17); - mipi_dsi_dcs_write_seq(dsi, 0xB5, 0x3B); - mipi_dsi_dcs_write_seq(dsi, 0xD5, 0x3C); - mipi_dsi_dcs_write_seq(dsi, 0xB0, 0x0B); - mipi_dsi_dcs_write_seq(dsi, 0xD0, 0x0C); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x03); - mipi_dsi_dcs_write_seq(dsi, 0x00, 0x2A); - mipi_dsi_dcs_write_seq(dsi, 0x01, 0x2A); - mipi_dsi_dcs_write_seq(dsi, 0x02, 0x2A); - mipi_dsi_dcs_write_seq(dsi, 0x03, 0x2A); - mipi_dsi_dcs_write_seq(dsi, 0x04, 0x61); - mipi_dsi_dcs_write_seq(dsi, 0x05, 0x80); - mipi_dsi_dcs_write_seq(dsi, 0x06, 0xC7); - mipi_dsi_dcs_write_seq(dsi, 0x07, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0x08, 0x82); - mipi_dsi_dcs_write_seq(dsi, 0x09, 0x83); - mipi_dsi_dcs_write_seq(dsi, 0x30, 0x2A); - mipi_dsi_dcs_write_seq(dsi, 0x31, 0x2A); - mipi_dsi_dcs_write_seq(dsi, 0x32, 0x2A); - mipi_dsi_dcs_write_seq(dsi, 0x33, 0x2A); - mipi_dsi_dcs_write_seq(dsi, 0x34, 0x61); - mipi_dsi_dcs_write_seq(dsi, 0x35, 0xC5); - mipi_dsi_dcs_write_seq(dsi, 0x36, 0x80); - mipi_dsi_dcs_write_seq(dsi, 0x37, 0x23); - mipi_dsi_dcs_write_seq(dsi, 0x40, 0x82); - mipi_dsi_dcs_write_seq(dsi, 0x41, 0x83); - mipi_dsi_dcs_write_seq(dsi, 0x42, 0x80); - mipi_dsi_dcs_write_seq(dsi, 0x43, 0x81); - mipi_dsi_dcs_write_seq(dsi, 0x44, 0x11); - mipi_dsi_dcs_write_seq(dsi, 0x45, 0xF2); - mipi_dsi_dcs_write_seq(dsi, 0x46, 0xF1); - mipi_dsi_dcs_write_seq(dsi, 0x47, 0x11); - mipi_dsi_dcs_write_seq(dsi, 0x48, 0xF4); - mipi_dsi_dcs_write_seq(dsi, 0x49, 0xF3); - mipi_dsi_dcs_write_seq(dsi, 0x50, 0x02); - mipi_dsi_dcs_write_seq(dsi, 0x51, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0x52, 0x04); - mipi_dsi_dcs_write_seq(dsi, 0x53, 0x03); - mipi_dsi_dcs_write_seq(dsi, 0x54, 0x11); - mipi_dsi_dcs_write_seq(dsi, 0x55, 0xF6); - mipi_dsi_dcs_write_seq(dsi, 0x56, 0xF5); - mipi_dsi_dcs_write_seq(dsi, 0x57, 0x11); - mipi_dsi_dcs_write_seq(dsi, 0x58, 0xF8); - mipi_dsi_dcs_write_seq(dsi, 0x59, 0xF7); - mipi_dsi_dcs_write_seq(dsi, 0x7E, 0x02); - mipi_dsi_dcs_write_seq(dsi, 0x7F, 0x80); - mipi_dsi_dcs_write_seq(dsi, 0xE0, 0x5A); - mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x0E); - mipi_dsi_dcs_write_seq(dsi, 0xB5, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xB6, 0x04); - mipi_dsi_dcs_write_seq(dsi, 0xB7, 0x07); - mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x06); - mipi_dsi_dcs_write_seq(dsi, 0xB9, 0x05); - mipi_dsi_dcs_write_seq(dsi, 0xBA, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xC7, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xCA, 0x0E); - mipi_dsi_dcs_write_seq(dsi, 0xCB, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0xCC, 0x04); - mipi_dsi_dcs_write_seq(dsi, 0xCD, 0x07); - mipi_dsi_dcs_write_seq(dsi, 0xCE, 0x06); - mipi_dsi_dcs_write_seq(dsi, 0xCF, 0x05); - mipi_dsi_dcs_write_seq(dsi, 0xD0, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0x81, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0x84, 0x0E); - mipi_dsi_dcs_write_seq(dsi, 0x85, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0x86, 0x07); - mipi_dsi_dcs_write_seq(dsi, 0x87, 0x04); - mipi_dsi_dcs_write_seq(dsi, 0x88, 0x05); - mipi_dsi_dcs_write_seq(dsi, 0x89, 0x06); - mipi_dsi_dcs_write_seq(dsi, 0x8A, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x97, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0x9A, 0x0E); - mipi_dsi_dcs_write_seq(dsi, 0x9B, 0x0F); - mipi_dsi_dcs_write_seq(dsi, 0x9C, 0x07); - mipi_dsi_dcs_write_seq(dsi, 0x9D, 0x04); - mipi_dsi_dcs_write_seq(dsi, 0x9E, 0x05); - mipi_dsi_dcs_write_seq(dsi, 0x9F, 0x06); - mipi_dsi_dcs_write_seq(dsi, 0xA0, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x02); - mipi_dsi_dcs_write_seq(dsi, 0x01, 0x01); - mipi_dsi_dcs_write_seq(dsi, 0x02, 0xDA); - mipi_dsi_dcs_write_seq(dsi, 0x03, 0xBA); - mipi_dsi_dcs_write_seq(dsi, 0x04, 0xA8); - mipi_dsi_dcs_write_seq(dsi, 0x05, 0x9A); - mipi_dsi_dcs_write_seq(dsi, 0x06, 0x70); - mipi_dsi_dcs_write_seq(dsi, 0x07, 0xFF); - mipi_dsi_dcs_write_seq(dsi, 0x08, 0x91); - mipi_dsi_dcs_write_seq(dsi, 0x09, 0x90); - mipi_dsi_dcs_write_seq(dsi, 0x0A, 0xFF); - mipi_dsi_dcs_write_seq(dsi, 0x0B, 0x8F); - mipi_dsi_dcs_write_seq(dsi, 0x0C, 0x60); - mipi_dsi_dcs_write_seq(dsi, 0x0D, 0x58); - mipi_dsi_dcs_write_seq(dsi, 0x0E, 0x48); - mipi_dsi_dcs_write_seq(dsi, 0x0F, 0x38); - mipi_dsi_dcs_write_seq(dsi, 0x10, 0x2B); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x30); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x52); - mipi_dsi_dcs_write_seq(dsi, 0xFF, 0x00); - mipi_dsi_dcs_write_seq(dsi, 0x36, 0x02); - mipi_dsi_dcs_write_seq(dsi, 0x3A, 0x70); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE3, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x12); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x57); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0xDF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x9C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0xA7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x53); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x3C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0xFE); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x91, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x92, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA0, 0x55); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA1, 0x50); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA4, 0x9C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA7, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA8, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA9, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAA, 0xFC); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAB, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAC, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAD, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAE, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xAF, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB0, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB1, 0x26); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB2, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB3, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB4, 0x33); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB5, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB6, 0x26); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB7, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB8, 0x26); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB1, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD1, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB4, 0x29); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD4, 0x2B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB2, 0x0C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD2, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB3, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD3, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB6, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD6, 0x0D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB7, 0x32); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD7, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xC1, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE1, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB8, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD8, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB9, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD9, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBD, 0x13); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDD, 0x13); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBC, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDC, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBB, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDB, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBA, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDA, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBE, 0x18); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDE, 0x18); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBF, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xDF, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xC0, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x17); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB5, 0x3B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD5, 0x3C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB0, 0x0B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD0, 0x0C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x61); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x80); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0xC7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x82); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x83); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x2A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x61); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0xC5); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x80); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x23); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x82); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x83); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x80); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x81); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0xF2); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0xF1); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0xF4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0xF3); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0xF6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0xF5); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0xF8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0xF7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x80); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x5A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB1, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB4, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB5, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB6, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB7, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB8, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xB9, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xBA, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xC7, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCA, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCB, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCC, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCD, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCE, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xCF, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xD0, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x84, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x85, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x86, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x88, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x89, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8A, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x97, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9A, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9B, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9C, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9D, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9E, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9F, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xA0, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xDA); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0xBA); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0xA8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x9A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x70); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x91); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x90); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x8F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x60); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x58); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x2B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x52); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xFF, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x70); dev_dbg(ctx->dev, "Panel init sequence done\n"); -- cgit v1.2.3 From f7945d9fa8b72dd6e0b1e4bfa68f6fe54fd521fe Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 22 Aug 2024 20:42:47 +0300 Subject: drm/sti/sti_hdmi: convert to struct drm_edid Prefer the struct drm_edid based functions for reading the EDID and updating the connector. The functional change is that the CEC physical address gets invalidated when the EDID could not be read. v2: Use drm_edid_read() instead of drm_edid_read_ddc() (Sima) Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/25879a0183e30792bf0d63bdf56a03f11018e4a3.1724348429.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/sti/sti_hdmi.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index b0d84440a87b..847470f747c0 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -974,28 +974,32 @@ static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = { static int sti_hdmi_connector_get_modes(struct drm_connector *connector) { + const struct drm_display_info *info = &connector->display_info; struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; - struct edid *edid; + const struct drm_edid *drm_edid; int count; DRM_DEBUG_DRIVER("\n"); - edid = drm_get_edid(connector, hdmi->ddc_adapt); - if (!edid) - goto fail; + drm_edid = drm_edid_read(connector); + + drm_edid_connector_update(connector, drm_edid); - cec_notifier_set_phys_addr_from_edid(hdmi->notifier, edid); + cec_notifier_set_phys_addr(hdmi->notifier, + connector->display_info.source_physical_address); + + if (!drm_edid) + goto fail; - count = drm_add_edid_modes(connector, edid); - drm_connector_update_edid_property(connector, edid); + count = drm_edid_connector_add_modes(connector); DRM_DEBUG_KMS("%s : %dx%d cm\n", - (connector->display_info.is_hdmi ? "hdmi monitor" : "dvi monitor"), - edid->width_cm, edid->height_cm); + info->is_hdmi ? "hdmi monitor" : "dvi monitor", + info->width_mm / 10, info->height_mm / 10); - kfree(edid); + drm_edid_free(drm_edid); return count; fail: -- cgit v1.2.3 From 98365ca74cbfae16b793b62a7ecabb5be8d77785 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 22 Aug 2024 20:42:50 +0300 Subject: drm/tegra: convert to struct drm_edid Prefer the struct drm_edid based functions for reading the EDID and updating the connector. Acked-by: Thierry Reding Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/e764b50f4ad2de95e449ccb37f49c3f37b3333fc.1724348429.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/tegra/drm.h | 2 +- drivers/gpu/drm/tegra/output.c | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 682011166a8f..2f3781e04b0a 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -133,7 +133,7 @@ struct tegra_output { struct drm_bridge *bridge; struct drm_panel *panel; struct i2c_adapter *ddc; - const struct edid *edid; + const struct drm_edid *drm_edid; struct cec_notifier *cec; unsigned int hpd_irq; struct gpio_desc *hpd_gpio; diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 4da3c3d1abbc..e6b5863fec71 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -21,7 +21,7 @@ int tegra_output_connector_get_modes(struct drm_connector *connector) { struct tegra_output *output = connector_to_output(connector); - struct edid *edid = NULL; + const struct drm_edid *drm_edid; int err = 0; /* @@ -34,18 +34,17 @@ int tegra_output_connector_get_modes(struct drm_connector *connector) return err; } - if (output->edid) - edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL); + if (output->drm_edid) + drm_edid = drm_edid_dup(output->drm_edid); else if (output->ddc) - edid = drm_get_edid(connector, output->ddc); + drm_edid = drm_edid_read_ddc(connector, output->ddc); - cec_notifier_set_phys_addr_from_edid(output->cec, edid); - drm_connector_update_edid_property(connector, edid); + drm_edid_connector_update(connector, drm_edid); + cec_notifier_set_phys_addr(output->cec, + connector->display_info.source_physical_address); - if (edid) { - err = drm_add_edid_modes(connector, edid); - kfree(edid); - } + err = drm_edid_connector_add_modes(connector); + drm_edid_free(drm_edid); return err; } @@ -98,6 +97,7 @@ static irqreturn_t hpd_irq(int irq, void *data) int tegra_output_probe(struct tegra_output *output) { struct device_node *ddc, *panel; + const void *edid; unsigned long flags; int err, size; @@ -124,8 +124,6 @@ int tegra_output_probe(struct tegra_output *output) return PTR_ERR(output->panel); } - output->edid = of_get_property(output->of_node, "nvidia,edid", &size); - ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0); if (ddc) { output->ddc = of_get_i2c_adapter_by_node(ddc); @@ -137,6 +135,9 @@ int tegra_output_probe(struct tegra_output *output) } } + edid = of_get_property(output->of_node, "nvidia,edid", &size); + output->drm_edid = drm_edid_alloc(edid, size); + output->hpd_gpio = devm_fwnode_gpiod_get(output->dev, of_fwnode_handle(output->of_node), "nvidia,hpd", @@ -187,6 +188,8 @@ put_i2c: if (output->ddc) i2c_put_adapter(output->ddc); + drm_edid_free(output->drm_edid); + return err; } @@ -197,6 +200,8 @@ void tegra_output_remove(struct tegra_output *output) if (output->ddc) i2c_put_adapter(output->ddc); + + drm_edid_free(output->drm_edid); } int tegra_output_init(struct drm_device *drm, struct tegra_output *output) -- cgit v1.2.3 From 42e08287a3185409a7a1923374a557e04fc36e48 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 22 Aug 2024 20:42:51 +0300 Subject: drm/ipuv3/parallel: convert to struct drm_edid Prefer the struct drm_edid based functions for storing the EDID and updating the connector. Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/a1698044d556072e79041d69b8702099fd17bd90.1724348429.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/imx/ipuv3/parallel-display.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c index 55dedd73f528..91d7808a2d8d 100644 --- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c +++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c @@ -34,7 +34,7 @@ struct imx_parallel_display_encoder { struct imx_parallel_display { struct device *dev; - void *edid; + const struct drm_edid *drm_edid; u32 bus_format; u32 bus_flags; struct drm_display_mode mode; @@ -62,9 +62,9 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) if (num_modes > 0) return num_modes; - if (imxpd->edid) { - drm_connector_update_edid_property(connector, imxpd->edid); - num_modes = drm_add_edid_modes(connector, imxpd->edid); + if (imxpd->drm_edid) { + drm_edid_connector_update(connector, imxpd->drm_edid); + num_modes = drm_edid_connector_add_modes(connector); } if (np) { @@ -331,7 +331,7 @@ static int imx_pd_probe(struct platform_device *pdev) edidp = of_get_property(np, "edid", &edid_len); if (edidp) - imxpd->edid = devm_kmemdup(dev, edidp, edid_len, GFP_KERNEL); + imxpd->drm_edid = drm_edid_alloc(edidp, edid_len); ret = of_property_read_string(np, "interface-pix-fmt", &fmt); if (!ret) { @@ -355,7 +355,11 @@ static int imx_pd_probe(struct platform_device *pdev) static void imx_pd_remove(struct platform_device *pdev) { + struct imx_parallel_display *imxpd = platform_get_drvdata(pdev); + component_del(&pdev->dev, &imx_pd_ops); + + drm_edid_free(imxpd->drm_edid); } static const struct of_device_id imx_pd_dt_ids[] = { -- cgit v1.2.3 From 84addde447fd9d713e101437db0d4924855eff4f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 22 Aug 2024 20:42:52 +0300 Subject: drm/tiny/gm12u320: convert to struct drm_edid Prefer the struct drm_edid based functions for allocating the EDID and updating the connector. Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/c31c3afa883a3321345608c480c26161b638a83e.1724348429.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/tiny/gm12u320.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index e0defb1d134f..0bd7707c053e 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -464,7 +464,7 @@ static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320) * Note this assumes this driver is only ever used with the Acer C120, if we * add support for other devices the vendor and model should be parameterized. */ -static struct edid gm12u320_edid = { +static const struct edid gm12u320_edid = { .header = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }, .mfg_id = { 0x04, 0x72 }, /* "ACR" */ .prod_code = { 0x20, 0xc1 }, /* C120h */ @@ -523,8 +523,15 @@ static struct edid gm12u320_edid = { static int gm12u320_conn_get_modes(struct drm_connector *connector) { - drm_connector_update_edid_property(connector, &gm12u320_edid); - return drm_add_edid_modes(connector, &gm12u320_edid); + const struct drm_edid *drm_edid; + int count; + + drm_edid = drm_edid_alloc(&gm12u320_edid, sizeof(gm12u320_edid)); + drm_edid_connector_update(connector, drm_edid); + count = drm_edid_connector_add_modes(connector); + drm_edid_free(drm_edid); + + return count; } static const struct drm_connector_helper_funcs gm12u320_conn_helper_funcs = { -- cgit v1.2.3