summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_panic.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-09-19 10:18:15 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2024-09-19 10:18:15 +0200
commitde848da12f752170c2ebe114804a985314fd5a6a (patch)
tree039a686b062cfb5c1188456f228a040e04b7cdd0 /drivers/gpu/drm/drm_panic.c
parent176000734ee2978121fde22a954eb1eabb204329 (diff)
parentae2c6d8b3b88c176dff92028941a4023f1b4cb91 (diff)
Merge tag 'drm-next-2024-09-19' of https://gitlab.freedesktop.org/drm/kernel
Pull drm updates from Dave Airlie: "This adds a couple of patches outside the drm core, all should be acked appropriately, the string and pstore ones are the main ones that come to mind. Otherwise it's the usual drivers, xe is getting enabled by default on some new hardware, we've changed the device number handling to allow more devices, and we added some optional rust code to create QR codes in the panic handler, an idea first suggested I think 10 years ago :-) string: - add mem_is_zero() core: - support more device numbers - use XArray for minor ids - add backlight constants - Split dma fence array creation into alloc and arm fbdev: - remove usage of old fbdev hooks kms: - Add might_fault() to drm_modeset_lock priming - Add dynamic per-crtc vblank configuration support dma-buf: - docs cleanup buddy: - Add start address support for trim function printk: - pass description to kmsg_dump scheduler: - Remove full_recover from drm_sched_start ttm: - Make LRU walk restartable after dropping locks - Allow direct reclaim to allocate local memory panic: - add display QR code (in rust) displayport: - mst: GUID improvements bridge: - Silence error message on -EPROBE_DEFER - analogix: Clean aup - bridge-connector: Fix double free - lt6505: Disable interrupt when powered off - tc358767: Make default DP port preemphasis configurable - lt9611uxc: require DRM_BRIDGE_ATTACH_NO_CONNECTOR - anx7625: simplify OF array handling - dw-hdmi: simplify clock handling - lontium-lt8912b: fix mode validation - nwl-dsi: fix mode vsync/hsync polarity xe: - Enable LunarLake and Battlemage support - Introducing Xe2 ccs modifiers for integrated and discrete graphics - rename xe perf to xe observation - use wb caching on DGFX for system memory - add fence timeouts - Lunar Lake graphics/media/display workarounds - Battlemage workarounds - Battlemage GSC support - GSC and HuC fw updates for LL/BM - use dma_fence_chain_free - refactor hw engine lookup and mmio access - enable priority mem read for Xe2 - Add first GuC BMG fw - fix dma-resv lock - Fix DGFX display suspend/resume - Use xe_managed for kernel BOs - Use reserved copy engine for user binds on faulting devices - Allow mixing dma-fence jobs and long-running faulting jobs - fix media TLB invalidation - fix rpm in TTM swapout path - track resources and VF state by PF i915: - Type-C programming fix for MTL+ - FBC cleanup - Calc vblank delay more accurately - On DP MST, Enable LT fallback for UHBR<->non-UHBR rates - Fix DP LTTPR detection - limit relocations to INT_MAX - fix long hangs in buddy allocator on DG2/A380 amdgpu: - Per-queue reset support - SDMA devcoredump support - DCN 4.0.1 updates - GFX12/VCN4/JPEG4 updates - Convert vbios embedded EDID to drm_edid - GFX9.3/9.4 devcoredump support - process isolation framework for GFX 9.4.3/4 - take IOMMU mappings into account for P2P DMA amdkfd: - CRIU fixes - HMM fix - Enable process isolation support for GFX 9.4.3/4 - Allow users to target recommended SDMA engines - KFD support for targetting queues on recommended SDMA engines radeon: - remove .load and drm_dev_alloc - Fix vbios embedded EDID size handling - Convert vbios embedded EDID to drm_edid - Use GEM references instead of TTM - r100 cp init cleanup - Fix potential overflows in evergreen CS offset tracking msm: - DPU: - implement DP/PHY mapping on SC8180X - Enable writeback on SM8150, SC8180X, SM6125, SM6350 - DP: - Enable widebus on all relevant chipsets - MSM8998 HDMI support - GPU: - A642L speedbin support - A615/A306/A621 support - A7xx devcoredump support ast: - astdp: Support AST2600 with VGA - Clean up HPD - Fix timeout loop for DP link training - reorganize output code by type (VGA, DP, etc) - convert to struct drm_edid - fix BMC handling for all outputs exynos: - drop stale MAINTAINERS pattern - constify struct loongson: - use GEM refcount over TTM mgag200: - Improve BMC handling - Support VBLANK intterupts - transparently support BMC outputs nouveau: - Refactor and clean up internals - Use GEM refcount over TTM's gm12u320: - convert to struct drm_edid gma500: - update i2c terms lcdif: - pixel clock fix host1x: - fix syncpoint IRQ during resume - use iommu_paging_domain_alloc() imx: - ipuv3: convert to struct drm_edid omapdrm: - improve error handling - use common helper for_each_endpoint_of_node() panel: - add support for BOE TV101WUM-LL2 plus DT bindings - novatek-nt35950: improve error handling - nv3051d: improve error handling - panel-edp: - add support for BOE NE140WUM-N6G - revert support for SDC ATNA45AF01 - visionox-vtdr6130: - improve error handling - use devm_regulator_bulk_get_const() - boe-th101mb31ig002: - Support for starry-er88577 MIPI-DSI panel plus DT - Fix porch parameter - edp: Support AOU B116XTN02.3, AUO B116XAN06.1, AOU B116XAT04.1, BOE NV140WUM-N41, BOE NV133WUM-N63, BOE NV116WHM-A4D, CMN N116BCA-EA2, CMN N116BCP-EA2, CSW MNB601LS1-4 - himax-hx8394: Support Microchip AC40T08A MIPI Display panel plus DT - ilitek-ili9806e: Support Densitron DMT028VGHMCMI-1D TFT plus DT - jd9365da: - Support Melfas lmfbx101117480 MIPI-DSI panel plus DT - Refactor for code sharing - panel-edp: fix name for HKC MB116AN01 - jd9365da: fix "exit sleep" commands - jdi-fhd-r63452: simplify error handling with DSI multi-style helpers - mantix-mlaf057we51: simplify error handling with DSI multi-style helpers - simple: - support Innolux G070ACE-LH3 plus DT bindings - support On Tat Industrial Company KD50G21-40NT-A1 plus DT bindings - st7701: - decouple DSI and DRM code - add SPI support - support Anbernic RG28XX plus DT bindings mediatek: - support alpha blending - remove cl in struct cmdq_pkt - ovl adaptor fix - add power domain binding for mediatek DPI controller renesas: - rz-du: add support for RZ/G2UL plus DT bindings rockchip: - Improve DP sink-capability reporting - dw_hdmi: Support 4k@60Hz - vop: - Support RGB display on Rockchip RK3066 - Support 4096px width sti: - convert to struct drm_edid stm: - Avoid UAF wih managed plane and CRTC helpers - Fix module owner - Fix error handling in probe - Depend on COMMON_CLK - ltdc: - Fix transparency after disabling plane - Remove unused interrupt tegra: - gr3d: improve PM domain handling - convert to struct drm_edid - Call drm_atomic_helper_shutdown() vc4: - fix PM during detect - replace DRM_ERROR() with drm_error() - v3d: simplify clock retrieval v3d: - Clean up perfmon virtio: - add DRM capset" * tag 'drm-next-2024-09-19' of https://gitlab.freedesktop.org/drm/kernel: (1326 commits) drm/xe: Fix missing conversion to xe_display_pm_runtime_resume drm/xe/xe2hpg: Add Wa_15016589081 drm/xe: Don't keep stale pointer to bo->ggtt_node drm/xe: fix missing 'xe_vm_put' drm/xe: fix build warning with CONFIG_PM=n drm/xe: Suppress missing outer rpm protection warning drm/xe: prevent potential UAF in pf_provision_vf_ggtt() drm/amd/display: Add all planes on CRTC to state for overlay cursor drm/i915/bios: fix printk format width drm/i915/display: Fix BMG CCS modifiers drm/amdgpu: get rid of bogus includes of fdtable.h drm/amdkfd: CRIU fixes drm/amdgpu: fix a race in kfd_mem_export_dmabuf() drm: new helper: drm_gem_prime_handle_to_dmabuf() drm/amdgpu/atomfirmware: Silence UBSAN warning drm/amdgpu: Fix kdoc entry in 'amdgpu_vm_cpu_prepare' drm/amd/amdgpu: apply command submission parser for JPEG v1 drm/amd/amdgpu: apply command submission parser for JPEG v2+ drm/amd/pm: fix the pp_dpm_pcie issue on smu v14.0.2/3 drm/amd/pm: update the features set on smu v14.0.2/3 ...
Diffstat (limited to 'drivers/gpu/drm/drm_panic.c')
-rw-r--r--drivers/gpu/drm/drm_panic.c406
1 files changed, 359 insertions, 47 deletions
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 948aed00595e..74412b7bf936 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -18,6 +18,8 @@
#include <linux/overflow.h>
#include <linux/printk.h>
#include <linux/types.h>
+#include <linux/utsname.h>
+#include <linux/zlib.h>
#include <drm/drm_drv.h>
#include <drm/drm_fourcc.h>
@@ -26,6 +28,9 @@
#include <drm/drm_panic.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
+#include <drm/drm_rect.h>
+
+#include "drm_crtc_internal.h"
MODULE_AUTHOR("Jocelyn Falempe");
MODULE_DESCRIPTION("DRM panic handler");
@@ -76,11 +81,15 @@ struct drm_panic_line {
#define PANIC_LINE(s) {.len = sizeof(s) - 1, .txt = s}
static struct drm_panic_line panic_msg[] = {
- PANIC_LINE("KERNEL PANIC !"),
+ PANIC_LINE("KERNEL PANIC!"),
PANIC_LINE(""),
PANIC_LINE("Please reboot your computer."),
+ PANIC_LINE(""),
+ PANIC_LINE(""), /* will be replaced by the panic description */
};
+static const size_t panic_msg_lines = ARRAY_SIZE(panic_msg);
+
static const struct drm_panic_line logo_ascii[] = {
PANIC_LINE(" .--. _"),
PANIC_LINE(" |o_o | | |"),
@@ -91,6 +100,8 @@ static const struct drm_panic_line logo_ascii[] = {
PANIC_LINE(" \\___)=(___/"),
};
+static const size_t logo_ascii_lines = ARRAY_SIZE(logo_ascii);
+
#if defined(CONFIG_LOGO) && !defined(MODULE)
static const struct linux_logo *logo_mono;
@@ -249,20 +260,20 @@ static bool drm_panic_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, int x, i
static void drm_panic_blit16(struct iosys_map *dmap, unsigned int dpitch,
const u8 *sbuf8, unsigned int spitch,
unsigned int height, unsigned int width,
- u16 fg16)
+ unsigned int scale, u16 fg16)
{
unsigned int y, x;
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
- if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y))
+ if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale))
iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, fg16);
}
static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch,
const u8 *sbuf8, unsigned int spitch,
unsigned int height, unsigned int width,
- u32 fg32)
+ unsigned int scale, u32 fg32)
{
unsigned int y, x;
@@ -270,7 +281,7 @@ static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch,
for (x = 0; x < width; x++) {
u32 off = y * dpitch + x * 3;
- if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) {
+ if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) {
/* write blue-green-red to output in little endianness */
iosys_map_wr(dmap, off, u8, (fg32 & 0x000000FF) >> 0);
iosys_map_wr(dmap, off + 1, u8, (fg32 & 0x0000FF00) >> 8);
@@ -283,24 +294,25 @@ static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch,
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)
+ unsigned int scale, u32 fg32)
{
unsigned int y, x;
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
- if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y))
+ if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale))
iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, fg32);
}
static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect *clip,
- const u8 *sbuf8, unsigned int spitch, u32 fg_color)
+ const u8 *sbuf8, unsigned int spitch, unsigned int scale,
+ u32 fg_color)
{
unsigned int y, x;
for (y = 0; y < drm_rect_height(clip); y++)
for (x = 0; x < drm_rect_width(clip); x++)
- if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y))
+ if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale))
sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color);
}
@@ -310,18 +322,22 @@ static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect
* @clip: destination rectangle
* @sbuf8: source buffer, in monochrome format, 8 pixels per byte.
* @spitch: source pitch in bytes
+ * @scale: integer scale, source buffer is scale time smaller than destination
+ * rectangle
* @fg_color: foreground color, in destination format
*
* This can be used to draw a font character, which is a monochrome image, to a
* framebuffer in other supported format.
*/
static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip,
- const u8 *sbuf8, unsigned int spitch, u32 fg_color)
+ const u8 *sbuf8, unsigned int spitch,
+ unsigned int scale, u32 fg_color)
+
{
struct iosys_map map;
if (sb->set_pixel)
- return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, fg_color);
+ return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, scale, fg_color);
map = sb->map[0];
iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
@@ -329,15 +345,15 @@ static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip,
switch (sb->format->cpp[0]) {
case 2:
drm_panic_blit16(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), fg_color);
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
break;
case 3:
drm_panic_blit24(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), fg_color);
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
break;
case 4:
drm_panic_blit32(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), fg_color);
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
break;
default:
WARN_ONCE(1, "Can't blit with pixel width %d\n", sb->format->cpp[0]);
@@ -477,39 +493,51 @@ static void draw_txt_rectangle(struct drm_scanout_buffer *sb,
for (j = 0; j < line_len; j++) {
src = get_char_bitmap(font, msg[i].txt[j], font_pitch);
rec.x2 = rec.x1 + font->width;
- drm_panic_blit(sb, &rec, src, font_pitch, color);
+ drm_panic_blit(sb, &rec, src, font_pitch, 1, color);
rec.x1 += font->width;
}
}
}
+static void drm_panic_logo_rect(struct drm_rect *rect, const struct font_desc *font)
+{
+ if (logo_mono) {
+ drm_rect_init(rect, 0, 0, logo_mono->width, logo_mono->height);
+ } else {
+ int logo_width = get_max_line_len(logo_ascii, logo_ascii_lines) * font->width;
+
+ drm_rect_init(rect, 0, 0, logo_width, logo_ascii_lines * font->height);
+ }
+}
+
+static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *rect,
+ const struct font_desc *font, u32 fg_color)
+{
+ if (logo_mono)
+ drm_panic_blit(sb, rect, logo_mono->data,
+ DIV_ROUND_UP(drm_rect_width(rect), 8), 1, fg_color);
+ else
+ draw_txt_rectangle(sb, font, logo_ascii, logo_ascii_lines, false, rect,
+ fg_color);
+}
+
static void draw_panic_static_user(struct drm_scanout_buffer *sb)
{
- size_t msg_lines = ARRAY_SIZE(panic_msg);
- size_t logo_ascii_lines = ARRAY_SIZE(logo_ascii);
u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format);
u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format);
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
struct drm_rect r_screen, r_logo, r_msg;
- unsigned int logo_width, logo_height;
+ unsigned int msg_width, msg_height;
if (!font)
return;
r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
+ drm_panic_logo_rect(&r_logo, font);
- if (logo_mono) {
- logo_width = logo_mono->width;
- logo_height = logo_mono->height;
- } else {
- logo_width = get_max_line_len(logo_ascii, logo_ascii_lines) * font->width;
- logo_height = logo_ascii_lines * font->height;
- }
-
- r_logo = DRM_RECT_INIT(0, 0, logo_width, logo_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));
+ msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
+ msg_height = min(panic_msg_lines * font->height, sb->height);
+ r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
/* Center the panic message */
drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, (sb->height - r_msg.y2) / 2);
@@ -517,16 +545,10 @@ static void draw_panic_static_user(struct drm_scanout_buffer *sb)
/* Fill with the background color, and draw text on top */
drm_panic_fill(sb, &r_screen, bg_color);
- if ((r_msg.x1 >= logo_width || r_msg.y1 >= logo_height) &&
- logo_width <= sb->width && logo_height <= sb->height) {
- if (logo_mono)
- drm_panic_blit(sb, &r_logo, logo_mono->data, DIV_ROUND_UP(logo_width, 8),
- fg_color);
- else
- draw_txt_rectangle(sb, font, logo_ascii, logo_ascii_lines, false, &r_logo,
- fg_color);
- }
- draw_txt_rectangle(sb, font, panic_msg, msg_lines, true, &r_msg, fg_color);
+ if (!drm_rect_overlap(&r_logo, &r_msg))
+ drm_panic_logo_draw(sb, &r_logo, font, fg_color);
+
+ draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
}
/*
@@ -608,6 +630,233 @@ static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
}
}
+#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
+/*
+ * It is unwise to allocate memory in the panic callback, so the buffers are
+ * pre-allocated. Only 2 buffers and the zlib workspace are needed.
+ * Two buffers are enough, using the following buffer usage:
+ * 1) kmsg messages are dumped in buffer1
+ * 2) kmsg is zlib-compressed into buffer2
+ * 3) compressed kmsg is encoded as QR-code Numeric stream in buffer1
+ * 4) QR-code image is generated in buffer2
+ * The Max QR code size is V40, 177x177, 4071 bytes for image, 2956 bytes for
+ * data segments.
+ *
+ * Typically, ~7500 bytes of kmsg, are compressed into 2800 bytes, which fits in
+ * a V40 QR-code (177x177).
+ *
+ * If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data will be put
+ * directly in the QR code.
+ * 1) kmsg messages are dumped in buffer1
+ * 2) kmsg message is encoded as byte stream in buffer2
+ * 3) QR-code image is generated in buffer1
+ */
+
+static uint panic_qr_version = CONFIG_DRM_PANIC_SCREEN_QR_VERSION;
+module_param(panic_qr_version, uint, 0644);
+MODULE_PARM_DESC(panic_qr_version, "maximum version (size) of the QR code");
+
+#define MAX_QR_DATA 2956
+#define MAX_ZLIB_RATIO 3
+#define QR_BUFFER1_SIZE (MAX_ZLIB_RATIO * MAX_QR_DATA) /* Must also be > 4071 */
+#define QR_BUFFER2_SIZE 4096
+#define QR_MARGIN 4 /* 4 modules of foreground color around the qr code */
+
+/* Compression parameters */
+#define COMPR_LEVEL 6
+#define WINDOW_BITS 12
+#define MEM_LEVEL 4
+
+static char *qrbuf1;
+static char *qrbuf2;
+static struct z_stream_s stream;
+
+static void __init drm_panic_qr_init(void)
+{
+ qrbuf1 = kmalloc(QR_BUFFER1_SIZE, GFP_KERNEL);
+ qrbuf2 = kmalloc(QR_BUFFER2_SIZE, GFP_KERNEL);
+ stream.workspace = kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
+ GFP_KERNEL);
+}
+
+static void drm_panic_qr_exit(void)
+{
+ kfree(qrbuf1);
+ qrbuf1 = NULL;
+ kfree(qrbuf2);
+ qrbuf2 = NULL;
+ kfree(stream.workspace);
+ 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;
+ char url[256];
+ size_t kmsg_len, max_kmsg_size;
+ 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=",
+ utsname()->machine, utsname()->release);
+
+ max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len);
+ max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, QR_BUFFER1_SIZE);
+
+ /* get kmsg to buffer 1 */
+ kmsg_dump_rewind(&iter);
+ kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
+
+ if (!kmsg_len)
+ return -ENODATA;
+ kmsg = qrbuf1;
+
+try_again:
+ if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
+ MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
+ return -EINVAL;
+
+ stream.next_in = kmsg;
+ stream.avail_in = kmsg_len;
+ stream.total_in = 0;
+ stream.next_out = qrbuf2;
+ stream.avail_out = QR_BUFFER2_SIZE;
+ stream.total_out = 0;
+
+ if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END)
+ return -EINVAL;
+
+ if (zlib_deflateEnd(&stream) != Z_OK)
+ return -EINVAL;
+
+ if (stream.total_out > max_qr_data_size) {
+ /* too much data for the QR code, so skip the first line and try again */
+ kmsg = strchr(kmsg, '\n');
+ if (!kmsg)
+ return -EINVAL;
+ /* skip the first \n */
+ kmsg += 1;
+ kmsg_len = strlen(kmsg);
+ goto try_again;
+ }
+ *qr_image = qrbuf2;
+
+ /* generate qr code image in buffer2 */
+ return drm_panic_qr_generate(url, qrbuf2, stream.total_out, QR_BUFFER2_SIZE,
+ qrbuf1, QR_BUFFER1_SIZE);
+}
+
+static int drm_panic_get_qr_code_raw(u8 **qr_image)
+{
+ struct kmsg_dump_iter iter;
+ size_t kmsg_len;
+ size_t max_kmsg_size = min(drm_panic_qr_max_data_size(panic_qr_version, 0),
+ QR_BUFFER1_SIZE);
+
+ kmsg_dump_rewind(&iter);
+ kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
+ if (!kmsg_len)
+ return -ENODATA;
+
+ *qr_image = qrbuf1;
+ return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, QR_BUFFER1_SIZE,
+ qrbuf2, QR_BUFFER2_SIZE);
+}
+
+static int drm_panic_get_qr_code(u8 **qr_image)
+{
+ if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0)
+ return drm_panic_get_qr_code_url(qr_image);
+ else
+ return drm_panic_get_qr_code_raw(qr_image);
+}
+
+/*
+ * Draw the panic message at the center of the screen, with a QR Code
+ */
+static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
+{
+ u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format);
+ u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format);
+ const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
+ struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas;
+ unsigned int max_qr_size, scale;
+ unsigned int msg_width, msg_height;
+ int qr_width, qr_canvas_width, qr_pitch, v_margin;
+ u8 *qr_image;
+
+ if (!font || !qrbuf1 || !qrbuf2 || !stream.workspace)
+ return -ENOMEM;
+
+ r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
+
+ drm_panic_logo_rect(&r_logo, font);
+
+ msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
+ msg_height = min(panic_msg_lines * font->height, sb->height);
+ r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
+
+ max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4);
+
+ qr_width = drm_panic_get_qr_code(&qr_image);
+ if (qr_width <= 0)
+ return -ENOSPC;
+
+ qr_canvas_width = qr_width + QR_MARGIN * 2;
+ scale = max_qr_size / qr_canvas_width;
+ /* QR code is not readable if not scaled at least by 2 */
+ if (scale < 2)
+ return -ENOSPC;
+
+ pr_debug("QR width %d and scale %d\n", qr_width, scale);
+ r_qr_canvas = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_width * scale);
+
+ v_margin = (sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg)) / 5;
+
+ drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_margin);
+ r_qr = DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, r_qr_canvas.y1 + QR_MARGIN * scale,
+ qr_width * scale, qr_width * scale);
+
+ /* Center the panic message */
+ drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2,
+ 3 * v_margin + drm_rect_height(&r_qr_canvas));
+
+ /* Fill with the background color, and draw text on top */
+ drm_panic_fill(sb, &r_screen, bg_color);
+
+ if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr))
+ drm_panic_logo_draw(sb, &r_logo, font, fg_color);
+
+ draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
+
+ /* Draw the qr code */
+ qr_pitch = DIV_ROUND_UP(qr_width, 8);
+ drm_panic_fill(sb, &r_qr_canvas, fg_color);
+ drm_panic_fill(sb, &r_qr, bg_color);
+ drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color);
+ return 0;
+}
+
+static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
+{
+ if (_draw_panic_static_qr_code(sb))
+ draw_panic_static_user(sb);
+}
+#else
+static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
+{
+ draw_panic_static_user(sb);
+}
+
+static void drm_panic_qr_init(void) {};
+static void drm_panic_qr_exit(void) {};
+#endif
+
/*
* drm_panic_is_format_supported()
* @format: a fourcc color code
@@ -626,12 +875,38 @@ static void draw_panic_dispatch(struct drm_scanout_buffer *sb)
{
if (!strcmp(drm_panic_screen, "kmsg")) {
draw_panic_static_kmsg(sb);
+ } else if (!strcmp(drm_panic_screen, "qr_code")) {
+ draw_panic_static_qr_code(sb);
} else {
draw_panic_static_user(sb);
}
}
-static void draw_panic_plane(struct drm_plane *plane)
+static void drm_panic_set_description(const char *description)
+{
+ u32 len;
+
+ if (description) {
+ struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
+
+ desc_line->txt = description;
+ len = strlen(description);
+ /* ignore the last newline character */
+ if (len && description[len - 1] == '\n')
+ len -= 1;
+ desc_line->len = len;
+ }
+}
+
+static void drm_panic_clear_description(void)
+{
+ struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
+
+ desc_line->len = 0;
+ desc_line->txt = NULL;
+}
+
+static void draw_panic_plane(struct drm_plane *plane, const char *description)
{
struct drm_scanout_buffer sb = { };
int ret;
@@ -640,6 +915,8 @@ static void draw_panic_plane(struct drm_plane *plane)
if (!drm_panic_trylock(plane->dev, flags))
return;
+ drm_panic_set_description(description);
+
ret = plane->helper_private->get_scanout_buffer(plane, &sb);
if (!ret && drm_panic_is_format_supported(sb.format)) {
@@ -647,6 +924,7 @@ static void draw_panic_plane(struct drm_plane *plane)
if (plane->helper_private->panic_flush)
plane->helper_private->panic_flush(plane);
}
+ drm_panic_clear_description();
drm_panic_unlock(plane->dev, flags);
}
@@ -655,12 +933,12 @@ 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)
+static void drm_panic(struct kmsg_dumper *dumper, struct kmsg_dump_detail *detail)
{
struct drm_plane *plane = to_drm_plane(dumper);
- if (reason == KMSG_DUMP_PANIC)
- draw_panic_plane(plane);
+ if (detail->reason == KMSG_DUMP_PANIC)
+ draw_panic_plane(plane, detail->description);
}
@@ -680,7 +958,7 @@ static ssize_t debugfs_trigger_write(struct file *file, const char __user *user_
if (kstrtobool_from_user(user_buf, count, &run) == 0 && run) {
struct drm_plane *plane = file->private_data;
- draw_panic_plane(plane);
+ draw_panic_plane(plane, "Test from debugfs");
}
return count;
}
@@ -704,6 +982,26 @@ static void debugfs_register_plane(struct drm_plane *plane, int index) {}
#endif /* CONFIG_DRM_PANIC_DEBUG */
/**
+ * drm_panic_is_enabled
+ * @dev: the drm device that may supports drm_panic
+ *
+ * returns true if the drm device supports drm_panic
+ */
+bool drm_panic_is_enabled(struct drm_device *dev)
+{
+ struct drm_plane *plane;
+
+ if (!dev->mode_config.num_total_plane)
+ return false;
+
+ drm_for_each_plane(plane, dev)
+ if (plane->helper_private && plane->helper_private->get_scanout_buffer)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(drm_panic_is_enabled);
+
+/**
* drm_panic_register() - Initialize DRM panic for a device
* @dev: the drm device on which the panic screen will be displayed.
*/
@@ -730,7 +1028,6 @@ void drm_panic_register(struct drm_device *dev)
if (registered_plane)
drm_info(dev, "Registered %d planes with drm panic\n", registered_plane);
}
-EXPORT_SYMBOL(drm_panic_register);
/**
* drm_panic_unregister()
@@ -749,4 +1046,19 @@ void drm_panic_unregister(struct drm_device *dev)
kmsg_dump_unregister(&plane->kmsg_panic);
}
}
-EXPORT_SYMBOL(drm_panic_unregister);
+
+/**
+ * drm_panic_init() - initialize DRM panic.
+ */
+void __init drm_panic_init(void)
+{
+ drm_panic_qr_init();
+}
+
+/**
+ * drm_panic_exit() - Free the resources taken by drm_panic_exit()
+ */
+void drm_panic_exit(void)
+{
+ drm_panic_qr_exit();
+}