// SPDX-License-Identifier: MIT /* Copyright © 2026 Intel Corporation */ #include #include #include #include "i915_gem_object.h" #include "i915_gem_panic.h" struct intel_panic { struct page **pages; int page; void *vaddr; unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); }; static void i915_panic_kunmap(struct intel_panic *panic) { if (panic->vaddr) { drm_clflush_virt_range(panic->vaddr, PAGE_SIZE); kunmap_local(panic->vaddr); panic->vaddr = NULL; } } static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj) { unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i; struct page *page; struct page **pages; struct sgt_iter iter; /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */ pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC); if (!pages) return NULL; i = 0; for_each_sgt_page(page, iter, obj->mm.pages) pages[i++] = page; return pages; } static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { struct intel_panic *panic = sb->private; unsigned int offset = panic->tiling(sb->width, x, y); iosys_map_wr(&sb->map[0], offset, u32, color); } /* * The scanout buffer pages are not mapped, so for each pixel, * use kmap_local_page_try_from_panic() to map the page, and write the pixel. * Try to keep the map from the previous pixel, to avoid too much map/unmap. */ static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { struct intel_panic *panic = sb->private; unsigned int new_page; unsigned int offset; if (panic->tiling) offset = panic->tiling(sb->width, x, y); else offset = y * sb->pitch[0] + x * sb->format->cpp[0]; new_page = offset >> PAGE_SHIFT; offset = offset % PAGE_SIZE; if (new_page != panic->page) { i915_panic_kunmap(panic); panic->page = new_page; panic->vaddr = kmap_local_page_try_from_panic(panic->pages[panic->page]); } if (panic->vaddr) { u32 *pix = panic->vaddr + offset; *pix = color; } } static struct intel_panic *i915_gem_object_alloc_panic(void) { struct intel_panic *panic; panic = kzalloc_obj(*panic); return panic; } /* * Setup the gem framebuffer for drm_panic access. * Use current vaddr if it exists, or setup a list of pages. * pfn is not supported yet. */ static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, struct drm_gem_object *_obj, unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { enum i915_map_type has_type; struct drm_i915_gem_object *obj = to_intel_bo(_obj); void *ptr; sb->private = panic; ptr = page_unpack_bits(obj->mm.mapping, &has_type); if (ptr) { if (i915_gem_object_has_iomem(obj)) iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr); else iosys_map_set_vaddr(&sb->map[0], ptr); if (tiling) { panic->tiling = tiling; sb->set_pixel = i915_gem_object_panic_map_set_pixel; } return 0; } if (i915_gem_object_has_struct_page(obj)) { panic->pages = i915_gem_object_panic_pages(obj); if (!panic->pages) return -ENOMEM; panic->page = -1; panic->tiling = tiling; sb->set_pixel = i915_gem_object_panic_page_set_pixel; return 0; } return -EOPNOTSUPP; } static void i915_gem_object_panic_finish(struct intel_panic *panic) { i915_panic_kunmap(panic); panic->page = -1; kfree(panic->pages); panic->pages = NULL; } const struct intel_display_panic_interface i915_display_panic_interface = { .alloc = i915_gem_object_alloc_panic, .setup = i915_gem_object_panic_setup, .finish = i915_gem_object_panic_finish, };