xref: /linux/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c (revision e45f72b6782f88ed50932033ad206df5dd3d7103)
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 int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,
195 					struct drm_atomic_state *new_state)
196 {
197 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
198 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
199 	struct drm_shadow_plane_state *new_shadow_plane_state =
200 		to_drm_shadow_plane_state(new_plane_state);
201 	struct drm_framebuffer *new_fb = new_plane_state->fb;
202 	struct drm_crtc *new_crtc = new_plane_state->crtc;
203 	struct drm_crtc_state *new_crtc_state = NULL;
204 	struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
205 	int ret;
206 
207 	if (new_crtc)
208 		new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
209 
210 	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
211 						  DRM_PLANE_NO_SCALING,
212 						  DRM_PLANE_NO_SCALING,
213 						  false, false);
214 	if (ret)
215 		return ret;
216 	else if (!new_plane_state->visible)
217 		return 0;
218 
219 	new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
220 
221 	new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state);
222 	new_sysfb_crtc_state->format = sysfb->fb_format;
223 
224 	if (new_fb->format != new_sysfb_crtc_state->format) {
225 		void *buf;
226 
227 		/* format conversion necessary; reserve buffer */
228 		buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state,
229 						    sysfb->fb_pitch, GFP_KERNEL);
230 		if (!buf)
231 			return -ENOMEM;
232 	}
233 
234 	return 0;
235 }
236 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check);
237 
238 void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
239 {
240 	struct drm_device *dev = plane->dev;
241 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
242 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
243 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
244 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
245 	struct drm_framebuffer *fb = plane_state->fb;
246 	unsigned int dst_pitch = sysfb->fb_pitch;
247 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
248 	struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
249 	const struct drm_format_info *dst_format = sysfb_crtc_state->format;
250 	struct drm_atomic_helper_damage_iter iter;
251 	struct drm_rect damage;
252 	int ret, idx;
253 
254 	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
255 	if (ret)
256 		return;
257 
258 	if (!drm_dev_enter(dev, &idx))
259 		goto out_drm_gem_fb_end_cpu_access;
260 
261 	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
262 	drm_atomic_for_each_plane_damage(&iter, &damage) {
263 		struct iosys_map dst = sysfb->fb_addr;
264 		struct drm_rect dst_clip = plane_state->dst;
265 
266 		if (!drm_rect_intersect(&dst_clip, &damage))
267 			continue;
268 
269 		iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip));
270 		drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb,
271 			    &damage, &shadow_plane_state->fmtcnv_state);
272 	}
273 
274 	drm_dev_exit(idx);
275 out_drm_gem_fb_end_cpu_access:
276 	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
277 }
278 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update);
279 
280 void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane,
281 					   struct drm_atomic_state *state)
282 {
283 	struct drm_device *dev = plane->dev;
284 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
285 	struct iosys_map dst = sysfb->fb_addr;
286 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
287 	void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */
288 	unsigned int dst_pitch = sysfb->fb_pitch;
289 	const struct drm_format_info *dst_format = sysfb->fb_format;
290 	struct drm_rect dst_clip;
291 	unsigned long lines, linepixels, i;
292 	int idx;
293 
294 	drm_rect_init(&dst_clip,
295 		      plane_state->src_x >> 16, plane_state->src_y >> 16,
296 		      plane_state->src_w >> 16, plane_state->src_h >> 16);
297 
298 	lines = drm_rect_height(&dst_clip);
299 	linepixels = drm_rect_width(&dst_clip);
300 
301 	if (!drm_dev_enter(dev, &idx))
302 		return;
303 
304 	/* Clear buffer to black if disabled */
305 	dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip);
306 	for (i = 0; i < lines; ++i) {
307 		memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]);
308 		dst_vmap += dst_pitch;
309 	}
310 
311 	drm_dev_exit(idx);
312 }
313 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable);
314 
315 int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
316 					      struct drm_scanout_buffer *sb)
317 {
318 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
319 
320 	sb->width = sysfb->fb_mode.hdisplay;
321 	sb->height = sysfb->fb_mode.vdisplay;
322 	sb->format = sysfb->fb_format;
323 	sb->pitch[0] = sysfb->fb_pitch;
324 	sb->map[0] = sysfb->fb_addr;
325 
326 	return 0;
327 }
328 EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer);
329 
330 void drm_sysfb_plane_reset(struct drm_plane *plane)
331 {
332 	struct drm_sysfb_plane_state *sysfb_plane_state;
333 
334 	if (plane->state)
335 		drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane->state));
336 
337 	sysfb_plane_state = kzalloc(sizeof(*sysfb_plane_state), GFP_KERNEL);
338 	if (sysfb_plane_state)
339 		__drm_gem_reset_shadow_plane(plane, &sysfb_plane_state->base);
340 	else
341 		__drm_gem_reset_shadow_plane(plane, NULL);
342 }
343 EXPORT_SYMBOL(drm_sysfb_plane_reset);
344 
345 struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane)
346 {
347 	struct drm_device *dev = plane->dev;
348 	struct drm_plane_state *plane_state = plane->state;
349 	struct drm_sysfb_plane_state *new_sysfb_plane_state;
350 	struct drm_shadow_plane_state *new_shadow_plane_state;
351 
352 	if (drm_WARN_ON(dev, !plane_state))
353 		return NULL;
354 
355 	new_sysfb_plane_state = kzalloc(sizeof(*new_sysfb_plane_state), GFP_KERNEL);
356 	if (!new_sysfb_plane_state)
357 		return NULL;
358 	new_shadow_plane_state = &new_sysfb_plane_state->base;
359 
360 	__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
361 
362 	return &new_shadow_plane_state->base;
363 }
364 EXPORT_SYMBOL(drm_sysfb_plane_atomic_duplicate_state);
365 
366 void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane,
367 					  struct drm_plane_state *plane_state)
368 {
369 	drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane_state));
370 }
371 EXPORT_SYMBOL(drm_sysfb_plane_atomic_destroy_state);
372 
373 /*
374  * CRTC
375  */
376 
377 static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state)
378 {
379 	__drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base);
380 
381 	kfree(sysfb_crtc_state);
382 }
383 
384 enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc,
385 						      const struct drm_display_mode *mode)
386 {
387 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev);
388 
389 	return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode);
390 }
391 EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid);
392 
393 int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state)
394 {
395 	struct drm_device *dev = crtc->dev;
396 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
397 	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
398 	int ret;
399 
400 	if (!new_crtc_state->enable)
401 		return 0;
402 
403 	ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
404 	if (ret)
405 		return ret;
406 
407 	if (new_crtc_state->color_mgmt_changed) {
408 		const size_t gamma_lut_length =
409 			sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut);
410 		const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut;
411 
412 		if (gamma_lut && (gamma_lut->length != gamma_lut_length)) {
413 			drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length);
414 			return -EINVAL;
415 		}
416 	}
417 
418 	return 0;
419 }
420 EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check);
421 
422 void drm_sysfb_crtc_reset(struct drm_crtc *crtc)
423 {
424 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev);
425 	struct drm_sysfb_crtc_state *sysfb_crtc_state;
426 
427 	if (crtc->state)
428 		drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state));
429 
430 	sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL);
431 	if (sysfb_crtc_state) {
432 		sysfb_crtc_state->format = sysfb->fb_format;
433 		__drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base);
434 	} else {
435 		__drm_atomic_helper_crtc_reset(crtc, NULL);
436 	}
437 }
438 EXPORT_SYMBOL(drm_sysfb_crtc_reset);
439 
440 struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
441 {
442 	struct drm_device *dev = crtc->dev;
443 	struct drm_crtc_state *crtc_state = crtc->state;
444 	struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
445 	struct drm_sysfb_crtc_state *sysfb_crtc_state;
446 
447 	if (drm_WARN_ON(dev, !crtc_state))
448 		return NULL;
449 
450 	new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL);
451 	if (!new_sysfb_crtc_state)
452 		return NULL;
453 
454 	sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
455 
456 	__drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base);
457 	new_sysfb_crtc_state->format = sysfb_crtc_state->format;
458 
459 	return &new_sysfb_crtc_state->base;
460 }
461 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state);
462 
463 void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state)
464 {
465 	drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state));
466 }
467 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state);
468 
469 /*
470  * Connector
471  */
472 
473 static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
474 {
475 	struct drm_sysfb_device *sysfb = data;
476 	const u8 *edid = sysfb->edid;
477 	size_t off = block * EDID_LENGTH;
478 	size_t end = off + len;
479 
480 	if (!edid)
481 		return -EINVAL;
482 	if (end > EDID_LENGTH)
483 		return -EINVAL;
484 	memcpy(buf, &edid[off], len);
485 
486 	/*
487 	 * We don't have EDID extensions available and reporting them
488 	 * will upset DRM helpers. Thus clear the extension field and
489 	 * update the checksum. Adding the extension flag to the checksum
490 	 * does this.
491 	 */
492 	buf[127] += buf[126];
493 	buf[126] = 0;
494 
495 	return 0;
496 }
497 
498 int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector)
499 {
500 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev);
501 	const struct drm_edid *drm_edid;
502 
503 	if (sysfb->edid) {
504 		drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb);
505 		drm_edid_connector_update(connector, drm_edid);
506 		drm_edid_free(drm_edid);
507 	}
508 
509 	/* Return the fixed mode even with EDID */
510 	return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode);
511 }
512 EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);
513