Merge tag 'devprop-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-2.6-microblaze.git] / drivers / gpu / drm / drm_format_helper.c
1 // SPDX-License-Identifier: GPL-2.0 or MIT
2 /*
3  * Copyright (C) 2016 Noralf Trønnes
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  */
10
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <linux/io.h>
14
15 #include <drm/drm_device.h>
16 #include <drm/drm_format_helper.h>
17 #include <drm/drm_framebuffer.h>
18 #include <drm/drm_fourcc.h>
19 #include <drm/drm_print.h>
20 #include <drm/drm_rect.h>
21
22 static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp)
23 {
24         return clip->y1 * pitch + clip->x1 * cpp;
25 }
26
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
43 /**
44  * drm_fb_memcpy - Copy clip buffer
45  * @dst: Destination buffer
46  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
47  * @vaddr: Source buffer
48  * @fb: DRM framebuffer
49  * @clip: Clip rectangle area to copy
50  *
51  * This function does not apply clipping on dst, i.e. the destination
52  * is at the top-left corner.
53  */
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)
56 {
57         unsigned int cpp = fb->format->cpp[0];
58         size_t len = (clip->x2 - clip->x1) * cpp;
59         unsigned int y, lines = clip->y2 - clip->y1;
60
61         if (!dst_pitch)
62                 dst_pitch = len;
63
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];
68                 dst += dst_pitch;
69         }
70 }
71 EXPORT_SYMBOL(drm_fb_memcpy);
72
73 /**
74  * drm_fb_memcpy_toio - Copy clip buffer
75  * @dst: Destination buffer (iomem)
76  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
77  * @vaddr: Source buffer
78  * @fb: DRM framebuffer
79  * @clip: Clip rectangle area to copy
80  *
81  * This function does not apply clipping on dst, i.e. the destination
82  * is at the top-left corner.
83  */
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)
86 {
87         unsigned int cpp = fb->format->cpp[0];
88         size_t len = (clip->x2 - clip->x1) * cpp;
89         unsigned int y, lines = clip->y2 - clip->y1;
90
91         if (!dst_pitch)
92                 dst_pitch = len;
93
94         vaddr += clip_offset(clip, fb->pitches[0], cpp);
95         for (y = 0; y < lines; y++) {
96                 memcpy_toio(dst, vaddr, len);
97                 vaddr += fb->pitches[0];
98                 dst += dst_pitch;
99         }
100 }
101 EXPORT_SYMBOL(drm_fb_memcpy_toio);
102
103 /**
104  * drm_fb_swab - Swap bytes into clip buffer
105  * @dst: Destination buffer
106  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
107  * @src: Source buffer
108  * @fb: DRM framebuffer
109  * @clip: Clip rectangle area to copy
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
116  * is at the top-left corner.
117  */
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)
121 {
122         u8 cpp = fb->format->cpp[0];
123         size_t len = drm_rect_width(clip) * cpp;
124         const u16 *src16;
125         const u32 *src32;
126         u16 *dst16;
127         u32 *dst32;
128         unsigned int x, y;
129         void *buf = NULL;
130
131         if (WARN_ON_ONCE(cpp != 2 && cpp != 4))
132                 return;
133
134         if (!dst_pitch)
135                 dst_pitch = len;
136
137         if (!cached)
138                 buf = kmalloc(len, GFP_KERNEL);
139
140         src += clip_offset(clip, fb->pitches[0], cpp);
141
142         for (y = clip->y1; y < clip->y2; y++) {
143                 if (buf) {
144                         memcpy(buf, src, len);
145                         src16 = buf;
146                         src32 = buf;
147                 } else {
148                         src16 = src;
149                         src32 = src;
150                 }
151
152                 dst16 = dst;
153                 dst32 = dst;
154
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];
163                 dst += dst_pitch;
164         }
165
166         kfree(buf);
167 }
168 EXPORT_SYMBOL(drm_fb_swab);
169
170 static void drm_fb_xrgb8888_to_rgb332_line(u8 *dbuf, const __le32 *sbuf, unsigned int pixels)
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
186  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
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.
192  */
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)
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
201         if (!dst_pitch)
202                 dst_pitch = width;
203
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];
214                 dst += dst_pitch;
215         }
216
217         kfree(sbuf);
218 }
219 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332);
220
221 static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, const u32 *sbuf,
222                                            unsigned int pixels,
223                                            bool swab)
224 {
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;
236         }
237 }
238
239 /**
240  * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
241  * @dst: RGB565 destination buffer
242  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
243  * @vaddr: XRGB8888 source buffer
244  * @fb: DRM framebuffer
245  * @clip: Clip rectangle area to copy
246  * @swab: Swap bytes
247  *
248  * Drivers can use this function for RGB565 devices that don't natively
249  * support XRGB8888.
250  */
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)
254 {
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
261         if (!dst_pitch)
262                 dst_pitch = dst_len;
263
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];
277                 dst += dst_pitch;
278         }
279
280         kfree(sbuf);
281 }
282 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
283
284 /**
285  * drm_fb_xrgb8888_to_rgb565_toio - Convert XRGB8888 to RGB565 clip buffer
286  * @dst: RGB565 destination buffer (iomem)
287  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
288  * @vaddr: XRGB8888 source buffer
289  * @fb: DRM framebuffer
290  * @clip: Clip rectangle area to copy
291  * @swab: Swap bytes
292  *
293  * Drivers can use this function for RGB565 devices that don't natively
294  * support XRGB8888.
295  */
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)
299 {
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
305         if (!dst_pitch)
306                 dst_pitch = dst_len;
307
308         dbuf = kmalloc(dst_len, GFP_KERNEL);
309         if (!dbuf)
310                 return;
311
312         vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
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];
317                 dst += dst_pitch;
318         }
319
320         kfree(dbuf);
321 }
322 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_toio);
323
324 static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, const u32 *sbuf,
325                                            unsigned int pixels)
326 {
327         unsigned int x;
328
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;
333         }
334 }
335
336 /**
337  * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer
338  * @dst: RGB888 destination buffer
339  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
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.
346  */
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)
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
355         if (!dst_pitch)
356                 dst_pitch = width * 3;
357
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];
368                 dst += dst_pitch;
369         }
370
371         kfree(sbuf);
372 }
373 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888);
374
375 /**
376  * drm_fb_xrgb8888_to_rgb888_toio - Convert XRGB8888 to RGB888 clip buffer
377  * @dst: RGB565 destination buffer (iomem)
378  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
379  * @vaddr: XRGB8888 source buffer
380  * @fb: DRM framebuffer
381  * @clip: Clip rectangle area to copy
382  *
383  * Drivers can use this function for RGB888 devices that don't natively
384  * support XRGB8888.
385  */
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)
389 {
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;
394
395         if (!dst_pitch)
396                 dst_pitch = dst_len;
397
398         dbuf = kmalloc(dst_len, GFP_KERNEL);
399         if (!dbuf)
400                 return;
401
402         vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
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];
407                 dst += dst_pitch;
408         }
409
410         kfree(dbuf);
411 }
412 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_toio);
413
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
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
484 /**
485  * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
486  * @dst: 8-bit grayscale destination buffer
487  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
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  */
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)
503 {
504         unsigned int linepixels = clip->x2 - clip->x1;
505         unsigned int len = linepixels * sizeof(u32);
506         unsigned int y;
507         void *buf;
508         u8 *dst8;
509         u32 *src32;
510
511         if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
512                 return;
513
514         if (!dst_pitch)
515                 dst_pitch = drm_rect_width(clip);
516
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
525         vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
526         for (y = clip->y1; y < clip->y2; y++) {
527                 dst8 = dst;
528                 src32 = memcpy(buf, vaddr, len);
529                 drm_fb_xrgb8888_to_gray8_line(dst8, src32, linepixels);
530                 vaddr += fb->pitches[0];
531                 dst += dst_pitch;
532         }
533
534         kfree(buf);
535 }
536 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
537
538 /**
539  * drm_fb_blit_toio - Copy parts of a framebuffer to display memory
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  *
551  * Returns:
552  * 0 on success, or
553  * -EINVAL if the color-format conversion failed, or
554  * a negative error code otherwise.
555  */
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)
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;
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;
571
572         if (dst_format == fb_format) {
573                 drm_fb_memcpy_toio(dst, dst_pitch, vmap, fb, clip);
574                 return 0;
575
576         } else if (dst_format == DRM_FORMAT_RGB565) {
577                 if (fb_format == DRM_FORMAT_XRGB8888) {
578                         drm_fb_xrgb8888_to_rgb565_toio(dst, dst_pitch, vmap, fb, clip, false);
579                         return 0;
580                 }
581         } else if (dst_format == DRM_FORMAT_RGB888) {
582                 if (fb_format == DRM_FORMAT_XRGB8888) {
583                         drm_fb_xrgb8888_to_rgb888_toio(dst, dst_pitch, vmap, fb, clip);
584                         return 0;
585                 }
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                 }
591         }
592
593         return -EINVAL;
594 }
595 EXPORT_SYMBOL(drm_fb_blit_toio);
596
597 static 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  */
638 void 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 }
703 EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono_reversed);