xref: /linux/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c (revision face6a3615a649456eb4549f6d474221d877d604)
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