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);
587