Merge tag 'libnvdimm-for-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdim...
[platform/kernel/linux-rpi.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(struct drm_rect *clip,
21                                 unsigned int pitch, unsigned int cpp)
22 {
23         return clip->y1 * pitch + clip->x1 * cpp;
24 }
25
26 /**
27  * drm_fb_memcpy - Copy clip buffer
28  * @dst: Destination buffer
29  * @vaddr: Source buffer
30  * @fb: DRM framebuffer
31  * @clip: Clip rectangle area to copy
32  *
33  * This function does not apply clipping on dst, i.e. the destination
34  * is a small buffer containing the clip rect only.
35  */
36 void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
37                    struct drm_rect *clip)
38 {
39         unsigned int cpp = fb->format->cpp[0];
40         size_t len = (clip->x2 - clip->x1) * cpp;
41         unsigned int y, lines = clip->y2 - clip->y1;
42
43         vaddr += clip_offset(clip, fb->pitches[0], cpp);
44         for (y = 0; y < lines; y++) {
45                 memcpy(dst, vaddr, len);
46                 vaddr += fb->pitches[0];
47                 dst += len;
48         }
49 }
50 EXPORT_SYMBOL(drm_fb_memcpy);
51
52 /**
53  * drm_fb_memcpy_dstclip - Copy clip buffer
54  * @dst: Destination buffer (iomem)
55  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
56  * @vaddr: Source buffer
57  * @fb: DRM framebuffer
58  * @clip: Clip rectangle area to copy
59  *
60  * This function applies clipping on dst, i.e. the destination is a
61  * full (iomem) framebuffer but only the clip rect content is copied over.
62  */
63 void drm_fb_memcpy_dstclip(void __iomem *dst, unsigned int dst_pitch,
64                            void *vaddr, struct drm_framebuffer *fb,
65                            struct drm_rect *clip)
66 {
67         unsigned int cpp = fb->format->cpp[0];
68         unsigned int offset = clip_offset(clip, dst_pitch, cpp);
69         size_t len = (clip->x2 - clip->x1) * cpp;
70         unsigned int y, lines = clip->y2 - clip->y1;
71
72         vaddr += offset;
73         dst += offset;
74         for (y = 0; y < lines; y++) {
75                 memcpy_toio(dst, vaddr, len);
76                 vaddr += fb->pitches[0];
77                 dst += dst_pitch;
78         }
79 }
80 EXPORT_SYMBOL(drm_fb_memcpy_dstclip);
81
82 /**
83  * drm_fb_swab - Swap bytes into clip buffer
84  * @dst: Destination buffer
85  * @src: Source buffer
86  * @fb: DRM framebuffer
87  * @clip: Clip rectangle area to copy
88  * @cached: Source buffer is mapped cached (eg. not write-combined)
89  *
90  * If @cached is false a temporary buffer is used to cache one pixel line at a
91  * time to speed up slow uncached reads.
92  *
93  * This function does not apply clipping on dst, i.e. the destination
94  * is a small buffer containing the clip rect only.
95  */
96 void drm_fb_swab(void *dst, void *src, struct drm_framebuffer *fb,
97                  struct drm_rect *clip, bool cached)
98 {
99         u8 cpp = fb->format->cpp[0];
100         size_t len = drm_rect_width(clip) * cpp;
101         u16 *src16, *dst16 = dst;
102         u32 *src32, *dst32 = dst;
103         unsigned int x, y;
104         void *buf = NULL;
105
106         if (WARN_ON_ONCE(cpp != 2 && cpp != 4))
107                 return;
108
109         if (!cached)
110                 buf = kmalloc(len, GFP_KERNEL);
111
112         src += clip_offset(clip, fb->pitches[0], cpp);
113
114         for (y = clip->y1; y < clip->y2; y++) {
115                 if (buf) {
116                         memcpy(buf, src, len);
117                         src16 = buf;
118                         src32 = buf;
119                 } else {
120                         src16 = src;
121                         src32 = src;
122                 }
123
124                 for (x = clip->x1; x < clip->x2; x++) {
125                         if (cpp == 4)
126                                 *dst32++ = swab32(*src32++);
127                         else
128                                 *dst16++ = swab16(*src16++);
129                 }
130
131                 src += fb->pitches[0];
132         }
133
134         kfree(buf);
135 }
136 EXPORT_SYMBOL(drm_fb_swab);
137
138 static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf,
139                                            unsigned int pixels,
140                                            bool swab)
141 {
142         unsigned int x;
143         u16 val16;
144
145         for (x = 0; x < pixels; x++) {
146                 val16 = ((sbuf[x] & 0x00F80000) >> 8) |
147                         ((sbuf[x] & 0x0000FC00) >> 5) |
148                         ((sbuf[x] & 0x000000F8) >> 3);
149                 if (swab)
150                         dbuf[x] = swab16(val16);
151                 else
152                         dbuf[x] = val16;
153         }
154 }
155
156 /**
157  * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
158  * @dst: RGB565 destination buffer
159  * @vaddr: XRGB8888 source buffer
160  * @fb: DRM framebuffer
161  * @clip: Clip rectangle area to copy
162  * @swab: Swap bytes
163  *
164  * Drivers can use this function for RGB565 devices that don't natively
165  * support XRGB8888.
166  *
167  * This function does not apply clipping on dst, i.e. the destination
168  * is a small buffer containing the clip rect only.
169  */
170 void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr,
171                                struct drm_framebuffer *fb,
172                                struct drm_rect *clip, bool swab)
173 {
174         size_t linepixels = clip->x2 - clip->x1;
175         size_t src_len = linepixels * sizeof(u32);
176         size_t dst_len = linepixels * sizeof(u16);
177         unsigned y, lines = clip->y2 - clip->y1;
178         void *sbuf;
179
180         /*
181          * The cma memory is write-combined so reads are uncached.
182          * Speed up by fetching one line at a time.
183          */
184         sbuf = kmalloc(src_len, GFP_KERNEL);
185         if (!sbuf)
186                 return;
187
188         vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
189         for (y = 0; y < lines; y++) {
190                 memcpy(sbuf, vaddr, src_len);
191                 drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab);
192                 vaddr += fb->pitches[0];
193                 dst += dst_len;
194         }
195
196         kfree(sbuf);
197 }
198 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
199
200 /**
201  * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer
202  * @dst: RGB565 destination buffer (iomem)
203  * @dst_pitch: destination buffer pitch
204  * @vaddr: XRGB8888 source buffer
205  * @fb: DRM framebuffer
206  * @clip: Clip rectangle area to copy
207  * @swab: Swap bytes
208  *
209  * Drivers can use this function for RGB565 devices that don't natively
210  * support XRGB8888.
211  *
212  * This function applies clipping on dst, i.e. the destination is a
213  * full (iomem) framebuffer but only the clip rect content is copied over.
214  */
215 void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch,
216                                        void *vaddr, struct drm_framebuffer *fb,
217                                        struct drm_rect *clip, bool swab)
218 {
219         size_t linepixels = clip->x2 - clip->x1;
220         size_t dst_len = linepixels * sizeof(u16);
221         unsigned y, lines = clip->y2 - clip->y1;
222         void *dbuf;
223
224         dbuf = kmalloc(dst_len, GFP_KERNEL);
225         if (!dbuf)
226                 return;
227
228         vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
229         dst += clip_offset(clip, dst_pitch, sizeof(u16));
230         for (y = 0; y < lines; y++) {
231                 drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab);
232                 memcpy_toio(dst, dbuf, dst_len);
233                 vaddr += fb->pitches[0];
234                 dst += dst_len;
235         }
236
237         kfree(dbuf);
238 }
239 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip);
240
241 static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf,
242                                            unsigned int pixels)
243 {
244         unsigned int x;
245
246         for (x = 0; x < pixels; x++) {
247                 *dbuf++ = (sbuf[x] & 0x000000FF) >>  0;
248                 *dbuf++ = (sbuf[x] & 0x0000FF00) >>  8;
249                 *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16;
250         }
251 }
252
253 /**
254  * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer
255  * @dst: RGB565 destination buffer (iomem)
256  * @dst_pitch: destination buffer pitch
257  * @vaddr: XRGB8888 source buffer
258  * @fb: DRM framebuffer
259  * @clip: Clip rectangle area to copy
260  *
261  * Drivers can use this function for RGB888 devices that don't natively
262  * support XRGB8888.
263  *
264  * This function applies clipping on dst, i.e. the destination is a
265  * full (iomem) framebuffer but only the clip rect content is copied over.
266  */
267 void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch,
268                                        void *vaddr, struct drm_framebuffer *fb,
269                                        struct drm_rect *clip)
270 {
271         size_t linepixels = clip->x2 - clip->x1;
272         size_t dst_len = linepixels * 3;
273         unsigned y, lines = clip->y2 - clip->y1;
274         void *dbuf;
275
276         dbuf = kmalloc(dst_len, GFP_KERNEL);
277         if (!dbuf)
278                 return;
279
280         vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
281         dst += clip_offset(clip, dst_pitch, sizeof(u16));
282         for (y = 0; y < lines; y++) {
283                 drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels);
284                 memcpy_toio(dst, dbuf, dst_len);
285                 vaddr += fb->pitches[0];
286                 dst += dst_len;
287         }
288
289         kfree(dbuf);
290 }
291 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip);
292
293 /**
294  * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
295  * @dst: 8-bit grayscale destination buffer
296  * @vaddr: XRGB8888 source buffer
297  * @fb: DRM framebuffer
298  * @clip: Clip rectangle area to copy
299  *
300  * Drm doesn't have native monochrome or grayscale support.
301  * Such drivers can announce the commonly supported XR24 format to userspace
302  * and use this function to convert to the native format.
303  *
304  * Monochrome drivers will use the most significant bit,
305  * where 1 means foreground color and 0 background color.
306  *
307  * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
308  */
309 void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
310                                struct drm_rect *clip)
311 {
312         unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
313         unsigned int x, y;
314         void *buf;
315         u32 *src;
316
317         if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
318                 return;
319         /*
320          * The cma memory is write-combined so reads are uncached.
321          * Speed up by fetching one line at a time.
322          */
323         buf = kmalloc(len, GFP_KERNEL);
324         if (!buf)
325                 return;
326
327         for (y = clip->y1; y < clip->y2; y++) {
328                 src = vaddr + (y * fb->pitches[0]);
329                 src += clip->x1;
330                 memcpy(buf, src, len);
331                 src = buf;
332                 for (x = clip->x1; x < clip->x2; x++) {
333                         u8 r = (*src & 0x00ff0000) >> 16;
334                         u8 g = (*src & 0x0000ff00) >> 8;
335                         u8 b =  *src & 0x000000ff;
336
337                         /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
338                         *dst++ = (3 * r + 6 * g + b) / 10;
339                         src++;
340                 }
341         }
342
343         kfree(buf);
344 }
345 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
346
347 /**
348  * drm_fb_blit_rect_dstclip - Copy parts of a framebuffer to display memory
349  * @dst:        The display memory to copy to
350  * @dst_pitch:  Number of bytes between two consecutive scanlines within dst
351  * @dst_format: FOURCC code of the display's color format
352  * @vmap:       The framebuffer memory to copy from
353  * @fb:         The framebuffer to copy from
354  * @clip:       Clip rectangle area to copy
355  *
356  * This function copies parts of a framebuffer to display memory. If the
357  * formats of the display and the framebuffer mismatch, the blit function
358  * will attempt to convert between them.
359  *
360  * Use drm_fb_blit_dstclip() to copy the full framebuffer.
361  *
362  * Returns:
363  * 0 on success, or
364  * -EINVAL if the color-format conversion failed, or
365  * a negative error code otherwise.
366  */
367 int drm_fb_blit_rect_dstclip(void __iomem *dst, unsigned int dst_pitch,
368                              uint32_t dst_format, void *vmap,
369                              struct drm_framebuffer *fb,
370                              struct drm_rect *clip)
371 {
372         uint32_t fb_format = fb->format->format;
373
374         /* treat alpha channel like filler bits */
375         if (fb_format == DRM_FORMAT_ARGB8888)
376                 fb_format = DRM_FORMAT_XRGB8888;
377         if (dst_format == DRM_FORMAT_ARGB8888)
378                 dst_format = DRM_FORMAT_XRGB8888;
379
380         if (dst_format == fb_format) {
381                 drm_fb_memcpy_dstclip(dst, dst_pitch, vmap, fb, clip);
382                 return 0;
383
384         } else if (dst_format == DRM_FORMAT_RGB565) {
385                 if (fb_format == DRM_FORMAT_XRGB8888) {
386                         drm_fb_xrgb8888_to_rgb565_dstclip(dst, dst_pitch,
387                                                           vmap, fb, clip,
388                                                           false);
389                         return 0;
390                 }
391         } else if (dst_format == DRM_FORMAT_RGB888) {
392                 if (fb_format == DRM_FORMAT_XRGB8888) {
393                         drm_fb_xrgb8888_to_rgb888_dstclip(dst, dst_pitch,
394                                                           vmap, fb, clip);
395                         return 0;
396                 }
397         }
398
399         return -EINVAL;
400 }
401 EXPORT_SYMBOL(drm_fb_blit_rect_dstclip);
402
403 /**
404  * drm_fb_blit_dstclip - Copy framebuffer to display memory
405  * @dst:        The display memory to copy to
406  * @dst_pitch:  Number of bytes between two consecutive scanlines within dst
407  * @dst_format: FOURCC code of the display's color format
408  * @vmap:       The framebuffer memory to copy from
409  * @fb:         The framebuffer to copy from
410  *
411  * This function copies a full framebuffer to display memory. If the formats
412  * of the display and the framebuffer mismatch, the copy function will
413  * attempt to convert between them.
414  *
415  * See drm_fb_blit_rect_dstclip() for more information.
416  *
417  * Returns:
418  * 0 on success, or a negative error code otherwise.
419  */
420 int drm_fb_blit_dstclip(void __iomem *dst, unsigned int dst_pitch,
421                         uint32_t dst_format, void *vmap,
422                         struct drm_framebuffer *fb)
423 {
424         struct drm_rect fullscreen = {
425                 .x1 = 0,
426                 .x2 = fb->width,
427                 .y1 = 0,
428                 .y2 = fb->height,
429         };
430         return drm_fb_blit_rect_dstclip(dst, dst_pitch, dst_format, vmap, fb,
431                                         &fullscreen);
432 }
433 EXPORT_SYMBOL(drm_fb_blit_dstclip);