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 = font_data_glyph_buf(font->data, font->width, font->height, 126 (unsigned char)s[i]); 127 if (src) 128 drm_log_blit(&map, fb->pitches[0], src, font_pitch, 129 scanout->scaled_font_h, scanout->scaled_font_w, 130 px_width, color); 131 iosys_map_incr(&map, scanout->scaled_font_w * px_width); 132 } 133 134 scanout->line++; 135 if (scanout->line >= scanout->rows) 136 scanout->line = 0; 137 drm_client_buffer_vunmap_local(scanout->buffer); 138 drm_client_buffer_flush(scanout->buffer, &r); 139 } 140 141 static void drm_log_draw_new_line(struct drm_log_scanout *scanout, 142 const char *s, unsigned int len, unsigned int prefix_len) 143 { 144 if (scanout->line == 0) { 145 drm_log_clear_line(scanout, 0); 146 drm_log_clear_line(scanout, 1); 147 drm_log_clear_line(scanout, 2); 148 } else if (scanout->line + 2 < scanout->rows) 149 drm_log_clear_line(scanout, scanout->line + 2); 150 151 drm_log_draw_line(scanout, s, len, prefix_len); 152 } 153 154 /* 155 * Depends on print_time() in printk.c 156 * Timestamp is written with "[%5lu.%06lu]" 157 */ 158 #define TS_PREFIX_LEN 13 159 160 static void drm_log_draw_kmsg_record(struct drm_log_scanout *scanout, 161 const char *s, unsigned int len) 162 { 163 u32 prefix_len = 0; 164 165 if (!len) 166 return; 167 168 if (len > TS_PREFIX_LEN && s[0] == '[' && s[6] == '.' && s[TS_PREFIX_LEN] == ']') 169 prefix_len = TS_PREFIX_LEN + 1; 170 171 /* do not print the ending \n character */ 172 if (s[len - 1] == '\n') 173 len--; 174 175 while (len > scanout->columns) { 176 drm_log_draw_new_line(scanout, s, scanout->columns, prefix_len); 177 s += scanout->columns; 178 len -= scanout->columns; 179 prefix_len = 0; 180 } 181 if (len) 182 drm_log_draw_new_line(scanout, s, len, prefix_len); 183 } 184 185 static u32 drm_log_find_usable_format(struct drm_plane *plane) 186 { 187 int i; 188 189 for (i = 0; i < plane->format_count; i++) 190 if (drm_draw_can_convert_from_xrgb8888(plane->format_types[i])) 191 return plane->format_types[i]; 192 return DRM_FORMAT_INVALID; 193 } 194 195 static int drm_log_setup_modeset(struct drm_client_dev *client, 196 struct drm_mode_set *mode_set, 197 struct drm_log_scanout *scanout) 198 { 199 struct drm_crtc *crtc = mode_set->crtc; 200 u32 width = mode_set->mode->hdisplay; 201 u32 height = mode_set->mode->vdisplay; 202 u32 format; 203 204 scanout->font = get_default_font(width, height, NULL, NULL); 205 if (!scanout->font) 206 return -ENOENT; 207 208 format = drm_log_find_usable_format(crtc->primary); 209 if (format == DRM_FORMAT_INVALID) 210 return -EINVAL; 211 212 scanout->buffer = drm_client_buffer_create_dumb(client, width, height, format); 213 if (IS_ERR(scanout->buffer)) { 214 drm_warn(client->dev, "drm_log can't create framebuffer %d %d %p4cc\n", 215 width, height, &format); 216 return -ENOMEM; 217 } 218 mode_set->fb = scanout->buffer->fb; 219 scanout->scaled_font_h = scanout->font->height * scale; 220 scanout->scaled_font_w = scanout->font->width * scale; 221 scanout->rows = height / scanout->scaled_font_h; 222 scanout->columns = width / scanout->scaled_font_w; 223 if (!scanout->rows || !scanout->columns) { 224 drm_client_buffer_delete(scanout->buffer); 225 scanout->buffer = NULL; 226 mode_set->fb = NULL; 227 return -EINVAL; 228 } 229 scanout->front_color = drm_draw_color_from_xrgb8888(0xffffff, format); 230 scanout->prefix_color = drm_draw_color_from_xrgb8888(0x4e9a06, format); 231 return 0; 232 } 233 234 static int drm_log_count_modeset(struct drm_client_dev *client) 235 { 236 struct drm_mode_set *mode_set; 237 int count = 0; 238 239 mutex_lock(&client->modeset_mutex); 240 drm_client_for_each_modeset(mode_set, client) 241 count++; 242 mutex_unlock(&client->modeset_mutex); 243 return count; 244 } 245 246 static void drm_log_init_client(struct drm_log *dlog) 247 { 248 struct drm_client_dev *client = &dlog->client; 249 struct drm_mode_set *mode_set; 250 int i, max_modeset; 251 int n_modeset = 0; 252 253 dlog->probed = true; 254 255 if (drm_client_modeset_probe(client, 0, 0)) 256 return; 257 258 max_modeset = drm_log_count_modeset(client); 259 if (!max_modeset) 260 return; 261 262 dlog->scanout = kzalloc_objs(*dlog->scanout, max_modeset); 263 if (!dlog->scanout) 264 return; 265 266 mutex_lock(&client->modeset_mutex); 267 drm_client_for_each_modeset(mode_set, client) { 268 if (!mode_set->mode) 269 continue; 270 if (drm_log_setup_modeset(client, mode_set, &dlog->scanout[n_modeset])) 271 continue; 272 n_modeset++; 273 } 274 mutex_unlock(&client->modeset_mutex); 275 if (n_modeset == 0) 276 goto err_nomodeset; 277 278 if (drm_client_modeset_commit(client)) 279 goto err_failed_commit; 280 281 dlog->n_scanout = n_modeset; 282 return; 283 284 err_failed_commit: 285 for (i = 0; i < n_modeset; i++) 286 drm_client_buffer_delete(dlog->scanout[i].buffer); 287 288 err_nomodeset: 289 kfree(dlog->scanout); 290 dlog->scanout = NULL; 291 } 292 293 static void drm_log_free_scanout(struct drm_client_dev *client) 294 { 295 struct drm_log *dlog = client_to_drm_log(client); 296 int i; 297 298 if (dlog->n_scanout) { 299 for (i = 0; i < dlog->n_scanout; i++) 300 drm_client_buffer_delete(dlog->scanout[i].buffer); 301 dlog->n_scanout = 0; 302 kfree(dlog->scanout); 303 dlog->scanout = NULL; 304 } 305 } 306 307 static void drm_log_client_free(struct drm_client_dev *client) 308 { 309 struct drm_log *dlog = client_to_drm_log(client); 310 struct drm_device *dev = client->dev; 311 312 kfree(dlog); 313 314 drm_dbg(dev, "Unregistered with drm log\n"); 315 } 316 317 static void drm_log_client_unregister(struct drm_client_dev *client) 318 { 319 struct drm_log *dlog = client_to_drm_log(client); 320 321 unregister_console(&dlog->con); 322 323 mutex_lock(&dlog->lock); 324 drm_log_free_scanout(client); 325 mutex_unlock(&dlog->lock); 326 drm_client_release(client); 327 } 328 329 static int drm_log_client_restore(struct drm_client_dev *client, bool force) 330 { 331 int ret; 332 333 if (force) 334 ret = drm_client_modeset_commit_locked(client); 335 else 336 ret = drm_client_modeset_commit(client); 337 338 return ret; 339 } 340 341 static int drm_log_client_hotplug(struct drm_client_dev *client) 342 { 343 struct drm_log *dlog = client_to_drm_log(client); 344 345 mutex_lock(&dlog->lock); 346 drm_log_free_scanout(client); 347 dlog->probed = false; 348 mutex_unlock(&dlog->lock); 349 return 0; 350 } 351 352 static int drm_log_client_suspend(struct drm_client_dev *client) 353 { 354 struct drm_log *dlog = client_to_drm_log(client); 355 356 console_suspend(&dlog->con); 357 358 return 0; 359 } 360 361 static int drm_log_client_resume(struct drm_client_dev *client) 362 { 363 struct drm_log *dlog = client_to_drm_log(client); 364 365 console_resume(&dlog->con); 366 367 return 0; 368 } 369 370 static const struct drm_client_funcs drm_log_client_funcs = { 371 .owner = THIS_MODULE, 372 .free = drm_log_client_free, 373 .unregister = drm_log_client_unregister, 374 .restore = drm_log_client_restore, 375 .hotplug = drm_log_client_hotplug, 376 .suspend = drm_log_client_suspend, 377 .resume = drm_log_client_resume, 378 }; 379 380 static void drm_log_write_thread(struct console *con, struct nbcon_write_context *wctxt) 381 { 382 struct drm_log *dlog = console_to_drm_log(con); 383 int i; 384 385 if (!dlog->probed) 386 drm_log_init_client(dlog); 387 388 /* Check that we are still the master before drawing */ 389 if (drm_master_internal_acquire(dlog->client.dev)) { 390 drm_master_internal_release(dlog->client.dev); 391 392 for (i = 0; i < dlog->n_scanout; i++) 393 drm_log_draw_kmsg_record(&dlog->scanout[i], wctxt->outbuf, wctxt->len); 394 } 395 } 396 397 static void drm_log_lock(struct console *con, unsigned long *flags) 398 { 399 struct drm_log *dlog = console_to_drm_log(con); 400 401 mutex_lock(&dlog->lock); 402 migrate_disable(); 403 } 404 405 static void drm_log_unlock(struct console *con, unsigned long flags) 406 { 407 struct drm_log *dlog = console_to_drm_log(con); 408 409 migrate_enable(); 410 mutex_unlock(&dlog->lock); 411 } 412 413 static void drm_log_register_console(struct console *con) 414 { 415 strscpy(con->name, "drm_log"); 416 con->write_thread = drm_log_write_thread; 417 con->device_lock = drm_log_lock; 418 con->device_unlock = drm_log_unlock; 419 con->flags = CON_PRINTBUFFER | CON_NBCON; 420 con->index = -1; 421 422 register_console(con); 423 } 424 425 /** 426 * drm_log_register() - Register a drm device to drm_log 427 * @dev: the drm device to register. 428 */ 429 void drm_log_register(struct drm_device *dev) 430 { 431 struct drm_log *new; 432 433 if (!scale) 434 scale = 1; 435 436 new = kzalloc_obj(*new); 437 if (!new) 438 goto err_warn; 439 440 mutex_init(&new->lock); 441 if (drm_client_init(dev, &new->client, "drm_log", &drm_log_client_funcs)) 442 goto err_free; 443 444 drm_client_register(&new->client); 445 446 drm_log_register_console(&new->con); 447 448 drm_dbg(dev, "Registered with drm log as %s\n", new->con.name); 449 return; 450 451 err_free: 452 kfree(new); 453 err_warn: 454 drm_warn(dev, "Failed to register with drm log\n"); 455 } 456