File Index
Files and directories
Sign in
FILE

drm_log.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/clients/drm_log.c C
  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
 24MODULE_AUTHOR("Jocelyn Falempe");
 25MODULE_DESCRIPTION("DRM boot logger");
 26MODULE_LICENSE("GPL");
 27
 28static unsigned int scale = 1;
 29module_param(scale, uint, 0444);
 30MODULE_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
 40struct 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
 54struct 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
 63static struct drm_log *client_to_drm_log(struct drm_client_dev *client)
 64{
 65	return container_of(client, struct drm_log, client);
 66}
 67
 68static struct drm_log *console_to_drm_log(struct console *con)
 69{
 70	return container_of(con, struct drm_log, con);
 71}
 72
 73static 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
 92static 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
106static 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
139static 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
158static 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 > TS_PREFIX_LEN && s[0] == '[' && s[6] == '.' && s[TS_PREFIX_LEN] == ']')
164		prefix_len = TS_PREFIX_LEN + 1;
165
166	/* do not print the ending \n character */
167	if (s[len - 1] == '\n')
168		len--;
169
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;
174		prefix_len = 0;
175	}
176	if (len)
177		drm_log_draw_new_line(scanout, s, len, prefix_len);
178}
179
180static u32 drm_log_find_usable_format(struct drm_plane *plane)
181{
182	int i;
183
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;
188}
189
190static int drm_log_setup_modeset(struct drm_client_dev *client,
191				 struct drm_mode_set *mode_set,
192				 struct drm_log_scanout *scanout)
193{
194	struct drm_crtc *crtc = mode_set->crtc;
195	u32 width = mode_set->mode->hdisplay;
196	u32 height = mode_set->mode->vdisplay;
197	u32 format;
198
199	scanout->font = get_default_font(width, height, NULL, NULL);
200	if (!scanout->font)
201		return -ENOENT;
202
203	format = drm_log_find_usable_format(crtc->primary);
204	if (format == DRM_FORMAT_INVALID)
205		return -EINVAL;
206
207	scanout->buffer = drm_client_buffer_create_dumb(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);
211		return -ENOMEM;
212	}
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);
220	return 0;
221}
222
223static int drm_log_count_modeset(struct drm_client_dev *client)
224{
225	struct drm_mode_set *mode_set;
226	int count = 0;
227
228	mutex_lock(&client->modeset_mutex);
229	drm_client_for_each_modeset(mode_set, client)
230		count++;
231	mutex_unlock(&client->modeset_mutex);
232	return count;
233}
234
235static void drm_log_init_client(struct drm_log *dlog)
236{
237	struct drm_client_dev *client = &dlog->client;
238	struct drm_mode_set *mode_set;
239	int i, max_modeset;
240	int n_modeset = 0;
241
242	dlog->probed = true;
243
244	if (drm_client_modeset_probe(client, 0, 0))
245		return;
246
247	max_modeset = drm_log_count_modeset(client);
248	if (!max_modeset)
249		return;
250
251	dlog->scanout = kcalloc(max_modeset, sizeof(*dlog->scanout), GFP_KERNEL);
252	if (!dlog->scanout)
253		return;
254
255	mutex_lock(&client->modeset_mutex);
256	drm_client_for_each_modeset(mode_set, client) {
257		if (!mode_set->mode)
258			continue;
259		if (drm_log_setup_modeset(client, mode_set, &dlog->scanout[n_modeset]))
260			continue;
261		n_modeset++;
262	}
263	mutex_unlock(&client->modeset_mutex);
264	if (n_modeset == 0)
265		goto err_nomodeset;
266
267	if (drm_client_modeset_commit(client))
268		goto err_failed_commit;
269
270	dlog->n_scanout = n_modeset;
271	return;
272
273err_failed_commit:
274	for (i = 0; i < n_modeset; i++)
275		drm_client_buffer_delete(dlog->scanout[i].buffer);
276
277err_nomodeset:
278	kfree(dlog->scanout);
279	dlog->scanout = NULL;
280}
281
282static void drm_log_free_scanout(struct drm_client_dev *client)
283{
284	struct drm_log *dlog = client_to_drm_log(client);
285	int i;
286
287	if (dlog->n_scanout) {
288		for (i = 0; i < dlog->n_scanout; i++)
289			drm_client_buffer_delete(dlog->scanout[i].buffer);
290		dlog->n_scanout = 0;
291		kfree(dlog->scanout);
292		dlog->scanout = NULL;
293	}
294}
295
296static void drm_log_client_free(struct drm_client_dev *client)
297{
298	struct drm_log *dlog = client_to_drm_log(client);
299	struct drm_device *dev = client->dev;
300
301	kfree(dlog);
302
303	drm_dbg(dev, "Unregistered with drm log\n");
304}
305
306static void drm_log_client_unregister(struct drm_client_dev *client)
307{
308	struct drm_log *dlog = client_to_drm_log(client);
309
310	unregister_console(&dlog->con);
311
312	mutex_lock(&dlog->lock);
313	drm_log_free_scanout(client);
314	mutex_unlock(&dlog->lock);
315	drm_client_release(client);
316}
317
318static int drm_log_client_restore(struct drm_client_dev *client, bool force)
319{
320	int ret;
321
322	if (force)
323		ret = drm_client_modeset_commit_locked(client);
324	else
325		ret = drm_client_modeset_commit(client);
326
327	return ret;
328}
329
330static int drm_log_client_hotplug(struct drm_client_dev *client)
331{
332	struct drm_log *dlog = client_to_drm_log(client);
333
334	mutex_lock(&dlog->lock);
335	drm_log_free_scanout(client);
336	dlog->probed = false;
337	mutex_unlock(&dlog->lock);
338	return 0;
339}
340
341static int drm_log_client_suspend(struct drm_client_dev *client)
342{
343	struct drm_log *dlog = client_to_drm_log(client);
344
345	console_suspend(&dlog->con);
346
347	return 0;
348}
349
350static int drm_log_client_resume(struct drm_client_dev *client)
351{
352	struct drm_log *dlog = client_to_drm_log(client);
353
354	console_resume(&dlog->con);
355
356	return 0;
357}
358
359static const struct drm_client_funcs drm_log_client_funcs = {
360	.owner		= THIS_MODULE,
361	.free		= drm_log_client_free,
362	.unregister	= drm_log_client_unregister,
363	.restore	= drm_log_client_restore,
364	.hotplug	= drm_log_client_hotplug,
365	.suspend	= drm_log_client_suspend,
366	.resume		= drm_log_client_resume,
367};
368
369static void drm_log_write_thread(struct console *con, struct nbcon_write_context *wctxt)
370{
371	struct drm_log *dlog = console_to_drm_log(con);
372	int i;
373
374	if (!dlog->probed)
375		drm_log_init_client(dlog);
376
377	/* Check that we are still the master before drawing */
378	if (drm_master_internal_acquire(dlog->client.dev)) {
379		drm_master_internal_release(dlog->client.dev);
380
381		for (i = 0; i < dlog->n_scanout; i++)
382			drm_log_draw_kmsg_record(&dlog->scanout[i], wctxt->outbuf, wctxt->len);
383	}
384}
385
386static void drm_log_lock(struct console *con, unsigned long *flags)
387{
388	struct drm_log *dlog = console_to_drm_log(con);
389
390	mutex_lock(&dlog->lock);
391	migrate_disable();
392}
393
394static void drm_log_unlock(struct console *con, unsigned long flags)
395{
396	struct drm_log *dlog = console_to_drm_log(con);
397
398	migrate_enable();
399	mutex_unlock(&dlog->lock);
400}
401
402static void drm_log_register_console(struct console *con)
403{
404	strscpy(con->name, "drm_log");
405	con->write_thread = drm_log_write_thread;
406	con->device_lock = drm_log_lock;
407	con->device_unlock = drm_log_unlock;
408	con->flags = CON_PRINTBUFFER | CON_NBCON;
409	con->index = -1;
410
411	register_console(con);
412}
413
414/**
415 * drm_log_register() - Register a drm device to drm_log
416 * @dev: the drm device to register.
417 */
418void drm_log_register(struct drm_device *dev)
419{
420	struct drm_log *new;
421
422	new = kzalloc(sizeof(*new), GFP_KERNEL);
423	if (!new)
424		goto err_warn;
425
426	mutex_init(&new->lock);
427	if (drm_client_init(dev, &new->client, "drm_log", &drm_log_client_funcs))
428		goto err_free;
429
430	drm_client_register(&new->client);
431
432	drm_log_register_console(&new->con);
433
434	drm_dbg(dev, "Registered with drm log as %s\n", new->con.name);
435	return;
436
437err_free:
438	kfree(new);
439err_warn:
440	drm_warn(dev, "Failed to register with drm log\n");
441}
~/ Read-only file preview.