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_atomic_helper.h> 15 #include <drm/drm_gem_framebuffer_helper.h> 16 #include <drm/drm_panic.h> 17 #include <drm/drm_print.h> 18 #include <drm/drm_probe_helper.h> 19 20 #include "drm_sysfb_helper.h" 21 22 struct drm_display_mode drm_sysfb_mode(unsigned int width, 23 unsigned int height, 24 unsigned int width_mm, 25 unsigned int height_mm) 26 { 27 /* 28 * Assume a monitor resolution of 96 dpi to 29 * get a somewhat reasonable screen size. 30 */ 31 if (!width_mm) 32 width_mm = DRM_MODE_RES_MM(width, 96ul); 33 if (!height_mm) 34 height_mm = DRM_MODE_RES_MM(height, 96ul); 35 36 { 37 const struct drm_display_mode mode = { 38 DRM_MODE_INIT(60, width, height, width_mm, height_mm) 39 }; 40 41 return mode; 42 } 43 } 44 EXPORT_SYMBOL(drm_sysfb_mode); 45 46 /* 47 * Plane 48 */ 49 50 static u32 to_nonalpha_fourcc(u32 fourcc) 51 { 52 /* only handle formats with depth != 0 and alpha channel */ 53 switch (fourcc) { 54 case DRM_FORMAT_ARGB1555: 55 return DRM_FORMAT_XRGB1555; 56 case DRM_FORMAT_ABGR1555: 57 return DRM_FORMAT_XBGR1555; 58 case DRM_FORMAT_RGBA5551: 59 return DRM_FORMAT_RGBX5551; 60 case DRM_FORMAT_BGRA5551: 61 return DRM_FORMAT_BGRX5551; 62 case DRM_FORMAT_ARGB8888: 63 return DRM_FORMAT_XRGB8888; 64 case DRM_FORMAT_ABGR8888: 65 return DRM_FORMAT_XBGR8888; 66 case DRM_FORMAT_RGBA8888: 67 return DRM_FORMAT_RGBX8888; 68 case DRM_FORMAT_BGRA8888: 69 return DRM_FORMAT_BGRX8888; 70 case DRM_FORMAT_ARGB2101010: 71 return DRM_FORMAT_XRGB2101010; 72 case DRM_FORMAT_ABGR2101010: 73 return DRM_FORMAT_XBGR2101010; 74 case DRM_FORMAT_RGBA1010102: 75 return DRM_FORMAT_RGBX1010102; 76 case DRM_FORMAT_BGRA1010102: 77 return DRM_FORMAT_BGRX1010102; 78 } 79 80 return fourcc; 81 } 82 83 static bool is_listed_fourcc(const u32 *fourccs, size_t nfourccs, u32 fourcc) 84 { 85 const u32 *fourccs_end = fourccs + nfourccs; 86 87 while (fourccs < fourccs_end) { 88 if (*fourccs == fourcc) 89 return true; 90 ++fourccs; 91 } 92 return false; 93 } 94 95 /** 96 * drm_sysfb_build_fourcc_list - Filters a list of supported color formats against 97 * the device's native formats 98 * @dev: DRM device 99 * @native_fourccs: 4CC codes of natively supported color formats 100 * @native_nfourccs: The number of entries in @native_fourccs 101 * @fourccs_out: Returns 4CC codes of supported color formats 102 * @nfourccs_out: The number of available entries in @fourccs_out 103 * 104 * This function create a list of supported color format from natively 105 * supported formats and additional emulated formats. 106 * At a minimum, most userspace programs expect at least support for 107 * XRGB8888 on the primary plane. Sysfb devices that have to emulate 108 * the format should use drm_sysfb_build_fourcc_list() to create a list 109 * of supported color formats. The returned list can be handed over to 110 * drm_universal_plane_init() et al. Native formats will go before 111 * emulated formats. Native formats with alpha channel will be replaced 112 * by equal formats without alpha channel, as primary planes usually 113 * don't support alpha. Other heuristics might be applied to optimize 114 * the sorting order. Formats near the beginning of the list are usually 115 * preferred over formats near the end of the list. 116 * 117 * Returns: 118 * The number of color-formats 4CC codes returned in @fourccs_out. 119 */ 120 size_t drm_sysfb_build_fourcc_list(struct drm_device *dev, 121 const u32 *native_fourccs, size_t native_nfourccs, 122 u32 *fourccs_out, size_t nfourccs_out) 123 { 124 /* 125 * XRGB8888 is the default fallback format for most of userspace 126 * and it's currently the only format that should be emulated for 127 * the primary plane. Only if there's ever another default fallback, 128 * it should be added here. 129 */ 130 static const u32 extra_fourccs[] = { 131 DRM_FORMAT_XRGB8888, 132 }; 133 static const size_t extra_nfourccs = ARRAY_SIZE(extra_fourccs); 134 135 u32 *fourccs = fourccs_out; 136 const u32 *fourccs_end = fourccs_out + nfourccs_out; 137 size_t i; 138 139 /* 140 * The device's native formats go first. 141 */ 142 143 for (i = 0; i < native_nfourccs; ++i) { 144 /* 145 * Several DTs, boot loaders and firmware report native 146 * alpha formats that are non-alpha formats instead. So 147 * replace alpha formats by non-alpha formats. 148 */ 149 u32 fourcc = to_nonalpha_fourcc(native_fourccs[i]); 150 151 if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) { 152 continue; /* skip duplicate entries */ 153 } else if (fourccs == fourccs_end) { 154 drm_warn(dev, "Ignoring native format %p4cc\n", &fourcc); 155 continue; /* end of available output buffer */ 156 } 157 158 drm_dbg_kms(dev, "adding native format %p4cc\n", &fourcc); 159 160 *fourccs = fourcc; 161 ++fourccs; 162 } 163 164 /* 165 * The extra formats, emulated by the driver, go second. 166 */ 167 168 for (i = 0; (i < extra_nfourccs) && (fourccs < fourccs_end); ++i) { 169 u32 fourcc = extra_fourccs[i]; 170 171 if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) { 172 continue; /* skip duplicate and native entries */ 173 } else if (fourccs == fourccs_end) { 174 drm_warn(dev, "Ignoring emulated format %p4cc\n", &fourcc); 175 continue; /* end of available output buffer */ 176 } 177 178 drm_dbg_kms(dev, "adding emulated format %p4cc\n", &fourcc); 179 180 *fourccs = fourcc; 181 ++fourccs; 182 } 183 184 return fourccs - fourccs_out; 185 } 186 EXPORT_SYMBOL(drm_sysfb_build_fourcc_list); 187 188 int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane, 189 struct drm_atomic_state *new_state) 190 { 191 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 192 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); 193 struct drm_shadow_plane_state *new_shadow_plane_state = 194 to_drm_shadow_plane_state(new_plane_state); 195 struct drm_framebuffer *new_fb = new_plane_state->fb; 196 struct drm_crtc *new_crtc = new_plane_state->crtc; 197 struct drm_crtc_state *new_crtc_state = NULL; 198 struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 199 int ret; 200 201 if (new_crtc) 202 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 203 204 ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 205 DRM_PLANE_NO_SCALING, 206 DRM_PLANE_NO_SCALING, 207 false, false); 208 if (ret) 209 return ret; 210 else if (!new_plane_state->visible) 211 return 0; 212 213 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 214 215 new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state); 216 new_sysfb_crtc_state->format = sysfb->fb_format; 217 218 if (new_fb->format != new_sysfb_crtc_state->format) { 219 void *buf; 220 221 /* format conversion necessary; reserve buffer */ 222 buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state, 223 sysfb->fb_pitch, GFP_KERNEL); 224 if (!buf) 225 return -ENOMEM; 226 } 227 228 return 0; 229 } 230 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check); 231 232 void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) 233 { 234 struct drm_device *dev = plane->dev; 235 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 236 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 237 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 238 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 239 struct drm_framebuffer *fb = plane_state->fb; 240 unsigned int dst_pitch = sysfb->fb_pitch; 241 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); 242 struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 243 const struct drm_format_info *dst_format = sysfb_crtc_state->format; 244 struct drm_atomic_helper_damage_iter iter; 245 struct drm_rect damage; 246 int ret, idx; 247 248 ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 249 if (ret) 250 return; 251 252 if (!drm_dev_enter(dev, &idx)) 253 goto out_drm_gem_fb_end_cpu_access; 254 255 drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 256 drm_atomic_for_each_plane_damage(&iter, &damage) { 257 struct iosys_map dst = sysfb->fb_addr; 258 struct drm_rect dst_clip = plane_state->dst; 259 260 if (!drm_rect_intersect(&dst_clip, &damage)) 261 continue; 262 263 iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); 264 drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, 265 &damage, &shadow_plane_state->fmtcnv_state); 266 } 267 268 drm_dev_exit(idx); 269 out_drm_gem_fb_end_cpu_access: 270 drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 271 } 272 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update); 273 274 void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane, 275 struct drm_atomic_state *state) 276 { 277 struct drm_device *dev = plane->dev; 278 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 279 struct iosys_map dst = sysfb->fb_addr; 280 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 281 void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ 282 unsigned int dst_pitch = sysfb->fb_pitch; 283 const struct drm_format_info *dst_format = sysfb->fb_format; 284 struct drm_rect dst_clip; 285 unsigned long lines, linepixels, i; 286 int idx; 287 288 drm_rect_init(&dst_clip, 289 plane_state->src_x >> 16, plane_state->src_y >> 16, 290 plane_state->src_w >> 16, plane_state->src_h >> 16); 291 292 lines = drm_rect_height(&dst_clip); 293 linepixels = drm_rect_width(&dst_clip); 294 295 if (!drm_dev_enter(dev, &idx)) 296 return; 297 298 /* Clear buffer to black if disabled */ 299 dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); 300 for (i = 0; i < lines; ++i) { 301 memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); 302 dst_vmap += dst_pitch; 303 } 304 305 drm_dev_exit(idx); 306 } 307 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable); 308 309 int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, 310 struct drm_scanout_buffer *sb) 311 { 312 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 313 314 sb->width = sysfb->fb_mode.hdisplay; 315 sb->height = sysfb->fb_mode.vdisplay; 316 sb->format = sysfb->fb_format; 317 sb->pitch[0] = sysfb->fb_pitch; 318 sb->map[0] = sysfb->fb_addr; 319 320 return 0; 321 } 322 EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer); 323 324 /* 325 * CRTC 326 */ 327 328 static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state) 329 { 330 __drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base); 331 332 kfree(sysfb_crtc_state); 333 } 334 335 enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc, 336 const struct drm_display_mode *mode) 337 { 338 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); 339 340 return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode); 341 } 342 EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid); 343 344 int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) 345 { 346 struct drm_device *dev = crtc->dev; 347 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 348 struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); 349 int ret; 350 351 if (!new_crtc_state->enable) 352 return 0; 353 354 ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); 355 if (ret) 356 return ret; 357 358 if (new_crtc_state->color_mgmt_changed) { 359 const size_t gamma_lut_length = 360 sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut); 361 const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut; 362 363 if (gamma_lut && (gamma_lut->length != gamma_lut_length)) { 364 drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length); 365 return -EINVAL; 366 } 367 } 368 369 return 0; 370 } 371 EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check); 372 373 void drm_sysfb_crtc_reset(struct drm_crtc *crtc) 374 { 375 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); 376 struct drm_sysfb_crtc_state *sysfb_crtc_state; 377 378 if (crtc->state) 379 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state)); 380 381 sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL); 382 if (sysfb_crtc_state) { 383 sysfb_crtc_state->format = sysfb->fb_format; 384 __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base); 385 } else { 386 __drm_atomic_helper_crtc_reset(crtc, NULL); 387 } 388 } 389 EXPORT_SYMBOL(drm_sysfb_crtc_reset); 390 391 struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 392 { 393 struct drm_device *dev = crtc->dev; 394 struct drm_crtc_state *crtc_state = crtc->state; 395 struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 396 struct drm_sysfb_crtc_state *sysfb_crtc_state; 397 398 if (drm_WARN_ON(dev, !crtc_state)) 399 return NULL; 400 401 new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL); 402 if (!new_sysfb_crtc_state) 403 return NULL; 404 405 sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 406 407 __drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base); 408 new_sysfb_crtc_state->format = sysfb_crtc_state->format; 409 410 return &new_sysfb_crtc_state->base; 411 } 412 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state); 413 414 void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) 415 { 416 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state)); 417 } 418 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state); 419 420 /* 421 * Connector 422 */ 423 424 static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) 425 { 426 struct drm_sysfb_device *sysfb = data; 427 const u8 *edid = sysfb->edid; 428 size_t off = block * EDID_LENGTH; 429 size_t end = off + len; 430 431 if (!edid) 432 return -EINVAL; 433 if (end > EDID_LENGTH) 434 return -EINVAL; 435 memcpy(buf, &edid[off], len); 436 437 /* 438 * We don't have EDID extensions available and reporting them 439 * will upset DRM helpers. Thus clear the extension field and 440 * update the checksum. Adding the extension flag to the checksum 441 * does this. 442 */ 443 buf[127] += buf[126]; 444 buf[126] = 0; 445 446 return 0; 447 } 448 449 int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector) 450 { 451 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev); 452 const struct drm_edid *drm_edid; 453 454 if (sysfb->edid) { 455 drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb); 456 drm_edid_connector_update(connector, drm_edid); 457 drm_edid_free(drm_edid); 458 } 459 460 /* Return the fixed mode even with EDID */ 461 return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode); 462 } 463 EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes); 464