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_atomic_helper.h>
15 #include <drm/drm_gem_framebuffer_helper.h>
16 #include <drm/drm_panic.h>
17 #include <drm/drm_print.h>
18 #include <drm/drm_probe_helper.h>
19
20 #include "drm_sysfb_helper.h"
21
drm_sysfb_mode(unsigned int width,unsigned int height,unsigned int width_mm,unsigned int height_mm)22 struct drm_display_mode drm_sysfb_mode(unsigned int width,
23 unsigned int height,
24 unsigned int width_mm,
25 unsigned int height_mm)
26 {
27 /*
28 * Assume a monitor resolution of 96 dpi to
29 * get a somewhat reasonable screen size.
30 */
31 if (!width_mm)
32 width_mm = DRM_MODE_RES_MM(width, 96ul);
33 if (!height_mm)
34 height_mm = DRM_MODE_RES_MM(height, 96ul);
35
36 {
37 const struct drm_display_mode mode = {
38 DRM_MODE_INIT(60, width, height, width_mm, height_mm)
39 };
40
41 return mode;
42 }
43 }
44 EXPORT_SYMBOL(drm_sysfb_mode);
45
46 /*
47 * Plane
48 */
49
to_nonalpha_fourcc(u32 fourcc)50 static u32 to_nonalpha_fourcc(u32 fourcc)
51 {
52 /* only handle formats with depth != 0 and alpha channel */
53 switch (fourcc) {
54 case DRM_FORMAT_ARGB1555:
55 return DRM_FORMAT_XRGB1555;
56 case DRM_FORMAT_ABGR1555:
57 return DRM_FORMAT_XBGR1555;
58 case DRM_FORMAT_RGBA5551:
59 return DRM_FORMAT_RGBX5551;
60 case DRM_FORMAT_BGRA5551:
61 return DRM_FORMAT_BGRX5551;
62 case DRM_FORMAT_ARGB8888:
63 return DRM_FORMAT_XRGB8888;
64 case DRM_FORMAT_ABGR8888:
65 return DRM_FORMAT_XBGR8888;
66 case DRM_FORMAT_RGBA8888:
67 return DRM_FORMAT_RGBX8888;
68 case DRM_FORMAT_BGRA8888:
69 return DRM_FORMAT_BGRX8888;
70 case DRM_FORMAT_ARGB2101010:
71 return DRM_FORMAT_XRGB2101010;
72 case DRM_FORMAT_ABGR2101010:
73 return DRM_FORMAT_XBGR2101010;
74 case DRM_FORMAT_RGBA1010102:
75 return DRM_FORMAT_RGBX1010102;
76 case DRM_FORMAT_BGRA1010102:
77 return DRM_FORMAT_BGRX1010102;
78 }
79
80 return fourcc;
81 }
82
is_listed_fourcc(const u32 * fourccs,size_t nfourccs,u32 fourcc)83 static bool is_listed_fourcc(const u32 *fourccs, size_t nfourccs, u32 fourcc)
84 {
85 const u32 *fourccs_end = fourccs + nfourccs;
86
87 while (fourccs < fourccs_end) {
88 if (*fourccs == fourcc)
89 return true;
90 ++fourccs;
91 }
92 return false;
93 }
94
95 /**
96 * drm_sysfb_build_fourcc_list - Filters a list of supported color formats against
97 * the device's native formats
98 * @dev: DRM device
99 * @native_fourccs: 4CC codes of natively supported color formats
100 * @native_nfourccs: The number of entries in @native_fourccs
101 * @fourccs_out: Returns 4CC codes of supported color formats
102 * @nfourccs_out: The number of available entries in @fourccs_out
103 *
104 * This function create a list of supported color format from natively
105 * supported formats and additional emulated formats.
106 * At a minimum, most userspace programs expect at least support for
107 * XRGB8888 on the primary plane. Sysfb devices that have to emulate
108 * the format should use drm_sysfb_build_fourcc_list() to create a list
109 * of supported color formats. The returned list can be handed over to
110 * drm_universal_plane_init() et al. Native formats will go before
111 * emulated formats. Native formats with alpha channel will be replaced
112 * by equal formats without alpha channel, as primary planes usually
113 * don't support alpha. Other heuristics might be applied to optimize
114 * the sorting order. Formats near the beginning of the list are usually
115 * preferred over formats near the end of the list.
116 *
117 * Returns:
118 * The number of color-formats 4CC codes returned in @fourccs_out.
119 */
drm_sysfb_build_fourcc_list(struct drm_device * dev,const u32 * native_fourccs,size_t native_nfourccs,u32 * fourccs_out,size_t nfourccs_out)120 size_t drm_sysfb_build_fourcc_list(struct drm_device *dev,
121 const u32 *native_fourccs, size_t native_nfourccs,
122 u32 *fourccs_out, size_t nfourccs_out)
123 {
124 /*
125 * XRGB8888 is the default fallback format for most of userspace
126 * and it's currently the only format that should be emulated for
127 * the primary plane. Only if there's ever another default fallback,
128 * it should be added here.
129 */
130 static const u32 extra_fourccs[] = {
131 DRM_FORMAT_XRGB8888,
132 };
133 static const size_t extra_nfourccs = ARRAY_SIZE(extra_fourccs);
134
135 u32 *fourccs = fourccs_out;
136 const u32 *fourccs_end = fourccs_out + nfourccs_out;
137 size_t i;
138
139 /*
140 * The device's native formats go first.
141 */
142
143 for (i = 0; i < native_nfourccs; ++i) {
144 /*
145 * Several DTs, boot loaders and firmware report native
146 * alpha formats that are non-alpha formats instead. So
147 * replace alpha formats by non-alpha formats.
148 */
149 u32 fourcc = to_nonalpha_fourcc(native_fourccs[i]);
150
151 if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) {
152 continue; /* skip duplicate entries */
153 } else if (fourccs == fourccs_end) {
154 drm_warn(dev, "Ignoring native format %p4cc\n", &fourcc);
155 continue; /* end of available output buffer */
156 }
157
158 drm_dbg_kms(dev, "adding native format %p4cc\n", &fourcc);
159
160 *fourccs = fourcc;
161 ++fourccs;
162 }
163
164 /*
165 * The extra formats, emulated by the driver, go second.
166 */
167
168 for (i = 0; (i < extra_nfourccs) && (fourccs < fourccs_end); ++i) {
169 u32 fourcc = extra_fourccs[i];
170
171 if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) {
172 continue; /* skip duplicate and native entries */
173 } else if (fourccs == fourccs_end) {
174 drm_warn(dev, "Ignoring emulated format %p4cc\n", &fourcc);
175 continue; /* end of available output buffer */
176 }
177
178 drm_dbg_kms(dev, "adding emulated format %p4cc\n", &fourcc);
179
180 *fourccs = fourcc;
181 ++fourccs;
182 }
183
184 return fourccs - fourccs_out;
185 }
186 EXPORT_SYMBOL(drm_sysfb_build_fourcc_list);
187
drm_sysfb_plane_helper_atomic_check(struct drm_plane * plane,struct drm_atomic_state * new_state)188 int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,
189 struct drm_atomic_state *new_state)
190 {
191 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
192 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
193 struct drm_shadow_plane_state *new_shadow_plane_state =
194 to_drm_shadow_plane_state(new_plane_state);
195 struct drm_framebuffer *new_fb = new_plane_state->fb;
196 struct drm_crtc *new_crtc = new_plane_state->crtc;
197 struct drm_crtc_state *new_crtc_state = NULL;
198 struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
199 int ret;
200
201 if (new_crtc)
202 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
203
204 ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
205 DRM_PLANE_NO_SCALING,
206 DRM_PLANE_NO_SCALING,
207 false, false);
208 if (ret)
209 return ret;
210 else if (!new_plane_state->visible)
211 return 0;
212
213 if (new_fb->format != sysfb->fb_format) {
214 void *buf;
215
216 /* format conversion necessary; reserve buffer */
217 buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state,
218 sysfb->fb_pitch, GFP_KERNEL);
219 if (!buf)
220 return -ENOMEM;
221 }
222
223 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
224
225 new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state);
226 new_sysfb_crtc_state->format = new_fb->format;
227
228 return 0;
229 }
230 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check);
231
drm_sysfb_plane_helper_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)232 void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
233 {
234 struct drm_device *dev = plane->dev;
235 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
236 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
237 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
238 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
239 struct drm_framebuffer *fb = plane_state->fb;
240 unsigned int dst_pitch = sysfb->fb_pitch;
241 const struct drm_format_info *dst_format = sysfb->fb_format;
242 struct drm_atomic_helper_damage_iter iter;
243 struct drm_rect damage;
244 int ret, idx;
245
246 ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
247 if (ret)
248 return;
249
250 if (!drm_dev_enter(dev, &idx))
251 goto out_drm_gem_fb_end_cpu_access;
252
253 drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
254 drm_atomic_for_each_plane_damage(&iter, &damage) {
255 struct iosys_map dst = sysfb->fb_addr;
256 struct drm_rect dst_clip = plane_state->dst;
257
258 if (!drm_rect_intersect(&dst_clip, &damage))
259 continue;
260
261 iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip));
262 drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb,
263 &damage, &shadow_plane_state->fmtcnv_state);
264 }
265
266 drm_dev_exit(idx);
267 out_drm_gem_fb_end_cpu_access:
268 drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
269 }
270 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update);
271
drm_sysfb_plane_helper_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)272 void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane,
273 struct drm_atomic_state *state)
274 {
275 struct drm_device *dev = plane->dev;
276 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
277 struct iosys_map dst = sysfb->fb_addr;
278 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
279 void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */
280 unsigned int dst_pitch = sysfb->fb_pitch;
281 const struct drm_format_info *dst_format = sysfb->fb_format;
282 struct drm_rect dst_clip;
283 unsigned long lines, linepixels, i;
284 int idx;
285
286 drm_rect_init(&dst_clip,
287 plane_state->src_x >> 16, plane_state->src_y >> 16,
288 plane_state->src_w >> 16, plane_state->src_h >> 16);
289
290 lines = drm_rect_height(&dst_clip);
291 linepixels = drm_rect_width(&dst_clip);
292
293 if (!drm_dev_enter(dev, &idx))
294 return;
295
296 /* Clear buffer to black if disabled */
297 dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip);
298 for (i = 0; i < lines; ++i) {
299 memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]);
300 dst_vmap += dst_pitch;
301 }
302
303 drm_dev_exit(idx);
304 }
305 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable);
306
drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane * plane,struct drm_scanout_buffer * sb)307 int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
308 struct drm_scanout_buffer *sb)
309 {
310 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
311
312 sb->width = sysfb->fb_mode.hdisplay;
313 sb->height = sysfb->fb_mode.vdisplay;
314 sb->format = sysfb->fb_format;
315 sb->pitch[0] = sysfb->fb_pitch;
316 sb->map[0] = sysfb->fb_addr;
317
318 return 0;
319 }
320 EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer);
321
322 /*
323 * CRTC
324 */
325
drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state * sysfb_crtc_state)326 static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state)
327 {
328 __drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base);
329
330 kfree(sysfb_crtc_state);
331 }
332
drm_sysfb_crtc_helper_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)333 enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc,
334 const struct drm_display_mode *mode)
335 {
336 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev);
337
338 return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode);
339 }
340 EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid);
341
drm_sysfb_crtc_helper_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * new_state)342 int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state)
343 {
344 struct drm_device *dev = crtc->dev;
345 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
346 struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
347 int ret;
348
349 if (!new_crtc_state->enable)
350 return 0;
351
352 ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
353 if (ret)
354 return ret;
355
356 if (new_crtc_state->color_mgmt_changed) {
357 const size_t gamma_lut_length =
358 sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut);
359 const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut;
360
361 if (gamma_lut && (gamma_lut->length != gamma_lut_length)) {
362 drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length);
363 return -EINVAL;
364 }
365 }
366
367 return 0;
368 }
369 EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check);
370
drm_sysfb_crtc_reset(struct drm_crtc * crtc)371 void drm_sysfb_crtc_reset(struct drm_crtc *crtc)
372 {
373 struct drm_sysfb_crtc_state *sysfb_crtc_state;
374
375 if (crtc->state)
376 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state));
377
378 sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL);
379 if (sysfb_crtc_state)
380 __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base);
381 else
382 __drm_atomic_helper_crtc_reset(crtc, NULL);
383 }
384 EXPORT_SYMBOL(drm_sysfb_crtc_reset);
385
drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc * crtc)386 struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
387 {
388 struct drm_device *dev = crtc->dev;
389 struct drm_crtc_state *crtc_state = crtc->state;
390 struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
391 struct drm_sysfb_crtc_state *sysfb_crtc_state;
392
393 if (drm_WARN_ON(dev, !crtc_state))
394 return NULL;
395
396 new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL);
397 if (!new_sysfb_crtc_state)
398 return NULL;
399
400 sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
401
402 __drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base);
403 new_sysfb_crtc_state->format = sysfb_crtc_state->format;
404
405 return &new_sysfb_crtc_state->base;
406 }
407 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state);
408
drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * crtc_state)409 void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state)
410 {
411 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state));
412 }
413 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state);
414
415 /*
416 * Connector
417 */
418
drm_sysfb_get_edid_block(void * data,u8 * buf,unsigned int block,size_t len)419 static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
420 {
421 struct drm_sysfb_device *sysfb = data;
422 const u8 *edid = sysfb->edid;
423 size_t off = block * EDID_LENGTH;
424 size_t end = off + len;
425
426 if (!edid)
427 return -EINVAL;
428 if (end > EDID_LENGTH)
429 return -EINVAL;
430 memcpy(buf, &edid[off], len);
431
432 /*
433 * We don't have EDID extensions available and reporting them
434 * will upset DRM helpers. Thus clear the extension field and
435 * update the checksum. Adding the extension flag to the checksum
436 * does this.
437 */
438 buf[127] += buf[126];
439 buf[126] = 0;
440
441 return 0;
442 }
443
drm_sysfb_connector_helper_get_modes(struct drm_connector * connector)444 int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector)
445 {
446 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev);
447 const struct drm_edid *drm_edid;
448
449 if (sysfb->edid) {
450 drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb);
451 drm_edid_connector_update(connector, drm_edid);
452 drm_edid_free(drm_edid);
453 }
454
455 /* Return the fixed mode even with EDID */
456 return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode);
457 }
458 EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);
459