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 = crtc_state = 242 drm_atomic_get_new_crtc_state(state, plane_state->crtc); 243 struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 244 const struct drm_format_info *dst_format = sysfb_crtc_state->format; 245 struct drm_atomic_helper_damage_iter iter; 246 struct drm_rect damage; 247 int ret, idx; 248 249 ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 250 if (ret) 251 return; 252 253 if (!drm_dev_enter(dev, &idx)) 254 goto out_drm_gem_fb_end_cpu_access; 255 256 drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 257 drm_atomic_for_each_plane_damage(&iter, &damage) { 258 struct iosys_map dst = sysfb->fb_addr; 259 struct drm_rect dst_clip = plane_state->dst; 260 261 if (!drm_rect_intersect(&dst_clip, &damage)) 262 continue; 263 264 iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); 265 drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, 266 &damage, &shadow_plane_state->fmtcnv_state); 267 } 268 269 drm_dev_exit(idx); 270 out_drm_gem_fb_end_cpu_access: 271 drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 272 } 273 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update); 274 275 void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane, 276 struct drm_atomic_state *state) 277 { 278 struct drm_device *dev = plane->dev; 279 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 280 struct iosys_map dst = sysfb->fb_addr; 281 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 282 void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ 283 unsigned int dst_pitch = sysfb->fb_pitch; 284 const struct drm_format_info *dst_format = sysfb->fb_format; 285 struct drm_rect dst_clip; 286 unsigned long lines, linepixels, i; 287 int idx; 288 289 drm_rect_init(&dst_clip, 290 plane_state->src_x >> 16, plane_state->src_y >> 16, 291 plane_state->src_w >> 16, plane_state->src_h >> 16); 292 293 lines = drm_rect_height(&dst_clip); 294 linepixels = drm_rect_width(&dst_clip); 295 296 if (!drm_dev_enter(dev, &idx)) 297 return; 298 299 /* Clear buffer to black if disabled */ 300 dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); 301 for (i = 0; i < lines; ++i) { 302 memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); 303 dst_vmap += dst_pitch; 304 } 305 306 drm_dev_exit(idx); 307 } 308 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable); 309 310 int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, 311 struct drm_scanout_buffer *sb) 312 { 313 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 314 315 sb->width = sysfb->fb_mode.hdisplay; 316 sb->height = sysfb->fb_mode.vdisplay; 317 sb->format = sysfb->fb_format; 318 sb->pitch[0] = sysfb->fb_pitch; 319 sb->map[0] = sysfb->fb_addr; 320 321 return 0; 322 } 323 EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer); 324 325 /* 326 * CRTC 327 */ 328 329 static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state) 330 { 331 __drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base); 332 333 kfree(sysfb_crtc_state); 334 } 335 336 enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc, 337 const struct drm_display_mode *mode) 338 { 339 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); 340 341 return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode); 342 } 343 EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid); 344 345 int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) 346 { 347 struct drm_device *dev = crtc->dev; 348 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 349 struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); 350 int ret; 351 352 if (!new_crtc_state->enable) 353 return 0; 354 355 ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); 356 if (ret) 357 return ret; 358 359 if (new_crtc_state->color_mgmt_changed) { 360 const size_t gamma_lut_length = 361 sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut); 362 const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut; 363 364 if (gamma_lut && (gamma_lut->length != gamma_lut_length)) { 365 drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length); 366 return -EINVAL; 367 } 368 } 369 370 return 0; 371 } 372 EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check); 373 374 void drm_sysfb_crtc_reset(struct drm_crtc *crtc) 375 { 376 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); 377 struct drm_sysfb_crtc_state *sysfb_crtc_state; 378 379 if (crtc->state) 380 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state)); 381 382 sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL); 383 if (sysfb_crtc_state) { 384 sysfb_crtc_state->format = sysfb->fb_format; 385 __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base); 386 } else { 387 __drm_atomic_helper_crtc_reset(crtc, NULL); 388 } 389 } 390 EXPORT_SYMBOL(drm_sysfb_crtc_reset); 391 392 struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 393 { 394 struct drm_device *dev = crtc->dev; 395 struct drm_crtc_state *crtc_state = crtc->state; 396 struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 397 struct drm_sysfb_crtc_state *sysfb_crtc_state; 398 399 if (drm_WARN_ON(dev, !crtc_state)) 400 return NULL; 401 402 new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL); 403 if (!new_sysfb_crtc_state) 404 return NULL; 405 406 sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 407 408 __drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base); 409 new_sysfb_crtc_state->format = sysfb_crtc_state->format; 410 411 return &new_sysfb_crtc_state->base; 412 } 413 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state); 414 415 void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) 416 { 417 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state)); 418 } 419 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state); 420 421 /* 422 * Connector 423 */ 424 425 static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) 426 { 427 struct drm_sysfb_device *sysfb = data; 428 const u8 *edid = sysfb->edid; 429 size_t off = block * EDID_LENGTH; 430 size_t end = off + len; 431 432 if (!edid) 433 return -EINVAL; 434 if (end > EDID_LENGTH) 435 return -EINVAL; 436 memcpy(buf, &edid[off], len); 437 438 /* 439 * We don't have EDID extensions available and reporting them 440 * will upset DRM helpers. Thus clear the extension field and 441 * update the checksum. Adding the extension flag to the checksum 442 * does this. 443 */ 444 buf[127] += buf[126]; 445 buf[126] = 0; 446 447 return 0; 448 } 449 450 int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector) 451 { 452 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev); 453 const struct drm_edid *drm_edid; 454 455 if (sysfb->edid) { 456 drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb); 457 drm_edid_connector_update(connector, drm_edid); 458 drm_edid_free(drm_edid); 459 } 460 461 /* Return the fixed mode even with EDID */ 462 return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode); 463 } 464 EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes); 465