xref: /linux/drivers/gpu/drm/i915/gem/i915_gem_panic.c (revision 4e69c1856bfd9ffb7e9d335a25842fa211628929)
1 // SPDX-License-Identifier: MIT
2 /* Copyright © 2026 Intel Corporation */
3 
4 #include <drm/drm_cache.h>
5 #include <drm/drm_panic.h>
6 #include <drm/intel/display_parent_interface.h>
7 
8 #include "i915_gem_object.h"
9 #include "i915_gem_panic.h"
10 
11 struct intel_panic {
12 	struct page **pages;
13 	int page;
14 	void *vaddr;
15 
16 	unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width);
17 };
18 
19 static void i915_panic_kunmap(struct intel_panic *panic)
20 {
21 	if (panic->vaddr) {
22 		drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
23 		kunmap_local(panic->vaddr);
24 		panic->vaddr = NULL;
25 	}
26 }
27 
28 static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj)
29 {
30 	unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i;
31 	struct page *page;
32 	struct page **pages;
33 	struct sgt_iter iter;
34 
35 	/* For a 3840x2160 32 bits Framebuffer, this should require ~64K */
36 	pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC);
37 	if (!pages)
38 		return NULL;
39 
40 	i = 0;
41 	for_each_sgt_page(page, iter, obj->mm.pages)
42 		pages[i++] = page;
43 	return pages;
44 }
45 
46 static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
47 						unsigned int y, u32 color)
48 {
49 	struct intel_panic *panic = sb->private;
50 	unsigned int offset = panic->tiling(sb->width, x, y);
51 
52 	iosys_map_wr(&sb->map[0], offset, u32, color);
53 }
54 
55 /*
56  * The scanout buffer pages are not mapped, so for each pixel,
57  * use kmap_local_page_try_from_panic() to map the page, and write the pixel.
58  * Try to keep the map from the previous pixel, to avoid too much map/unmap.
59  */
60 static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
61 						 unsigned int y, u32 color)
62 {
63 	struct intel_panic *panic = sb->private;
64 	unsigned int new_page;
65 	unsigned int offset;
66 
67 	if (panic->tiling)
68 		offset = panic->tiling(sb->width, x, y);
69 	else
70 		offset = y * sb->pitch[0] + x * sb->format->cpp[0];
71 
72 	new_page = offset >> PAGE_SHIFT;
73 	offset = offset % PAGE_SIZE;
74 	if (new_page != panic->page) {
75 		i915_panic_kunmap(panic);
76 		panic->page = new_page;
77 		panic->vaddr =
78 			kmap_local_page_try_from_panic(panic->pages[panic->page]);
79 	}
80 	if (panic->vaddr) {
81 		u32 *pix = panic->vaddr + offset;
82 		*pix = color;
83 	}
84 }
85 
86 static struct intel_panic *i915_gem_object_alloc_panic(void)
87 {
88 	struct intel_panic *panic;
89 
90 	panic = kzalloc_obj(*panic);
91 
92 	return panic;
93 }
94 
95 /*
96  * Setup the gem framebuffer for drm_panic access.
97  * Use current vaddr if it exists, or setup a list of pages.
98  * pfn is not supported yet.
99  */
100 static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
101 				       struct drm_gem_object *_obj,
102 				       unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width))
103 {
104 	enum i915_map_type has_type;
105 	struct drm_i915_gem_object *obj = to_intel_bo(_obj);
106 	void *ptr;
107 
108 	sb->private = panic;
109 
110 	ptr = page_unpack_bits(obj->mm.mapping, &has_type);
111 	if (ptr) {
112 		if (i915_gem_object_has_iomem(obj))
113 			iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr);
114 		else
115 			iosys_map_set_vaddr(&sb->map[0], ptr);
116 
117 		if (tiling) {
118 			panic->tiling = tiling;
119 			sb->set_pixel = i915_gem_object_panic_map_set_pixel;
120 		}
121 		return 0;
122 	}
123 	if (i915_gem_object_has_struct_page(obj)) {
124 		panic->pages = i915_gem_object_panic_pages(obj);
125 		if (!panic->pages)
126 			return -ENOMEM;
127 		panic->page = -1;
128 		panic->tiling = tiling;
129 		sb->set_pixel = i915_gem_object_panic_page_set_pixel;
130 		return 0;
131 	}
132 	return -EOPNOTSUPP;
133 }
134 
135 static void i915_gem_object_panic_finish(struct intel_panic *panic)
136 {
137 	i915_panic_kunmap(panic);
138 	panic->page = -1;
139 	kfree(panic->pages);
140 	panic->pages = NULL;
141 }
142 
143 const struct intel_display_panic_interface i915_display_panic_interface = {
144 	.alloc = i915_gem_object_alloc_panic,
145 	.setup = i915_gem_object_panic_setup,
146 	.finish = i915_gem_object_panic_finish,
147 };
148