| // SPDX-License-Identifier: MIT |
| /* Copyright © 2026 Intel Corporation */ |
| |
| #include <drm/drm_cache.h> |
| #include <drm/drm_panic.h> |
| #include <drm/intel/display_parent_interface.h> |
| |
| #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, |
| }; |