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