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

drm/sysfb: Lookup blit function during atomic check

Some configurations of sysfb outputs require format conversion from
framebuffer to scanout buffer. It is a driver bug if the conversion
helper is missing, yet it might happen on odd scanout formats. The old
code, based on drm_fb_blit(), only detects this situation during the
commit's hardware update, which is too late to abort the update.

Lookup the correct blit helper as part of the check phase. Then store
it in the sysfb plane state. Allows for detection of a missing helper
before the commit has started. Also avoids drm_fb_blit()'s large switch
statement on each updated scanline. Only a single lookup has to be done.

The lookup is in drm_sysfb_get_blit_func(), which only tracks formats
supported by sysfb drivers.

The lookup happens in sysfb's begin_fb_access helper instead of its
atomic_check helper. This allows vesadrm, and possibly other drivers,
to implement their own atomic_check without interfering with blit
lookups. Vesadrm implements XRGB8888 on top of R8 formats with the
help of the atomic_check. Doing the blit lookup in begin_fb_access then
always uses the correct CRTC format on all drivers.

v2:
- vesadrm: use drm_sysfb_plane_helper_begin_fb_access()
- fix type in commit description (Javier)

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://lore.kernel.org/r/20250918154207.84714-3-tzimmermann@suse.de

+114 -5
+13 -1
drivers/gpu/drm/sysfb/drm_sysfb_helper.h
··· 17 17 struct drm_scanout_buffer; 18 18 struct screen_info; 19 19 20 + typedef void (*drm_sysfb_blit_func)(struct iosys_map *, const unsigned int *, 21 + const struct iosys_map *, 22 + const struct drm_framebuffer *, 23 + const struct drm_rect *, 24 + struct drm_format_conv_state *); 25 + 20 26 /* 21 27 * Input parsing 22 28 */ ··· 102 96 103 97 struct drm_sysfb_plane_state { 104 98 struct drm_shadow_plane_state base; 99 + 100 + /* transfers framebuffer data to scanout buffer in CRTC format */ 101 + drm_sysfb_blit_func blit_to_crtc; 105 102 }; 106 103 107 104 static inline struct drm_sysfb_plane_state * ··· 117 108 const u32 *native_fourccs, size_t native_nfourccs, 118 109 u32 *fourccs_out, size_t nfourccs_out); 119 110 111 + int drm_sysfb_plane_helper_begin_fb_access(struct drm_plane *plane, 112 + struct drm_plane_state *plane_state); 120 113 int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane, 121 114 struct drm_atomic_state *new_state); 122 115 void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, ··· 136 125 DRM_FORMAT_MOD_INVALID 137 126 138 127 #define DRM_SYSFB_PLANE_HELPER_FUNCS \ 139 - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, \ 128 + .begin_fb_access = drm_sysfb_plane_helper_begin_fb_access, \ 129 + .end_fb_access = drm_gem_end_shadow_fb_access, \ 140 130 .atomic_check = drm_sysfb_plane_helper_atomic_check, \ 141 131 .atomic_update = drm_sysfb_plane_helper_atomic_update, \ 142 132 .atomic_disable = drm_sysfb_plane_helper_atomic_disable, \
+99 -3
drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
··· 191 191 kfree(sysfb_plane_state); 192 192 } 193 193 194 + static void drm_sysfb_memcpy(struct iosys_map *dst, const unsigned int *dst_pitch, 195 + const struct iosys_map *src, const struct drm_framebuffer *fb, 196 + const struct drm_rect *clip, struct drm_format_conv_state *state) 197 + { 198 + drm_fb_memcpy(dst, dst_pitch, src, fb, clip); 199 + } 200 + 201 + static drm_sysfb_blit_func drm_sysfb_get_blit_func(u32 dst_format, u32 src_format) 202 + { 203 + if (src_format == dst_format) { 204 + return drm_sysfb_memcpy; 205 + } else if (src_format == DRM_FORMAT_XRGB8888) { 206 + switch (dst_format) { 207 + case DRM_FORMAT_RGB565: 208 + return drm_fb_xrgb8888_to_rgb565; 209 + case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN: 210 + return drm_fb_xrgb8888_to_rgb565be; 211 + case DRM_FORMAT_XRGB1555: 212 + return drm_fb_xrgb8888_to_xrgb1555; 213 + case DRM_FORMAT_ARGB1555: 214 + return drm_fb_xrgb8888_to_argb1555; 215 + case DRM_FORMAT_RGBA5551: 216 + return drm_fb_xrgb8888_to_rgba5551; 217 + case DRM_FORMAT_RGB888: 218 + return drm_fb_xrgb8888_to_rgb888; 219 + case DRM_FORMAT_BGR888: 220 + return drm_fb_xrgb8888_to_bgr888; 221 + case DRM_FORMAT_ARGB8888: 222 + return drm_fb_xrgb8888_to_argb8888; 223 + case DRM_FORMAT_XBGR8888: 224 + return drm_fb_xrgb8888_to_xbgr8888; 225 + case DRM_FORMAT_ABGR8888: 226 + return drm_fb_xrgb8888_to_abgr8888; 227 + case DRM_FORMAT_XRGB2101010: 228 + return drm_fb_xrgb8888_to_xrgb2101010; 229 + case DRM_FORMAT_ARGB2101010: 230 + return drm_fb_xrgb8888_to_argb2101010; 231 + case DRM_FORMAT_BGRX8888: 232 + return drm_fb_xrgb8888_to_bgrx8888; 233 + case DRM_FORMAT_RGB332: 234 + return drm_fb_xrgb8888_to_rgb332; 235 + } 236 + } 237 + 238 + return NULL; 239 + } 240 + 241 + int drm_sysfb_plane_helper_begin_fb_access(struct drm_plane *plane, 242 + struct drm_plane_state *plane_state) 243 + { 244 + struct drm_device *dev = plane->dev; 245 + struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); 246 + struct drm_framebuffer *fb = plane_state->fb; 247 + struct drm_crtc_state *crtc_state; 248 + struct drm_sysfb_crtc_state *sysfb_crtc_state; 249 + drm_sysfb_blit_func blit_to_crtc; 250 + int ret; 251 + 252 + ret = drm_gem_begin_shadow_fb_access(plane, plane_state); 253 + if (ret) 254 + return ret; 255 + 256 + if (!fb) 257 + return 0; 258 + 259 + ret = -EINVAL; 260 + 261 + crtc_state = drm_atomic_get_crtc_state(plane_state->state, plane_state->crtc); 262 + if (drm_WARN_ON_ONCE(dev, !crtc_state)) 263 + goto err_drm_gem_end_shadow_fb_access; 264 + sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 265 + 266 + if (drm_WARN_ON_ONCE(dev, !sysfb_crtc_state->format)) 267 + goto err_drm_gem_end_shadow_fb_access; 268 + blit_to_crtc = drm_sysfb_get_blit_func(sysfb_crtc_state->format->format, 269 + fb->format->format); 270 + if (!blit_to_crtc) { 271 + drm_warn_once(dev, "No blit helper from %p4cc to %p4cc found.\n", 272 + &fb->format->format, &sysfb_crtc_state->format->format); 273 + goto err_drm_gem_end_shadow_fb_access; 274 + } 275 + sysfb_plane_state->blit_to_crtc = blit_to_crtc; 276 + 277 + return 0; 278 + 279 + err_drm_gem_end_shadow_fb_access: 280 + drm_gem_end_shadow_fb_access(plane, plane_state); 281 + return ret; 282 + } 283 + EXPORT_SYMBOL(drm_sysfb_plane_helper_begin_fb_access); 284 + 194 285 int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane, 195 286 struct drm_atomic_state *new_state) 196 287 { ··· 332 241 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 333 242 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 334 243 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 335 - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 244 + struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); 245 + struct drm_shadow_plane_state *shadow_plane_state = &sysfb_plane_state->base; 336 246 struct drm_framebuffer *fb = plane_state->fb; 337 247 unsigned int dst_pitch = sysfb->fb_pitch; 338 248 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); 339 249 struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 340 250 const struct drm_format_info *dst_format = sysfb_crtc_state->format; 251 + drm_sysfb_blit_func blit_to_crtc = sysfb_plane_state->blit_to_crtc; 341 252 struct drm_atomic_helper_damage_iter iter; 342 253 struct drm_rect damage; 343 254 int ret, idx; ··· 360 267 continue; 361 268 362 269 iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); 363 - drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, 364 - &damage, &shadow_plane_state->fmtcnv_state); 270 + blit_to_crtc(&dst, &dst_pitch, shadow_plane_state->data, fb, &damage, 271 + &shadow_plane_state->fmtcnv_state); 365 272 } 366 273 367 274 drm_dev_exit(idx); ··· 439 346 { 440 347 struct drm_device *dev = plane->dev; 441 348 struct drm_plane_state *plane_state = plane->state; 349 + struct drm_sysfb_plane_state *sysfb_plane_state; 442 350 struct drm_sysfb_plane_state *new_sysfb_plane_state; 443 351 struct drm_shadow_plane_state *new_shadow_plane_state; 444 352 445 353 if (drm_WARN_ON(dev, !plane_state)) 446 354 return NULL; 355 + sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); 447 356 448 357 new_sysfb_plane_state = kzalloc(sizeof(*new_sysfb_plane_state), GFP_KERNEL); 449 358 if (!new_sysfb_plane_state) ··· 453 358 new_shadow_plane_state = &new_sysfb_plane_state->base; 454 359 455 360 __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); 361 + new_sysfb_plane_state->blit_to_crtc = sysfb_plane_state->blit_to_crtc; 456 362 457 363 return &new_shadow_plane_state->base; 458 364 }
+2 -1
drivers/gpu/drm/sysfb/vesadrm.c
··· 295 295 } 296 296 297 297 static const struct drm_plane_helper_funcs vesadrm_primary_plane_helper_funcs = { 298 - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 298 + .begin_fb_access = drm_sysfb_plane_helper_begin_fb_access, 299 + .end_fb_access = drm_gem_end_shadow_fb_access, 299 300 .atomic_check = vesadrm_primary_plane_helper_atomic_check, 300 301 .atomic_update = drm_sysfb_plane_helper_atomic_update, 301 302 .atomic_disable = drm_sysfb_plane_helper_atomic_disable,