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