1 // SPDX-License-Identifier: GPL-2.0 or MIT
3 * Copyright (c) 2024 Red Hat.
7 #include <linux/console.h>
8 #include <linux/font.h>
9 #include <linux/init.h>
10 #include <linux/iosys-map.h>
11 #include <linux/module.h>
12 #include <linux/types.h>
14 #include <drm/drm_client.h>
15 #include <drm/drm_drv.h>
16 #include <drm/drm_fourcc.h>
17 #include <drm/drm_framebuffer.h>
18 #include <drm/drm_print.h>
20 #include "drm_client_internal.h"
21 #include "drm_draw_internal.h"
22 #include "drm_internal.h"
24 MODULE_AUTHOR("Jocelyn Falempe");
25 MODULE_DESCRIPTION("DRM boot logger");
26 MODULE_LICENSE("GPL");
28 static unsigned int scale = 1;
29 module_param(scale, uint, 0444);
30 MODULE_PARM_DESC(scale, "Integer scaling factor for drm_log, default is 1");
35 * This is a simple graphic logger, to print the kernel message on screen, until
36 * a userspace application is able to take over.
37 * It is only for debugging purpose.
40 struct drm_log_scanout {
41 struct drm_client_buffer *buffer;
42 const struct font_desc *font;
56 struct drm_client_dev client;
60 struct drm_log_scanout *scanout;
63 static struct drm_log *client_to_drm_log(struct drm_client_dev *client)
65 return container_of(client, struct drm_log, client);
68 static struct drm_log *console_to_drm_log(struct console *con)
70 return container_of(con, struct drm_log, con);
73 static void drm_log_blit(struct iosys_map *dst, unsigned int dst_pitch,
74 const u8 *src, unsigned int src_pitch,
75 u32 height, u32 width, u32 px_width, u32 color)
79 drm_draw_blit16(dst, dst_pitch, src, src_pitch, height, width, scale, color);
82 drm_draw_blit24(dst, dst_pitch, src, src_pitch, height, width, scale, color);
85 drm_draw_blit32(dst, dst_pitch, src, src_pitch, height, width, scale, color);
88 WARN_ONCE(1, "Can't blit with pixel width %d\n", px_width);
92 static void drm_log_clear_line(struct drm_log_scanout *scanout, u32 line)
94 struct drm_framebuffer *fb = scanout->buffer->fb;
95 unsigned long height = scanout->scaled_font_h;
97 struct drm_rect r = DRM_RECT_INIT(0, line * height, fb->width, height);
99 if (drm_client_buffer_vmap_local(scanout->buffer, &map))
101 iosys_map_memset(&map, r.y1 * fb->pitches[0], 0, height * fb->pitches[0]);
102 drm_client_buffer_vunmap_local(scanout->buffer);
103 drm_client_framebuffer_flush(scanout->buffer, &r);
106 static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s,
107 unsigned int len, unsigned int prefix_len)
109 struct drm_framebuffer *fb = scanout->buffer->fb;
110 struct iosys_map map;
111 const struct font_desc *font = scanout->font;
112 size_t font_pitch = DIV_ROUND_UP(font->width, 8);
114 u32 px_width = fb->format->cpp[0];
115 struct drm_rect r = DRM_RECT_INIT(0, scanout->line * scanout->scaled_font_h,
116 fb->width, (scanout->line + 1) * scanout->scaled_font_h);
119 if (drm_client_buffer_vmap_local(scanout->buffer, &map))
122 iosys_map_incr(&map, r.y1 * fb->pitches[0]);
123 for (i = 0; i < len && i < scanout->columns; i++) {
124 u32 color = (i < prefix_len) ? scanout->prefix_color : scanout->front_color;
125 src = drm_draw_get_char_bitmap(font, s[i], font_pitch);
126 drm_log_blit(&map, fb->pitches[0], src, font_pitch,
127 scanout->scaled_font_h, scanout->scaled_font_w,
129 iosys_map_incr(&map, scanout->scaled_font_w * px_width);
133 if (scanout->line >= scanout->rows)
135 drm_client_buffer_vunmap_local(scanout->buffer);
136 drm_client_framebuffer_flush(scanout->buffer, &r);
139 static void drm_log_draw_new_line(struct drm_log_scanout *scanout,
140 const char *s, unsigned int len, unsigned int prefix_len)
142 if (scanout->line == 0) {
143 drm_log_clear_line(scanout, 0);
144 drm_log_clear_line(scanout, 1);
145 drm_log_clear_line(scanout, 2);
146 } else if (scanout->line + 2 < scanout->rows)
147 drm_log_clear_line(scanout, scanout->line + 2);
149 drm_log_draw_line(scanout, s, len, prefix_len);
153 * Depends on print_time() in printk.c
154 * Timestamp is written with "[%5lu.%06lu]"
156 #define TS_PREFIX_LEN 13
158 static void drm_log_draw_kmsg_record(struct drm_log_scanout *scanout,
159 const char *s, unsigned int len)
163 if (len > TS_PREFIX_LEN && s[0] == '[' && s[6] == '.' && s[TS_PREFIX_LEN] == ']')
164 prefix_len = TS_PREFIX_LEN + 1;
166 /* do not print the ending \n character */
167 if (s[len - 1] == '\n')
170 while (len > scanout->columns) {
171 drm_log_draw_new_line(scanout, s, scanout->columns, prefix_len);
172 s += scanout->columns;
173 len -= scanout->columns;
177 drm_log_draw_new_line(scanout, s, len, prefix_len);
180 static u32 drm_log_find_usable_format(struct drm_plane *plane)
184 for (i = 0; i < plane->format_count; i++)
185 if (drm_draw_color_from_xrgb8888(0xffffff, plane->format_types[i]) != 0)
186 return plane->format_types[i];
187 return DRM_FORMAT_INVALID;
190 static int drm_log_setup_modeset(struct drm_client_dev *client,
191 struct drm_mode_set *mode_set,
192 struct drm_log_scanout *scanout)
194 struct drm_crtc *crtc = mode_set->crtc;
195 u32 width = mode_set->mode->hdisplay;
196 u32 height = mode_set->mode->vdisplay;
199 scanout->font = get_default_font(width, height, NULL, NULL);
203 format = drm_log_find_usable_format(crtc->primary);
204 if (format == DRM_FORMAT_INVALID)
207 scanout->buffer = drm_client_framebuffer_create(client, width, height, format);
208 if (IS_ERR(scanout->buffer)) {
209 drm_warn(client->dev, "drm_log can't create framebuffer %d %d %p4cc\n",
210 width, height, &format);
213 mode_set->fb = scanout->buffer->fb;
214 scanout->scaled_font_h = scanout->font->height * scale;
215 scanout->scaled_font_w = scanout->font->width * scale;
216 scanout->rows = height / scanout->scaled_font_h;
217 scanout->columns = width / scanout->scaled_font_w;
218 scanout->front_color = drm_draw_color_from_xrgb8888(0xffffff, format);
219 scanout->prefix_color = drm_draw_color_from_xrgb8888(0x4e9a06, format);
223 static int drm_log_count_modeset(struct drm_client_dev *client)
225 struct drm_mode_set *mode_set;
228 mutex_lock(&client->modeset_mutex);
229 drm_client_for_each_modeset(mode_set, client)
231 mutex_unlock(&client->modeset_mutex);
235 static void drm_log_init_client(struct drm_log *dlog)
237 struct drm_client_dev *client = &dlog->client;
238 struct drm_mode_set *mode_set;
244 if (drm_client_modeset_probe(client, 0, 0))
247 max_modeset = drm_log_count_modeset(client);
251 dlog->scanout = kcalloc(max_modeset, sizeof(*dlog->scanout), GFP_KERNEL);
255 mutex_lock(&client->modeset_mutex);
256 drm_client_for_each_modeset(mode_set, client) {
259 if (drm_log_setup_modeset(client, mode_set, &dlog->scanout[n_modeset]))
263 mutex_unlock(&client->modeset_mutex);
267 if (drm_client_modeset_commit(client))
268 goto err_failed_commit;
270 dlog->n_scanout = n_modeset;
274 for (i = 0; i < n_modeset; i++)
275 drm_client_framebuffer_delete(dlog->scanout[i].buffer);
278 kfree(dlog->scanout);
279 dlog->scanout = NULL;
282 static void drm_log_free_scanout(struct drm_client_dev *client)
284 struct drm_log *dlog = client_to_drm_log(client);
287 if (dlog->n_scanout) {
288 for (i = 0; i < dlog->n_scanout; i++)
289 drm_client_framebuffer_delete(dlog->scanout[i].buffer);
291 kfree(dlog->scanout);
292 dlog->scanout = NULL;
296 static void drm_log_client_unregister(struct drm_client_dev *client)
298 struct drm_log *dlog = client_to_drm_log(client);
299 struct drm_device *dev = client->dev;
301 unregister_console(&dlog->con);
303 mutex_lock(&dlog->lock);
304 drm_log_free_scanout(client);
305 drm_client_release(client);
306 mutex_unlock(&dlog->lock);
308 drm_dbg(dev, "Unregistered with drm log\n");
311 static int drm_log_client_hotplug(struct drm_client_dev *client)
313 struct drm_log *dlog = client_to_drm_log(client);
315 mutex_lock(&dlog->lock);
316 drm_log_free_scanout(client);
317 dlog->probed = false;
318 mutex_unlock(&dlog->lock);
322 static int drm_log_client_suspend(struct drm_client_dev *client, bool _console_lock)
324 struct drm_log *dlog = client_to_drm_log(client);
326 console_stop(&dlog->con);
331 static int drm_log_client_resume(struct drm_client_dev *client, bool _console_lock)
333 struct drm_log *dlog = client_to_drm_log(client);
335 console_start(&dlog->con);
340 static const struct drm_client_funcs drm_log_client_funcs = {
341 .owner = THIS_MODULE,
342 .unregister = drm_log_client_unregister,
343 .hotplug = drm_log_client_hotplug,
344 .suspend = drm_log_client_suspend,
345 .resume = drm_log_client_resume,
348 static void drm_log_write_thread(struct console *con, struct nbcon_write_context *wctxt)
350 struct drm_log *dlog = console_to_drm_log(con);
354 drm_log_init_client(dlog);
356 /* Check that we are still the master before drawing */
357 if (drm_master_internal_acquire(dlog->client.dev)) {
358 drm_master_internal_release(dlog->client.dev);
360 for (i = 0; i < dlog->n_scanout; i++)
361 drm_log_draw_kmsg_record(&dlog->scanout[i], wctxt->outbuf, wctxt->len);
365 static void drm_log_lock(struct console *con, unsigned long *flags)
367 struct drm_log *dlog = console_to_drm_log(con);
369 mutex_lock(&dlog->lock);
373 static void drm_log_unlock(struct console *con, unsigned long flags)
375 struct drm_log *dlog = console_to_drm_log(con);
378 mutex_unlock(&dlog->lock);
381 static void drm_log_register_console(struct console *con)
383 strscpy(con->name, "drm_log");
384 con->write_thread = drm_log_write_thread;
385 con->device_lock = drm_log_lock;
386 con->device_unlock = drm_log_unlock;
387 con->flags = CON_PRINTBUFFER | CON_NBCON;
390 register_console(con);
394 * drm_log_register() - Register a drm device to drm_log
395 * @dev: the drm device to register.
397 void drm_log_register(struct drm_device *dev)
401 new = kzalloc(sizeof(*new), GFP_KERNEL);
405 mutex_init(&new->lock);
406 if (drm_client_init(dev, &new->client, "drm_log", &drm_log_client_funcs))
409 drm_client_register(&new->client);
411 drm_log_register_console(&new->con);
413 drm_dbg(dev, "Registered with drm log as %s\n", new->con.name);
419 drm_warn(dev, "Failed to register with drm log\n");