1 | // SPDX-License-Identifier: GPL-2.0 or MIT |
2 | /* |
3 | * Copyright (C) 2016 Noralf Trønnes |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | */ |
10 | |
11 | #include <linux/io.h> |
12 | #include <linux/iosys-map.h> |
13 | #include <linux/module.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include <drm/drm_device.h> |
17 | #include <drm/drm_format_helper.h> |
18 | #include <drm/drm_framebuffer.h> |
19 | #include <drm/drm_fourcc.h> |
20 | #include <drm/drm_print.h> |
21 | #include <drm/drm_rect.h> |
22 | |
23 | static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp) |
24 | { |
25 | return clip->y1 * pitch + clip->x1 * cpp; |
26 | } |
27 | |
28 | /** |
29 | * drm_fb_clip_offset - Returns the clipping rectangles byte-offset in a framebuffer |
30 | * @pitch: Framebuffer line pitch in byte |
31 | * @format: Framebuffer format |
32 | * @clip: Clip rectangle |
33 | * |
34 | * Returns: |
35 | * The byte offset of the clip rectangle's top-left corner within the framebuffer. |
36 | */ |
37 | unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format, |
38 | const struct drm_rect *clip) |
39 | { |
40 | return clip_offset(clip, pitch, cpp: format->cpp[0]); |
41 | } |
42 | EXPORT_SYMBOL(drm_fb_clip_offset); |
43 | |
44 | /* TODO: Make this function work with multi-plane formats. */ |
45 | static int __drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_pixsize, |
46 | const void *vaddr, const struct drm_framebuffer *fb, |
47 | const struct drm_rect *clip, bool vaddr_cached_hint, |
48 | void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels)) |
49 | { |
50 | unsigned long linepixels = drm_rect_width(r: clip); |
51 | unsigned long lines = drm_rect_height(r: clip); |
52 | size_t sbuf_len = linepixels * fb->format->cpp[0]; |
53 | void *stmp = NULL; |
54 | unsigned long i; |
55 | const void *sbuf; |
56 | |
57 | /* |
58 | * Some source buffers, such as DMA memory, use write-combine |
59 | * caching, so reads are uncached. Speed up access by fetching |
60 | * one line at a time. |
61 | */ |
62 | if (!vaddr_cached_hint) { |
63 | stmp = kmalloc(size: sbuf_len, GFP_KERNEL); |
64 | if (!stmp) |
65 | return -ENOMEM; |
66 | } |
67 | |
68 | if (!dst_pitch) |
69 | dst_pitch = drm_rect_width(r: clip) * dst_pixsize; |
70 | vaddr += clip_offset(clip, pitch: fb->pitches[0], cpp: fb->format->cpp[0]); |
71 | |
72 | for (i = 0; i < lines; ++i) { |
73 | if (stmp) |
74 | sbuf = memcpy(stmp, vaddr, sbuf_len); |
75 | else |
76 | sbuf = vaddr; |
77 | xfrm_line(dst, sbuf, linepixels); |
78 | vaddr += fb->pitches[0]; |
79 | dst += dst_pitch; |
80 | } |
81 | |
82 | kfree(objp: stmp); |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | /* TODO: Make this function work with multi-plane formats. */ |
88 | static int __drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsigned long dst_pixsize, |
89 | const void *vaddr, const struct drm_framebuffer *fb, |
90 | const struct drm_rect *clip, bool vaddr_cached_hint, |
91 | void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels)) |
92 | { |
93 | unsigned long linepixels = drm_rect_width(r: clip); |
94 | unsigned long lines = drm_rect_height(r: clip); |
95 | size_t dbuf_len = linepixels * dst_pixsize; |
96 | size_t stmp_off = round_up(dbuf_len, ARCH_KMALLOC_MINALIGN); /* for sbuf alignment */ |
97 | size_t sbuf_len = linepixels * fb->format->cpp[0]; |
98 | void *stmp = NULL; |
99 | unsigned long i; |
100 | const void *sbuf; |
101 | void *dbuf; |
102 | |
103 | if (vaddr_cached_hint) { |
104 | dbuf = kmalloc(size: dbuf_len, GFP_KERNEL); |
105 | } else { |
106 | dbuf = kmalloc(size: stmp_off + sbuf_len, GFP_KERNEL); |
107 | stmp = dbuf + stmp_off; |
108 | } |
109 | if (!dbuf) |
110 | return -ENOMEM; |
111 | |
112 | if (!dst_pitch) |
113 | dst_pitch = linepixels * dst_pixsize; |
114 | vaddr += clip_offset(clip, pitch: fb->pitches[0], cpp: fb->format->cpp[0]); |
115 | |
116 | for (i = 0; i < lines; ++i) { |
117 | if (stmp) |
118 | sbuf = memcpy(stmp, vaddr, sbuf_len); |
119 | else |
120 | sbuf = vaddr; |
121 | xfrm_line(dbuf, sbuf, linepixels); |
122 | memcpy_toio(dst, dbuf, dbuf_len); |
123 | vaddr += fb->pitches[0]; |
124 | dst += dst_pitch; |
125 | } |
126 | |
127 | kfree(objp: dbuf); |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | /* TODO: Make this function work with multi-plane formats. */ |
133 | static int drm_fb_xfrm(struct iosys_map *dst, |
134 | const unsigned int *dst_pitch, const u8 *dst_pixsize, |
135 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
136 | const struct drm_rect *clip, bool vaddr_cached_hint, |
137 | void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels)) |
138 | { |
139 | static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = { |
140 | 0, 0, 0, 0 |
141 | }; |
142 | |
143 | if (!dst_pitch) |
144 | dst_pitch = default_dst_pitch; |
145 | |
146 | /* TODO: handle src in I/O memory here */ |
147 | if (dst[0].is_iomem) |
148 | return __drm_fb_xfrm_toio(dst: dst[0].vaddr_iomem, dst_pitch: dst_pitch[0], dst_pixsize: dst_pixsize[0], |
149 | vaddr: src[0].vaddr, fb, clip, vaddr_cached_hint, xfrm_line); |
150 | else |
151 | return __drm_fb_xfrm(dst: dst[0].vaddr, dst_pitch: dst_pitch[0], dst_pixsize: dst_pixsize[0], |
152 | vaddr: src[0].vaddr, fb, clip, vaddr_cached_hint, xfrm_line); |
153 | } |
154 | |
155 | /** |
156 | * drm_fb_memcpy - Copy clip buffer |
157 | * @dst: Array of destination buffers |
158 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
159 | * within @dst; can be NULL if scanlines are stored next to each other. |
160 | * @src: Array of source buffers |
161 | * @fb: DRM framebuffer |
162 | * @clip: Clip rectangle area to copy |
163 | * |
164 | * This function copies parts of a framebuffer to display memory. Destination and |
165 | * framebuffer formats must match. No conversion takes place. The parameters @dst, |
166 | * @dst_pitch and @src refer to arrays. Each array must have at least as many entries |
167 | * as there are planes in @fb's format. Each entry stores the value for the format's |
168 | * respective color plane at the same index. |
169 | * |
170 | * This function does not apply clipping on @dst (i.e. the destination is at the |
171 | * top-left corner). |
172 | */ |
173 | void drm_fb_memcpy(struct iosys_map *dst, const unsigned int *dst_pitch, |
174 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
175 | const struct drm_rect *clip) |
176 | { |
177 | static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = { |
178 | 0, 0, 0, 0 |
179 | }; |
180 | |
181 | const struct drm_format_info *format = fb->format; |
182 | unsigned int i, y, lines = drm_rect_height(r: clip); |
183 | |
184 | if (!dst_pitch) |
185 | dst_pitch = default_dst_pitch; |
186 | |
187 | for (i = 0; i < format->num_planes; ++i) { |
188 | unsigned int bpp_i = drm_format_info_bpp(info: format, plane: i); |
189 | unsigned int cpp_i = DIV_ROUND_UP(bpp_i, 8); |
190 | size_t len_i = DIV_ROUND_UP(drm_rect_width(clip) * bpp_i, 8); |
191 | unsigned int dst_pitch_i = dst_pitch[i]; |
192 | struct iosys_map dst_i = dst[i]; |
193 | struct iosys_map src_i = src[i]; |
194 | |
195 | if (!dst_pitch_i) |
196 | dst_pitch_i = len_i; |
197 | |
198 | iosys_map_incr(map: &src_i, incr: clip_offset(clip, pitch: fb->pitches[i], cpp: cpp_i)); |
199 | for (y = 0; y < lines; y++) { |
200 | /* TODO: handle src_i in I/O memory here */ |
201 | iosys_map_memcpy_to(dst: &dst_i, dst_offset: 0, src: src_i.vaddr, len: len_i); |
202 | iosys_map_incr(map: &src_i, incr: fb->pitches[i]); |
203 | iosys_map_incr(map: &dst_i, incr: dst_pitch_i); |
204 | } |
205 | } |
206 | } |
207 | EXPORT_SYMBOL(drm_fb_memcpy); |
208 | |
209 | static void drm_fb_swab16_line(void *dbuf, const void *sbuf, unsigned int pixels) |
210 | { |
211 | u16 *dbuf16 = dbuf; |
212 | const u16 *sbuf16 = sbuf; |
213 | const u16 *send16 = sbuf16 + pixels; |
214 | |
215 | while (sbuf16 < send16) |
216 | *dbuf16++ = swab16(*sbuf16++); |
217 | } |
218 | |
219 | static void drm_fb_swab32_line(void *dbuf, const void *sbuf, unsigned int pixels) |
220 | { |
221 | u32 *dbuf32 = dbuf; |
222 | const u32 *sbuf32 = sbuf; |
223 | const u32 *send32 = sbuf32 + pixels; |
224 | |
225 | while (sbuf32 < send32) |
226 | *dbuf32++ = swab32(*sbuf32++); |
227 | } |
228 | |
229 | /** |
230 | * drm_fb_swab - Swap bytes into clip buffer |
231 | * @dst: Array of destination buffers |
232 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
233 | * within @dst; can be NULL if scanlines are stored next to each other. |
234 | * @src: Array of source buffers |
235 | * @fb: DRM framebuffer |
236 | * @clip: Clip rectangle area to copy |
237 | * @cached: Source buffer is mapped cached (eg. not write-combined) |
238 | * |
239 | * This function copies parts of a framebuffer to display memory and swaps per-pixel |
240 | * bytes during the process. Destination and framebuffer formats must match. The |
241 | * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at |
242 | * least as many entries as there are planes in @fb's format. Each entry stores the |
243 | * value for the format's respective color plane at the same index. If @cached is |
244 | * false a temporary buffer is used to cache one pixel line at a time to speed up |
245 | * slow uncached reads. |
246 | * |
247 | * This function does not apply clipping on @dst (i.e. the destination is at the |
248 | * top-left corner). |
249 | */ |
250 | void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch, |
251 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
252 | const struct drm_rect *clip, bool cached) |
253 | { |
254 | const struct drm_format_info *format = fb->format; |
255 | u8 cpp = DIV_ROUND_UP(drm_format_info_bpp(format, 0), 8); |
256 | void (*swab_line)(void *dbuf, const void *sbuf, unsigned int npixels); |
257 | |
258 | switch (cpp) { |
259 | case 4: |
260 | swab_line = drm_fb_swab32_line; |
261 | break; |
262 | case 2: |
263 | swab_line = drm_fb_swab16_line; |
264 | break; |
265 | default: |
266 | drm_warn_once(fb->dev, "Format %p4cc has unsupported pixel size.\n" , |
267 | &format->format); |
268 | return; |
269 | } |
270 | |
271 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize: &cpp, src, fb, clip, vaddr_cached_hint: cached, xfrm_line: swab_line); |
272 | } |
273 | EXPORT_SYMBOL(drm_fb_swab); |
274 | |
275 | static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigned int pixels) |
276 | { |
277 | u8 *dbuf8 = dbuf; |
278 | const __le32 *sbuf32 = sbuf; |
279 | unsigned int x; |
280 | u32 pix; |
281 | |
282 | for (x = 0; x < pixels; x++) { |
283 | pix = le32_to_cpu(sbuf32[x]); |
284 | dbuf8[x] = ((pix & 0x00e00000) >> 16) | |
285 | ((pix & 0x0000e000) >> 11) | |
286 | ((pix & 0x000000c0) >> 6); |
287 | } |
288 | } |
289 | |
290 | /** |
291 | * drm_fb_xrgb8888_to_rgb332 - Convert XRGB8888 to RGB332 clip buffer |
292 | * @dst: Array of RGB332 destination buffers |
293 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
294 | * within @dst; can be NULL if scanlines are stored next to each other. |
295 | * @src: Array of XRGB8888 source buffers |
296 | * @fb: DRM framebuffer |
297 | * @clip: Clip rectangle area to copy |
298 | * |
299 | * This function copies parts of a framebuffer to display memory and converts the |
300 | * color format during the process. Destination and framebuffer formats must match. The |
301 | * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at |
302 | * least as many entries as there are planes in @fb's format. Each entry stores the |
303 | * value for the format's respective color plane at the same index. |
304 | * |
305 | * This function does not apply clipping on @dst (i.e. the destination is at the |
306 | * top-left corner). |
307 | * |
308 | * Drivers can use this function for RGB332 devices that don't support XRGB8888 natively. |
309 | */ |
310 | void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pitch, |
311 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
312 | const struct drm_rect *clip) |
313 | { |
314 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
315 | 1, |
316 | }; |
317 | |
318 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
319 | xfrm_line: drm_fb_xrgb8888_to_rgb332_line); |
320 | } |
321 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332); |
322 | |
323 | static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigned int pixels) |
324 | { |
325 | __le16 *dbuf16 = dbuf; |
326 | const __le32 *sbuf32 = sbuf; |
327 | unsigned int x; |
328 | u16 val16; |
329 | u32 pix; |
330 | |
331 | for (x = 0; x < pixels; x++) { |
332 | pix = le32_to_cpu(sbuf32[x]); |
333 | val16 = ((pix & 0x00F80000) >> 8) | |
334 | ((pix & 0x0000FC00) >> 5) | |
335 | ((pix & 0x000000F8) >> 3); |
336 | dbuf16[x] = cpu_to_le16(val16); |
337 | } |
338 | } |
339 | |
340 | /* TODO: implement this helper as conversion to RGB565|BIG_ENDIAN */ |
341 | static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf, |
342 | unsigned int pixels) |
343 | { |
344 | __le16 *dbuf16 = dbuf; |
345 | const __le32 *sbuf32 = sbuf; |
346 | unsigned int x; |
347 | u16 val16; |
348 | u32 pix; |
349 | |
350 | for (x = 0; x < pixels; x++) { |
351 | pix = le32_to_cpu(sbuf32[x]); |
352 | val16 = ((pix & 0x00F80000) >> 8) | |
353 | ((pix & 0x0000FC00) >> 5) | |
354 | ((pix & 0x000000F8) >> 3); |
355 | dbuf16[x] = cpu_to_le16(swab16(val16)); |
356 | } |
357 | } |
358 | |
359 | /** |
360 | * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer |
361 | * @dst: Array of RGB565 destination buffers |
362 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
363 | * within @dst; can be NULL if scanlines are stored next to each other. |
364 | * @src: Array of XRGB8888 source buffer |
365 | * @fb: DRM framebuffer |
366 | * @clip: Clip rectangle area to copy |
367 | * @swab: Swap bytes |
368 | * |
369 | * This function copies parts of a framebuffer to display memory and converts the |
370 | * color format during the process. Destination and framebuffer formats must match. The |
371 | * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at |
372 | * least as many entries as there are planes in @fb's format. Each entry stores the |
373 | * value for the format's respective color plane at the same index. |
374 | * |
375 | * This function does not apply clipping on @dst (i.e. the destination is at the |
376 | * top-left corner). |
377 | * |
378 | * Drivers can use this function for RGB565 devices that don't support XRGB8888 natively. |
379 | */ |
380 | void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch, |
381 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
382 | const struct drm_rect *clip, bool swab) |
383 | { |
384 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
385 | 2, |
386 | }; |
387 | |
388 | void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels); |
389 | |
390 | if (swab) |
391 | xfrm_line = drm_fb_xrgb8888_to_rgb565_swab_line; |
392 | else |
393 | xfrm_line = drm_fb_xrgb8888_to_rgb565_line; |
394 | |
395 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, xfrm_line); |
396 | } |
397 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); |
398 | |
399 | static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsigned int pixels) |
400 | { |
401 | __le16 *dbuf16 = dbuf; |
402 | const __le32 *sbuf32 = sbuf; |
403 | unsigned int x; |
404 | u16 val16; |
405 | u32 pix; |
406 | |
407 | for (x = 0; x < pixels; x++) { |
408 | pix = le32_to_cpu(sbuf32[x]); |
409 | val16 = ((pix & 0x00f80000) >> 9) | |
410 | ((pix & 0x0000f800) >> 6) | |
411 | ((pix & 0x000000f8) >> 3); |
412 | dbuf16[x] = cpu_to_le16(val16); |
413 | } |
414 | } |
415 | |
416 | /** |
417 | * drm_fb_xrgb8888_to_xrgb1555 - Convert XRGB8888 to XRGB1555 clip buffer |
418 | * @dst: Array of XRGB1555 destination buffers |
419 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
420 | * within @dst; can be NULL if scanlines are stored next to each other. |
421 | * @src: Array of XRGB8888 source buffer |
422 | * @fb: DRM framebuffer |
423 | * @clip: Clip rectangle area to copy |
424 | * |
425 | * This function copies parts of a framebuffer to display memory and converts |
426 | * the color format during the process. The parameters @dst, @dst_pitch and |
427 | * @src refer to arrays. Each array must have at least as many entries as |
428 | * there are planes in @fb's format. Each entry stores the value for the |
429 | * format's respective color plane at the same index. |
430 | * |
431 | * This function does not apply clipping on @dst (i.e. the destination is at the |
432 | * top-left corner). |
433 | * |
434 | * Drivers can use this function for XRGB1555 devices that don't support |
435 | * XRGB8888 natively. |
436 | */ |
437 | void drm_fb_xrgb8888_to_xrgb1555(struct iosys_map *dst, const unsigned int *dst_pitch, |
438 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
439 | const struct drm_rect *clip) |
440 | { |
441 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
442 | 2, |
443 | }; |
444 | |
445 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
446 | xfrm_line: drm_fb_xrgb8888_to_xrgb1555_line); |
447 | } |
448 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb1555); |
449 | |
450 | static void drm_fb_xrgb8888_to_argb1555_line(void *dbuf, const void *sbuf, unsigned int pixels) |
451 | { |
452 | __le16 *dbuf16 = dbuf; |
453 | const __le32 *sbuf32 = sbuf; |
454 | unsigned int x; |
455 | u16 val16; |
456 | u32 pix; |
457 | |
458 | for (x = 0; x < pixels; x++) { |
459 | pix = le32_to_cpu(sbuf32[x]); |
460 | val16 = BIT(15) | /* set alpha bit */ |
461 | ((pix & 0x00f80000) >> 9) | |
462 | ((pix & 0x0000f800) >> 6) | |
463 | ((pix & 0x000000f8) >> 3); |
464 | dbuf16[x] = cpu_to_le16(val16); |
465 | } |
466 | } |
467 | |
468 | /** |
469 | * drm_fb_xrgb8888_to_argb1555 - Convert XRGB8888 to ARGB1555 clip buffer |
470 | * @dst: Array of ARGB1555 destination buffers |
471 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
472 | * within @dst; can be NULL if scanlines are stored next to each other. |
473 | * @src: Array of XRGB8888 source buffer |
474 | * @fb: DRM framebuffer |
475 | * @clip: Clip rectangle area to copy |
476 | * |
477 | * This function copies parts of a framebuffer to display memory and converts |
478 | * the color format during the process. The parameters @dst, @dst_pitch and |
479 | * @src refer to arrays. Each array must have at least as many entries as |
480 | * there are planes in @fb's format. Each entry stores the value for the |
481 | * format's respective color plane at the same index. |
482 | * |
483 | * This function does not apply clipping on @dst (i.e. the destination is at the |
484 | * top-left corner). |
485 | * |
486 | * Drivers can use this function for ARGB1555 devices that don't support |
487 | * XRGB8888 natively. It sets an opaque alpha channel as part of the conversion. |
488 | */ |
489 | void drm_fb_xrgb8888_to_argb1555(struct iosys_map *dst, const unsigned int *dst_pitch, |
490 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
491 | const struct drm_rect *clip) |
492 | { |
493 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
494 | 2, |
495 | }; |
496 | |
497 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
498 | xfrm_line: drm_fb_xrgb8888_to_argb1555_line); |
499 | } |
500 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb1555); |
501 | |
502 | static void drm_fb_xrgb8888_to_rgba5551_line(void *dbuf, const void *sbuf, unsigned int pixels) |
503 | { |
504 | __le16 *dbuf16 = dbuf; |
505 | const __le32 *sbuf32 = sbuf; |
506 | unsigned int x; |
507 | u16 val16; |
508 | u32 pix; |
509 | |
510 | for (x = 0; x < pixels; x++) { |
511 | pix = le32_to_cpu(sbuf32[x]); |
512 | val16 = ((pix & 0x00f80000) >> 8) | |
513 | ((pix & 0x0000f800) >> 5) | |
514 | ((pix & 0x000000f8) >> 2) | |
515 | BIT(0); /* set alpha bit */ |
516 | dbuf16[x] = cpu_to_le16(val16); |
517 | } |
518 | } |
519 | |
520 | /** |
521 | * drm_fb_xrgb8888_to_rgba5551 - Convert XRGB8888 to RGBA5551 clip buffer |
522 | * @dst: Array of RGBA5551 destination buffers |
523 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
524 | * within @dst; can be NULL if scanlines are stored next to each other. |
525 | * @src: Array of XRGB8888 source buffer |
526 | * @fb: DRM framebuffer |
527 | * @clip: Clip rectangle area to copy |
528 | * |
529 | * This function copies parts of a framebuffer to display memory and converts |
530 | * the color format during the process. The parameters @dst, @dst_pitch and |
531 | * @src refer to arrays. Each array must have at least as many entries as |
532 | * there are planes in @fb's format. Each entry stores the value for the |
533 | * format's respective color plane at the same index. |
534 | * |
535 | * This function does not apply clipping on @dst (i.e. the destination is at the |
536 | * top-left corner). |
537 | * |
538 | * Drivers can use this function for RGBA5551 devices that don't support |
539 | * XRGB8888 natively. It sets an opaque alpha channel as part of the conversion. |
540 | */ |
541 | void drm_fb_xrgb8888_to_rgba5551(struct iosys_map *dst, const unsigned int *dst_pitch, |
542 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
543 | const struct drm_rect *clip) |
544 | { |
545 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
546 | 2, |
547 | }; |
548 | |
549 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
550 | xfrm_line: drm_fb_xrgb8888_to_rgba5551_line); |
551 | } |
552 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgba5551); |
553 | |
554 | static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigned int pixels) |
555 | { |
556 | u8 *dbuf8 = dbuf; |
557 | const __le32 *sbuf32 = sbuf; |
558 | unsigned int x; |
559 | u32 pix; |
560 | |
561 | for (x = 0; x < pixels; x++) { |
562 | pix = le32_to_cpu(sbuf32[x]); |
563 | /* write blue-green-red to output in little endianness */ |
564 | *dbuf8++ = (pix & 0x000000FF) >> 0; |
565 | *dbuf8++ = (pix & 0x0000FF00) >> 8; |
566 | *dbuf8++ = (pix & 0x00FF0000) >> 16; |
567 | } |
568 | } |
569 | |
570 | /** |
571 | * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer |
572 | * @dst: Array of RGB888 destination buffers |
573 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
574 | * within @dst; can be NULL if scanlines are stored next to each other. |
575 | * @src: Array of XRGB8888 source buffers |
576 | * @fb: DRM framebuffer |
577 | * @clip: Clip rectangle area to copy |
578 | * |
579 | * This function copies parts of a framebuffer to display memory and converts the |
580 | * color format during the process. Destination and framebuffer formats must match. The |
581 | * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at |
582 | * least as many entries as there are planes in @fb's format. Each entry stores the |
583 | * value for the format's respective color plane at the same index. |
584 | * |
585 | * This function does not apply clipping on @dst (i.e. the destination is at the |
586 | * top-left corner). |
587 | * |
588 | * Drivers can use this function for RGB888 devices that don't natively |
589 | * support XRGB8888. |
590 | */ |
591 | void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch, |
592 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
593 | const struct drm_rect *clip) |
594 | { |
595 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
596 | 3, |
597 | }; |
598 | |
599 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
600 | xfrm_line: drm_fb_xrgb8888_to_rgb888_line); |
601 | } |
602 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); |
603 | |
604 | static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsigned int pixels) |
605 | { |
606 | __le32 *dbuf32 = dbuf; |
607 | const __le32 *sbuf32 = sbuf; |
608 | unsigned int x; |
609 | u32 pix; |
610 | |
611 | for (x = 0; x < pixels; x++) { |
612 | pix = le32_to_cpu(sbuf32[x]); |
613 | pix |= GENMASK(31, 24); /* fill alpha bits */ |
614 | dbuf32[x] = cpu_to_le32(pix); |
615 | } |
616 | } |
617 | |
618 | /** |
619 | * drm_fb_xrgb8888_to_argb8888 - Convert XRGB8888 to ARGB8888 clip buffer |
620 | * @dst: Array of ARGB8888 destination buffers |
621 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
622 | * within @dst; can be NULL if scanlines are stored next to each other. |
623 | * @src: Array of XRGB8888 source buffer |
624 | * @fb: DRM framebuffer |
625 | * @clip: Clip rectangle area to copy |
626 | * |
627 | * This function copies parts of a framebuffer to display memory and converts the |
628 | * color format during the process. The parameters @dst, @dst_pitch and @src refer |
629 | * to arrays. Each array must have at least as many entries as there are planes in |
630 | * @fb's format. Each entry stores the value for the format's respective color plane |
631 | * at the same index. |
632 | * |
633 | * This function does not apply clipping on @dst (i.e. the destination is at the |
634 | * top-left corner). |
635 | * |
636 | * Drivers can use this function for ARGB8888 devices that don't support XRGB8888 |
637 | * natively. It sets an opaque alpha channel as part of the conversion. |
638 | */ |
639 | void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_pitch, |
640 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
641 | const struct drm_rect *clip) |
642 | { |
643 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
644 | 4, |
645 | }; |
646 | |
647 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
648 | xfrm_line: drm_fb_xrgb8888_to_argb8888_line); |
649 | } |
650 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb8888); |
651 | |
652 | static void drm_fb_xrgb8888_to_abgr8888_line(void *dbuf, const void *sbuf, unsigned int pixels) |
653 | { |
654 | __le32 *dbuf32 = dbuf; |
655 | const __le32 *sbuf32 = sbuf; |
656 | unsigned int x; |
657 | u32 pix; |
658 | |
659 | for (x = 0; x < pixels; x++) { |
660 | pix = le32_to_cpu(sbuf32[x]); |
661 | pix = ((pix & 0x00ff0000) >> 16) << 0 | |
662 | ((pix & 0x0000ff00) >> 8) << 8 | |
663 | ((pix & 0x000000ff) >> 0) << 16 | |
664 | GENMASK(31, 24); /* fill alpha bits */ |
665 | *dbuf32++ = cpu_to_le32(pix); |
666 | } |
667 | } |
668 | |
669 | static void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, |
670 | const struct iosys_map *src, |
671 | const struct drm_framebuffer *fb, |
672 | const struct drm_rect *clip) |
673 | { |
674 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
675 | 4, |
676 | }; |
677 | |
678 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
679 | xfrm_line: drm_fb_xrgb8888_to_abgr8888_line); |
680 | } |
681 | |
682 | static void drm_fb_xrgb8888_to_xbgr8888_line(void *dbuf, const void *sbuf, unsigned int pixels) |
683 | { |
684 | __le32 *dbuf32 = dbuf; |
685 | const __le32 *sbuf32 = sbuf; |
686 | unsigned int x; |
687 | u32 pix; |
688 | |
689 | for (x = 0; x < pixels; x++) { |
690 | pix = le32_to_cpu(sbuf32[x]); |
691 | pix = ((pix & 0x00ff0000) >> 16) << 0 | |
692 | ((pix & 0x0000ff00) >> 8) << 8 | |
693 | ((pix & 0x000000ff) >> 0) << 16 | |
694 | ((pix & 0xff000000) >> 24) << 24; |
695 | *dbuf32++ = cpu_to_le32(pix); |
696 | } |
697 | } |
698 | |
699 | static void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, |
700 | const struct iosys_map *src, |
701 | const struct drm_framebuffer *fb, |
702 | const struct drm_rect *clip) |
703 | { |
704 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
705 | 4, |
706 | }; |
707 | |
708 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
709 | xfrm_line: drm_fb_xrgb8888_to_xbgr8888_line); |
710 | } |
711 | |
712 | static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels) |
713 | { |
714 | __le32 *dbuf32 = dbuf; |
715 | const __le32 *sbuf32 = sbuf; |
716 | unsigned int x; |
717 | u32 val32; |
718 | u32 pix; |
719 | |
720 | for (x = 0; x < pixels; x++) { |
721 | pix = le32_to_cpu(sbuf32[x]); |
722 | val32 = ((pix & 0x000000FF) << 2) | |
723 | ((pix & 0x0000FF00) << 4) | |
724 | ((pix & 0x00FF0000) << 6); |
725 | pix = val32 | ((val32 >> 8) & 0x00300C03); |
726 | *dbuf32++ = cpu_to_le32(pix); |
727 | } |
728 | } |
729 | |
730 | /** |
731 | * drm_fb_xrgb8888_to_xrgb2101010 - Convert XRGB8888 to XRGB2101010 clip buffer |
732 | * @dst: Array of XRGB2101010 destination buffers |
733 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
734 | * within @dst; can be NULL if scanlines are stored next to each other. |
735 | * @src: Array of XRGB8888 source buffers |
736 | * @fb: DRM framebuffer |
737 | * @clip: Clip rectangle area to copy |
738 | * |
739 | * This function copies parts of a framebuffer to display memory and converts the |
740 | * color format during the process. Destination and framebuffer formats must match. The |
741 | * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at |
742 | * least as many entries as there are planes in @fb's format. Each entry stores the |
743 | * value for the format's respective color plane at the same index. |
744 | * |
745 | * This function does not apply clipping on @dst (i.e. the destination is at the |
746 | * top-left corner). |
747 | * |
748 | * Drivers can use this function for XRGB2101010 devices that don't support XRGB8888 |
749 | * natively. |
750 | */ |
751 | void drm_fb_xrgb8888_to_xrgb2101010(struct iosys_map *dst, const unsigned int *dst_pitch, |
752 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
753 | const struct drm_rect *clip) |
754 | { |
755 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
756 | 4, |
757 | }; |
758 | |
759 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
760 | xfrm_line: drm_fb_xrgb8888_to_xrgb2101010_line); |
761 | } |
762 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010); |
763 | |
764 | static void drm_fb_xrgb8888_to_argb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels) |
765 | { |
766 | __le32 *dbuf32 = dbuf; |
767 | const __le32 *sbuf32 = sbuf; |
768 | unsigned int x; |
769 | u32 val32; |
770 | u32 pix; |
771 | |
772 | for (x = 0; x < pixels; x++) { |
773 | pix = le32_to_cpu(sbuf32[x]); |
774 | val32 = ((pix & 0x000000ff) << 2) | |
775 | ((pix & 0x0000ff00) << 4) | |
776 | ((pix & 0x00ff0000) << 6); |
777 | pix = GENMASK(31, 30) | /* set alpha bits */ |
778 | val32 | ((val32 >> 8) & 0x00300c03); |
779 | *dbuf32++ = cpu_to_le32(pix); |
780 | } |
781 | } |
782 | |
783 | /** |
784 | * drm_fb_xrgb8888_to_argb2101010 - Convert XRGB8888 to ARGB2101010 clip buffer |
785 | * @dst: Array of ARGB2101010 destination buffers |
786 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
787 | * within @dst; can be NULL if scanlines are stored next to each other. |
788 | * @src: Array of XRGB8888 source buffers |
789 | * @fb: DRM framebuffer |
790 | * @clip: Clip rectangle area to copy |
791 | * |
792 | * This function copies parts of a framebuffer to display memory and converts |
793 | * the color format during the process. The parameters @dst, @dst_pitch and |
794 | * @src refer to arrays. Each array must have at least as many entries as |
795 | * there are planes in @fb's format. Each entry stores the value for the |
796 | * format's respective color plane at the same index. |
797 | * |
798 | * This function does not apply clipping on @dst (i.e. the destination is at the |
799 | * top-left corner). |
800 | * |
801 | * Drivers can use this function for ARGB2101010 devices that don't support XRGB8888 |
802 | * natively. |
803 | */ |
804 | void drm_fb_xrgb8888_to_argb2101010(struct iosys_map *dst, const unsigned int *dst_pitch, |
805 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
806 | const struct drm_rect *clip) |
807 | { |
808 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
809 | 4, |
810 | }; |
811 | |
812 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
813 | xfrm_line: drm_fb_xrgb8888_to_argb2101010_line); |
814 | } |
815 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb2101010); |
816 | |
817 | static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned int pixels) |
818 | { |
819 | u8 *dbuf8 = dbuf; |
820 | const __le32 *sbuf32 = sbuf; |
821 | unsigned int x; |
822 | |
823 | for (x = 0; x < pixels; x++) { |
824 | u32 pix = le32_to_cpu(sbuf32[x]); |
825 | u8 r = (pix & 0x00ff0000) >> 16; |
826 | u8 g = (pix & 0x0000ff00) >> 8; |
827 | u8 b = pix & 0x000000ff; |
828 | |
829 | /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ |
830 | *dbuf8++ = (3 * r + 6 * g + b) / 10; |
831 | } |
832 | } |
833 | |
834 | /** |
835 | * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale |
836 | * @dst: Array of 8-bit grayscale destination buffers |
837 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
838 | * within @dst; can be NULL if scanlines are stored next to each other. |
839 | * @src: Array of XRGB8888 source buffers |
840 | * @fb: DRM framebuffer |
841 | * @clip: Clip rectangle area to copy |
842 | * |
843 | * This function copies parts of a framebuffer to display memory and converts the |
844 | * color format during the process. Destination and framebuffer formats must match. The |
845 | * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at |
846 | * least as many entries as there are planes in @fb's format. Each entry stores the |
847 | * value for the format's respective color plane at the same index. |
848 | * |
849 | * This function does not apply clipping on @dst (i.e. the destination is at the |
850 | * top-left corner). |
851 | * |
852 | * DRM doesn't have native monochrome or grayscale support. Drivers can use this |
853 | * function for grayscale devices that don't support XRGB8888 natively.Such |
854 | * drivers can announce the commonly supported XR24 format to userspace and use |
855 | * this function to convert to the native format. Monochrome drivers will use the |
856 | * most significant bit, where 1 means foreground color and 0 background color. |
857 | * ITU BT.601 is being used for the RGB -> luma (brightness) conversion. |
858 | */ |
859 | void drm_fb_xrgb8888_to_gray8(struct iosys_map *dst, const unsigned int *dst_pitch, |
860 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
861 | const struct drm_rect *clip) |
862 | { |
863 | static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { |
864 | 1, |
865 | }; |
866 | |
867 | drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, vaddr_cached_hint: false, |
868 | xfrm_line: drm_fb_xrgb8888_to_gray8_line); |
869 | } |
870 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); |
871 | |
872 | /** |
873 | * drm_fb_blit - Copy parts of a framebuffer to display memory |
874 | * @dst: Array of display-memory addresses to copy to |
875 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
876 | * within @dst; can be NULL if scanlines are stored next to each other. |
877 | * @dst_format: FOURCC code of the display's color format |
878 | * @src: The framebuffer memory to copy from |
879 | * @fb: The framebuffer to copy from |
880 | * @clip: Clip rectangle area to copy |
881 | * |
882 | * This function copies parts of a framebuffer to display memory. If the |
883 | * formats of the display and the framebuffer mismatch, the blit function |
884 | * will attempt to convert between them during the process. The parameters @dst, |
885 | * @dst_pitch and @src refer to arrays. Each array must have at least as many |
886 | * entries as there are planes in @dst_format's format. Each entry stores the |
887 | * value for the format's respective color plane at the same index. |
888 | * |
889 | * This function does not apply clipping on @dst (i.e. the destination is at the |
890 | * top-left corner). |
891 | * |
892 | * Returns: |
893 | * 0 on success, or |
894 | * -EINVAL if the color-format conversion failed, or |
895 | * a negative error code otherwise. |
896 | */ |
897 | int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t dst_format, |
898 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
899 | const struct drm_rect *clip) |
900 | { |
901 | uint32_t fb_format = fb->format->format; |
902 | |
903 | if (fb_format == dst_format) { |
904 | drm_fb_memcpy(dst, dst_pitch, src, fb, clip); |
905 | return 0; |
906 | } else if (fb_format == (dst_format | DRM_FORMAT_BIG_ENDIAN)) { |
907 | drm_fb_swab(dst, dst_pitch, src, fb, clip, false); |
908 | return 0; |
909 | } else if (fb_format == (dst_format & ~DRM_FORMAT_BIG_ENDIAN)) { |
910 | drm_fb_swab(dst, dst_pitch, src, fb, clip, false); |
911 | return 0; |
912 | } else if (fb_format == DRM_FORMAT_XRGB8888) { |
913 | if (dst_format == DRM_FORMAT_RGB565) { |
914 | drm_fb_xrgb8888_to_rgb565(dst, dst_pitch, src, fb, clip, false); |
915 | return 0; |
916 | } else if (dst_format == DRM_FORMAT_XRGB1555) { |
917 | drm_fb_xrgb8888_to_xrgb1555(dst, dst_pitch, src, fb, clip); |
918 | return 0; |
919 | } else if (dst_format == DRM_FORMAT_ARGB1555) { |
920 | drm_fb_xrgb8888_to_argb1555(dst, dst_pitch, src, fb, clip); |
921 | return 0; |
922 | } else if (dst_format == DRM_FORMAT_RGBA5551) { |
923 | drm_fb_xrgb8888_to_rgba5551(dst, dst_pitch, src, fb, clip); |
924 | return 0; |
925 | } else if (dst_format == DRM_FORMAT_RGB888) { |
926 | drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip); |
927 | return 0; |
928 | } else if (dst_format == DRM_FORMAT_ARGB8888) { |
929 | drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip); |
930 | return 0; |
931 | } else if (dst_format == DRM_FORMAT_XBGR8888) { |
932 | drm_fb_xrgb8888_to_xbgr8888(dst, dst_pitch, src, fb, clip); |
933 | return 0; |
934 | } else if (dst_format == DRM_FORMAT_ABGR8888) { |
935 | drm_fb_xrgb8888_to_abgr8888(dst, dst_pitch, src, fb, clip); |
936 | return 0; |
937 | } else if (dst_format == DRM_FORMAT_XRGB2101010) { |
938 | drm_fb_xrgb8888_to_xrgb2101010(dst, dst_pitch, src, fb, clip); |
939 | return 0; |
940 | } else if (dst_format == DRM_FORMAT_ARGB2101010) { |
941 | drm_fb_xrgb8888_to_argb2101010(dst, dst_pitch, src, fb, clip); |
942 | return 0; |
943 | } else if (dst_format == DRM_FORMAT_BGRX8888) { |
944 | drm_fb_swab(dst, dst_pitch, src, fb, clip, false); |
945 | return 0; |
946 | } |
947 | } |
948 | |
949 | drm_warn_once(fb->dev, "No conversion helper from %p4cc to %p4cc found.\n" , |
950 | &fb_format, &dst_format); |
951 | |
952 | return -EINVAL; |
953 | } |
954 | EXPORT_SYMBOL(drm_fb_blit); |
955 | |
956 | static void drm_fb_gray8_to_mono_line(void *dbuf, const void *sbuf, unsigned int pixels) |
957 | { |
958 | u8 *dbuf8 = dbuf; |
959 | const u8 *sbuf8 = sbuf; |
960 | |
961 | while (pixels) { |
962 | unsigned int i, bits = min(pixels, 8U); |
963 | u8 byte = 0; |
964 | |
965 | for (i = 0; i < bits; i++, pixels--) { |
966 | if (*sbuf8++ >= 128) |
967 | byte |= BIT(i); |
968 | } |
969 | *dbuf8++ = byte; |
970 | } |
971 | } |
972 | |
973 | /** |
974 | * drm_fb_xrgb8888_to_mono - Convert XRGB8888 to monochrome |
975 | * @dst: Array of monochrome destination buffers (0=black, 1=white) |
976 | * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines |
977 | * within @dst; can be NULL if scanlines are stored next to each other. |
978 | * @src: Array of XRGB8888 source buffers |
979 | * @fb: DRM framebuffer |
980 | * @clip: Clip rectangle area to copy |
981 | * |
982 | * This function copies parts of a framebuffer to display memory and converts the |
983 | * color format during the process. Destination and framebuffer formats must match. The |
984 | * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at |
985 | * least as many entries as there are planes in @fb's format. Each entry stores the |
986 | * value for the format's respective color plane at the same index. |
987 | * |
988 | * This function does not apply clipping on @dst (i.e. the destination is at the |
989 | * top-left corner). The first pixel (upper left corner of the clip rectangle) will |
990 | * be converted and copied to the first bit (LSB) in the first byte of the monochrome |
991 | * destination buffer. If the caller requires that the first pixel in a byte must |
992 | * be located at an x-coordinate that is a multiple of 8, then the caller must take |
993 | * care itself of supplying a suitable clip rectangle. |
994 | * |
995 | * DRM doesn't have native monochrome support. Drivers can use this function for |
996 | * monochrome devices that don't support XRGB8888 natively. Such drivers can |
997 | * announce the commonly supported XR24 format to userspace and use this function |
998 | * to convert to the native format. |
999 | * |
1000 | * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and |
1001 | * then the result is converted from grayscale to monochrome. |
1002 | */ |
1003 | void drm_fb_xrgb8888_to_mono(struct iosys_map *dst, const unsigned int *dst_pitch, |
1004 | const struct iosys_map *src, const struct drm_framebuffer *fb, |
1005 | const struct drm_rect *clip) |
1006 | { |
1007 | static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = { |
1008 | 0, 0, 0, 0 |
1009 | }; |
1010 | unsigned int linepixels = drm_rect_width(r: clip); |
1011 | unsigned int lines = drm_rect_height(r: clip); |
1012 | unsigned int cpp = fb->format->cpp[0]; |
1013 | unsigned int len_src32 = linepixels * cpp; |
1014 | struct drm_device *dev = fb->dev; |
1015 | void *vaddr = src[0].vaddr; |
1016 | unsigned int dst_pitch_0; |
1017 | unsigned int y; |
1018 | u8 *mono = dst[0].vaddr, *gray8; |
1019 | u32 *src32; |
1020 | |
1021 | if (drm_WARN_ON(dev, fb->format->format != DRM_FORMAT_XRGB8888)) |
1022 | return; |
1023 | |
1024 | if (!dst_pitch) |
1025 | dst_pitch = default_dst_pitch; |
1026 | dst_pitch_0 = dst_pitch[0]; |
1027 | |
1028 | /* |
1029 | * The mono destination buffer contains 1 bit per pixel |
1030 | */ |
1031 | if (!dst_pitch_0) |
1032 | dst_pitch_0 = DIV_ROUND_UP(linepixels, 8); |
1033 | |
1034 | /* |
1035 | * The dma memory is write-combined so reads are uncached. |
1036 | * Speed up by fetching one line at a time. |
1037 | * |
1038 | * Also, format conversion from XR24 to monochrome are done |
1039 | * line-by-line but are converted to 8-bit grayscale as an |
1040 | * intermediate step. |
1041 | * |
1042 | * Allocate a buffer to be used for both copying from the cma |
1043 | * memory and to store the intermediate grayscale line pixels. |
1044 | */ |
1045 | src32 = kmalloc(size: len_src32 + linepixels, GFP_KERNEL); |
1046 | if (!src32) |
1047 | return; |
1048 | |
1049 | gray8 = (u8 *)src32 + len_src32; |
1050 | |
1051 | vaddr += clip_offset(clip, pitch: fb->pitches[0], cpp); |
1052 | for (y = 0; y < lines; y++) { |
1053 | src32 = memcpy(src32, vaddr, len_src32); |
1054 | drm_fb_xrgb8888_to_gray8_line(dbuf: gray8, sbuf: src32, pixels: linepixels); |
1055 | drm_fb_gray8_to_mono_line(dbuf: mono, sbuf: gray8, pixels: linepixels); |
1056 | vaddr += fb->pitches[0]; |
1057 | mono += dst_pitch_0; |
1058 | } |
1059 | |
1060 | kfree(objp: src32); |
1061 | } |
1062 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono); |
1063 | |
1064 | static uint32_t drm_fb_nonalpha_fourcc(uint32_t fourcc) |
1065 | { |
1066 | /* only handle formats with depth != 0 and alpha channel */ |
1067 | switch (fourcc) { |
1068 | case DRM_FORMAT_ARGB1555: |
1069 | return DRM_FORMAT_XRGB1555; |
1070 | case DRM_FORMAT_ABGR1555: |
1071 | return DRM_FORMAT_XBGR1555; |
1072 | case DRM_FORMAT_RGBA5551: |
1073 | return DRM_FORMAT_RGBX5551; |
1074 | case DRM_FORMAT_BGRA5551: |
1075 | return DRM_FORMAT_BGRX5551; |
1076 | case DRM_FORMAT_ARGB8888: |
1077 | return DRM_FORMAT_XRGB8888; |
1078 | case DRM_FORMAT_ABGR8888: |
1079 | return DRM_FORMAT_XBGR8888; |
1080 | case DRM_FORMAT_RGBA8888: |
1081 | return DRM_FORMAT_RGBX8888; |
1082 | case DRM_FORMAT_BGRA8888: |
1083 | return DRM_FORMAT_BGRX8888; |
1084 | case DRM_FORMAT_ARGB2101010: |
1085 | return DRM_FORMAT_XRGB2101010; |
1086 | case DRM_FORMAT_ABGR2101010: |
1087 | return DRM_FORMAT_XBGR2101010; |
1088 | case DRM_FORMAT_RGBA1010102: |
1089 | return DRM_FORMAT_RGBX1010102; |
1090 | case DRM_FORMAT_BGRA1010102: |
1091 | return DRM_FORMAT_BGRX1010102; |
1092 | } |
1093 | |
1094 | return fourcc; |
1095 | } |
1096 | |
1097 | static bool is_listed_fourcc(const uint32_t *fourccs, size_t nfourccs, uint32_t fourcc) |
1098 | { |
1099 | const uint32_t *fourccs_end = fourccs + nfourccs; |
1100 | |
1101 | while (fourccs < fourccs_end) { |
1102 | if (*fourccs == fourcc) |
1103 | return true; |
1104 | ++fourccs; |
1105 | } |
1106 | return false; |
1107 | } |
1108 | |
1109 | /** |
1110 | * drm_fb_build_fourcc_list - Filters a list of supported color formats against |
1111 | * the device's native formats |
1112 | * @dev: DRM device |
1113 | * @native_fourccs: 4CC codes of natively supported color formats |
1114 | * @native_nfourccs: The number of entries in @native_fourccs |
1115 | * @fourccs_out: Returns 4CC codes of supported color formats |
1116 | * @nfourccs_out: The number of available entries in @fourccs_out |
1117 | * |
1118 | * This function create a list of supported color format from natively |
1119 | * supported formats and additional emulated formats. |
1120 | * At a minimum, most userspace programs expect at least support for |
1121 | * XRGB8888 on the primary plane. Devices that have to emulate the |
1122 | * format, and possibly others, can use drm_fb_build_fourcc_list() to |
1123 | * create a list of supported color formats. The returned list can |
1124 | * be handed over to drm_universal_plane_init() et al. Native formats |
1125 | * will go before emulated formats. Native formats with alpha channel |
1126 | * will be replaced by such without, as primary planes usually don't |
1127 | * support alpha. Other heuristics might be applied |
1128 | * to optimize the order. Formats near the beginning of the list are |
1129 | * usually preferred over formats near the end of the list. |
1130 | * |
1131 | * Returns: |
1132 | * The number of color-formats 4CC codes returned in @fourccs_out. |
1133 | */ |
1134 | size_t drm_fb_build_fourcc_list(struct drm_device *dev, |
1135 | const u32 *native_fourccs, size_t native_nfourccs, |
1136 | u32 *fourccs_out, size_t nfourccs_out) |
1137 | { |
1138 | /* |
1139 | * XRGB8888 is the default fallback format for most of userspace |
1140 | * and it's currently the only format that should be emulated for |
1141 | * the primary plane. Only if there's ever another default fallback, |
1142 | * it should be added here. |
1143 | */ |
1144 | static const uint32_t [] = { |
1145 | DRM_FORMAT_XRGB8888, |
1146 | }; |
1147 | static const size_t = ARRAY_SIZE(extra_fourccs); |
1148 | |
1149 | u32 *fourccs = fourccs_out; |
1150 | const u32 *fourccs_end = fourccs_out + nfourccs_out; |
1151 | size_t i; |
1152 | |
1153 | /* |
1154 | * The device's native formats go first. |
1155 | */ |
1156 | |
1157 | for (i = 0; i < native_nfourccs; ++i) { |
1158 | /* |
1159 | * Several DTs, boot loaders and firmware report native |
1160 | * alpha formats that are non-alpha formats instead. So |
1161 | * replace alpha formats by non-alpha formats. |
1162 | */ |
1163 | u32 fourcc = drm_fb_nonalpha_fourcc(fourcc: native_fourccs[i]); |
1164 | |
1165 | if (is_listed_fourcc(fourccs: fourccs_out, nfourccs: fourccs - fourccs_out, fourcc)) { |
1166 | continue; /* skip duplicate entries */ |
1167 | } else if (fourccs == fourccs_end) { |
1168 | drm_warn(dev, "Ignoring native format %p4cc\n" , &fourcc); |
1169 | continue; /* end of available output buffer */ |
1170 | } |
1171 | |
1172 | drm_dbg_kms(dev, "adding native format %p4cc\n" , &fourcc); |
1173 | |
1174 | *fourccs = fourcc; |
1175 | ++fourccs; |
1176 | } |
1177 | |
1178 | /* |
1179 | * The extra formats, emulated by the driver, go second. |
1180 | */ |
1181 | |
1182 | for (i = 0; (i < extra_nfourccs) && (fourccs < fourccs_end); ++i) { |
1183 | u32 fourcc = extra_fourccs[i]; |
1184 | |
1185 | if (is_listed_fourcc(fourccs: fourccs_out, nfourccs: fourccs - fourccs_out, fourcc)) { |
1186 | continue; /* skip duplicate and native entries */ |
1187 | } else if (fourccs == fourccs_end) { |
1188 | drm_warn(dev, "Ignoring emulated format %p4cc\n" , &fourcc); |
1189 | continue; /* end of available output buffer */ |
1190 | } |
1191 | |
1192 | drm_dbg_kms(dev, "adding emulated format %p4cc\n" , &fourcc); |
1193 | |
1194 | *fourccs = fourcc; |
1195 | ++fourccs; |
1196 | } |
1197 | |
1198 | return fourccs - fourccs_out; |
1199 | } |
1200 | EXPORT_SYMBOL(drm_fb_build_fourcc_list); |
1201 | |