xref: /linux/drivers/gpu/drm/drm_format_helper.c (revision 604be85547ce4d61b89292d2f9a78c721b778c16)
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_rgb565_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
415 {
416 	u32 *dbuf32 = dbuf;
417 	const u16 *sbuf16 = sbuf;
418 	unsigned int x;
419 
420 	for (x = 0; x < pixels; x++, ++sbuf16, ++dbuf32) {
421 		u32 val32 = ((*sbuf16 & 0xf800) << 8) |
422 			    ((*sbuf16 & 0x07e0) << 5) |
423 			    ((*sbuf16 & 0x001f) << 3);
424 		*dbuf32 = 0xff000000 | val32 |
425 			  ((val32 >> 3) & 0x00070007) |
426 			  ((val32 >> 2) & 0x00000300);
427 	}
428 }
429 
430 static void drm_fb_rgb565_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_pitch,
431 					   const void *vaddr, const struct drm_framebuffer *fb,
432 					   const struct drm_rect *clip)
433 {
434 	size_t linepixels = drm_rect_width(clip);
435 	size_t dst_len = linepixels * 4;
436 	unsigned int y, lines = drm_rect_height(clip);
437 	void *dbuf;
438 
439 	if (!dst_pitch)
440 		dst_pitch = dst_len;
441 
442 	dbuf = kmalloc(dst_len, GFP_KERNEL);
443 	if (!dbuf)
444 		return;
445 
446 	vaddr += clip_offset(clip, fb->pitches[0], 2);
447 	for (y = 0; y < lines; y++) {
448 		drm_fb_rgb565_to_xrgb8888_line(dbuf, vaddr, linepixels);
449 		memcpy_toio(dst, dbuf, dst_len);
450 		vaddr += fb->pitches[0];
451 		dst += dst_pitch;
452 	}
453 
454 	kfree(dbuf);
455 }
456 
457 static void drm_fb_rgb888_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
458 {
459 	u32 *dbuf32 = dbuf;
460 	const u8 *sbuf8 = sbuf;
461 	unsigned int x;
462 
463 	for (x = 0; x < pixels; x++) {
464 		u8 r = *sbuf8++;
465 		u8 g = *sbuf8++;
466 		u8 b = *sbuf8++;
467 		*dbuf32++ = 0xff000000 | (r << 16) | (g << 8) | b;
468 	}
469 }
470 
471 static void drm_fb_rgb888_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_pitch,
472 					   const void *vaddr, const struct drm_framebuffer *fb,
473 					   const struct drm_rect *clip)
474 {
475 	size_t linepixels = drm_rect_width(clip);
476 	size_t dst_len = linepixels * 4;
477 	unsigned int y, lines = drm_rect_height(clip);
478 	void *dbuf;
479 
480 	if (!dst_pitch)
481 		dst_pitch = dst_len;
482 
483 	dbuf = kmalloc(dst_len, GFP_KERNEL);
484 	if (!dbuf)
485 		return;
486 
487 	vaddr += clip_offset(clip, fb->pitches[0], 3);
488 	for (y = 0; y < lines; y++) {
489 		drm_fb_rgb888_to_xrgb8888_line(dbuf, vaddr, linepixels);
490 		memcpy_toio(dst, dbuf, dst_len);
491 		vaddr += fb->pitches[0];
492 		dst += dst_pitch;
493 	}
494 
495 	kfree(dbuf);
496 }
497 
498 static void drm_fb_xrgb8888_to_xrgb2101010_line(u32 *dbuf, const u32 *sbuf,
499 						unsigned int pixels)
500 {
501 	unsigned int x;
502 	u32 val32;
503 
504 	for (x = 0; x < pixels; x++) {
505 		val32 = ((sbuf[x] & 0x000000FF) << 2) |
506 			((sbuf[x] & 0x0000FF00) << 4) |
507 			((sbuf[x] & 0x00FF0000) << 6);
508 		*dbuf++ = val32 | ((val32 >> 8) & 0x00300C03);
509 	}
510 }
511 
512 /**
513  * drm_fb_xrgb8888_to_xrgb2101010_toio - Convert XRGB8888 to XRGB2101010 clip
514  * buffer
515  * @dst: XRGB2101010 destination buffer (iomem)
516  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
517  * @vaddr: XRGB8888 source buffer
518  * @fb: DRM framebuffer
519  * @clip: Clip rectangle area to copy
520  *
521  * Drivers can use this function for XRGB2101010 devices that don't natively
522  * support XRGB8888.
523  */
524 void drm_fb_xrgb8888_to_xrgb2101010_toio(void __iomem *dst,
525 					 unsigned int dst_pitch, const void *vaddr,
526 					 const struct drm_framebuffer *fb,
527 					 const struct drm_rect *clip)
528 {
529 	size_t linepixels = clip->x2 - clip->x1;
530 	size_t dst_len = linepixels * sizeof(u32);
531 	unsigned int y, lines = clip->y2 - clip->y1;
532 	void *dbuf;
533 
534 	if (!dst_pitch)
535 		dst_pitch = dst_len;
536 
537 	dbuf = kmalloc(dst_len, GFP_KERNEL);
538 	if (!dbuf)
539 		return;
540 
541 	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
542 	for (y = 0; y < lines; y++) {
543 		drm_fb_xrgb8888_to_xrgb2101010_line(dbuf, vaddr, linepixels);
544 		memcpy_toio(dst, dbuf, dst_len);
545 		vaddr += fb->pitches[0];
546 		dst += dst_pitch;
547 	}
548 
549 	kfree(dbuf);
550 }
551 EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010_toio);
552 
553 static void drm_fb_xrgb8888_to_gray8_line(u8 *dst, const u32 *src, unsigned int pixels)
554 {
555 	unsigned int x;
556 
557 	for (x = 0; x < pixels; x++) {
558 		u8 r = (*src & 0x00ff0000) >> 16;
559 		u8 g = (*src & 0x0000ff00) >> 8;
560 		u8 b =  *src & 0x000000ff;
561 
562 		/* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
563 		*dst++ = (3 * r + 6 * g + b) / 10;
564 		src++;
565 	}
566 }
567 
568 /**
569  * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
570  * @dst: 8-bit grayscale destination buffer
571  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
572  * @vaddr: XRGB8888 source buffer
573  * @fb: DRM framebuffer
574  * @clip: Clip rectangle area to copy
575  *
576  * Drm doesn't have native monochrome or grayscale support.
577  * Such drivers can announce the commonly supported XR24 format to userspace
578  * and use this function to convert to the native format.
579  *
580  * Monochrome drivers will use the most significant bit,
581  * where 1 means foreground color and 0 background color.
582  *
583  * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
584  */
585 void drm_fb_xrgb8888_to_gray8(void *dst, unsigned int dst_pitch, const void *vaddr,
586 			      const struct drm_framebuffer *fb, const struct drm_rect *clip)
587 {
588 	unsigned int linepixels = clip->x2 - clip->x1;
589 	unsigned int len = linepixels * sizeof(u32);
590 	unsigned int y;
591 	void *buf;
592 	u8 *dst8;
593 	u32 *src32;
594 
595 	if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
596 		return;
597 
598 	if (!dst_pitch)
599 		dst_pitch = drm_rect_width(clip);
600 
601 	/*
602 	 * The cma memory is write-combined so reads are uncached.
603 	 * Speed up by fetching one line at a time.
604 	 */
605 	buf = kmalloc(len, GFP_KERNEL);
606 	if (!buf)
607 		return;
608 
609 	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
610 	for (y = clip->y1; y < clip->y2; y++) {
611 		dst8 = dst;
612 		src32 = memcpy(buf, vaddr, len);
613 		drm_fb_xrgb8888_to_gray8_line(dst8, src32, linepixels);
614 		vaddr += fb->pitches[0];
615 		dst += dst_pitch;
616 	}
617 
618 	kfree(buf);
619 }
620 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
621 
622 /**
623  * drm_fb_blit_toio - Copy parts of a framebuffer to display memory
624  * @dst:	The display memory to copy to
625  * @dst_pitch:	Number of bytes between two consecutive scanlines within dst
626  * @dst_format:	FOURCC code of the display's color format
627  * @vmap:	The framebuffer memory to copy from
628  * @fb:		The framebuffer to copy from
629  * @clip:	Clip rectangle area to copy
630  *
631  * This function copies parts of a framebuffer to display memory. If the
632  * formats of the display and the framebuffer mismatch, the blit function
633  * will attempt to convert between them.
634  *
635  * Returns:
636  * 0 on success, or
637  * -EINVAL if the color-format conversion failed, or
638  * a negative error code otherwise.
639  */
640 int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_format,
641 		     const void *vmap, const struct drm_framebuffer *fb,
642 		     const struct drm_rect *clip)
643 {
644 	uint32_t fb_format = fb->format->format;
645 
646 	/* treat alpha channel like filler bits */
647 	if (fb_format == DRM_FORMAT_ARGB8888)
648 		fb_format = DRM_FORMAT_XRGB8888;
649 	if (dst_format == DRM_FORMAT_ARGB8888)
650 		dst_format = DRM_FORMAT_XRGB8888;
651 	if (fb_format == DRM_FORMAT_ARGB2101010)
652 		fb_format = DRM_FORMAT_XRGB2101010;
653 	if (dst_format == DRM_FORMAT_ARGB2101010)
654 		dst_format = DRM_FORMAT_XRGB2101010;
655 
656 	if (dst_format == fb_format) {
657 		drm_fb_memcpy_toio(dst, dst_pitch, vmap, fb, clip);
658 		return 0;
659 
660 	} else if (dst_format == DRM_FORMAT_RGB565) {
661 		if (fb_format == DRM_FORMAT_XRGB8888) {
662 			drm_fb_xrgb8888_to_rgb565_toio(dst, dst_pitch, vmap, fb, clip, false);
663 			return 0;
664 		}
665 	} else if (dst_format == DRM_FORMAT_RGB888) {
666 		if (fb_format == DRM_FORMAT_XRGB8888) {
667 			drm_fb_xrgb8888_to_rgb888_toio(dst, dst_pitch, vmap, fb, clip);
668 			return 0;
669 		}
670 	} else if (dst_format == DRM_FORMAT_XRGB8888) {
671 		if (fb_format == DRM_FORMAT_RGB888) {
672 			drm_fb_rgb888_to_xrgb8888_toio(dst, dst_pitch, vmap, fb, clip);
673 			return 0;
674 		} else if (fb_format == DRM_FORMAT_RGB565) {
675 			drm_fb_rgb565_to_xrgb8888_toio(dst, dst_pitch, vmap, fb, clip);
676 			return 0;
677 		}
678 	} else if (dst_format == DRM_FORMAT_XRGB2101010) {
679 		if (fb_format == DRM_FORMAT_XRGB8888) {
680 			drm_fb_xrgb8888_to_xrgb2101010_toio(dst, dst_pitch, vmap, fb, clip);
681 			return 0;
682 		}
683 	}
684 
685 	drm_warn_once(fb->dev, "No conversion helper from %p4cc to %p4cc found.\n",
686 		      &fb_format, &dst_format);
687 
688 	return -EINVAL;
689 }
690 EXPORT_SYMBOL(drm_fb_blit_toio);
691 
692 
693 static void drm_fb_gray8_to_mono_line(u8 *dst, const u8 *src, unsigned int pixels)
694 {
695 	while (pixels) {
696 		unsigned int i, bits = min(pixels, 8U);
697 		u8 byte = 0;
698 
699 		for (i = 0; i < bits; i++, pixels--) {
700 			if (*src++ >= 128)
701 				byte |= BIT(i);
702 		}
703 		*dst++ = byte;
704 	}
705 }
706 
707 /**
708  * drm_fb_xrgb8888_to_mono - Convert XRGB8888 to monochrome
709  * @dst: monochrome destination buffer (0=black, 1=white)
710  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
711  * @vaddr: XRGB8888 source buffer
712  * @fb: DRM framebuffer
713  * @clip: Clip rectangle area to copy
714  *
715  * DRM doesn't have native monochrome support.
716  * Such drivers can announce the commonly supported XR24 format to userspace
717  * and use this function to convert to the native format.
718  *
719  * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and
720  * then the result is converted from grayscale to monochrome.
721  *
722  * The first pixel (upper left corner of the clip rectangle) will be converted
723  * and copied to the first bit (LSB) in the first byte of the monochrome
724  * destination buffer.
725  * If the caller requires that the first pixel in a byte must be located at an
726  * x-coordinate that is a multiple of 8, then the caller must take care itself
727  * of supplying a suitable clip rectangle.
728  */
729 void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *vaddr,
730 			     const struct drm_framebuffer *fb, const struct drm_rect *clip)
731 {
732 	unsigned int linepixels = drm_rect_width(clip);
733 	unsigned int lines = drm_rect_height(clip);
734 	unsigned int cpp = fb->format->cpp[0];
735 	unsigned int len_src32 = linepixels * cpp;
736 	struct drm_device *dev = fb->dev;
737 	unsigned int y;
738 	u8 *mono = dst, *gray8;
739 	u32 *src32;
740 
741 	if (drm_WARN_ON(dev, fb->format->format != DRM_FORMAT_XRGB8888))
742 		return;
743 
744 	/*
745 	 * The mono destination buffer contains 1 bit per pixel
746 	 */
747 	if (!dst_pitch)
748 		dst_pitch = DIV_ROUND_UP(linepixels, 8);
749 
750 	/*
751 	 * The cma memory is write-combined so reads are uncached.
752 	 * Speed up by fetching one line at a time.
753 	 *
754 	 * Also, format conversion from XR24 to monochrome are done
755 	 * line-by-line but are converted to 8-bit grayscale as an
756 	 * intermediate step.
757 	 *
758 	 * Allocate a buffer to be used for both copying from the cma
759 	 * memory and to store the intermediate grayscale line pixels.
760 	 */
761 	src32 = kmalloc(len_src32 + linepixels, GFP_KERNEL);
762 	if (!src32)
763 		return;
764 
765 	gray8 = (u8 *)src32 + len_src32;
766 
767 	vaddr += clip_offset(clip, fb->pitches[0], cpp);
768 	for (y = 0; y < lines; y++) {
769 		src32 = memcpy(src32, vaddr, len_src32);
770 		drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels);
771 		drm_fb_gray8_to_mono_line(mono, gray8, linepixels);
772 		vaddr += fb->pitches[0];
773 		mono += dst_pitch;
774 	}
775 
776 	kfree(src32);
777 }
778 EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono);
779