Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/clients/drm_log.c
26493 views
1
// SPDX-License-Identifier: GPL-2.0 or MIT
2
/*
3
* Copyright (c) 2024 Red Hat.
4
* Author: Jocelyn Falempe <[email protected]>
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_framebuffer_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_framebuffer_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 > 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
180
static 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
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)
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_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);
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
223
static 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
235
static 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
273
err_failed_commit:
274
for (i = 0; i < n_modeset; i++)
275
drm_client_framebuffer_delete(dlog->scanout[i].buffer);
276
277
err_nomodeset:
278
kfree(dlog->scanout);
279
dlog->scanout = NULL;
280
}
281
282
static 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_framebuffer_delete(dlog->scanout[i].buffer);
290
dlog->n_scanout = 0;
291
kfree(dlog->scanout);
292
dlog->scanout = NULL;
293
}
294
}
295
296
static void drm_log_client_unregister(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
unregister_console(&dlog->con);
302
303
mutex_lock(&dlog->lock);
304
drm_log_free_scanout(client);
305
drm_client_release(client);
306
mutex_unlock(&dlog->lock);
307
kfree(dlog);
308
drm_dbg(dev, "Unregistered with drm log\n");
309
}
310
311
static int drm_log_client_hotplug(struct drm_client_dev *client)
312
{
313
struct drm_log *dlog = client_to_drm_log(client);
314
315
mutex_lock(&dlog->lock);
316
drm_log_free_scanout(client);
317
dlog->probed = false;
318
mutex_unlock(&dlog->lock);
319
return 0;
320
}
321
322
static int drm_log_client_suspend(struct drm_client_dev *client, bool _console_lock)
323
{
324
struct drm_log *dlog = client_to_drm_log(client);
325
326
console_suspend(&dlog->con);
327
328
return 0;
329
}
330
331
static int drm_log_client_resume(struct drm_client_dev *client, bool _console_lock)
332
{
333
struct drm_log *dlog = client_to_drm_log(client);
334
335
console_resume(&dlog->con);
336
337
return 0;
338
}
339
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,
346
};
347
348
static void drm_log_write_thread(struct console *con, struct nbcon_write_context *wctxt)
349
{
350
struct drm_log *dlog = console_to_drm_log(con);
351
int i;
352
353
if (!dlog->probed)
354
drm_log_init_client(dlog);
355
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);
359
360
for (i = 0; i < dlog->n_scanout; i++)
361
drm_log_draw_kmsg_record(&dlog->scanout[i], wctxt->outbuf, wctxt->len);
362
}
363
}
364
365
static void drm_log_lock(struct console *con, unsigned long *flags)
366
{
367
struct drm_log *dlog = console_to_drm_log(con);
368
369
mutex_lock(&dlog->lock);
370
migrate_disable();
371
}
372
373
static void drm_log_unlock(struct console *con, unsigned long flags)
374
{
375
struct drm_log *dlog = console_to_drm_log(con);
376
377
migrate_enable();
378
mutex_unlock(&dlog->lock);
379
}
380
381
static void drm_log_register_console(struct console *con)
382
{
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;
388
con->index = -1;
389
390
register_console(con);
391
}
392
393
/**
394
* drm_log_register() - Register a drm device to drm_log
395
* @dev: the drm device to register.
396
*/
397
void drm_log_register(struct drm_device *dev)
398
{
399
struct drm_log *new;
400
401
new = kzalloc(sizeof(*new), GFP_KERNEL);
402
if (!new)
403
goto err_warn;
404
405
mutex_init(&new->lock);
406
if (drm_client_init(dev, &new->client, "drm_log", &drm_log_client_funcs))
407
goto err_free;
408
409
drm_client_register(&new->client);
410
411
drm_log_register_console(&new->con);
412
413
drm_dbg(dev, "Registered with drm log as %s\n", new->con.name);
414
return;
415
416
err_free:
417
kfree(new);
418
err_warn:
419
drm_warn(dev, "Failed to register with drm log\n");
420
}
421
422