xref: /linux/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
1314c45e3SThomas Zimmermann // SPDX-License-Identifier: GPL-2.0-only
2314c45e3SThomas Zimmermann 
3314c45e3SThomas Zimmermann #include <linux/export.h>
4314c45e3SThomas Zimmermann #include <linux/slab.h>
5314c45e3SThomas Zimmermann 
6314c45e3SThomas Zimmermann #include <drm/drm_atomic.h>
7314c45e3SThomas Zimmermann #include <drm/drm_atomic_helper.h>
8314c45e3SThomas Zimmermann #include <drm/drm_atomic_state_helper.h>
9314c45e3SThomas Zimmermann #include <drm/drm_damage_helper.h>
10314c45e3SThomas Zimmermann #include <drm/drm_drv.h>
11314c45e3SThomas Zimmermann #include <drm/drm_edid.h>
12314c45e3SThomas Zimmermann #include <drm/drm_fourcc.h>
13314c45e3SThomas Zimmermann #include <drm/drm_framebuffer.h>
14314c45e3SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
15314c45e3SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
16314c45e3SThomas Zimmermann #include <drm/drm_panic.h>
17314c45e3SThomas Zimmermann #include <drm/drm_print.h>
18314c45e3SThomas Zimmermann #include <drm/drm_probe_helper.h>
19314c45e3SThomas Zimmermann 
20314c45e3SThomas Zimmermann #include "drm_sysfb_helper.h"
21314c45e3SThomas Zimmermann 
22314c45e3SThomas Zimmermann struct drm_display_mode drm_sysfb_mode(unsigned int width,
23314c45e3SThomas Zimmermann 				       unsigned int height,
24314c45e3SThomas Zimmermann 				       unsigned int width_mm,
25314c45e3SThomas Zimmermann 				       unsigned int height_mm)
26314c45e3SThomas Zimmermann {
27314c45e3SThomas Zimmermann 	/*
28314c45e3SThomas Zimmermann 	 * Assume a monitor resolution of 96 dpi to
29314c45e3SThomas Zimmermann 	 * get a somewhat reasonable screen size.
30314c45e3SThomas Zimmermann 	 */
31314c45e3SThomas Zimmermann 	if (!width_mm)
32314c45e3SThomas Zimmermann 		width_mm = DRM_MODE_RES_MM(width, 96ul);
33314c45e3SThomas Zimmermann 	if (!height_mm)
34314c45e3SThomas Zimmermann 		height_mm = DRM_MODE_RES_MM(height, 96ul);
35314c45e3SThomas Zimmermann 
36314c45e3SThomas Zimmermann 	{
37314c45e3SThomas Zimmermann 		const struct drm_display_mode mode = {
38314c45e3SThomas Zimmermann 			DRM_MODE_INIT(60, width, height, width_mm, height_mm)
39314c45e3SThomas Zimmermann 		};
40314c45e3SThomas Zimmermann 
41314c45e3SThomas Zimmermann 		return mode;
42314c45e3SThomas Zimmermann 	}
43314c45e3SThomas Zimmermann }
44314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_mode);
45314c45e3SThomas Zimmermann 
46314c45e3SThomas Zimmermann /*
47314c45e3SThomas Zimmermann  * Plane
48314c45e3SThomas Zimmermann  */
49314c45e3SThomas Zimmermann 
50*1a45ef02SThomas Zimmermann static u32 to_nonalpha_fourcc(u32 fourcc)
51*1a45ef02SThomas Zimmermann {
52*1a45ef02SThomas Zimmermann 	/* only handle formats with depth != 0 and alpha channel */
53*1a45ef02SThomas Zimmermann 	switch (fourcc) {
54*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_ARGB1555:
55*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_XRGB1555;
56*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_ABGR1555:
57*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_XBGR1555;
58*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_RGBA5551:
59*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_RGBX5551;
60*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_BGRA5551:
61*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_BGRX5551;
62*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_ARGB8888:
63*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_XRGB8888;
64*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_ABGR8888:
65*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_XBGR8888;
66*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_RGBA8888:
67*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_RGBX8888;
68*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_BGRA8888:
69*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_BGRX8888;
70*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_ARGB2101010:
71*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_XRGB2101010;
72*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_ABGR2101010:
73*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_XBGR2101010;
74*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_RGBA1010102:
75*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_RGBX1010102;
76*1a45ef02SThomas Zimmermann 	case DRM_FORMAT_BGRA1010102:
77*1a45ef02SThomas Zimmermann 		return DRM_FORMAT_BGRX1010102;
78*1a45ef02SThomas Zimmermann 	}
79*1a45ef02SThomas Zimmermann 
80*1a45ef02SThomas Zimmermann 	return fourcc;
81*1a45ef02SThomas Zimmermann }
82*1a45ef02SThomas Zimmermann 
83*1a45ef02SThomas Zimmermann static bool is_listed_fourcc(const u32 *fourccs, size_t nfourccs, u32 fourcc)
84*1a45ef02SThomas Zimmermann {
85*1a45ef02SThomas Zimmermann 	const u32 *fourccs_end = fourccs + nfourccs;
86*1a45ef02SThomas Zimmermann 
87*1a45ef02SThomas Zimmermann 	while (fourccs < fourccs_end) {
88*1a45ef02SThomas Zimmermann 		if (*fourccs == fourcc)
89*1a45ef02SThomas Zimmermann 			return true;
90*1a45ef02SThomas Zimmermann 		++fourccs;
91*1a45ef02SThomas Zimmermann 	}
92*1a45ef02SThomas Zimmermann 	return false;
93*1a45ef02SThomas Zimmermann }
94*1a45ef02SThomas Zimmermann 
95*1a45ef02SThomas Zimmermann /**
96*1a45ef02SThomas Zimmermann  * drm_sysfb_build_fourcc_list - Filters a list of supported color formats against
97*1a45ef02SThomas Zimmermann  *                               the device's native formats
98*1a45ef02SThomas Zimmermann  * @dev: DRM device
99*1a45ef02SThomas Zimmermann  * @native_fourccs: 4CC codes of natively supported color formats
100*1a45ef02SThomas Zimmermann  * @native_nfourccs: The number of entries in @native_fourccs
101*1a45ef02SThomas Zimmermann  * @fourccs_out: Returns 4CC codes of supported color formats
102*1a45ef02SThomas Zimmermann  * @nfourccs_out: The number of available entries in @fourccs_out
103*1a45ef02SThomas Zimmermann  *
104*1a45ef02SThomas Zimmermann  * This function create a list of supported color format from natively
105*1a45ef02SThomas Zimmermann  * supported formats and additional emulated formats.
106*1a45ef02SThomas Zimmermann  * At a minimum, most userspace programs expect at least support for
107*1a45ef02SThomas Zimmermann  * XRGB8888 on the primary plane. Sysfb devices that have to emulate
108*1a45ef02SThomas Zimmermann  * the format should use drm_sysfb_build_fourcc_list() to create a list
109*1a45ef02SThomas Zimmermann  * of supported color formats. The returned list can be handed over to
110*1a45ef02SThomas Zimmermann  * drm_universal_plane_init() et al. Native formats will go before
111*1a45ef02SThomas Zimmermann  * emulated formats. Native formats with alpha channel will be replaced
112*1a45ef02SThomas Zimmermann  * by equal formats without alpha channel, as primary planes usually
113*1a45ef02SThomas Zimmermann  * don't support alpha. Other heuristics might be applied to optimize
114*1a45ef02SThomas Zimmermann  * the sorting order. Formats near the beginning of the list are usually
115*1a45ef02SThomas Zimmermann  * preferred over formats near the end of the list.
116*1a45ef02SThomas Zimmermann  *
117*1a45ef02SThomas Zimmermann  * Returns:
118*1a45ef02SThomas Zimmermann  * The number of color-formats 4CC codes returned in @fourccs_out.
119*1a45ef02SThomas Zimmermann  */
120*1a45ef02SThomas Zimmermann size_t drm_sysfb_build_fourcc_list(struct drm_device *dev,
121*1a45ef02SThomas Zimmermann 				   const u32 *native_fourccs, size_t native_nfourccs,
122*1a45ef02SThomas Zimmermann 				   u32 *fourccs_out, size_t nfourccs_out)
123*1a45ef02SThomas Zimmermann {
124*1a45ef02SThomas Zimmermann 	/*
125*1a45ef02SThomas Zimmermann 	 * XRGB8888 is the default fallback format for most of userspace
126*1a45ef02SThomas Zimmermann 	 * and it's currently the only format that should be emulated for
127*1a45ef02SThomas Zimmermann 	 * the primary plane. Only if there's ever another default fallback,
128*1a45ef02SThomas Zimmermann 	 * it should be added here.
129*1a45ef02SThomas Zimmermann 	 */
130*1a45ef02SThomas Zimmermann 	static const u32 extra_fourccs[] = {
131*1a45ef02SThomas Zimmermann 		DRM_FORMAT_XRGB8888,
132*1a45ef02SThomas Zimmermann 	};
133*1a45ef02SThomas Zimmermann 	static const size_t extra_nfourccs = ARRAY_SIZE(extra_fourccs);
134*1a45ef02SThomas Zimmermann 
135*1a45ef02SThomas Zimmermann 	u32 *fourccs = fourccs_out;
136*1a45ef02SThomas Zimmermann 	const u32 *fourccs_end = fourccs_out + nfourccs_out;
137*1a45ef02SThomas Zimmermann 	size_t i;
138*1a45ef02SThomas Zimmermann 
139*1a45ef02SThomas Zimmermann 	/*
140*1a45ef02SThomas Zimmermann 	 * The device's native formats go first.
141*1a45ef02SThomas Zimmermann 	 */
142*1a45ef02SThomas Zimmermann 
143*1a45ef02SThomas Zimmermann 	for (i = 0; i < native_nfourccs; ++i) {
144*1a45ef02SThomas Zimmermann 		/*
145*1a45ef02SThomas Zimmermann 		 * Several DTs, boot loaders and firmware report native
146*1a45ef02SThomas Zimmermann 		 * alpha formats that are non-alpha formats instead. So
147*1a45ef02SThomas Zimmermann 		 * replace alpha formats by non-alpha formats.
148*1a45ef02SThomas Zimmermann 		 */
149*1a45ef02SThomas Zimmermann 		u32 fourcc = to_nonalpha_fourcc(native_fourccs[i]);
150*1a45ef02SThomas Zimmermann 
151*1a45ef02SThomas Zimmermann 		if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) {
152*1a45ef02SThomas Zimmermann 			continue; /* skip duplicate entries */
153*1a45ef02SThomas Zimmermann 		} else if (fourccs == fourccs_end) {
154*1a45ef02SThomas Zimmermann 			drm_warn(dev, "Ignoring native format %p4cc\n", &fourcc);
155*1a45ef02SThomas Zimmermann 			continue; /* end of available output buffer */
156*1a45ef02SThomas Zimmermann 		}
157*1a45ef02SThomas Zimmermann 
158*1a45ef02SThomas Zimmermann 		drm_dbg_kms(dev, "adding native format %p4cc\n", &fourcc);
159*1a45ef02SThomas Zimmermann 
160*1a45ef02SThomas Zimmermann 		*fourccs = fourcc;
161*1a45ef02SThomas Zimmermann 		++fourccs;
162*1a45ef02SThomas Zimmermann 	}
163*1a45ef02SThomas Zimmermann 
164*1a45ef02SThomas Zimmermann 	/*
165*1a45ef02SThomas Zimmermann 	 * The extra formats, emulated by the driver, go second.
166*1a45ef02SThomas Zimmermann 	 */
167*1a45ef02SThomas Zimmermann 
168*1a45ef02SThomas Zimmermann 	for (i = 0; (i < extra_nfourccs) && (fourccs < fourccs_end); ++i) {
169*1a45ef02SThomas Zimmermann 		u32 fourcc = extra_fourccs[i];
170*1a45ef02SThomas Zimmermann 
171*1a45ef02SThomas Zimmermann 		if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) {
172*1a45ef02SThomas Zimmermann 			continue; /* skip duplicate and native entries */
173*1a45ef02SThomas Zimmermann 		} else if (fourccs == fourccs_end) {
174*1a45ef02SThomas Zimmermann 			drm_warn(dev, "Ignoring emulated format %p4cc\n", &fourcc);
175*1a45ef02SThomas Zimmermann 			continue; /* end of available output buffer */
176*1a45ef02SThomas Zimmermann 		}
177*1a45ef02SThomas Zimmermann 
178*1a45ef02SThomas Zimmermann 		drm_dbg_kms(dev, "adding emulated format %p4cc\n", &fourcc);
179*1a45ef02SThomas Zimmermann 
180*1a45ef02SThomas Zimmermann 		*fourccs = fourcc;
181*1a45ef02SThomas Zimmermann 		++fourccs;
182*1a45ef02SThomas Zimmermann 	}
183*1a45ef02SThomas Zimmermann 
184*1a45ef02SThomas Zimmermann 	return fourccs - fourccs_out;
185*1a45ef02SThomas Zimmermann }
186*1a45ef02SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_build_fourcc_list);
187*1a45ef02SThomas Zimmermann 
188314c45e3SThomas Zimmermann int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,
189314c45e3SThomas Zimmermann 					struct drm_atomic_state *new_state)
190314c45e3SThomas Zimmermann {
191314c45e3SThomas Zimmermann 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
192314c45e3SThomas Zimmermann 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
193314c45e3SThomas Zimmermann 	struct drm_shadow_plane_state *new_shadow_plane_state =
194314c45e3SThomas Zimmermann 		to_drm_shadow_plane_state(new_plane_state);
195314c45e3SThomas Zimmermann 	struct drm_framebuffer *new_fb = new_plane_state->fb;
196314c45e3SThomas Zimmermann 	struct drm_crtc *new_crtc = new_plane_state->crtc;
197314c45e3SThomas Zimmermann 	struct drm_crtc_state *new_crtc_state = NULL;
198314c45e3SThomas Zimmermann 	struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
199314c45e3SThomas Zimmermann 	int ret;
200314c45e3SThomas Zimmermann 
201314c45e3SThomas Zimmermann 	if (new_crtc)
202314c45e3SThomas Zimmermann 		new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
203314c45e3SThomas Zimmermann 
204314c45e3SThomas Zimmermann 	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
205314c45e3SThomas Zimmermann 						  DRM_PLANE_NO_SCALING,
206314c45e3SThomas Zimmermann 						  DRM_PLANE_NO_SCALING,
207314c45e3SThomas Zimmermann 						  false, false);
208314c45e3SThomas Zimmermann 	if (ret)
209314c45e3SThomas Zimmermann 		return ret;
210314c45e3SThomas Zimmermann 	else if (!new_plane_state->visible)
211314c45e3SThomas Zimmermann 		return 0;
212314c45e3SThomas Zimmermann 
213314c45e3SThomas Zimmermann 	if (new_fb->format != sysfb->fb_format) {
214314c45e3SThomas Zimmermann 		void *buf;
215314c45e3SThomas Zimmermann 
216314c45e3SThomas Zimmermann 		/* format conversion necessary; reserve buffer */
217314c45e3SThomas Zimmermann 		buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state,
218314c45e3SThomas Zimmermann 						    sysfb->fb_pitch, GFP_KERNEL);
219314c45e3SThomas Zimmermann 		if (!buf)
220314c45e3SThomas Zimmermann 			return -ENOMEM;
221314c45e3SThomas Zimmermann 	}
222314c45e3SThomas Zimmermann 
223314c45e3SThomas Zimmermann 	new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
224314c45e3SThomas Zimmermann 
225314c45e3SThomas Zimmermann 	new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state);
226314c45e3SThomas Zimmermann 	new_sysfb_crtc_state->format = new_fb->format;
227314c45e3SThomas Zimmermann 
228314c45e3SThomas Zimmermann 	return 0;
229314c45e3SThomas Zimmermann }
230314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check);
231314c45e3SThomas Zimmermann 
232314c45e3SThomas Zimmermann void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
233314c45e3SThomas Zimmermann {
234314c45e3SThomas Zimmermann 	struct drm_device *dev = plane->dev;
235314c45e3SThomas Zimmermann 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
236314c45e3SThomas Zimmermann 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
237314c45e3SThomas Zimmermann 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
238314c45e3SThomas Zimmermann 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
239314c45e3SThomas Zimmermann 	struct drm_framebuffer *fb = plane_state->fb;
240314c45e3SThomas Zimmermann 	unsigned int dst_pitch = sysfb->fb_pitch;
241314c45e3SThomas Zimmermann 	const struct drm_format_info *dst_format = sysfb->fb_format;
242314c45e3SThomas Zimmermann 	struct drm_atomic_helper_damage_iter iter;
243314c45e3SThomas Zimmermann 	struct drm_rect damage;
244314c45e3SThomas Zimmermann 	int ret, idx;
245314c45e3SThomas Zimmermann 
246314c45e3SThomas Zimmermann 	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
247314c45e3SThomas Zimmermann 	if (ret)
248314c45e3SThomas Zimmermann 		return;
249314c45e3SThomas Zimmermann 
250314c45e3SThomas Zimmermann 	if (!drm_dev_enter(dev, &idx))
251314c45e3SThomas Zimmermann 		goto out_drm_gem_fb_end_cpu_access;
252314c45e3SThomas Zimmermann 
253314c45e3SThomas Zimmermann 	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
254314c45e3SThomas Zimmermann 	drm_atomic_for_each_plane_damage(&iter, &damage) {
255314c45e3SThomas Zimmermann 		struct iosys_map dst = sysfb->fb_addr;
256314c45e3SThomas Zimmermann 		struct drm_rect dst_clip = plane_state->dst;
257314c45e3SThomas Zimmermann 
258314c45e3SThomas Zimmermann 		if (!drm_rect_intersect(&dst_clip, &damage))
259314c45e3SThomas Zimmermann 			continue;
260314c45e3SThomas Zimmermann 
261314c45e3SThomas Zimmermann 		iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip));
262314c45e3SThomas Zimmermann 		drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb,
263314c45e3SThomas Zimmermann 			    &damage, &shadow_plane_state->fmtcnv_state);
264314c45e3SThomas Zimmermann 	}
265314c45e3SThomas Zimmermann 
266314c45e3SThomas Zimmermann 	drm_dev_exit(idx);
267314c45e3SThomas Zimmermann out_drm_gem_fb_end_cpu_access:
268314c45e3SThomas Zimmermann 	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
269314c45e3SThomas Zimmermann }
270314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update);
271314c45e3SThomas Zimmermann 
272314c45e3SThomas Zimmermann void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane,
273314c45e3SThomas Zimmermann 					   struct drm_atomic_state *state)
274314c45e3SThomas Zimmermann {
275314c45e3SThomas Zimmermann 	struct drm_device *dev = plane->dev;
276314c45e3SThomas Zimmermann 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
277314c45e3SThomas Zimmermann 	struct iosys_map dst = sysfb->fb_addr;
278314c45e3SThomas Zimmermann 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
279314c45e3SThomas Zimmermann 	void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */
280314c45e3SThomas Zimmermann 	unsigned int dst_pitch = sysfb->fb_pitch;
281314c45e3SThomas Zimmermann 	const struct drm_format_info *dst_format = sysfb->fb_format;
282314c45e3SThomas Zimmermann 	struct drm_rect dst_clip;
283314c45e3SThomas Zimmermann 	unsigned long lines, linepixels, i;
284314c45e3SThomas Zimmermann 	int idx;
285314c45e3SThomas Zimmermann 
286314c45e3SThomas Zimmermann 	drm_rect_init(&dst_clip,
287314c45e3SThomas Zimmermann 		      plane_state->src_x >> 16, plane_state->src_y >> 16,
288314c45e3SThomas Zimmermann 		      plane_state->src_w >> 16, plane_state->src_h >> 16);
289314c45e3SThomas Zimmermann 
290314c45e3SThomas Zimmermann 	lines = drm_rect_height(&dst_clip);
291314c45e3SThomas Zimmermann 	linepixels = drm_rect_width(&dst_clip);
292314c45e3SThomas Zimmermann 
293314c45e3SThomas Zimmermann 	if (!drm_dev_enter(dev, &idx))
294314c45e3SThomas Zimmermann 		return;
295314c45e3SThomas Zimmermann 
296314c45e3SThomas Zimmermann 	/* Clear buffer to black if disabled */
297314c45e3SThomas Zimmermann 	dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip);
298314c45e3SThomas Zimmermann 	for (i = 0; i < lines; ++i) {
299314c45e3SThomas Zimmermann 		memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]);
300314c45e3SThomas Zimmermann 		dst_vmap += dst_pitch;
301314c45e3SThomas Zimmermann 	}
302314c45e3SThomas Zimmermann 
303314c45e3SThomas Zimmermann 	drm_dev_exit(idx);
304314c45e3SThomas Zimmermann }
305314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable);
306314c45e3SThomas Zimmermann 
307314c45e3SThomas Zimmermann int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
308314c45e3SThomas Zimmermann 					      struct drm_scanout_buffer *sb)
309314c45e3SThomas Zimmermann {
310314c45e3SThomas Zimmermann 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
311314c45e3SThomas Zimmermann 
312314c45e3SThomas Zimmermann 	sb->width = sysfb->fb_mode.hdisplay;
313314c45e3SThomas Zimmermann 	sb->height = sysfb->fb_mode.vdisplay;
314314c45e3SThomas Zimmermann 	sb->format = sysfb->fb_format;
315314c45e3SThomas Zimmermann 	sb->pitch[0] = sysfb->fb_pitch;
316314c45e3SThomas Zimmermann 	sb->map[0] = sysfb->fb_addr;
317314c45e3SThomas Zimmermann 
318314c45e3SThomas Zimmermann 	return 0;
319314c45e3SThomas Zimmermann }
320314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer);
321314c45e3SThomas Zimmermann 
322314c45e3SThomas Zimmermann /*
323314c45e3SThomas Zimmermann  * CRTC
324314c45e3SThomas Zimmermann  */
325314c45e3SThomas Zimmermann 
326314c45e3SThomas Zimmermann static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state)
327314c45e3SThomas Zimmermann {
328314c45e3SThomas Zimmermann 	__drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base);
329314c45e3SThomas Zimmermann 
330314c45e3SThomas Zimmermann 	kfree(sysfb_crtc_state);
331314c45e3SThomas Zimmermann }
332314c45e3SThomas Zimmermann 
333314c45e3SThomas Zimmermann enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc,
334314c45e3SThomas Zimmermann 						      const struct drm_display_mode *mode)
335314c45e3SThomas Zimmermann {
336314c45e3SThomas Zimmermann 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev);
337314c45e3SThomas Zimmermann 
338314c45e3SThomas Zimmermann 	return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode);
339314c45e3SThomas Zimmermann }
340314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid);
341314c45e3SThomas Zimmermann 
342314c45e3SThomas Zimmermann int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state)
343314c45e3SThomas Zimmermann {
344314c45e3SThomas Zimmermann 	struct drm_device *dev = crtc->dev;
345314c45e3SThomas Zimmermann 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
346314c45e3SThomas Zimmermann 	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
347314c45e3SThomas Zimmermann 	int ret;
348314c45e3SThomas Zimmermann 
349314c45e3SThomas Zimmermann 	if (!new_crtc_state->enable)
350314c45e3SThomas Zimmermann 		return 0;
351314c45e3SThomas Zimmermann 
352314c45e3SThomas Zimmermann 	ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
353314c45e3SThomas Zimmermann 	if (ret)
354314c45e3SThomas Zimmermann 		return ret;
355314c45e3SThomas Zimmermann 
356314c45e3SThomas Zimmermann 	if (new_crtc_state->color_mgmt_changed) {
357314c45e3SThomas Zimmermann 		const size_t gamma_lut_length =
358314c45e3SThomas Zimmermann 			sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut);
359314c45e3SThomas Zimmermann 		const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut;
360314c45e3SThomas Zimmermann 
361314c45e3SThomas Zimmermann 		if (gamma_lut && (gamma_lut->length != gamma_lut_length)) {
362314c45e3SThomas Zimmermann 			drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length);
363314c45e3SThomas Zimmermann 			return -EINVAL;
364314c45e3SThomas Zimmermann 		}
365314c45e3SThomas Zimmermann 	}
366314c45e3SThomas Zimmermann 
367314c45e3SThomas Zimmermann 	return 0;
368314c45e3SThomas Zimmermann }
369314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check);
370314c45e3SThomas Zimmermann 
371314c45e3SThomas Zimmermann void drm_sysfb_crtc_reset(struct drm_crtc *crtc)
372314c45e3SThomas Zimmermann {
373314c45e3SThomas Zimmermann 	struct drm_sysfb_crtc_state *sysfb_crtc_state;
374314c45e3SThomas Zimmermann 
375314c45e3SThomas Zimmermann 	if (crtc->state)
376314c45e3SThomas Zimmermann 		drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state));
377314c45e3SThomas Zimmermann 
378314c45e3SThomas Zimmermann 	sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL);
379314c45e3SThomas Zimmermann 	if (sysfb_crtc_state)
380314c45e3SThomas Zimmermann 		__drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base);
381314c45e3SThomas Zimmermann 	else
382314c45e3SThomas Zimmermann 		__drm_atomic_helper_crtc_reset(crtc, NULL);
383314c45e3SThomas Zimmermann }
384314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_reset);
385314c45e3SThomas Zimmermann 
386314c45e3SThomas Zimmermann struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
387314c45e3SThomas Zimmermann {
388314c45e3SThomas Zimmermann 	struct drm_device *dev = crtc->dev;
389314c45e3SThomas Zimmermann 	struct drm_crtc_state *crtc_state = crtc->state;
390314c45e3SThomas Zimmermann 	struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
391314c45e3SThomas Zimmermann 	struct drm_sysfb_crtc_state *sysfb_crtc_state;
392314c45e3SThomas Zimmermann 
393314c45e3SThomas Zimmermann 	if (drm_WARN_ON(dev, !crtc_state))
394314c45e3SThomas Zimmermann 		return NULL;
395314c45e3SThomas Zimmermann 
396314c45e3SThomas Zimmermann 	new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL);
397314c45e3SThomas Zimmermann 	if (!new_sysfb_crtc_state)
398314c45e3SThomas Zimmermann 		return NULL;
399314c45e3SThomas Zimmermann 
400314c45e3SThomas Zimmermann 	sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
401314c45e3SThomas Zimmermann 
402314c45e3SThomas Zimmermann 	__drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base);
403314c45e3SThomas Zimmermann 	new_sysfb_crtc_state->format = sysfb_crtc_state->format;
404314c45e3SThomas Zimmermann 
405314c45e3SThomas Zimmermann 	return &new_sysfb_crtc_state->base;
406314c45e3SThomas Zimmermann }
407314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state);
408314c45e3SThomas Zimmermann 
409314c45e3SThomas Zimmermann void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state)
410314c45e3SThomas Zimmermann {
411314c45e3SThomas Zimmermann 	drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state));
412314c45e3SThomas Zimmermann }
413314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state);
414314c45e3SThomas Zimmermann 
415314c45e3SThomas Zimmermann /*
416314c45e3SThomas Zimmermann  * Connector
417314c45e3SThomas Zimmermann  */
418314c45e3SThomas Zimmermann 
419314c45e3SThomas Zimmermann static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
420314c45e3SThomas Zimmermann {
421314c45e3SThomas Zimmermann 	struct drm_sysfb_device *sysfb = data;
422314c45e3SThomas Zimmermann 	const u8 *edid = sysfb->edid;
423314c45e3SThomas Zimmermann 	size_t off = block * EDID_LENGTH;
424314c45e3SThomas Zimmermann 	size_t end = off + len;
425314c45e3SThomas Zimmermann 
426314c45e3SThomas Zimmermann 	if (!edid)
427314c45e3SThomas Zimmermann 		return -EINVAL;
428314c45e3SThomas Zimmermann 	if (end > EDID_LENGTH)
429314c45e3SThomas Zimmermann 		return -EINVAL;
430314c45e3SThomas Zimmermann 	memcpy(buf, &edid[off], len);
431314c45e3SThomas Zimmermann 
432314c45e3SThomas Zimmermann 	/*
433314c45e3SThomas Zimmermann 	 * We don't have EDID extensions available and reporting them
434314c45e3SThomas Zimmermann 	 * will upset DRM helpers. Thus clear the extension field and
435314c45e3SThomas Zimmermann 	 * update the checksum. Adding the extension flag to the checksum
436314c45e3SThomas Zimmermann 	 * does this.
437314c45e3SThomas Zimmermann 	 */
438314c45e3SThomas Zimmermann 	buf[127] += buf[126];
439314c45e3SThomas Zimmermann 	buf[126] = 0;
440314c45e3SThomas Zimmermann 
441314c45e3SThomas Zimmermann 	return 0;
442314c45e3SThomas Zimmermann }
443314c45e3SThomas Zimmermann 
444314c45e3SThomas Zimmermann int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector)
445314c45e3SThomas Zimmermann {
446314c45e3SThomas Zimmermann 	struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev);
447314c45e3SThomas Zimmermann 	const struct drm_edid *drm_edid;
448314c45e3SThomas Zimmermann 
449314c45e3SThomas Zimmermann 	if (sysfb->edid) {
450314c45e3SThomas Zimmermann 		drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb);
451314c45e3SThomas Zimmermann 		drm_edid_connector_update(connector, drm_edid);
452314c45e3SThomas Zimmermann 		drm_edid_free(drm_edid);
453314c45e3SThomas Zimmermann 	}
454314c45e3SThomas Zimmermann 
455314c45e3SThomas Zimmermann 	/* Return the fixed mode even with EDID */
456314c45e3SThomas Zimmermann 	return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode);
457314c45e3SThomas Zimmermann }
458314c45e3SThomas Zimmermann EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);
459