diff options
Diffstat (limited to 'drivers/gpu/drm/drm_panic.c')
-rw-r--r-- | drivers/gpu/drm/drm_panic.c | 149 |
1 files changed, 137 insertions, 12 deletions
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index f128d345b16d..b4de79583805 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -7,6 +7,7 @@ */ #include <linux/font.h> +#include <linux/highmem.h> #include <linux/init.h> #include <linux/iosys-map.h> #include <linux/kdebug.h> @@ -154,6 +155,90 @@ static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color); } +static void drm_panic_write_pixel16(void *vaddr, unsigned int offset, u16 color) +{ + u16 *p = vaddr + offset; + + *p = color; +} + +static void drm_panic_write_pixel24(void *vaddr, unsigned int offset, u32 color) +{ + u8 *p = vaddr + offset; + + *p++ = color & 0xff; + color >>= 8; + *p++ = color & 0xff; + color >>= 8; + *p = color & 0xff; +} + +static void drm_panic_write_pixel32(void *vaddr, unsigned int offset, u32 color) +{ + u32 *p = vaddr + offset; + + *p = color; +} + +static void drm_panic_write_pixel(void *vaddr, unsigned int offset, u32 color, unsigned int cpp) +{ + switch (cpp) { + case 2: + drm_panic_write_pixel16(vaddr, offset, color); + break; + case 3: + drm_panic_write_pixel24(vaddr, offset, color); + break; + case 4: + drm_panic_write_pixel32(vaddr, offset, color); + break; + default: + pr_debug_once("Can't blit with pixel width %d\n", cpp); + } +} + +/* + * 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 drm_panic_blit_page(struct page **pages, unsigned int dpitch, + unsigned int cpp, const u8 *sbuf8, + unsigned int spitch, struct drm_rect *clip, + unsigned int scale, u32 fg32) +{ + unsigned int y, x; + unsigned int page = ~0; + unsigned int height = drm_rect_height(clip); + unsigned int width = drm_rect_width(clip); + void *vaddr = NULL; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) { + unsigned int new_page; + unsigned int offset; + + offset = (y + clip->y1) * dpitch + (x + clip->x1) * cpp; + new_page = offset >> PAGE_SHIFT; + offset = offset % PAGE_SIZE; + if (new_page != page) { + if (!pages[new_page]) + continue; + if (vaddr) + kunmap_local(vaddr); + page = new_page; + vaddr = kmap_local_page_try_from_panic(pages[page]); + } + if (vaddr) + drm_panic_write_pixel(vaddr, offset, fg32, cpp); + } + } + } + if (vaddr) + kunmap_local(vaddr); +} + /* * drm_panic_blit - convert a monochrome image to a linear framebuffer * @sb: destination scanout buffer @@ -177,6 +262,10 @@ static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip, if (sb->set_pixel) return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, scale, fg_color); + if (sb->pages) + return drm_panic_blit_page(sb->pages, sb->pitch[0], sb->format->cpp[0], + sbuf8, spitch, clip, scale, fg_color); + map = sb->map[0]; iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]); @@ -209,6 +298,35 @@ static void drm_panic_fill_pixel(struct drm_scanout_buffer *sb, sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, color); } +static void drm_panic_fill_page(struct page **pages, unsigned int dpitch, + unsigned int cpp, struct drm_rect *clip, + u32 color) +{ + unsigned int y, x; + unsigned int page = ~0; + void *vaddr = NULL; + + for (y = clip->y1; y < clip->y2; y++) { + for (x = clip->x1; x < clip->x2; x++) { + unsigned int new_page; + unsigned int offset; + + offset = y * dpitch + x * cpp; + new_page = offset >> PAGE_SHIFT; + offset = offset % PAGE_SIZE; + if (new_page != page) { + if (vaddr) + kunmap_local(vaddr); + page = new_page; + vaddr = kmap_local_page_try_from_panic(pages[page]); + } + drm_panic_write_pixel(vaddr, offset, color, cpp); + } + } + if (vaddr) + kunmap_local(vaddr); +} + /* * drm_panic_fill - Fill a rectangle with a color * @sb: destination scanout buffer @@ -225,6 +343,10 @@ static void drm_panic_fill(struct drm_scanout_buffer *sb, struct drm_rect *clip, if (sb->set_pixel) return drm_panic_fill_pixel(sb, clip, color); + if (sb->pages) + return drm_panic_fill_page(sb->pages, sb->pitch[0], sb->format->cpp[0], + clip, color); + map = sb->map[0]; iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]); @@ -486,11 +608,6 @@ static void drm_panic_qr_exit(void) 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; @@ -499,7 +616,7 @@ static int drm_panic_get_qr_code_url(u8 **qr_image) 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=", + url_len = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&z=", utsname()->machine, utsname()->release); max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len); @@ -714,16 +831,24 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description) 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)) + goto unlock; + + /* One of these should be set, or it can't draw pixels */ + if (!sb.set_pixel && !sb.pages && iosys_map_is_null(&sb.map[0])) + goto unlock; + drm_panic_set_description(description); - ret = plane->helper_private->get_scanout_buffer(plane, &sb); + draw_panic_dispatch(&sb); + if (plane->helper_private->panic_flush) + plane->helper_private->panic_flush(plane); - if (!ret && drm_panic_is_format_supported(sb.format)) { - draw_panic_dispatch(&sb); - if (plane->helper_private->panic_flush) - plane->helper_private->panic_flush(plane); - } drm_panic_clear_description(); + +unlock: drm_panic_unlock(plane->dev, flags); } |