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 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/slab.h> | |
bf4f6d16 | 13 | #include <linux/io.h> |
7415287e | 14 | |
bcf8b616 | 15 | #include <drm/drm_device.h> |
7415287e GH |
16 | #include <drm/drm_format_helper.h> |
17 | #include <drm/drm_framebuffer.h> | |
18 | #include <drm/drm_fourcc.h> | |
bcf8b616 | 19 | #include <drm/drm_print.h> |
7415287e GH |
20 | #include <drm/drm_rect.h> |
21 | ||
452290f3 | 22 | static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp) |
26f024f5 | 23 | { |
bf4f6d16 | 24 | return clip->y1 * pitch + clip->x1 * cpp; |
26f024f5 GH |
25 | } |
26 | ||
452290f3 TZ |
27 | /** |
28 | * drm_fb_clip_offset - Returns the clipping rectangles byte-offset in a framebuffer | |
29 | * @pitch: Framebuffer line pitch in byte | |
30 | * @format: Framebuffer format | |
31 | * @clip: Clip rectangle | |
32 | * | |
33 | * Returns: | |
34 | * The byte offset of the clip rectangle's top-left corner within the framebuffer. | |
35 | */ | |
36 | unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format, | |
37 | const struct drm_rect *clip) | |
38 | { | |
39 | return clip_offset(clip, pitch, format->cpp[0]); | |
40 | } | |
41 | EXPORT_SYMBOL(drm_fb_clip_offset); | |
42 | ||
7415287e GH |
43 | /** |
44 | * drm_fb_memcpy - Copy clip buffer | |
45 | * @dst: Destination buffer | |
27bd66dd | 46 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
7415287e GH |
47 | * @vaddr: Source buffer |
48 | * @fb: DRM framebuffer | |
49 | * @clip: Clip rectangle area to copy | |
26f024f5 GH |
50 | * |
51 | * This function does not apply clipping on dst, i.e. the destination | |
27bd66dd | 52 | * is at the top-left corner. |
7415287e | 53 | */ |
27bd66dd TZ |
54 | void drm_fb_memcpy(void *dst, unsigned int dst_pitch, const void *vaddr, |
55 | const struct drm_framebuffer *fb, const struct drm_rect *clip) | |
7415287e | 56 | { |
b0f986b4 | 57 | unsigned int cpp = fb->format->cpp[0]; |
7415287e | 58 | size_t len = (clip->x2 - clip->x1) * cpp; |
bf4f6d16 | 59 | unsigned int y, lines = clip->y2 - clip->y1; |
7415287e | 60 | |
27bd66dd TZ |
61 | if (!dst_pitch) |
62 | dst_pitch = len; | |
63 | ||
bf4f6d16 GH |
64 | vaddr += clip_offset(clip, fb->pitches[0], cpp); |
65 | for (y = 0; y < lines; y++) { | |
66 | memcpy(dst, vaddr, len); | |
67 | vaddr += fb->pitches[0]; | |
27bd66dd | 68 | dst += dst_pitch; |
bf4f6d16 | 69 | } |
7415287e GH |
70 | } |
71 | EXPORT_SYMBOL(drm_fb_memcpy); | |
72 | ||
26f024f5 | 73 | /** |
27bd66dd | 74 | * drm_fb_memcpy_toio - Copy clip buffer |
bf4f6d16 | 75 | * @dst: Destination buffer (iomem) |
5ab7af71 | 76 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
26f024f5 GH |
77 | * @vaddr: Source buffer |
78 | * @fb: DRM framebuffer | |
79 | * @clip: Clip rectangle area to copy | |
80 | * | |
27bd66dd TZ |
81 | * This function does not apply clipping on dst, i.e. the destination |
82 | * is at the top-left corner. | |
26f024f5 | 83 | */ |
27bd66dd TZ |
84 | void drm_fb_memcpy_toio(void __iomem *dst, unsigned int dst_pitch, const void *vaddr, |
85 | const struct drm_framebuffer *fb, const struct drm_rect *clip) | |
26f024f5 | 86 | { |
b0f986b4 | 87 | unsigned int cpp = fb->format->cpp[0]; |
26f024f5 | 88 | size_t len = (clip->x2 - clip->x1) * cpp; |
bf4f6d16 | 89 | unsigned int y, lines = clip->y2 - clip->y1; |
26f024f5 | 90 | |
27bd66dd TZ |
91 | if (!dst_pitch) |
92 | dst_pitch = len; | |
93 | ||
94 | vaddr += clip_offset(clip, fb->pitches[0], cpp); | |
bf4f6d16 GH |
95 | for (y = 0; y < lines; y++) { |
96 | memcpy_toio(dst, vaddr, len); | |
97 | vaddr += fb->pitches[0]; | |
5ab7af71 | 98 | dst += dst_pitch; |
bf4f6d16 | 99 | } |
26f024f5 | 100 | } |
27bd66dd | 101 | EXPORT_SYMBOL(drm_fb_memcpy_toio); |
26f024f5 | 102 | |
7415287e | 103 | /** |
bd34cea2 NT |
104 | * drm_fb_swab - Swap bytes into clip buffer |
105 | * @dst: Destination buffer | |
3e3543c8 | 106 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
bd34cea2 | 107 | * @src: Source buffer |
7415287e GH |
108 | * @fb: DRM framebuffer |
109 | * @clip: Clip rectangle area to copy | |
bd34cea2 NT |
110 | * @cached: Source buffer is mapped cached (eg. not write-combined) |
111 | * | |
112 | * If @cached is false a temporary buffer is used to cache one pixel line at a | |
113 | * time to speed up slow uncached reads. | |
114 | * | |
115 | * This function does not apply clipping on dst, i.e. the destination | |
3e3543c8 | 116 | * is at the top-left corner. |
7415287e | 117 | */ |
3e3543c8 TZ |
118 | void drm_fb_swab(void *dst, unsigned int dst_pitch, const void *src, |
119 | const struct drm_framebuffer *fb, const struct drm_rect *clip, | |
120 | bool cached) | |
7415287e | 121 | { |
bd34cea2 NT |
122 | u8 cpp = fb->format->cpp[0]; |
123 | size_t len = drm_rect_width(clip) * cpp; | |
3e3543c8 TZ |
124 | const u16 *src16; |
125 | const u32 *src32; | |
126 | u16 *dst16; | |
127 | u32 *dst32; | |
7415287e | 128 | unsigned int x, y; |
bd34cea2 NT |
129 | void *buf = NULL; |
130 | ||
131 | if (WARN_ON_ONCE(cpp != 2 && cpp != 4)) | |
7415287e GH |
132 | return; |
133 | ||
3e3543c8 TZ |
134 | if (!dst_pitch) |
135 | dst_pitch = len; | |
136 | ||
bd34cea2 NT |
137 | if (!cached) |
138 | buf = kmalloc(len, GFP_KERNEL); | |
139 | ||
140 | src += clip_offset(clip, fb->pitches[0], cpp); | |
141 | ||
7415287e | 142 | for (y = clip->y1; y < clip->y2; y++) { |
bd34cea2 NT |
143 | if (buf) { |
144 | memcpy(buf, src, len); | |
145 | src16 = buf; | |
146 | src32 = buf; | |
147 | } else { | |
148 | src16 = src; | |
149 | src32 = src; | |
150 | } | |
151 | ||
3e3543c8 TZ |
152 | dst16 = dst; |
153 | dst32 = dst; | |
154 | ||
bd34cea2 NT |
155 | for (x = clip->x1; x < clip->x2; x++) { |
156 | if (cpp == 4) | |
157 | *dst32++ = swab32(*src32++); | |
158 | else | |
159 | *dst16++ = swab16(*src16++); | |
160 | } | |
161 | ||
162 | src += fb->pitches[0]; | |
3e3543c8 | 163 | dst += dst_pitch; |
7415287e GH |
164 | } |
165 | ||
166 | kfree(buf); | |
167 | } | |
bd34cea2 | 168 | EXPORT_SYMBOL(drm_fb_swab); |
7415287e | 169 | |
53bc2098 | 170 | static void drm_fb_xrgb8888_to_rgb332_line(u8 *dbuf, const __le32 *sbuf, unsigned int pixels) |
cee0b7cb NT |
171 | { |
172 | unsigned int x; | |
173 | u32 pix; | |
174 | ||
175 | for (x = 0; x < pixels; x++) { | |
176 | pix = le32_to_cpu(sbuf[x]); | |
177 | dbuf[x] = ((pix & 0x00e00000) >> 16) | | |
178 | ((pix & 0x0000e000) >> 11) | | |
179 | ((pix & 0x000000c0) >> 6); | |
180 | } | |
181 | } | |
182 | ||
183 | /** | |
184 | * drm_fb_xrgb8888_to_rgb332 - Convert XRGB8888 to RGB332 clip buffer | |
185 | * @dst: RGB332 destination buffer | |
53bc2098 | 186 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
cee0b7cb NT |
187 | * @src: XRGB8888 source buffer |
188 | * @fb: DRM framebuffer | |
189 | * @clip: Clip rectangle area to copy | |
190 | * | |
191 | * Drivers can use this function for RGB332 devices that don't natively support XRGB8888. | |
cee0b7cb | 192 | */ |
53bc2098 TZ |
193 | void drm_fb_xrgb8888_to_rgb332(void *dst, unsigned int dst_pitch, const void *src, |
194 | const struct drm_framebuffer *fb, const struct drm_rect *clip) | |
cee0b7cb NT |
195 | { |
196 | size_t width = drm_rect_width(clip); | |
197 | size_t src_len = width * sizeof(u32); | |
198 | unsigned int y; | |
199 | void *sbuf; | |
200 | ||
53bc2098 TZ |
201 | if (!dst_pitch) |
202 | dst_pitch = width; | |
203 | ||
cee0b7cb NT |
204 | /* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */ |
205 | sbuf = kmalloc(src_len, GFP_KERNEL); | |
206 | if (!sbuf) | |
207 | return; | |
208 | ||
209 | src += clip_offset(clip, fb->pitches[0], sizeof(u32)); | |
210 | for (y = 0; y < drm_rect_height(clip); y++) { | |
211 | memcpy(sbuf, src, src_len); | |
212 | drm_fb_xrgb8888_to_rgb332_line(dst, sbuf, width); | |
213 | src += fb->pitches[0]; | |
53bc2098 | 214 | dst += dst_pitch; |
cee0b7cb NT |
215 | } |
216 | ||
217 | kfree(sbuf); | |
218 | } | |
219 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332); | |
220 | ||
53bc2098 | 221 | static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, const u32 *sbuf, |
d653bd39 GH |
222 | unsigned int pixels, |
223 | bool swab) | |
7415287e | 224 | { |
d653bd39 GH |
225 | unsigned int x; |
226 | u16 val16; | |
227 | ||
228 | for (x = 0; x < pixels; x++) { | |
229 | val16 = ((sbuf[x] & 0x00F80000) >> 8) | | |
230 | ((sbuf[x] & 0x0000FC00) >> 5) | | |
231 | ((sbuf[x] & 0x000000F8) >> 3); | |
232 | if (swab) | |
233 | dbuf[x] = swab16(val16); | |
234 | else | |
235 | dbuf[x] = val16; | |
7415287e | 236 | } |
bcc44420 GH |
237 | } |
238 | ||
239 | /** | |
240 | * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer | |
241 | * @dst: RGB565 destination buffer | |
53bc2098 | 242 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
bcc44420 GH |
243 | * @vaddr: XRGB8888 source buffer |
244 | * @fb: DRM framebuffer | |
245 | * @clip: Clip rectangle area to copy | |
d653bd39 | 246 | * @swab: Swap bytes |
bcc44420 GH |
247 | * |
248 | * Drivers can use this function for RGB565 devices that don't natively | |
249 | * support XRGB8888. | |
bcc44420 | 250 | */ |
53bc2098 TZ |
251 | void drm_fb_xrgb8888_to_rgb565(void *dst, unsigned int dst_pitch, const void *vaddr, |
252 | const struct drm_framebuffer *fb, const struct drm_rect *clip, | |
253 | bool swab) | |
bcc44420 | 254 | { |
d653bd39 GH |
255 | size_t linepixels = clip->x2 - clip->x1; |
256 | size_t src_len = linepixels * sizeof(u32); | |
257 | size_t dst_len = linepixels * sizeof(u16); | |
258 | unsigned y, lines = clip->y2 - clip->y1; | |
259 | void *sbuf; | |
260 | ||
53bc2098 TZ |
261 | if (!dst_pitch) |
262 | dst_pitch = dst_len; | |
263 | ||
d653bd39 GH |
264 | /* |
265 | * The cma memory is write-combined so reads are uncached. | |
266 | * Speed up by fetching one line at a time. | |
267 | */ | |
268 | sbuf = kmalloc(src_len, GFP_KERNEL); | |
269 | if (!sbuf) | |
270 | return; | |
271 | ||
272 | vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); | |
273 | for (y = 0; y < lines; y++) { | |
274 | memcpy(sbuf, vaddr, src_len); | |
275 | drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab); | |
276 | vaddr += fb->pitches[0]; | |
53bc2098 | 277 | dst += dst_pitch; |
d653bd39 GH |
278 | } |
279 | ||
280 | kfree(sbuf); | |
7415287e GH |
281 | } |
282 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); | |
283 | ||
bcc44420 | 284 | /** |
53bc2098 | 285 | * drm_fb_xrgb8888_to_rgb565_toio - Convert XRGB8888 to RGB565 clip buffer |
d653bd39 | 286 | * @dst: RGB565 destination buffer (iomem) |
53bc2098 | 287 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
bcc44420 GH |
288 | * @vaddr: XRGB8888 source buffer |
289 | * @fb: DRM framebuffer | |
290 | * @clip: Clip rectangle area to copy | |
d653bd39 | 291 | * @swab: Swap bytes |
bcc44420 GH |
292 | * |
293 | * Drivers can use this function for RGB565 devices that don't natively | |
294 | * support XRGB8888. | |
bcc44420 | 295 | */ |
53bc2098 TZ |
296 | void drm_fb_xrgb8888_to_rgb565_toio(void __iomem *dst, unsigned int dst_pitch, |
297 | const void *vaddr, const struct drm_framebuffer *fb, | |
298 | const struct drm_rect *clip, bool swab) | |
bcc44420 | 299 | { |
d653bd39 GH |
300 | size_t linepixels = clip->x2 - clip->x1; |
301 | size_t dst_len = linepixels * sizeof(u16); | |
302 | unsigned y, lines = clip->y2 - clip->y1; | |
303 | void *dbuf; | |
304 | ||
53bc2098 TZ |
305 | if (!dst_pitch) |
306 | dst_pitch = dst_len; | |
307 | ||
d653bd39 GH |
308 | dbuf = kmalloc(dst_len, GFP_KERNEL); |
309 | if (!dbuf) | |
310 | return; | |
311 | ||
312 | vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); | |
d653bd39 GH |
313 | for (y = 0; y < lines; y++) { |
314 | drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab); | |
315 | memcpy_toio(dst, dbuf, dst_len); | |
316 | vaddr += fb->pitches[0]; | |
53bc2098 | 317 | dst += dst_pitch; |
d653bd39 GH |
318 | } |
319 | ||
320 | kfree(dbuf); | |
bcc44420 | 321 | } |
53bc2098 | 322 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_toio); |
bcc44420 | 323 | |
53bc2098 | 324 | static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, const u32 *sbuf, |
5c5373b5 | 325 | unsigned int pixels) |
ec3de7a4 | 326 | { |
5c5373b5 | 327 | unsigned int x; |
ec3de7a4 | 328 | |
5c5373b5 GH |
329 | for (x = 0; x < pixels; x++) { |
330 | *dbuf++ = (sbuf[x] & 0x000000FF) >> 0; | |
331 | *dbuf++ = (sbuf[x] & 0x0000FF00) >> 8; | |
332 | *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16; | |
ec3de7a4 | 333 | } |
ec3de7a4 GH |
334 | } |
335 | ||
bcf80d6e NT |
336 | /** |
337 | * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer | |
338 | * @dst: RGB888 destination buffer | |
53bc2098 | 339 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
bcf80d6e NT |
340 | * @src: XRGB8888 source buffer |
341 | * @fb: DRM framebuffer | |
342 | * @clip: Clip rectangle area to copy | |
343 | * | |
344 | * Drivers can use this function for RGB888 devices that don't natively | |
345 | * support XRGB8888. | |
bcf80d6e | 346 | */ |
53bc2098 TZ |
347 | void drm_fb_xrgb8888_to_rgb888(void *dst, unsigned int dst_pitch, const void *src, |
348 | const struct drm_framebuffer *fb, const struct drm_rect *clip) | |
bcf80d6e NT |
349 | { |
350 | size_t width = drm_rect_width(clip); | |
351 | size_t src_len = width * sizeof(u32); | |
352 | unsigned int y; | |
353 | void *sbuf; | |
354 | ||
53bc2098 TZ |
355 | if (!dst_pitch) |
356 | dst_pitch = width * 3; | |
357 | ||
bcf80d6e NT |
358 | /* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */ |
359 | sbuf = kmalloc(src_len, GFP_KERNEL); | |
360 | if (!sbuf) | |
361 | return; | |
362 | ||
363 | src += clip_offset(clip, fb->pitches[0], sizeof(u32)); | |
364 | for (y = 0; y < drm_rect_height(clip); y++) { | |
365 | memcpy(sbuf, src, src_len); | |
366 | drm_fb_xrgb8888_to_rgb888_line(dst, sbuf, width); | |
367 | src += fb->pitches[0]; | |
53bc2098 | 368 | dst += dst_pitch; |
bcf80d6e NT |
369 | } |
370 | ||
371 | kfree(sbuf); | |
372 | } | |
373 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); | |
374 | ||
ec3de7a4 | 375 | /** |
53bc2098 | 376 | * drm_fb_xrgb8888_to_rgb888_toio - Convert XRGB8888 to RGB888 clip buffer |
79b97973 | 377 | * @dst: RGB565 destination buffer (iomem) |
53bc2098 | 378 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
ec3de7a4 GH |
379 | * @vaddr: XRGB8888 source buffer |
380 | * @fb: DRM framebuffer | |
381 | * @clip: Clip rectangle area to copy | |
ec3de7a4 GH |
382 | * |
383 | * Drivers can use this function for RGB888 devices that don't natively | |
384 | * support XRGB8888. | |
ec3de7a4 | 385 | */ |
53bc2098 TZ |
386 | void drm_fb_xrgb8888_to_rgb888_toio(void __iomem *dst, unsigned int dst_pitch, |
387 | const void *vaddr, const struct drm_framebuffer *fb, | |
388 | const struct drm_rect *clip) | |
ec3de7a4 | 389 | { |
5c5373b5 GH |
390 | size_t linepixels = clip->x2 - clip->x1; |
391 | size_t dst_len = linepixels * 3; | |
392 | unsigned y, lines = clip->y2 - clip->y1; | |
393 | void *dbuf; | |
ec3de7a4 | 394 | |
53bc2098 TZ |
395 | if (!dst_pitch) |
396 | dst_pitch = dst_len; | |
397 | ||
5c5373b5 GH |
398 | dbuf = kmalloc(dst_len, GFP_KERNEL); |
399 | if (!dbuf) | |
400 | return; | |
401 | ||
402 | vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); | |
5c5373b5 GH |
403 | for (y = 0; y < lines; y++) { |
404 | drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels); | |
405 | memcpy_toio(dst, dbuf, dst_len); | |
406 | vaddr += fb->pitches[0]; | |
53bc2098 | 407 | dst += dst_pitch; |
5c5373b5 GH |
408 | } |
409 | ||
410 | kfree(dbuf); | |
ec3de7a4 | 411 | } |
53bc2098 | 412 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_toio); |
ec3de7a4 | 413 | |
877691b9 HM |
414 | static void drm_fb_xrgb8888_to_xrgb2101010_line(u32 *dbuf, const u32 *sbuf, |
415 | unsigned int pixels) | |
416 | { | |
417 | unsigned int x; | |
418 | u32 val32; | |
419 | ||
420 | for (x = 0; x < pixels; x++) { | |
421 | val32 = ((sbuf[x] & 0x000000FF) << 2) | | |
422 | ((sbuf[x] & 0x0000FF00) << 4) | | |
423 | ((sbuf[x] & 0x00FF0000) << 6); | |
424 | *dbuf++ = val32 | ((val32 >> 8) & 0x00300C03); | |
425 | } | |
426 | } | |
427 | ||
428 | /** | |
429 | * drm_fb_xrgb8888_to_xrgb2101010_toio - Convert XRGB8888 to XRGB2101010 clip | |
430 | * buffer | |
431 | * @dst: XRGB2101010 destination buffer (iomem) | |
432 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst | |
433 | * @vaddr: XRGB8888 source buffer | |
434 | * @fb: DRM framebuffer | |
435 | * @clip: Clip rectangle area to copy | |
436 | * | |
437 | * Drivers can use this function for XRGB2101010 devices that don't natively | |
438 | * support XRGB8888. | |
439 | */ | |
440 | void drm_fb_xrgb8888_to_xrgb2101010_toio(void __iomem *dst, | |
441 | unsigned int dst_pitch, const void *vaddr, | |
442 | const struct drm_framebuffer *fb, | |
443 | const struct drm_rect *clip) | |
444 | { | |
445 | size_t linepixels = clip->x2 - clip->x1; | |
446 | size_t dst_len = linepixels * sizeof(u32); | |
447 | unsigned int y, lines = clip->y2 - clip->y1; | |
448 | void *dbuf; | |
449 | ||
450 | if (!dst_pitch) | |
451 | dst_pitch = dst_len; | |
452 | ||
453 | dbuf = kmalloc(dst_len, GFP_KERNEL); | |
454 | if (!dbuf) | |
455 | return; | |
456 | ||
457 | vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); | |
458 | for (y = 0; y < lines; y++) { | |
459 | drm_fb_xrgb8888_to_xrgb2101010_line(dbuf, vaddr, linepixels); | |
460 | memcpy_toio(dst, dbuf, dst_len); | |
461 | vaddr += fb->pitches[0]; | |
462 | dst += dst_pitch; | |
463 | } | |
464 | ||
465 | kfree(dbuf); | |
466 | } | |
467 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010_toio); | |
468 | ||
4a564e59 JMC |
469 | static void drm_fb_xrgb8888_to_gray8_line(u8 *dst, const u32 *src, unsigned int pixels) |
470 | { | |
471 | unsigned int x; | |
472 | ||
473 | for (x = 0; x < pixels; x++) { | |
474 | u8 r = (*src & 0x00ff0000) >> 16; | |
475 | u8 g = (*src & 0x0000ff00) >> 8; | |
476 | u8 b = *src & 0x000000ff; | |
477 | ||
478 | /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ | |
479 | *dst++ = (3 * r + 6 * g + b) / 10; | |
480 | src++; | |
481 | } | |
482 | } | |
483 | ||
7415287e GH |
484 | /** |
485 | * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale | |
486 | * @dst: 8-bit grayscale destination buffer | |
53bc2098 | 487 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
7415287e GH |
488 | * @vaddr: XRGB8888 source buffer |
489 | * @fb: DRM framebuffer | |
490 | * @clip: Clip rectangle area to copy | |
491 | * | |
492 | * Drm doesn't have native monochrome or grayscale support. | |
493 | * Such drivers can announce the commonly supported XR24 format to userspace | |
494 | * and use this function to convert to the native format. | |
495 | * | |
496 | * Monochrome drivers will use the most significant bit, | |
497 | * where 1 means foreground color and 0 background color. | |
498 | * | |
499 | * ITU BT.601 is used for the RGB -> luma (brightness) conversion. | |
500 | */ | |
53bc2098 TZ |
501 | void drm_fb_xrgb8888_to_gray8(void *dst, unsigned int dst_pitch, const void *vaddr, |
502 | const struct drm_framebuffer *fb, const struct drm_rect *clip) | |
7415287e | 503 | { |
4a564e59 JMC |
504 | unsigned int linepixels = clip->x2 - clip->x1; |
505 | unsigned int len = linepixels * sizeof(u32); | |
506 | unsigned int y; | |
7415287e | 507 | void *buf; |
53bc2098 TZ |
508 | u8 *dst8; |
509 | u32 *src32; | |
7415287e GH |
510 | |
511 | if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888)) | |
512 | return; | |
53bc2098 TZ |
513 | |
514 | if (!dst_pitch) | |
515 | dst_pitch = drm_rect_width(clip); | |
516 | ||
7415287e GH |
517 | /* |
518 | * The cma memory is write-combined so reads are uncached. | |
519 | * Speed up by fetching one line at a time. | |
520 | */ | |
521 | buf = kmalloc(len, GFP_KERNEL); | |
522 | if (!buf) | |
523 | return; | |
524 | ||
53bc2098 | 525 | vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); |
7415287e | 526 | for (y = clip->y1; y < clip->y2; y++) { |
53bc2098 TZ |
527 | dst8 = dst; |
528 | src32 = memcpy(buf, vaddr, len); | |
4a564e59 | 529 | drm_fb_xrgb8888_to_gray8_line(dst8, src32, linepixels); |
53bc2098 TZ |
530 | vaddr += fb->pitches[0]; |
531 | dst += dst_pitch; | |
7415287e GH |
532 | } |
533 | ||
534 | kfree(buf); | |
535 | } | |
536 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); | |
537 | ||
900d3e4a | 538 | /** |
19b20a80 | 539 | * drm_fb_blit_toio - Copy parts of a framebuffer to display memory |
900d3e4a TZ |
540 | * @dst: The display memory to copy to |
541 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst | |
542 | * @dst_format: FOURCC code of the display's color format | |
543 | * @vmap: The framebuffer memory to copy from | |
544 | * @fb: The framebuffer to copy from | |
545 | * @clip: Clip rectangle area to copy | |
546 | * | |
547 | * This function copies parts of a framebuffer to display memory. If the | |
548 | * formats of the display and the framebuffer mismatch, the blit function | |
549 | * will attempt to convert between them. | |
550 | * | |
900d3e4a TZ |
551 | * Returns: |
552 | * 0 on success, or | |
553 | * -EINVAL if the color-format conversion failed, or | |
554 | * a negative error code otherwise. | |
555 | */ | |
19b20a80 TZ |
556 | int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_format, |
557 | const void *vmap, const struct drm_framebuffer *fb, | |
558 | const struct drm_rect *clip) | |
900d3e4a TZ |
559 | { |
560 | uint32_t fb_format = fb->format->format; | |
561 | ||
562 | /* treat alpha channel like filler bits */ | |
563 | if (fb_format == DRM_FORMAT_ARGB8888) | |
564 | fb_format = DRM_FORMAT_XRGB8888; | |
565 | if (dst_format == DRM_FORMAT_ARGB8888) | |
566 | dst_format = DRM_FORMAT_XRGB8888; | |
877691b9 HM |
567 | if (fb_format == DRM_FORMAT_ARGB2101010) |
568 | fb_format = DRM_FORMAT_XRGB2101010; | |
569 | if (dst_format == DRM_FORMAT_ARGB2101010) | |
570 | dst_format = DRM_FORMAT_XRGB2101010; | |
900d3e4a TZ |
571 | |
572 | if (dst_format == fb_format) { | |
27bd66dd | 573 | drm_fb_memcpy_toio(dst, dst_pitch, vmap, fb, clip); |
900d3e4a TZ |
574 | return 0; |
575 | ||
576 | } else if (dst_format == DRM_FORMAT_RGB565) { | |
577 | if (fb_format == DRM_FORMAT_XRGB8888) { | |
19b20a80 | 578 | drm_fb_xrgb8888_to_rgb565_toio(dst, dst_pitch, vmap, fb, clip, false); |
900d3e4a TZ |
579 | return 0; |
580 | } | |
581 | } else if (dst_format == DRM_FORMAT_RGB888) { | |
582 | if (fb_format == DRM_FORMAT_XRGB8888) { | |
53bc2098 | 583 | drm_fb_xrgb8888_to_rgb888_toio(dst, dst_pitch, vmap, fb, clip); |
900d3e4a TZ |
584 | return 0; |
585 | } | |
877691b9 HM |
586 | } else if (dst_format == DRM_FORMAT_XRGB2101010) { |
587 | if (fb_format == DRM_FORMAT_XRGB8888) { | |
588 | drm_fb_xrgb8888_to_xrgb2101010_toio(dst, dst_pitch, vmap, fb, clip); | |
589 | return 0; | |
590 | } | |
900d3e4a TZ |
591 | } |
592 | ||
593 | return -EINVAL; | |
594 | } | |
19b20a80 | 595 | EXPORT_SYMBOL(drm_fb_blit_toio); |
bcf8b616 | 596 | |
bcf8b616 | 597 | |
7392f245 GU |
598 | static void drm_fb_gray8_to_mono_line(u8 *dst, const u8 *src, unsigned int pixels) |
599 | { | |
600 | while (pixels) { | |
601 | unsigned int i, bits = min(pixels, 8U); | |
602 | u8 byte = 0; | |
bcf8b616 | 603 | |
7392f245 GU |
604 | for (i = 0; i < bits; i++, pixels--) { |
605 | if (*src++ >= 128) | |
606 | byte |= BIT(i); | |
bcf8b616 JMC |
607 | } |
608 | *dst++ = byte; | |
609 | } | |
610 | } | |
611 | ||
612 | /** | |
9b13a3fc GU |
613 | * drm_fb_xrgb8888_to_mono - Convert XRGB8888 to monochrome |
614 | * @dst: monochrome destination buffer (0=black, 1=white) | |
bcf8b616 | 615 | * @dst_pitch: Number of bytes between two consecutive scanlines within dst |
36fe4f24 | 616 | * @vaddr: XRGB8888 source buffer |
bcf8b616 JMC |
617 | * @fb: DRM framebuffer |
618 | * @clip: Clip rectangle area to copy | |
619 | * | |
620 | * DRM doesn't have native monochrome support. | |
621 | * Such drivers can announce the commonly supported XR24 format to userspace | |
622 | * and use this function to convert to the native format. | |
623 | * | |
624 | * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and | |
9b13a3fc | 625 | * then the result is converted from grayscale to monochrome. |
7392f245 GU |
626 | * |
627 | * The first pixel (upper left corner of the clip rectangle) will be converted | |
628 | * and copied to the first bit (LSB) in the first byte of the monochrome | |
629 | * destination buffer. | |
630 | * If the caller requires that the first pixel in a byte must be located at an | |
631 | * x-coordinate that is a multiple of 8, then the caller must take care itself | |
632 | * of supplying a suitable clip rectangle. | |
bcf8b616 | 633 | */ |
9b13a3fc GU |
634 | void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *vaddr, |
635 | const struct drm_framebuffer *fb, const struct drm_rect *clip) | |
bcf8b616 JMC |
636 | { |
637 | unsigned int linepixels = drm_rect_width(clip); | |
7392f245 | 638 | unsigned int lines = drm_rect_height(clip); |
bcf8b616 JMC |
639 | unsigned int cpp = fb->format->cpp[0]; |
640 | unsigned int len_src32 = linepixels * cpp; | |
641 | struct drm_device *dev = fb->dev; | |
bcf8b616 JMC |
642 | unsigned int y; |
643 | u8 *mono = dst, *gray8; | |
644 | u32 *src32; | |
645 | ||
646 | if (drm_WARN_ON(dev, fb->format->format != DRM_FORMAT_XRGB8888)) | |
647 | return; | |
648 | ||
649 | /* | |
7392f245 | 650 | * The mono destination buffer contains 1 bit per pixel |
bcf8b616 JMC |
651 | */ |
652 | if (!dst_pitch) | |
653 | dst_pitch = DIV_ROUND_UP(linepixels, 8); | |
654 | ||
bcf8b616 JMC |
655 | /* |
656 | * The cma memory is write-combined so reads are uncached. | |
657 | * Speed up by fetching one line at a time. | |
658 | * | |
9b13a3fc GU |
659 | * Also, format conversion from XR24 to monochrome are done |
660 | * line-by-line but are converted to 8-bit grayscale as an | |
661 | * intermediate step. | |
bcf8b616 JMC |
662 | * |
663 | * Allocate a buffer to be used for both copying from the cma | |
664 | * memory and to store the intermediate grayscale line pixels. | |
665 | */ | |
666 | src32 = kmalloc(len_src32 + linepixels, GFP_KERNEL); | |
667 | if (!src32) | |
668 | return; | |
669 | ||
670 | gray8 = (u8 *)src32 + len_src32; | |
671 | ||
bcf8b616 JMC |
672 | vaddr += clip_offset(clip, fb->pitches[0], cpp); |
673 | for (y = 0; y < lines; y++) { | |
674 | src32 = memcpy(src32, vaddr, len_src32); | |
675 | drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels); | |
7392f245 | 676 | drm_fb_gray8_to_mono_line(mono, gray8, linepixels); |
bcf8b616 JMC |
677 | vaddr += fb->pitches[0]; |
678 | mono += dst_pitch; | |
679 | } | |
680 | ||
681 | kfree(src32); | |
682 | } | |
9b13a3fc | 683 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono); |