xref: /linux/drivers/gpu/drm/clients/drm_log.c (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
1 // SPDX-License-Identifier: GPL-2.0 or MIT
2 /*
3  * Copyright (c) 2024 Red Hat.
4  * Author: Jocelyn Falempe <jfalempe@redhat.com>
5  */
6 
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>
13 
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>
19 
20 #include "drm_client_internal.h"
21 #include "drm_draw_internal.h"
22 #include "drm_internal.h"
23 
24 MODULE_AUTHOR("Jocelyn Falempe");
25 MODULE_DESCRIPTION("DRM boot logger");
26 MODULE_LICENSE("GPL");
27 
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");
31 
32 /**
33  * DOC: overview
34  *
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.
38  */
39 
40 struct drm_log_scanout {
41 	struct drm_client_buffer *buffer;
42 	const struct font_desc *font;
43 	u32 rows;
44 	u32 columns;
45 	u32 scaled_font_h;
46 	u32 scaled_font_w;
47 	u32 line;
48 	u32 format;
49 	u32 px_width;
50 	u32 front_color;
51 	u32 prefix_color;
52 };
53 
54 struct drm_log {
55 	struct mutex lock;
56 	struct drm_client_dev client;
57 	struct console con;
58 	bool probed;
59 	u32 n_scanout;
60 	struct drm_log_scanout *scanout;
61 };
62 
63 static struct drm_log *client_to_drm_log(struct drm_client_dev *client)
64 {
65 	return container_of(client, struct drm_log, client);
66 }
67 
68 static struct drm_log *console_to_drm_log(struct console *con)
69 {
70 	return container_of(con, struct drm_log, con);
71 }
72 
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)
76 {
77 	switch (px_width) {
78 	case 2:
79 		drm_draw_blit16(dst, dst_pitch, src, src_pitch, height, width, scale, color);
80 		break;
81 	case 3:
82 		drm_draw_blit24(dst, dst_pitch, src, src_pitch, height, width, scale, color);
83 		break;
84 	case 4:
85 		drm_draw_blit32(dst, dst_pitch, src, src_pitch, height, width, scale, color);
86 		break;
87 	default:
88 		WARN_ONCE(1, "Can't blit with pixel width %d\n", px_width);
89 	}
90 }
91 
92 static void drm_log_clear_line(struct drm_log_scanout *scanout, u32 line)
93 {
94 	struct drm_framebuffer *fb = scanout->buffer->fb;
95 	unsigned long height = scanout->scaled_font_h;
96 	struct iosys_map map;
97 	struct drm_rect r = DRM_RECT_INIT(0, line * height, fb->width, height);
98 
99 	if (drm_client_buffer_vmap_local(scanout->buffer, &map))
100 		return;
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_buffer_flush(scanout->buffer, &r);
104 }
105 
106 static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s,
107 			      unsigned int len, unsigned int prefix_len)
108 {
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);
113 	const u8 *src;
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);
117 	u32 i;
118 
119 	if (drm_client_buffer_vmap_local(scanout->buffer, &map))
120 		return;
121 
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,
128 			     px_width, color);
129 		iosys_map_incr(&map, scanout->scaled_font_w * px_width);
130 	}
131 
132 	scanout->line++;
133 	if (scanout->line >= scanout->rows)
134 		scanout->line = 0;
135 	drm_client_buffer_vunmap_local(scanout->buffer);
136 	drm_client_buffer_flush(scanout->buffer, &r);
137 }
138 
139 static void drm_log_draw_new_line(struct drm_log_scanout *scanout,
140 				  const char *s, unsigned int len, unsigned int prefix_len)
141 {
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);
148 
149 	drm_log_draw_line(scanout, s, len, prefix_len);
150 }
151 
152 /*
153  * Depends on print_time() in printk.c
154  * Timestamp is written with "[%5lu.%06lu]"
155  */
156 #define TS_PREFIX_LEN 13
157 
158 static void drm_log_draw_kmsg_record(struct drm_log_scanout *scanout,
159 				     const char *s, unsigned int len)
160 {
161 	u32 prefix_len = 0;
162 
163 	if (!len)
164 		return;
165 
166 	if (len > TS_PREFIX_LEN && s[0] == '[' && s[6] == '.' && s[TS_PREFIX_LEN] == ']')
167 		prefix_len = TS_PREFIX_LEN + 1;
168 
169 	/* do not print the ending \n character */
170 	if (s[len - 1] == '\n')
171 		len--;
172 
173 	while (len > scanout->columns) {
174 		drm_log_draw_new_line(scanout, s, scanout->columns, prefix_len);
175 		s += scanout->columns;
176 		len -= scanout->columns;
177 		prefix_len = 0;
178 	}
179 	if (len)
180 		drm_log_draw_new_line(scanout, s, len, prefix_len);
181 }
182 
183 static u32 drm_log_find_usable_format(struct drm_plane *plane)
184 {
185 	int i;
186 
187 	for (i = 0; i < plane->format_count; i++)
188 		if (drm_draw_can_convert_from_xrgb8888(plane->format_types[i]))
189 			return plane->format_types[i];
190 	return DRM_FORMAT_INVALID;
191 }
192 
193 static int drm_log_setup_modeset(struct drm_client_dev *client,
194 				 struct drm_mode_set *mode_set,
195 				 struct drm_log_scanout *scanout)
196 {
197 	struct drm_crtc *crtc = mode_set->crtc;
198 	u32 width = mode_set->mode->hdisplay;
199 	u32 height = mode_set->mode->vdisplay;
200 	u32 format;
201 
202 	scanout->font = get_default_font(width, height, NULL, NULL);
203 	if (!scanout->font)
204 		return -ENOENT;
205 
206 	format = drm_log_find_usable_format(crtc->primary);
207 	if (format == DRM_FORMAT_INVALID)
208 		return -EINVAL;
209 
210 	scanout->buffer = drm_client_buffer_create_dumb(client, width, height, format);
211 	if (IS_ERR(scanout->buffer)) {
212 		drm_warn(client->dev, "drm_log can't create framebuffer %d %d %p4cc\n",
213 			 width, height, &format);
214 		return -ENOMEM;
215 	}
216 	mode_set->fb = scanout->buffer->fb;
217 	scanout->scaled_font_h = scanout->font->height * scale;
218 	scanout->scaled_font_w = scanout->font->width * scale;
219 	scanout->rows = height / scanout->scaled_font_h;
220 	scanout->columns = width / scanout->scaled_font_w;
221 	if (!scanout->rows || !scanout->columns) {
222 		drm_client_buffer_delete(scanout->buffer);
223 		scanout->buffer = NULL;
224 		mode_set->fb = NULL;
225 		return -EINVAL;
226 	}
227 	scanout->front_color = drm_draw_color_from_xrgb8888(0xffffff, format);
228 	scanout->prefix_color = drm_draw_color_from_xrgb8888(0x4e9a06, format);
229 	return 0;
230 }
231 
232 static int drm_log_count_modeset(struct drm_client_dev *client)
233 {
234 	struct drm_mode_set *mode_set;
235 	int count = 0;
236 
237 	mutex_lock(&client->modeset_mutex);
238 	drm_client_for_each_modeset(mode_set, client)
239 		count++;
240 	mutex_unlock(&client->modeset_mutex);
241 	return count;
242 }
243 
244 static void drm_log_init_client(struct drm_log *dlog)
245 {
246 	struct drm_client_dev *client = &dlog->client;
247 	struct drm_mode_set *mode_set;
248 	int i, max_modeset;
249 	int n_modeset = 0;
250 
251 	dlog->probed = true;
252 
253 	if (drm_client_modeset_probe(client, 0, 0))
254 		return;
255 
256 	max_modeset = drm_log_count_modeset(client);
257 	if (!max_modeset)
258 		return;
259 
260 	dlog->scanout = kzalloc_objs(*dlog->scanout, max_modeset);
261 	if (!dlog->scanout)
262 		return;
263 
264 	mutex_lock(&client->modeset_mutex);
265 	drm_client_for_each_modeset(mode_set, client) {
266 		if (!mode_set->mode)
267 			continue;
268 		if (drm_log_setup_modeset(client, mode_set, &dlog->scanout[n_modeset]))
269 			continue;
270 		n_modeset++;
271 	}
272 	mutex_unlock(&client->modeset_mutex);
273 	if (n_modeset == 0)
274 		goto err_nomodeset;
275 
276 	if (drm_client_modeset_commit(client))
277 		goto err_failed_commit;
278 
279 	dlog->n_scanout = n_modeset;
280 	return;
281 
282 err_failed_commit:
283 	for (i = 0; i < n_modeset; i++)
284 		drm_client_buffer_delete(dlog->scanout[i].buffer);
285 
286 err_nomodeset:
287 	kfree(dlog->scanout);
288 	dlog->scanout = NULL;
289 }
290 
291 static void drm_log_free_scanout(struct drm_client_dev *client)
292 {
293 	struct drm_log *dlog = client_to_drm_log(client);
294 	int i;
295 
296 	if (dlog->n_scanout) {
297 		for (i = 0; i < dlog->n_scanout; i++)
298 			drm_client_buffer_delete(dlog->scanout[i].buffer);
299 		dlog->n_scanout = 0;
300 		kfree(dlog->scanout);
301 		dlog->scanout = NULL;
302 	}
303 }
304 
305 static void drm_log_client_free(struct drm_client_dev *client)
306 {
307 	struct drm_log *dlog = client_to_drm_log(client);
308 	struct drm_device *dev = client->dev;
309 
310 	kfree(dlog);
311 
312 	drm_dbg(dev, "Unregistered with drm log\n");
313 }
314 
315 static void drm_log_client_unregister(struct drm_client_dev *client)
316 {
317 	struct drm_log *dlog = client_to_drm_log(client);
318 
319 	unregister_console(&dlog->con);
320 
321 	mutex_lock(&dlog->lock);
322 	drm_log_free_scanout(client);
323 	mutex_unlock(&dlog->lock);
324 	drm_client_release(client);
325 }
326 
327 static int drm_log_client_restore(struct drm_client_dev *client, bool force)
328 {
329 	int ret;
330 
331 	if (force)
332 		ret = drm_client_modeset_commit_locked(client);
333 	else
334 		ret = drm_client_modeset_commit(client);
335 
336 	return ret;
337 }
338 
339 static int drm_log_client_hotplug(struct drm_client_dev *client)
340 {
341 	struct drm_log *dlog = client_to_drm_log(client);
342 
343 	mutex_lock(&dlog->lock);
344 	drm_log_free_scanout(client);
345 	dlog->probed = false;
346 	mutex_unlock(&dlog->lock);
347 	return 0;
348 }
349 
350 static int drm_log_client_suspend(struct drm_client_dev *client)
351 {
352 	struct drm_log *dlog = client_to_drm_log(client);
353 
354 	console_suspend(&dlog->con);
355 
356 	return 0;
357 }
358 
359 static int drm_log_client_resume(struct drm_client_dev *client)
360 {
361 	struct drm_log *dlog = client_to_drm_log(client);
362 
363 	console_resume(&dlog->con);
364 
365 	return 0;
366 }
367 
368 static const struct drm_client_funcs drm_log_client_funcs = {
369 	.owner		= THIS_MODULE,
370 	.free		= drm_log_client_free,
371 	.unregister	= drm_log_client_unregister,
372 	.restore	= drm_log_client_restore,
373 	.hotplug	= drm_log_client_hotplug,
374 	.suspend	= drm_log_client_suspend,
375 	.resume		= drm_log_client_resume,
376 };
377 
378 static void drm_log_write_thread(struct console *con, struct nbcon_write_context *wctxt)
379 {
380 	struct drm_log *dlog = console_to_drm_log(con);
381 	int i;
382 
383 	if (!dlog->probed)
384 		drm_log_init_client(dlog);
385 
386 	/* Check that we are still the master before drawing */
387 	if (drm_master_internal_acquire(dlog->client.dev)) {
388 		drm_master_internal_release(dlog->client.dev);
389 
390 		for (i = 0; i < dlog->n_scanout; i++)
391 			drm_log_draw_kmsg_record(&dlog->scanout[i], wctxt->outbuf, wctxt->len);
392 	}
393 }
394 
395 static void drm_log_lock(struct console *con, unsigned long *flags)
396 {
397 	struct drm_log *dlog = console_to_drm_log(con);
398 
399 	mutex_lock(&dlog->lock);
400 	migrate_disable();
401 }
402 
403 static void drm_log_unlock(struct console *con, unsigned long flags)
404 {
405 	struct drm_log *dlog = console_to_drm_log(con);
406 
407 	migrate_enable();
408 	mutex_unlock(&dlog->lock);
409 }
410 
411 static void drm_log_register_console(struct console *con)
412 {
413 	strscpy(con->name, "drm_log");
414 	con->write_thread = drm_log_write_thread;
415 	con->device_lock = drm_log_lock;
416 	con->device_unlock = drm_log_unlock;
417 	con->flags = CON_PRINTBUFFER | CON_NBCON;
418 	con->index = -1;
419 
420 	register_console(con);
421 }
422 
423 /**
424  * drm_log_register() - Register a drm device to drm_log
425  * @dev: the drm device to register.
426  */
427 void drm_log_register(struct drm_device *dev)
428 {
429 	struct drm_log *new;
430 
431 	if (!scale)
432 		scale = 1;
433 
434 	new = kzalloc_obj(*new);
435 	if (!new)
436 		goto err_warn;
437 
438 	mutex_init(&new->lock);
439 	if (drm_client_init(dev, &new->client, "drm_log", &drm_log_client_funcs))
440 		goto err_free;
441 
442 	drm_client_register(&new->client);
443 
444 	drm_log_register_console(&new->con);
445 
446 	drm_dbg(dev, "Registered with drm log as %s\n", new->con.name);
447 	return;
448 
449 err_free:
450 	kfree(new);
451 err_warn:
452 	drm_warn(dev, "Failed to register with drm log\n");
453 }
454