1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/export.h> 4 #include <linux/slab.h> 5 6 #include <drm/drm_atomic.h> 7 #include <drm/drm_atomic_helper.h> 8 #include <drm/drm_atomic_state_helper.h> 9 #include <drm/drm_damage_helper.h> 10 #include <drm/drm_drv.h> 11 #include <drm/drm_edid.h> 12 #include <drm/drm_fourcc.h> 13 #include <drm/drm_framebuffer.h> 14 #include <drm/drm_gem_framebuffer_helper.h> 15 #include <drm/drm_panic.h> 16 #include <drm/drm_print.h> 17 #include <drm/drm_probe_helper.h> 18 19 #include "drm_sysfb_helper.h" 20 21 struct drm_display_mode drm_sysfb_mode(unsigned int width, 22 unsigned int height, 23 unsigned int width_mm, 24 unsigned int height_mm) 25 { 26 /* 27 * Assume a monitor resolution of 96 dpi to 28 * get a somewhat reasonable screen size. 29 */ 30 if (!width_mm) 31 width_mm = DRM_MODE_RES_MM(width, 96ul); 32 if (!height_mm) 33 height_mm = DRM_MODE_RES_MM(height, 96ul); 34 35 { 36 const struct drm_display_mode mode = { 37 DRM_MODE_INIT(60, width, height, width_mm, height_mm) 38 }; 39 40 return mode; 41 } 42 } 43 EXPORT_SYMBOL(drm_sysfb_mode); 44 45 /* 46 * Plane 47 */ 48 49 static u32 to_nonalpha_fourcc(u32 fourcc) 50 { 51 /* only handle formats with depth != 0 and alpha channel */ 52 switch (fourcc) { 53 case DRM_FORMAT_ARGB1555: 54 return DRM_FORMAT_XRGB1555; 55 case DRM_FORMAT_ABGR1555: 56 return DRM_FORMAT_XBGR1555; 57 case DRM_FORMAT_RGBA5551: 58 return DRM_FORMAT_RGBX5551; 59 case DRM_FORMAT_BGRA5551: 60 return DRM_FORMAT_BGRX5551; 61 case DRM_FORMAT_ARGB8888: 62 return DRM_FORMAT_XRGB8888; 63 case DRM_FORMAT_ABGR8888: 64 return DRM_FORMAT_XBGR8888; 65 case DRM_FORMAT_RGBA8888: 66 return DRM_FORMAT_RGBX8888; 67 case DRM_FORMAT_BGRA8888: 68 return DRM_FORMAT_BGRX8888; 69 case DRM_FORMAT_ARGB2101010: 70 return DRM_FORMAT_XRGB2101010; 71 case DRM_FORMAT_ABGR2101010: 72 return DRM_FORMAT_XBGR2101010; 73 case DRM_FORMAT_RGBA1010102: 74 return DRM_FORMAT_RGBX1010102; 75 case DRM_FORMAT_BGRA1010102: 76 return DRM_FORMAT_BGRX1010102; 77 } 78 79 return fourcc; 80 } 81 82 static bool is_listed_fourcc(const u32 *fourccs, size_t nfourccs, u32 fourcc) 83 { 84 const u32 *fourccs_end = fourccs + nfourccs; 85 86 while (fourccs < fourccs_end) { 87 if (*fourccs == fourcc) 88 return true; 89 ++fourccs; 90 } 91 return false; 92 } 93 94 /** 95 * drm_sysfb_build_fourcc_list - Filters a list of supported color formats against 96 * the device's native formats 97 * @dev: DRM device 98 * @native_fourccs: 4CC codes of natively supported color formats 99 * @native_nfourccs: The number of entries in @native_fourccs 100 * @fourccs_out: Returns 4CC codes of supported color formats 101 * @nfourccs_out: The number of available entries in @fourccs_out 102 * 103 * This function create a list of supported color format from natively 104 * supported formats and additional emulated formats. 105 * At a minimum, most userspace programs expect at least support for 106 * XRGB8888 on the primary plane. Sysfb devices that have to emulate 107 * the format should use drm_sysfb_build_fourcc_list() to create a list 108 * of supported color formats. The returned list can be handed over to 109 * drm_universal_plane_init() et al. Native formats will go before 110 * emulated formats. Native formats with alpha channel will be replaced 111 * by equal formats without alpha channel, as primary planes usually 112 * don't support alpha. Other heuristics might be applied to optimize 113 * the sorting order. Formats near the beginning of the list are usually 114 * preferred over formats near the end of the list. 115 * 116 * Returns: 117 * The number of color-formats 4CC codes returned in @fourccs_out. 118 */ 119 size_t drm_sysfb_build_fourcc_list(struct drm_device *dev, 120 const u32 *native_fourccs, size_t native_nfourccs, 121 u32 *fourccs_out, size_t nfourccs_out) 122 { 123 /* 124 * XRGB8888 is the default fallback format for most of userspace 125 * and it's currently the only format that should be emulated for 126 * the primary plane. Only if there's ever another default fallback, 127 * it should be added here. 128 */ 129 static const u32 extra_fourccs[] = { 130 DRM_FORMAT_XRGB8888, 131 }; 132 static const size_t extra_nfourccs = ARRAY_SIZE(extra_fourccs); 133 134 u32 *fourccs = fourccs_out; 135 const u32 *fourccs_end = fourccs_out + nfourccs_out; 136 size_t i; 137 138 /* 139 * The device's native formats go first. 140 */ 141 142 for (i = 0; i < native_nfourccs; ++i) { 143 /* 144 * Several DTs, boot loaders and firmware report native 145 * alpha formats that are non-alpha formats instead. So 146 * replace alpha formats by non-alpha formats. 147 */ 148 u32 fourcc = to_nonalpha_fourcc(native_fourccs[i]); 149 150 if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) { 151 continue; /* skip duplicate entries */ 152 } else if (fourccs == fourccs_end) { 153 drm_warn(dev, "Ignoring native format %p4cc\n", &fourcc); 154 continue; /* end of available output buffer */ 155 } 156 157 drm_dbg_kms(dev, "adding native format %p4cc\n", &fourcc); 158 159 *fourccs = fourcc; 160 ++fourccs; 161 } 162 163 /* 164 * The extra formats, emulated by the driver, go second. 165 */ 166 167 for (i = 0; (i < extra_nfourccs) && (fourccs < fourccs_end); ++i) { 168 u32 fourcc = extra_fourccs[i]; 169 170 if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) { 171 continue; /* skip duplicate and native entries */ 172 } else if (fourccs == fourccs_end) { 173 drm_warn(dev, "Ignoring emulated format %p4cc\n", &fourcc); 174 continue; /* end of available output buffer */ 175 } 176 177 drm_dbg_kms(dev, "adding emulated format %p4cc\n", &fourcc); 178 179 *fourccs = fourcc; 180 ++fourccs; 181 } 182 183 return fourccs - fourccs_out; 184 } 185 EXPORT_SYMBOL(drm_sysfb_build_fourcc_list); 186 187 static void drm_sysfb_plane_state_destroy(struct drm_sysfb_plane_state *sysfb_plane_state) 188 { 189 __drm_gem_destroy_shadow_plane_state(&sysfb_plane_state->base); 190 191 kfree(sysfb_plane_state); 192 } 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_new_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 285 int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane, 286 struct drm_atomic_state *new_state) 287 { 288 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 289 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); 290 struct drm_shadow_plane_state *new_shadow_plane_state = 291 to_drm_shadow_plane_state(new_plane_state); 292 struct drm_framebuffer *new_fb = new_plane_state->fb; 293 struct drm_crtc *new_crtc = new_plane_state->crtc; 294 struct drm_crtc_state *new_crtc_state = NULL; 295 struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 296 int ret; 297 298 if (new_crtc) 299 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 300 301 ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 302 DRM_PLANE_NO_SCALING, 303 DRM_PLANE_NO_SCALING, 304 false, false); 305 if (ret) 306 return ret; 307 else if (!new_plane_state->visible) 308 return 0; 309 310 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 311 312 new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state); 313 new_sysfb_crtc_state->format = sysfb->fb_format; 314 315 if (new_fb->format != new_sysfb_crtc_state->format) { 316 void *buf; 317 318 /* format conversion necessary; reserve buffer */ 319 buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state, 320 sysfb->fb_pitch, GFP_KERNEL); 321 if (!buf) 322 return -ENOMEM; 323 } 324 325 return 0; 326 } 327 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check); 328 329 void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) 330 { 331 struct drm_device *dev = plane->dev; 332 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 333 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 334 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 335 struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); 336 struct drm_shadow_plane_state *shadow_plane_state = &sysfb_plane_state->base; 337 struct drm_framebuffer *fb = plane_state->fb; 338 unsigned int dst_pitch = sysfb->fb_pitch; 339 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); 340 struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 341 const struct drm_format_info *dst_format = sysfb_crtc_state->format; 342 drm_sysfb_blit_func blit_to_crtc = sysfb_plane_state->blit_to_crtc; 343 struct drm_atomic_helper_damage_iter iter; 344 struct drm_rect damage; 345 int ret, idx; 346 347 ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 348 if (ret) 349 return; 350 351 if (!drm_dev_enter(dev, &idx)) 352 goto out_drm_gem_fb_end_cpu_access; 353 354 drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 355 drm_atomic_for_each_plane_damage(&iter, &damage) { 356 struct iosys_map dst = sysfb->fb_addr; 357 struct drm_rect dst_clip = plane_state->dst; 358 359 if (!drm_rect_intersect(&dst_clip, &damage)) 360 continue; 361 362 iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); 363 blit_to_crtc(&dst, &dst_pitch, shadow_plane_state->data, fb, &damage, 364 &shadow_plane_state->fmtcnv_state); 365 } 366 367 drm_dev_exit(idx); 368 out_drm_gem_fb_end_cpu_access: 369 drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 370 } 371 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update); 372 373 void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane, 374 struct drm_atomic_state *state) 375 { 376 struct drm_device *dev = plane->dev; 377 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 378 struct iosys_map dst = sysfb->fb_addr; 379 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 380 void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ 381 unsigned int dst_pitch = sysfb->fb_pitch; 382 const struct drm_format_info *dst_format = sysfb->fb_format; 383 struct drm_rect dst_clip; 384 unsigned long lines, linepixels, i; 385 int idx; 386 387 drm_rect_init(&dst_clip, 388 plane_state->src_x >> 16, plane_state->src_y >> 16, 389 plane_state->src_w >> 16, plane_state->src_h >> 16); 390 391 lines = drm_rect_height(&dst_clip); 392 linepixels = drm_rect_width(&dst_clip); 393 394 if (!drm_dev_enter(dev, &idx)) 395 return; 396 397 /* Clear buffer to black if disabled */ 398 dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); 399 for (i = 0; i < lines; ++i) { 400 memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); 401 dst_vmap += dst_pitch; 402 } 403 404 drm_dev_exit(idx); 405 } 406 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable); 407 408 int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, 409 struct drm_scanout_buffer *sb) 410 { 411 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 412 413 sb->width = sysfb->fb_mode.hdisplay; 414 sb->height = sysfb->fb_mode.vdisplay; 415 sb->format = sysfb->fb_format; 416 sb->pitch[0] = sysfb->fb_pitch; 417 sb->map[0] = sysfb->fb_addr; 418 419 return 0; 420 } 421 EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer); 422 423 void drm_sysfb_plane_reset(struct drm_plane *plane) 424 { 425 struct drm_sysfb_plane_state *sysfb_plane_state; 426 427 if (plane->state) 428 drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane->state)); 429 430 sysfb_plane_state = kzalloc(sizeof(*sysfb_plane_state), GFP_KERNEL); 431 if (sysfb_plane_state) 432 __drm_gem_reset_shadow_plane(plane, &sysfb_plane_state->base); 433 else 434 __drm_gem_reset_shadow_plane(plane, NULL); 435 } 436 EXPORT_SYMBOL(drm_sysfb_plane_reset); 437 438 struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane) 439 { 440 struct drm_device *dev = plane->dev; 441 struct drm_plane_state *plane_state = plane->state; 442 struct drm_sysfb_plane_state *sysfb_plane_state; 443 struct drm_sysfb_plane_state *new_sysfb_plane_state; 444 struct drm_shadow_plane_state *new_shadow_plane_state; 445 446 if (drm_WARN_ON(dev, !plane_state)) 447 return NULL; 448 sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); 449 450 new_sysfb_plane_state = kzalloc(sizeof(*new_sysfb_plane_state), GFP_KERNEL); 451 if (!new_sysfb_plane_state) 452 return NULL; 453 new_shadow_plane_state = &new_sysfb_plane_state->base; 454 455 __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); 456 new_sysfb_plane_state->blit_to_crtc = sysfb_plane_state->blit_to_crtc; 457 458 return &new_shadow_plane_state->base; 459 } 460 EXPORT_SYMBOL(drm_sysfb_plane_atomic_duplicate_state); 461 462 void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane, 463 struct drm_plane_state *plane_state) 464 { 465 drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane_state)); 466 } 467 EXPORT_SYMBOL(drm_sysfb_plane_atomic_destroy_state); 468 469 /* 470 * CRTC 471 */ 472 473 static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state) 474 { 475 __drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base); 476 477 kfree(sysfb_crtc_state); 478 } 479 480 enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc, 481 const struct drm_display_mode *mode) 482 { 483 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); 484 485 return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode); 486 } 487 EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid); 488 489 int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) 490 { 491 struct drm_device *dev = crtc->dev; 492 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 493 struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); 494 int ret; 495 496 if (!new_crtc_state->enable) 497 return 0; 498 499 ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); 500 if (ret) 501 return ret; 502 503 if (new_crtc_state->color_mgmt_changed) { 504 const size_t gamma_lut_length = 505 sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut); 506 const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut; 507 508 if (gamma_lut && (gamma_lut->length != gamma_lut_length)) { 509 drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length); 510 return -EINVAL; 511 } 512 } 513 514 return 0; 515 } 516 EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check); 517 518 void drm_sysfb_crtc_reset(struct drm_crtc *crtc) 519 { 520 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); 521 struct drm_sysfb_crtc_state *sysfb_crtc_state; 522 523 if (crtc->state) 524 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state)); 525 526 sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL); 527 if (sysfb_crtc_state) { 528 sysfb_crtc_state->format = sysfb->fb_format; 529 __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base); 530 } else { 531 __drm_atomic_helper_crtc_reset(crtc, NULL); 532 } 533 } 534 EXPORT_SYMBOL(drm_sysfb_crtc_reset); 535 536 struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 537 { 538 struct drm_device *dev = crtc->dev; 539 struct drm_crtc_state *crtc_state = crtc->state; 540 struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 541 struct drm_sysfb_crtc_state *sysfb_crtc_state; 542 543 if (drm_WARN_ON(dev, !crtc_state)) 544 return NULL; 545 546 new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL); 547 if (!new_sysfb_crtc_state) 548 return NULL; 549 550 sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 551 552 __drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base); 553 new_sysfb_crtc_state->format = sysfb_crtc_state->format; 554 555 return &new_sysfb_crtc_state->base; 556 } 557 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state); 558 559 void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) 560 { 561 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state)); 562 } 563 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state); 564 565 /* 566 * Connector 567 */ 568 569 static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) 570 { 571 struct drm_sysfb_device *sysfb = data; 572 const u8 *edid = sysfb->edid; 573 size_t off = block * EDID_LENGTH; 574 size_t end = off + len; 575 576 if (!edid) 577 return -EINVAL; 578 if (end > EDID_LENGTH) 579 return -EINVAL; 580 memcpy(buf, &edid[off], len); 581 582 /* 583 * We don't have EDID extensions available and reporting them 584 * will upset DRM helpers. Thus clear the extension field and 585 * update the checksum. Adding the extension flag to the checksum 586 * does this. 587 */ 588 buf[127] += buf[126]; 589 buf[126] = 0; 590 591 return 0; 592 } 593 594 int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector) 595 { 596 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev); 597 const struct drm_edid *drm_edid; 598 599 if (sysfb->edid) { 600 drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb); 601 drm_edid_connector_update(connector, drm_edid); 602 drm_edid_free(drm_edid); 603 } 604 605 /* Return the fixed mode even with EDID */ 606 return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode); 607 } 608 EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes); 609