Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/fb-helper: Disconnect damage worker from update logic

The fbdev helpers implement a damage worker that forwards fbdev
updates to the DRM driver. The worker's update logic depends on
the generic fbdev emulation. Separate the two via function pointer.

The generic fbdev emulation sets struct drm_fb_helper_funcs.fb_dirty,
a new callback that hides the update logic from the damage worker.
It's not possible to use the generic logic with other fbdev emulation,
because it contains additional code for the shadow buffering that
the generic emulation employs.

DRM drivers with internal fbdev emulation can set fb_dirty to their
own implementation if they require damage handling; although no such
drivers currently exist.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20221103151446.2638-16-tzimmermann@suse.de

+61 -29
+46 -29
drivers/gpu/drm/drm_fb_helper.c
··· 448 448 449 449 static void drm_fb_helper_damage_work(struct work_struct *work) 450 450 { 451 - struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, 452 - damage_work); 453 - struct drm_device *dev = helper->dev; 451 + struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, damage_work); 454 452 struct drm_clip_rect *clip = &helper->damage_clip; 455 453 struct drm_clip_rect clip_copy; 456 454 unsigned long flags; 457 455 int ret; 456 + 457 + if (!helper->funcs->fb_dirty) 458 + return; 458 459 459 460 spin_lock_irqsave(&helper->damage_lock, flags); 460 461 clip_copy = *clip; ··· 463 462 clip->x2 = clip->y2 = 0; 464 463 spin_unlock_irqrestore(&helper->damage_lock, flags); 465 464 466 - /* Call damage handlers only if necessary */ 467 - if (!(clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2)) 468 - return; 469 - 470 - if (helper->buffer) { 471 - ret = drm_fb_helper_damage_blit(helper, &clip_copy); 472 - if (drm_WARN_ONCE(dev, ret, "Damage blitter failed: ret=%d\n", ret)) 473 - goto err; 474 - } 475 - 476 - if (helper->fb->funcs->dirty) { 477 - ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1); 478 - if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret)) 479 - goto err; 480 - } 465 + ret = helper->funcs->fb_dirty(helper, &clip_copy); 466 + if (ret) 467 + goto err; 481 468 482 469 return; 483 470 ··· 659 670 } 660 671 EXPORT_SYMBOL(drm_fb_helper_fini); 661 672 662 - static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper) 663 - { 664 - struct drm_device *dev = fb_helper->dev; 665 - struct drm_framebuffer *fb = fb_helper->fb; 666 - 667 - return dev->mode_config.prefer_shadow_fbdev || 668 - dev->mode_config.prefer_shadow || 669 - fb->funcs->dirty; 670 - } 671 - 672 673 static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y, 673 674 u32 width, u32 height) 674 675 { ··· 666 687 struct drm_clip_rect *clip = &helper->damage_clip; 667 688 unsigned long flags; 668 689 669 - if (!drm_fbdev_use_shadow_fb(helper)) 690 + if (!helper->funcs->fb_dirty) 670 691 return; 671 692 672 693 spin_lock_irqsave(&helper->damage_lock, flags); ··· 2090 2111 } 2091 2112 EXPORT_SYMBOL(drm_fb_helper_output_poll_changed); 2092 2113 2114 + static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper) 2115 + { 2116 + struct drm_device *dev = fb_helper->dev; 2117 + struct drm_framebuffer *fb = fb_helper->fb; 2118 + 2119 + return dev->mode_config.prefer_shadow_fbdev || 2120 + dev->mode_config.prefer_shadow || 2121 + fb->funcs->dirty; 2122 + } 2123 + 2093 2124 /* @user: 1=userspace, 0=fbcon */ 2094 2125 static int drm_fbdev_fb_open(struct fb_info *info, int user) 2095 2126 { ··· 2476 2487 return 0; 2477 2488 } 2478 2489 2490 + static int drm_fbdev_fb_dirty(struct drm_fb_helper *helper, struct drm_clip_rect *clip) 2491 + { 2492 + struct drm_device *dev = helper->dev; 2493 + int ret; 2494 + 2495 + if (!drm_fbdev_use_shadow_fb(helper)) 2496 + return 0; 2497 + 2498 + /* Call damage handlers only if necessary */ 2499 + if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2)) 2500 + return 0; 2501 + 2502 + if (helper->buffer) { 2503 + ret = drm_fb_helper_damage_blit(helper, clip); 2504 + if (drm_WARN_ONCE(dev, ret, "Damage blitter failed: ret=%d\n", ret)) 2505 + return ret; 2506 + } 2507 + 2508 + if (helper->fb->funcs->dirty) { 2509 + ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1); 2510 + if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret)) 2511 + return ret; 2512 + } 2513 + 2514 + return 0; 2515 + } 2516 + 2479 2517 static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = { 2480 2518 .fb_probe = drm_fb_helper_generic_probe, 2519 + .fb_dirty = drm_fbdev_fb_dirty, 2481 2520 }; 2482 2521 2483 2522 static void drm_fbdev_client_unregister(struct drm_client_dev *client)
+15
include/drm/drm_fb_helper.h
··· 30 30 #ifndef DRM_FB_HELPER_H 31 31 #define DRM_FB_HELPER_H 32 32 33 + struct drm_clip_rect; 33 34 struct drm_fb_helper; 34 35 35 36 #include <linux/fb.h> ··· 90 89 */ 91 90 int (*fb_probe)(struct drm_fb_helper *helper, 92 91 struct drm_fb_helper_surface_size *sizes); 92 + 93 + /** 94 + * @fb_dirty: 95 + * 96 + * Driver callback to update the framebuffer memory. If set, fbdev 97 + * emulation will invoke this callback in regular intervals after 98 + * the framebuffer has been written. 99 + * 100 + * This callback is optional. 101 + * 102 + * Returns: 103 + * 0 on success, or an error code otherwise. 104 + */ 105 + int (*fb_dirty)(struct drm_fb_helper *helper, struct drm_clip_rect *clip); 93 106 }; 94 107 95 108 /**