Merge tag 'for-5.18/write-streams-2022-03-18' of git://git.kernel.dk/linux-block
[linux-block.git] / drivers / gpu / drm / drm_format_helper.c
CommitLineData
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 22static 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 */
36unsigned 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}
41EXPORT_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
54void 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}
71EXPORT_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
84void 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 101EXPORT_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
118void 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 168EXPORT_SYMBOL(drm_fb_swab);
7415287e 169
53bc2098 170static 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
193void 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}
219EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332);
220
53bc2098 221static 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
251void 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}
282EXPORT_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
296void 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 322EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_toio);
bcc44420 323
53bc2098 324static 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
347void 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}
373EXPORT_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
386void 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 412EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_toio);
ec3de7a4 413
877691b9
HM
414static 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 */
440void 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}
467EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010_toio);
468
4a564e59
JMC
469static 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
501void 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}
536EXPORT_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
556int 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 595EXPORT_SYMBOL(drm_fb_blit_toio);
bcf8b616
JMC
596
597static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, unsigned int pixels,
598 unsigned int start_offset, unsigned int end_len)
599{
600 unsigned int xb, i;
601
602 for (xb = 0; xb < pixels; xb++) {
603 unsigned int start = 0, end = 8;
604 u8 byte = 0x00;
605
606 if (xb == 0 && start_offset)
607 start = start_offset;
608
609 if (xb == pixels - 1 && end_len)
610 end = end_len;
611
612 for (i = start; i < end; i++) {
613 unsigned int x = xb * 8 + i;
614
615 byte >>= 1;
616 if (src[x] >> 7)
617 byte |= BIT(7);
618 }
619 *dst++ = byte;
620 }
621}
622
623/**
624 * drm_fb_xrgb8888_to_mono_reversed - Convert XRGB8888 to reversed monochrome
625 * @dst: reversed monochrome destination buffer
626 * @dst_pitch: Number of bytes between two consecutive scanlines within dst
627 * @src: XRGB8888 source buffer
628 * @fb: DRM framebuffer
629 * @clip: Clip rectangle area to copy
630 *
631 * DRM doesn't have native monochrome support.
632 * Such drivers can announce the commonly supported XR24 format to userspace
633 * and use this function to convert to the native format.
634 *
635 * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and
636 * then the result is converted from grayscale to reversed monohrome.
637 */
638void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *vaddr,
639 const struct drm_framebuffer *fb, const struct drm_rect *clip)
640{
641 unsigned int linepixels = drm_rect_width(clip);
642 unsigned int lines = clip->y2 - clip->y1;
643 unsigned int cpp = fb->format->cpp[0];
644 unsigned int len_src32 = linepixels * cpp;
645 struct drm_device *dev = fb->dev;
646 unsigned int start_offset, end_len;
647 unsigned int y;
648 u8 *mono = dst, *gray8;
649 u32 *src32;
650
651 if (drm_WARN_ON(dev, fb->format->format != DRM_FORMAT_XRGB8888))
652 return;
653
654 /*
655 * The reversed mono destination buffer contains 1 bit per pixel
656 * and destination scanlines have to be in multiple of 8 pixels.
657 */
658 if (!dst_pitch)
659 dst_pitch = DIV_ROUND_UP(linepixels, 8);
660
661 drm_WARN_ONCE(dev, dst_pitch % 8 != 0, "dst_pitch is not a multiple of 8\n");
662
663 /*
664 * The cma memory is write-combined so reads are uncached.
665 * Speed up by fetching one line at a time.
666 *
667 * Also, format conversion from XR24 to reversed monochrome
668 * are done line-by-line but are converted to 8-bit grayscale
669 * as an intermediate step.
670 *
671 * Allocate a buffer to be used for both copying from the cma
672 * memory and to store the intermediate grayscale line pixels.
673 */
674 src32 = kmalloc(len_src32 + linepixels, GFP_KERNEL);
675 if (!src32)
676 return;
677
678 gray8 = (u8 *)src32 + len_src32;
679
680 /*
681 * For damage handling, it is possible that only parts of the source
682 * buffer is copied and this could lead to start and end pixels that
683 * are not aligned to multiple of 8.
684 *
685 * Calculate if the start and end pixels are not aligned and set the
686 * offsets for the reversed mono line conversion function to adjust.
687 */
688 start_offset = clip->x1 % 8;
689 end_len = clip->x2 % 8;
690
691 vaddr += clip_offset(clip, fb->pitches[0], cpp);
692 for (y = 0; y < lines; y++) {
693 src32 = memcpy(src32, vaddr, len_src32);
694 drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels);
695 drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch,
696 start_offset, end_len);
697 vaddr += fb->pitches[0];
698 mono += dst_pitch;
699 }
700
701 kfree(src32);
702}
703EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono_reversed);