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