File Index
Files and directories
Sign in
FILE

drm_sysfb_modeset.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c C
  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
 21struct 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}
 43EXPORT_SYMBOL(drm_sysfb_mode);
 44
 45/*
 46 * Plane
 47 */
 48
 49static 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
 82static 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 */
119size_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}
185EXPORT_SYMBOL(drm_sysfb_build_fourcc_list);
186
187static 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
194static 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
201static 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
241int 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
279err_drm_gem_end_shadow_fb_access:
280	drm_gem_end_shadow_fb_access(plane, plane_state);
281	return ret;
282}
283EXPORT_SYMBOL(drm_sysfb_plane_helper_begin_fb_access);
284
285int 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}
327EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check);
328
329void 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);
368out_drm_gem_fb_end_cpu_access:
369	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
370}
371EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update);
372
373void 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}
406EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable);
407
408int 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}
421EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer);
422
423void 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}
436EXPORT_SYMBOL(drm_sysfb_plane_reset);
437
438struct 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}
460EXPORT_SYMBOL(drm_sysfb_plane_atomic_duplicate_state);
461
462void 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}
467EXPORT_SYMBOL(drm_sysfb_plane_atomic_destroy_state);
468
469/*
470 * CRTC
471 */
472
473static 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
480enum 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}
487EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid);
488
489int 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}
516EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check);
517
518void 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}
534EXPORT_SYMBOL(drm_sysfb_crtc_reset);
535
536struct 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}
557EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state);
558
559void 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}
563EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state);
564
565/*
566 * Connector
567 */
568
569static 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
594int 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}
608EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);
~/ Read-only file preview.