Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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);