mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	drm/panic: Add a drm panic handler
This module displays a user friendly message when a kernel panic occurs. It currently doesn't contain any debug information, but that can be added later. v2 * Use get_scanout_buffer() instead of the drm client API. (Thomas Zimmermann) * Add the panic reason to the panic message (Nerdopolis) * Add an exclamation mark (Nerdopolis) v3 * Rework the drawing functions, to write the pixels line by line and to use the drm conversion helper to support other formats. (Thomas Zimmermann) v4 * Use drm_fb_r1_to_32bit for fonts (Thomas Zimmermann) * Remove the default y to DRM_PANIC config option (Thomas Zimmermann) * Add foreground/background color config option * Fix the bottom lines not painted if the framebuffer height is not a multiple of the font height. * Automatically register the device to drm_panic, if the function get_scanout_buffer exists. (Thomas Zimmermann) v5 * Change the drawing API, use drm_fb_blit_from_r1() to draw the font. * Also add drm_fb_fill() to fill area with background color. * Add draw_pixel_xy() API for drivers that can't provide a linear buffer. * Add a flush() callback for drivers that needs to synchronize the buffer. * Add a void *private field, so drivers can pass private data to draw_pixel_xy() and flush(). v6 * Fix sparse warning for panic_msg and logo. v7 * Add select DRM_KMS_HELPER for the color conversion functions. v8 * Register directly each plane to the panic notifier (Sima) * Add raw_spinlock to properly handle concurrency (Sima) * Register plane instead of device, to avoid looping through plane list, and simplify code. * Replace get_scanout_buffer() logic with drm_panic_set_buffer() (Thomas Zimmermann) * Removed the draw_pixel_xy() API, will see later if it can be added back. v9 * Revert to using get_scanout_buffer() (Sima) * Move get_scanout_buffer() and panic_flush() to the plane helper functions (Thomas Zimmermann) * Register all planes with get_scanout_buffer() to the panic notifier * Use drm_panic_lock() to protect against race (Sima) v10 * Move blit and fill functions back in drm_panic (Thomas Zimmermann). * Simplify the text drawing functions. * Use kmsg_dumper instead of panic_notifier (Sima). v12 * Use array for map and pitch in struct drm_scanout_buffer to support multi-planar format later. (Thomas Zimmermann) * Better indent struct drm_scanout_buffer declaration. (Thomas Zimmermann) Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20240409163432.352518-3-jfalempe@redhat.com Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
		
							parent
							
								
									e2a1cda3e0
								
							
						
					
					
						commit
						bf9fb17c66
					
				
					 8 changed files with 435 additions and 0 deletions
				
			
		|  | @ -398,6 +398,21 @@ Plane Damage Tracking Functions Reference | ||||||
| .. kernel-doc:: include/drm/drm_damage_helper.h | .. kernel-doc:: include/drm/drm_damage_helper.h | ||||||
|    :internal: |    :internal: | ||||||
| 
 | 
 | ||||||
|  | Plane Panic Feature | ||||||
|  | ------------------- | ||||||
|  | 
 | ||||||
|  | .. kernel-doc:: drivers/gpu/drm/drm_panic.c | ||||||
|  |    :doc: overview | ||||||
|  | 
 | ||||||
|  | Plane Panic Functions Reference | ||||||
|  | ------------------------------- | ||||||
|  | 
 | ||||||
|  | .. kernel-doc:: include/drm/drm_panic.h | ||||||
|  |    :internal: | ||||||
|  | 
 | ||||||
|  | .. kernel-doc:: drivers/gpu/drm/drm_panic.c | ||||||
|  |    :export: | ||||||
|  | 
 | ||||||
| Display Modes Function Reference | Display Modes Function Reference | ||||||
| ================================ | ================================ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -104,6 +104,29 @@ config DRM_KMS_HELPER | ||||||
| 	help | 	help | ||||||
| 	  CRTC helpers for KMS drivers. | 	  CRTC helpers for KMS drivers. | ||||||
| 
 | 
 | ||||||
|  | config DRM_PANIC | ||||||
|  | 	bool "Display a user-friendly message when a kernel panic occurs" | ||||||
|  | 	depends on DRM && !FRAMEBUFFER_CONSOLE | ||||||
|  | 	select DRM_KMS_HELPER | ||||||
|  | 	select FONT_SUPPORT | ||||||
|  | 	help | ||||||
|  | 	  Enable a drm panic handler, which will display a user-friendly message | ||||||
|  | 	  when a kernel panic occurs. It's useful when using a user-space | ||||||
|  | 	  console instead of fbcon. | ||||||
|  | 	  It will only work if your graphic driver supports this feature. | ||||||
|  | 	  To support Hi-DPI Display, you can enable bigger fonts like | ||||||
|  | 	  FONT_TER16x32 | ||||||
|  | 
 | ||||||
|  | config DRM_PANIC_FOREGROUND_COLOR | ||||||
|  | 	hex "Drm panic screen foreground color, in RGB" | ||||||
|  | 	depends on DRM_PANIC | ||||||
|  | 	default 0xffffff | ||||||
|  | 
 | ||||||
|  | config DRM_PANIC_BACKGROUND_COLOR | ||||||
|  | 	hex "Drm panic screen background color, in RGB" | ||||||
|  | 	depends on DRM_PANIC | ||||||
|  | 	default 0x000000 | ||||||
|  | 
 | ||||||
| config DRM_DEBUG_DP_MST_TOPOLOGY_REFS | config DRM_DEBUG_DP_MST_TOPOLOGY_REFS | ||||||
|         bool "Enable refcount backtrace history in the DP MST helpers" |         bool "Enable refcount backtrace history in the DP MST helpers" | ||||||
| 	depends on STACKTRACE_SUPPORT | 	depends on STACKTRACE_SUPPORT | ||||||
|  |  | ||||||
|  | @ -88,6 +88,7 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \ | ||||||
| 	drm_privacy_screen.o \
 | 	drm_privacy_screen.o \
 | ||||||
| 	drm_privacy_screen_x86.o | 	drm_privacy_screen_x86.o | ||||||
| drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o | drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o | ||||||
|  | drm-$(CONFIG_DRM_PANIC) += drm_panic.o | ||||||
| obj-$(CONFIG_DRM)	+= drm.o | obj-$(CONFIG_DRM)	+= drm.o | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o | obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o | ||||||
|  |  | ||||||
|  | @ -43,6 +43,7 @@ | ||||||
| #include <drm/drm_file.h> | #include <drm/drm_file.h> | ||||||
| #include <drm/drm_managed.h> | #include <drm/drm_managed.h> | ||||||
| #include <drm/drm_mode_object.h> | #include <drm/drm_mode_object.h> | ||||||
|  | #include <drm/drm_panic.h> | ||||||
| #include <drm/drm_print.h> | #include <drm/drm_print.h> | ||||||
| #include <drm/drm_privacy_screen_machine.h> | #include <drm/drm_privacy_screen_machine.h> | ||||||
| 
 | 
 | ||||||
|  | @ -944,6 +945,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			goto err_unload; | 			goto err_unload; | ||||||
| 	} | 	} | ||||||
|  | 	drm_panic_register(dev); | ||||||
| 
 | 
 | ||||||
| 	DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", | 	DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", | ||||||
| 		 driver->name, driver->major, driver->minor, | 		 driver->name, driver->major, driver->minor, | ||||||
|  | @ -988,6 +990,8 @@ void drm_dev_unregister(struct drm_device *dev) | ||||||
| { | { | ||||||
| 	dev->registered = false; | 	dev->registered = false; | ||||||
| 
 | 
 | ||||||
|  | 	drm_panic_unregister(dev); | ||||||
|  | 
 | ||||||
| 	drm_client_dev_unregister(dev); | 	drm_client_dev_unregister(dev); | ||||||
| 
 | 
 | ||||||
| 	if (drm_core_check_feature(dev, DRIVER_MODESET)) | 	if (drm_core_check_feature(dev, DRIVER_MODESET)) | ||||||
|  |  | ||||||
							
								
								
									
										290
									
								
								drivers/gpu/drm/drm_panic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								drivers/gpu/drm/drm_panic.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,290 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0 or MIT
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2023 Red Hat. | ||||||
|  |  * Author: Jocelyn Falempe <jfalempe@redhat.com> | ||||||
|  |  * inspired by the drm_log driver from David Herrmann <dh.herrmann@gmail.com> | ||||||
|  |  * Tux Ascii art taken from cowsay written by Tony Monroe | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/font.h> | ||||||
|  | #include <linux/iosys-map.h> | ||||||
|  | #include <linux/kdebug.h> | ||||||
|  | #include <linux/kmsg_dump.h> | ||||||
|  | #include <linux/list.h> | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | 
 | ||||||
|  | #include <drm/drm_drv.h> | ||||||
|  | #include <drm/drm_format_helper.h> | ||||||
|  | #include <drm/drm_fourcc.h> | ||||||
|  | #include <drm/drm_framebuffer.h> | ||||||
|  | #include <drm/drm_modeset_helper_vtables.h> | ||||||
|  | #include <drm/drm_panic.h> | ||||||
|  | #include <drm/drm_plane.h> | ||||||
|  | #include <drm/drm_print.h> | ||||||
|  | 
 | ||||||
|  | MODULE_AUTHOR("Jocelyn Falempe"); | ||||||
|  | MODULE_DESCRIPTION("DRM panic handler"); | ||||||
|  | MODULE_LICENSE("GPL"); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * DOC: overview | ||||||
|  |  * | ||||||
|  |  * To enable DRM panic for a driver, the primary plane must implement a | ||||||
|  |  * &drm_plane_helper_funcs.get_scanout_buffer helper function. It is then | ||||||
|  |  * automatically registered to the drm panic handler. | ||||||
|  |  * When a panic occurs, the &drm_plane_helper_funcs.get_scanout_buffer will be | ||||||
|  |  * called, and the driver can provide a framebuffer so the panic handler can | ||||||
|  |  * draw the panic screen on it. Currently only linear buffer and a few color | ||||||
|  |  * formats are supported. | ||||||
|  |  * Optionally the driver can also provide a &drm_plane_helper_funcs.panic_flush | ||||||
|  |  * callback, that will be called after that, to send additional commands to the | ||||||
|  |  * hardware to make the scanout buffer visible. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * This module displays a user friendly message on screen when a kernel panic | ||||||
|  |  * occurs. This is conflicting with fbcon, so you can only enable it when fbcon | ||||||
|  |  * is disabled. | ||||||
|  |  * It's intended for end-user, so have minimal technical/debug information. | ||||||
|  |  * | ||||||
|  |  * Implementation details: | ||||||
|  |  * | ||||||
|  |  * It is a panic handler, so it can't take lock, allocate memory, run tasks/irq, | ||||||
|  |  * or attempt to sleep. It's a best effort, and it may not be able to display | ||||||
|  |  * the message in all situations (like if the panic occurs in the middle of a | ||||||
|  |  * modesetting). | ||||||
|  |  * It will display only one static frame, so performance optimizations are low | ||||||
|  |  * priority as the machine is already in an unusable state. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | struct drm_panic_line { | ||||||
|  | 	u32 len; | ||||||
|  | 	const char *txt; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define PANIC_LINE(s) {.len = sizeof(s) - 1, .txt = s} | ||||||
|  | 
 | ||||||
|  | static struct drm_panic_line panic_msg[] = { | ||||||
|  | 	PANIC_LINE("KERNEL PANIC !"), | ||||||
|  | 	PANIC_LINE(""), | ||||||
|  | 	PANIC_LINE("Please reboot your computer."), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const struct drm_panic_line logo[] = { | ||||||
|  | 	PANIC_LINE("     .--.        _"), | ||||||
|  | 	PANIC_LINE("    |o_o |      | |"), | ||||||
|  | 	PANIC_LINE("    |:_/ |      | |"), | ||||||
|  | 	PANIC_LINE("   //   \\ \\     |_|"), | ||||||
|  | 	PANIC_LINE("  (|     | )     _"), | ||||||
|  | 	PANIC_LINE(" /'\\_   _/`\\    (_)"), | ||||||
|  | 	PANIC_LINE(" \\___)=(___/"), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void drm_panic_fill32(struct iosys_map *map, unsigned int pitch, | ||||||
|  | 			     unsigned int height, unsigned int width, | ||||||
|  | 			     u32 color) | ||||||
|  | { | ||||||
|  | 	unsigned int y, x; | ||||||
|  | 
 | ||||||
|  | 	for (y = 0; y < height; y++) | ||||||
|  | 		for (x = 0; x < width; x++) | ||||||
|  | 			iosys_map_wr(map, y * pitch + x * sizeof(u32), u32, color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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, u32 bg32) | ||||||
|  | { | ||||||
|  | 	unsigned int y, x; | ||||||
|  | 	u32 val32; | ||||||
|  | 
 | ||||||
|  | 	for (y = 0; y < height; y++) { | ||||||
|  | 		for (x = 0; x < width; x++) { | ||||||
|  | 			val32 = (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32; | ||||||
|  | 			iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, val32); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const u8 *get_char_bitmap(const struct font_desc *font, char c, size_t font_pitch) | ||||||
|  | { | ||||||
|  | 	return font->data + (c * font->height) * font_pitch; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static unsigned int get_max_line_len(const struct drm_panic_line *lines, int len) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	unsigned int max = 0; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < len; i++) | ||||||
|  | 		max = max(lines[i].len, max); | ||||||
|  | 	return max; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Draw a text in a rectangle on a framebuffer. The text is truncated if it overflows the rectangle | ||||||
|  |  */ | ||||||
|  | static void draw_txt_rectangle(struct drm_scanout_buffer *sb, | ||||||
|  | 			       const struct font_desc *font, | ||||||
|  | 			       const struct drm_panic_line *msg, | ||||||
|  | 			       unsigned int msg_lines, | ||||||
|  | 			       bool centered, | ||||||
|  | 			       struct drm_rect *clip, | ||||||
|  | 			       u32 fg_color, | ||||||
|  | 			       u32 bg_color) | ||||||
|  | { | ||||||
|  | 	int i, j; | ||||||
|  | 	const u8 *src; | ||||||
|  | 	size_t font_pitch = DIV_ROUND_UP(font->width, 8); | ||||||
|  | 	struct iosys_map dst; | ||||||
|  | 	unsigned int px_width = sb->format->cpp[0]; | ||||||
|  | 	int left = 0; | ||||||
|  | 
 | ||||||
|  | 	msg_lines = min(msg_lines,  drm_rect_height(clip) / font->height); | ||||||
|  | 	for (i = 0; i < msg_lines; i++) { | ||||||
|  | 		size_t line_len = min(msg[i].len, drm_rect_width(clip) / font->width); | ||||||
|  | 
 | ||||||
|  | 		if (centered) | ||||||
|  | 			left = (drm_rect_width(clip) - (line_len * font->width)) / 2; | ||||||
|  | 
 | ||||||
|  | 		dst = sb->map[0]; | ||||||
|  | 		iosys_map_incr(&dst, (clip->y1 + i * font->height) * sb->pitch[0] + | ||||||
|  | 				     (clip->x1 + left) * px_width); | ||||||
|  | 		for (j = 0; j < line_len; j++) { | ||||||
|  | 			src = get_char_bitmap(font, msg[i].txt[j], font_pitch); | ||||||
|  | 			drm_panic_blit32(&dst, sb->pitch[0], src, font_pitch, | ||||||
|  | 					 font->height, font->width, | ||||||
|  | 					 fg_color, bg_color); | ||||||
|  | 			iosys_map_incr(&dst, font->width * px_width); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Draw the panic message at the center of the screen | ||||||
|  |  */ | ||||||
|  | static void draw_panic_static(struct drm_scanout_buffer *sb) | ||||||
|  | { | ||||||
|  | 	size_t msg_lines = ARRAY_SIZE(panic_msg); | ||||||
|  | 	size_t logo_lines = ARRAY_SIZE(logo); | ||||||
|  | 	u32 fg_color = CONFIG_DRM_PANIC_FOREGROUND_COLOR; | ||||||
|  | 	u32 bg_color = CONFIG_DRM_PANIC_BACKGROUND_COLOR; | ||||||
|  | 	const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); | ||||||
|  | 	struct drm_rect r_logo, r_msg; | ||||||
|  | 
 | ||||||
|  | 	if (!font) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	r_logo = DRM_RECT_INIT(0, 0, | ||||||
|  | 			       get_max_line_len(logo, logo_lines) * font->width, | ||||||
|  | 			       logo_lines * font->height); | ||||||
|  | 	r_msg = DRM_RECT_INIT(0, 0, | ||||||
|  | 			      min(get_max_line_len(panic_msg, msg_lines) * font->width, sb->width), | ||||||
|  | 			      min(msg_lines * font->height, sb->height)); | ||||||
|  | 
 | ||||||
|  | 	/* Center the panic message */ | ||||||
|  | 	drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, (sb->height - r_msg.y2) / 2); | ||||||
|  | 
 | ||||||
|  | 	/* Fill with the background color, and draw text on top */ | ||||||
|  | 	drm_panic_fill32(&sb->map[0], sb->pitch[0], sb->height, sb->width, bg_color); | ||||||
|  | 
 | ||||||
|  | 	if ((r_msg.x1 >= drm_rect_width(&r_logo) || r_msg.y1 >= drm_rect_height(&r_logo)) && | ||||||
|  | 	    drm_rect_width(&r_logo) < sb->width && drm_rect_height(&r_logo) < sb->height) { | ||||||
|  | 		draw_txt_rectangle(sb, font, logo, logo_lines, false, &r_logo, fg_color, bg_color); | ||||||
|  | 	} | ||||||
|  | 	draw_txt_rectangle(sb, font, panic_msg, msg_lines, true, &r_msg, fg_color, bg_color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * drm_panic_is_format_supported() | ||||||
|  |  * @format: a fourcc color code | ||||||
|  |  * Returns: true if supported, false otherwise. | ||||||
|  |  * | ||||||
|  |  * Check if drm_panic will be able to use this color format. | ||||||
|  |  */ | ||||||
|  | static bool drm_panic_is_format_supported(const struct drm_format_info *format) | ||||||
|  | { | ||||||
|  | 	if (format->num_planes != 1) | ||||||
|  | 		return false; | ||||||
|  | 	return format->format == DRM_FORMAT_XRGB8888; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void draw_panic_plane(struct drm_plane *plane) | ||||||
|  | { | ||||||
|  | 	struct drm_scanout_buffer sb; | ||||||
|  | 	int ret; | ||||||
|  | 	unsigned long flags; | ||||||
|  | 
 | ||||||
|  | 	if (!drm_panic_trylock(plane->dev, flags)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	ret = plane->helper_private->get_scanout_buffer(plane, &sb); | ||||||
|  | 
 | ||||||
|  | 	if (!ret && drm_panic_is_format_supported(sb.format)) { | ||||||
|  | 		draw_panic_static(&sb); | ||||||
|  | 		if (plane->helper_private->panic_flush) | ||||||
|  | 			plane->helper_private->panic_flush(plane); | ||||||
|  | 	} | ||||||
|  | 	drm_panic_unlock(plane->dev, flags); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct drm_plane *to_drm_plane(struct kmsg_dumper *kd) | ||||||
|  | { | ||||||
|  | 	return container_of(kd, struct drm_plane, kmsg_panic); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void drm_panic(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) | ||||||
|  | { | ||||||
|  | 	struct drm_plane *plane = to_drm_plane(dumper); | ||||||
|  | 
 | ||||||
|  | 	if (reason == KMSG_DUMP_PANIC) | ||||||
|  | 		draw_panic_plane(plane); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * drm_panic_register() - Initialize DRM panic for a device | ||||||
|  |  * @dev: the drm device on which the panic screen will be displayed. | ||||||
|  |  */ | ||||||
|  | void drm_panic_register(struct drm_device *dev) | ||||||
|  | { | ||||||
|  | 	struct drm_plane *plane; | ||||||
|  | 	int registered_plane = 0; | ||||||
|  | 
 | ||||||
|  | 	if (!dev->mode_config.num_total_plane) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	drm_for_each_plane(plane, dev) { | ||||||
|  | 		if (!plane->helper_private || !plane->helper_private->get_scanout_buffer) | ||||||
|  | 			continue; | ||||||
|  | 		plane->kmsg_panic.dump = drm_panic; | ||||||
|  | 		plane->kmsg_panic.max_reason = KMSG_DUMP_PANIC; | ||||||
|  | 		if (kmsg_dump_register(&plane->kmsg_panic)) | ||||||
|  | 			drm_warn(dev, "Failed to register panic handler\n"); | ||||||
|  | 		else | ||||||
|  | 			registered_plane++; | ||||||
|  | 	} | ||||||
|  | 	if (registered_plane) | ||||||
|  | 		drm_info(dev, "Registered %d planes with drm panic\n", registered_plane); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(drm_panic_register); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * drm_panic_unregister() | ||||||
|  |  * @dev: the drm device previously registered. | ||||||
|  |  */ | ||||||
|  | void drm_panic_unregister(struct drm_device *dev) | ||||||
|  | { | ||||||
|  | 	struct drm_plane *plane; | ||||||
|  | 
 | ||||||
|  | 	if (!dev->mode_config.num_total_plane) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	drm_for_each_plane(plane, dev) { | ||||||
|  | 		if (!plane->helper_private || !plane->helper_private->get_scanout_buffer) | ||||||
|  | 			continue; | ||||||
|  | 		kmsg_dump_unregister(&plane->kmsg_panic); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(drm_panic_unregister); | ||||||
|  | @ -48,6 +48,7 @@ | ||||||
|  * To make this clear all the helper vtables are pulled together in this location here. |  * To make this clear all the helper vtables are pulled together in this location here. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | struct drm_scanout_buffer; | ||||||
| struct drm_writeback_connector; | struct drm_writeback_connector; | ||||||
| struct drm_writeback_job; | struct drm_writeback_job; | ||||||
| 
 | 
 | ||||||
|  | @ -1443,6 +1444,44 @@ struct drm_plane_helper_funcs { | ||||||
| 	 */ | 	 */ | ||||||
| 	void (*atomic_async_update)(struct drm_plane *plane, | 	void (*atomic_async_update)(struct drm_plane *plane, | ||||||
| 				    struct drm_atomic_state *state); | 				    struct drm_atomic_state *state); | ||||||
|  | 
 | ||||||
|  | 	/**
 | ||||||
|  | 	 * @get_scanout_buffer: | ||||||
|  | 	 * | ||||||
|  | 	 * Get the current scanout buffer, to display a message with drm_panic. | ||||||
|  | 	 * The driver should do the minimum changes to provide a buffer, | ||||||
|  | 	 * that can be used to display the panic screen. Currently only linear | ||||||
|  | 	 * buffers are supported. Non-linear buffer support is on the TODO list. | ||||||
|  | 	 * The device &dev.mode_config.panic_lock is taken before calling this | ||||||
|  | 	 * function, so you can safely access the &plane.state | ||||||
|  | 	 * It is called from a panic callback, and must follow its restrictions. | ||||||
|  | 	 * Please look the documentation at drm_panic_trylock() for an in-depth | ||||||
|  | 	 * discussions of what's safe and what is not allowed. | ||||||
|  | 	 * It's a best effort mode, so it's expected that in some complex cases | ||||||
|  | 	 * the panic screen won't be displayed. | ||||||
|  | 	 * The returned &drm_scanout_buffer.map must be valid if no error code is | ||||||
|  | 	 * returned. | ||||||
|  | 	 * | ||||||
|  | 	 * Return: | ||||||
|  | 	 * %0 on success, negative errno on failure. | ||||||
|  | 	 */ | ||||||
|  | 	int (*get_scanout_buffer)(struct drm_plane *plane, | ||||||
|  | 				  struct drm_scanout_buffer *sb); | ||||||
|  | 
 | ||||||
|  | 	/**
 | ||||||
|  | 	 * @panic_flush: | ||||||
|  | 	 * | ||||||
|  | 	 * It is used by drm_panic, and is called after the panic screen is | ||||||
|  | 	 * drawn to the scanout buffer. In this function, the driver | ||||||
|  | 	 * can send additional commands to the hardware, to make the scanout | ||||||
|  | 	 * buffer visible. | ||||||
|  | 	 * It is only called if get_scanout_buffer() returned successfully, and | ||||||
|  | 	 * the &dev.mode_config.panic_lock is held during the entire sequence. | ||||||
|  | 	 * It is called from a panic callback, and must follow its restrictions. | ||||||
|  | 	 * Please look the documentation at drm_panic_trylock() for an in-depth | ||||||
|  | 	 * discussions of what's safe and what is not allowed. | ||||||
|  | 	 */ | ||||||
|  | 	void (*panic_flush)(struct drm_plane *plane); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -2,11 +2,56 @@ | ||||||
| #ifndef __DRM_PANIC_H__ | #ifndef __DRM_PANIC_H__ | ||||||
| #define __DRM_PANIC_H__ | #define __DRM_PANIC_H__ | ||||||
| 
 | 
 | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/iosys-map.h> | ||||||
|  | 
 | ||||||
| #include <drm/drm_device.h> | #include <drm/drm_device.h> | ||||||
|  | #include <drm/drm_fourcc.h> | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2024 Intel |  * Copyright (c) 2024 Intel | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct drm_scanout_buffer - DRM scanout buffer | ||||||
|  |  * | ||||||
|  |  * This structure holds the information necessary for drm_panic to draw the | ||||||
|  |  * panic screen, and display it. | ||||||
|  |  */ | ||||||
|  | struct drm_scanout_buffer { | ||||||
|  | 	/**
 | ||||||
|  | 	 * @format: | ||||||
|  | 	 * | ||||||
|  | 	 * drm format of the scanout buffer. | ||||||
|  | 	 */ | ||||||
|  | 	const struct drm_format_info *format; | ||||||
|  | 
 | ||||||
|  | 	/**
 | ||||||
|  | 	 * @map: | ||||||
|  | 	 * | ||||||
|  | 	 * Virtual address of the scanout buffer, either in memory or iomem. | ||||||
|  | 	 * The scanout buffer should be in linear format, and can be directly | ||||||
|  | 	 * sent to the display hardware. Tearing is not an issue for the panic | ||||||
|  | 	 * screen. | ||||||
|  | 	 */ | ||||||
|  | 	struct iosys_map map[DRM_FORMAT_MAX_PLANES]; | ||||||
|  | 
 | ||||||
|  | 	/**
 | ||||||
|  | 	 * @width: Width of the scanout buffer, in pixels. | ||||||
|  | 	 */ | ||||||
|  | 	unsigned int width; | ||||||
|  | 
 | ||||||
|  | 	/**
 | ||||||
|  | 	 * @height: Height of the scanout buffer, in pixels. | ||||||
|  | 	 */ | ||||||
|  | 	unsigned int height; | ||||||
|  | 
 | ||||||
|  | 	/**
 | ||||||
|  | 	 * @pitch: Length in bytes between the start of two consecutive lines. | ||||||
|  | 	 */ | ||||||
|  | 	unsigned int pitch[DRM_FORMAT_MAX_PLANES]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * drm_panic_trylock - try to enter the panic printing critical section |  * drm_panic_trylock - try to enter the panic printing critical section | ||||||
|  * @dev: struct drm_device |  * @dev: struct drm_device | ||||||
|  | @ -92,4 +137,16 @@ | ||||||
| #define drm_panic_unlock(dev, flags) \ | #define drm_panic_unlock(dev, flags) \ | ||||||
| 	raw_spin_unlock_irqrestore(&(dev)->mode_config.panic_lock, flags) | 	raw_spin_unlock_irqrestore(&(dev)->mode_config.panic_lock, flags) | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_DRM_PANIC | ||||||
|  | 
 | ||||||
|  | void drm_panic_register(struct drm_device *dev); | ||||||
|  | void drm_panic_unregister(struct drm_device *dev); | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | static inline void drm_panic_register(struct drm_device *dev) {} | ||||||
|  | static inline void drm_panic_unregister(struct drm_device *dev) {} | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #endif /* __DRM_PANIC_H__ */ | #endif /* __DRM_PANIC_H__ */ | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <linux/list.h> | #include <linux/list.h> | ||||||
| #include <linux/ctype.h> | #include <linux/ctype.h> | ||||||
|  | #include <linux/kmsg_dump.h> | ||||||
| #include <drm/drm_mode_object.h> | #include <drm/drm_mode_object.h> | ||||||
| #include <drm/drm_color_mgmt.h> | #include <drm/drm_color_mgmt.h> | ||||||
| #include <drm/drm_rect.h> | #include <drm/drm_rect.h> | ||||||
|  | @ -780,6 +781,11 @@ struct drm_plane { | ||||||
| 	 * @hotspot_y_property: property to set mouse hotspot y offset. | 	 * @hotspot_y_property: property to set mouse hotspot y offset. | ||||||
| 	 */ | 	 */ | ||||||
| 	struct drm_property *hotspot_y_property; | 	struct drm_property *hotspot_y_property; | ||||||
|  | 
 | ||||||
|  | 	/**
 | ||||||
|  | 	 * @kmsg_panic: Used to register a panic notifier for this plane | ||||||
|  | 	 */ | ||||||
|  | 	struct kmsg_dumper kmsg_panic; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define obj_to_plane(x) container_of(x, struct drm_plane, base) | #define obj_to_plane(x) container_of(x, struct drm_plane, base) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jocelyn Falempe
						Jocelyn Falempe