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
clip_offset(struct drm_rect * clip,unsigned int pitch,unsigned int cpp)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 */
drm_fb_memcpy(void * dst,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip)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 */
drm_fb_memcpy_dstclip(void __iomem * dst,unsigned int dst_pitch,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip)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 */
drm_fb_swab(void * dst,void * src,struct drm_framebuffer * fb,struct drm_rect * clip,bool cached)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
drm_fb_xrgb8888_to_rgb332_line(u8 * dbuf,__le32 * sbuf,unsigned int pixels)138 static void drm_fb_xrgb8888_to_rgb332_line(u8 *dbuf, __le32 *sbuf, unsigned int pixels)
139 {
140 unsigned int x;
141 u32 pix;
142
143 for (x = 0; x < pixels; x++) {
144 pix = le32_to_cpu(sbuf[x]);
145 dbuf[x] = ((pix & 0x00e00000) >> 16) |
146 ((pix & 0x0000e000) >> 11) |
147 ((pix & 0x000000c0) >> 6);
148 }
149 }
150
151 /**
152 * drm_fb_xrgb8888_to_rgb332 - Convert XRGB8888 to RGB332 clip buffer
153 * @dst: RGB332 destination buffer
154 * @src: XRGB8888 source buffer
155 * @fb: DRM framebuffer
156 * @clip: Clip rectangle area to copy
157 *
158 * Drivers can use this function for RGB332 devices that don't natively support XRGB8888.
159 *
160 * This function does not apply clipping on dst, i.e. the destination is a small buffer
161 * containing the clip rect only.
162 */
drm_fb_xrgb8888_to_rgb332(void * dst,void * src,struct drm_framebuffer * fb,struct drm_rect * clip)163 void drm_fb_xrgb8888_to_rgb332(void *dst, void *src, struct drm_framebuffer *fb,
164 struct drm_rect *clip)
165 {
166 size_t width = drm_rect_width(clip);
167 size_t src_len = width * sizeof(u32);
168 unsigned int y;
169 void *sbuf;
170
171 /* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */
172 sbuf = kmalloc(src_len, GFP_KERNEL);
173 if (!sbuf)
174 return;
175
176 src += clip_offset(clip, fb->pitches[0], sizeof(u32));
177 for (y = 0; y < drm_rect_height(clip); y++) {
178 memcpy(sbuf, src, src_len);
179 drm_fb_xrgb8888_to_rgb332_line(dst, sbuf, width);
180 src += fb->pitches[0];
181 dst += width;
182 }
183
184 kfree(sbuf);
185 }
186 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332);
187
drm_fb_xrgb8888_to_rgb565_line(u16 * dbuf,u32 * sbuf,unsigned int pixels,bool swab)188 static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf,
189 unsigned int pixels,
190 bool swab)
191 {
192 unsigned int x;
193 u16 val16;
194
195 for (x = 0; x < pixels; x++) {
196 val16 = ((sbuf[x] & 0x00F80000) >> 8) |
197 ((sbuf[x] & 0x0000FC00) >> 5) |
198 ((sbuf[x] & 0x000000F8) >> 3);
199 if (swab)
200 dbuf[x] = swab16(val16);
201 else
202 dbuf[x] = val16;
203 }
204 }
205
206 /**
207 * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
208 * @dst: RGB565 destination buffer
209 * @vaddr: XRGB8888 source buffer
210 * @fb: DRM framebuffer
211 * @clip: Clip rectangle area to copy
212 * @swab: Swap bytes
213 *
214 * Drivers can use this function for RGB565 devices that don't natively
215 * support XRGB8888.
216 *
217 * This function does not apply clipping on dst, i.e. the destination
218 * is a small buffer containing the clip rect only.
219 */
drm_fb_xrgb8888_to_rgb565(void * dst,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip,bool swab)220 void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr,
221 struct drm_framebuffer *fb,
222 struct drm_rect *clip, bool swab)
223 {
224 size_t linepixels = clip->x2 - clip->x1;
225 size_t src_len = linepixels * sizeof(u32);
226 size_t dst_len = linepixels * sizeof(u16);
227 unsigned y, lines = clip->y2 - clip->y1;
228 void *sbuf;
229
230 /*
231 * The cma memory is write-combined so reads are uncached.
232 * Speed up by fetching one line at a time.
233 */
234 sbuf = kmalloc(src_len, GFP_KERNEL);
235 if (!sbuf)
236 return;
237
238 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
239 for (y = 0; y < lines; y++) {
240 memcpy(sbuf, vaddr, src_len);
241 drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab);
242 vaddr += fb->pitches[0];
243 dst += dst_len;
244 }
245
246 kfree(sbuf);
247 }
248 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
249
250 /**
251 * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer
252 * @dst: RGB565 destination buffer (iomem)
253 * @dst_pitch: destination buffer pitch
254 * @vaddr: XRGB8888 source buffer
255 * @fb: DRM framebuffer
256 * @clip: Clip rectangle area to copy
257 * @swab: Swap bytes
258 *
259 * Drivers can use this function for RGB565 devices that don't natively
260 * support XRGB8888.
261 *
262 * This function applies clipping on dst, i.e. the destination is a
263 * full (iomem) framebuffer but only the clip rect content is copied over.
264 */
drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem * dst,unsigned int dst_pitch,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip,bool swab)265 void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch,
266 void *vaddr, struct drm_framebuffer *fb,
267 struct drm_rect *clip, bool swab)
268 {
269 size_t linepixels = clip->x2 - clip->x1;
270 size_t dst_len = linepixels * sizeof(u16);
271 unsigned y, lines = clip->y2 - clip->y1;
272 void *dbuf;
273
274 dbuf = kmalloc(dst_len, GFP_KERNEL);
275 if (!dbuf)
276 return;
277
278 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
279 dst += clip_offset(clip, dst_pitch, sizeof(u16));
280 for (y = 0; y < lines; y++) {
281 drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab);
282 memcpy_toio(dst, dbuf, dst_len);
283 vaddr += fb->pitches[0];
284 dst += dst_len;
285 }
286
287 kfree(dbuf);
288 }
289 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip);
290
drm_fb_xrgb8888_to_rgb888_line(u8 * dbuf,u32 * sbuf,unsigned int pixels)291 static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf,
292 unsigned int pixels)
293 {
294 unsigned int x;
295
296 for (x = 0; x < pixels; x++) {
297 *dbuf++ = (sbuf[x] & 0x000000FF) >> 0;
298 *dbuf++ = (sbuf[x] & 0x0000FF00) >> 8;
299 *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16;
300 }
301 }
302
303 /**
304 * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer
305 * @dst: RGB888 destination buffer
306 * @src: XRGB8888 source buffer
307 * @fb: DRM framebuffer
308 * @clip: Clip rectangle area to copy
309 *
310 * Drivers can use this function for RGB888 devices that don't natively
311 * support XRGB8888.
312 *
313 * This function does not apply clipping on dst, i.e. the destination
314 * is a small buffer containing the clip rect only.
315 */
drm_fb_xrgb8888_to_rgb888(void * dst,void * src,struct drm_framebuffer * fb,struct drm_rect * clip)316 void drm_fb_xrgb8888_to_rgb888(void *dst, void *src, struct drm_framebuffer *fb,
317 struct drm_rect *clip)
318 {
319 size_t width = drm_rect_width(clip);
320 size_t src_len = width * sizeof(u32);
321 unsigned int y;
322 void *sbuf;
323
324 /* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */
325 sbuf = kmalloc(src_len, GFP_KERNEL);
326 if (!sbuf)
327 return;
328
329 src += clip_offset(clip, fb->pitches[0], sizeof(u32));
330 for (y = 0; y < drm_rect_height(clip); y++) {
331 memcpy(sbuf, src, src_len);
332 drm_fb_xrgb8888_to_rgb888_line(dst, sbuf, width);
333 src += fb->pitches[0];
334 dst += width * 3;
335 }
336
337 kfree(sbuf);
338 }
339 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888);
340
341 /**
342 * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer
343 * @dst: RGB565 destination buffer (iomem)
344 * @dst_pitch: destination buffer pitch
345 * @vaddr: XRGB8888 source buffer
346 * @fb: DRM framebuffer
347 * @clip: Clip rectangle area to copy
348 *
349 * Drivers can use this function for RGB888 devices that don't natively
350 * support XRGB8888.
351 *
352 * This function applies clipping on dst, i.e. the destination is a
353 * full (iomem) framebuffer but only the clip rect content is copied over.
354 */
drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem * dst,unsigned int dst_pitch,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip)355 void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch,
356 void *vaddr, struct drm_framebuffer *fb,
357 struct drm_rect *clip)
358 {
359 size_t linepixels = clip->x2 - clip->x1;
360 size_t dst_len = linepixels * 3;
361 unsigned y, lines = clip->y2 - clip->y1;
362 void *dbuf;
363
364 dbuf = kmalloc(dst_len, GFP_KERNEL);
365 if (!dbuf)
366 return;
367
368 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
369 dst += clip_offset(clip, dst_pitch, sizeof(u16));
370 for (y = 0; y < lines; y++) {
371 drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels);
372 memcpy_toio(dst, dbuf, dst_len);
373 vaddr += fb->pitches[0];
374 dst += dst_len;
375 }
376
377 kfree(dbuf);
378 }
379 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip);
380
381 /**
382 * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
383 * @dst: 8-bit grayscale destination buffer
384 * @vaddr: XRGB8888 source buffer
385 * @fb: DRM framebuffer
386 * @clip: Clip rectangle area to copy
387 *
388 * Drm doesn't have native monochrome or grayscale support.
389 * Such drivers can announce the commonly supported XR24 format to userspace
390 * and use this function to convert to the native format.
391 *
392 * Monochrome drivers will use the most significant bit,
393 * where 1 means foreground color and 0 background color.
394 *
395 * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
396 */
drm_fb_xrgb8888_to_gray8(u8 * dst,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip)397 void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
398 struct drm_rect *clip)
399 {
400 unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
401 unsigned int x, y;
402 void *buf;
403 u32 *src;
404
405 if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
406 return;
407 /*
408 * The cma memory is write-combined so reads are uncached.
409 * Speed up by fetching one line at a time.
410 */
411 buf = kmalloc(len, GFP_KERNEL);
412 if (!buf)
413 return;
414
415 for (y = clip->y1; y < clip->y2; y++) {
416 src = vaddr + (y * fb->pitches[0]);
417 src += clip->x1;
418 memcpy(buf, src, len);
419 src = buf;
420 for (x = clip->x1; x < clip->x2; x++) {
421 u8 r = (*src & 0x00ff0000) >> 16;
422 u8 g = (*src & 0x0000ff00) >> 8;
423 u8 b = *src & 0x000000ff;
424
425 /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
426 *dst++ = (3 * r + 6 * g + b) / 10;
427 src++;
428 }
429 }
430
431 kfree(buf);
432 }
433 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
434
435 /**
436 * drm_fb_blit_rect_dstclip - Copy parts of a framebuffer to display memory
437 * @dst: The display memory to copy to
438 * @dst_pitch: Number of bytes between two consecutive scanlines within dst
439 * @dst_format: FOURCC code of the display's color format
440 * @vmap: The framebuffer memory to copy from
441 * @fb: The framebuffer to copy from
442 * @clip: Clip rectangle area to copy
443 *
444 * This function copies parts of a framebuffer to display memory. If the
445 * formats of the display and the framebuffer mismatch, the blit function
446 * will attempt to convert between them.
447 *
448 * Use drm_fb_blit_dstclip() to copy the full framebuffer.
449 *
450 * Returns:
451 * 0 on success, or
452 * -EINVAL if the color-format conversion failed, or
453 * a negative error code otherwise.
454 */
drm_fb_blit_rect_dstclip(void __iomem * dst,unsigned int dst_pitch,uint32_t dst_format,void * vmap,struct drm_framebuffer * fb,struct drm_rect * clip)455 int drm_fb_blit_rect_dstclip(void __iomem *dst, unsigned int dst_pitch,
456 uint32_t dst_format, void *vmap,
457 struct drm_framebuffer *fb,
458 struct drm_rect *clip)
459 {
460 uint32_t fb_format = fb->format->format;
461
462 /* treat alpha channel like filler bits */
463 if (fb_format == DRM_FORMAT_ARGB8888)
464 fb_format = DRM_FORMAT_XRGB8888;
465 if (dst_format == DRM_FORMAT_ARGB8888)
466 dst_format = DRM_FORMAT_XRGB8888;
467
468 if (dst_format == fb_format) {
469 drm_fb_memcpy_dstclip(dst, dst_pitch, vmap, fb, clip);
470 return 0;
471
472 } else if (dst_format == DRM_FORMAT_RGB565) {
473 if (fb_format == DRM_FORMAT_XRGB8888) {
474 drm_fb_xrgb8888_to_rgb565_dstclip(dst, dst_pitch,
475 vmap, fb, clip,
476 false);
477 return 0;
478 }
479 } else if (dst_format == DRM_FORMAT_RGB888) {
480 if (fb_format == DRM_FORMAT_XRGB8888) {
481 drm_fb_xrgb8888_to_rgb888_dstclip(dst, dst_pitch,
482 vmap, fb, clip);
483 return 0;
484 }
485 }
486
487 return -EINVAL;
488 }
489 EXPORT_SYMBOL(drm_fb_blit_rect_dstclip);
490
491 /**
492 * drm_fb_blit_dstclip - Copy framebuffer to display memory
493 * @dst: The display memory to copy to
494 * @dst_pitch: Number of bytes between two consecutive scanlines within dst
495 * @dst_format: FOURCC code of the display's color format
496 * @vmap: The framebuffer memory to copy from
497 * @fb: The framebuffer to copy from
498 *
499 * This function copies a full framebuffer to display memory. If the formats
500 * of the display and the framebuffer mismatch, the copy function will
501 * attempt to convert between them.
502 *
503 * See drm_fb_blit_rect_dstclip() for more information.
504 *
505 * Returns:
506 * 0 on success, or a negative error code otherwise.
507 */
drm_fb_blit_dstclip(void __iomem * dst,unsigned int dst_pitch,uint32_t dst_format,void * vmap,struct drm_framebuffer * fb)508 int drm_fb_blit_dstclip(void __iomem *dst, unsigned int dst_pitch,
509 uint32_t dst_format, void *vmap,
510 struct drm_framebuffer *fb)
511 {
512 struct drm_rect fullscreen = {
513 .x1 = 0,
514 .x2 = fb->width,
515 .y1 = 0,
516 .y2 = fb->height,
517 };
518 return drm_fb_blit_rect_dstclip(dst, dst_pitch, dst_format, vmap, fb,
519 &fullscreen);
520 }
521 EXPORT_SYMBOL(drm_fb_blit_dstclip);
522