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