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  * @vaddr: Source buffer
56  * @fb: DRM framebuffer
57  * @clip: Clip rectangle area to copy
58  *
59  * This function applies clipping on dst, i.e. the destination is a
60  * full (iomem) framebuffer but only the clip rect content is copied over.
61  */
drm_fb_memcpy_dstclip(void __iomem * dst,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip)62 void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr,
63 			   struct drm_framebuffer *fb,
64 			   struct drm_rect *clip)
65 {
66 	unsigned int cpp = fb->format->cpp[0];
67 	unsigned int offset = clip_offset(clip, fb->pitches[0], cpp);
68 	size_t len = (clip->x2 - clip->x1) * cpp;
69 	unsigned int y, lines = clip->y2 - clip->y1;
70 
71 	vaddr += offset;
72 	dst += offset;
73 	for (y = 0; y < lines; y++) {
74 		memcpy_toio(dst, vaddr, len);
75 		vaddr += fb->pitches[0];
76 		dst += fb->pitches[0];
77 	}
78 }
79 EXPORT_SYMBOL(drm_fb_memcpy_dstclip);
80 
81 /**
82  * drm_fb_swab - Swap bytes into clip buffer
83  * @dst: Destination buffer
84  * @src: Source buffer
85  * @fb: DRM framebuffer
86  * @clip: Clip rectangle area to copy
87  * @cached: Source buffer is mapped cached (eg. not write-combined)
88  *
89  * If @cached is false a temporary buffer is used to cache one pixel line at a
90  * time to speed up slow uncached reads.
91  *
92  * This function does not apply clipping on dst, i.e. the destination
93  * is a small buffer containing the clip rect only.
94  */
drm_fb_swab(void * dst,void * src,struct drm_framebuffer * fb,struct drm_rect * clip,bool cached)95 void drm_fb_swab(void *dst, void *src, struct drm_framebuffer *fb,
96 		 struct drm_rect *clip, bool cached)
97 {
98 	u8 cpp = fb->format->cpp[0];
99 	size_t len = drm_rect_width(clip) * cpp;
100 	u16 *src16, *dst16 = dst;
101 	u32 *src32, *dst32 = dst;
102 	unsigned int x, y;
103 	void *buf = NULL;
104 
105 	if (WARN_ON_ONCE(cpp != 2 && cpp != 4))
106 		return;
107 
108 	if (!cached)
109 		buf = kmalloc(len, GFP_KERNEL);
110 
111 	src += clip_offset(clip, fb->pitches[0], cpp);
112 
113 	for (y = clip->y1; y < clip->y2; y++) {
114 		if (buf) {
115 			memcpy(buf, src, len);
116 			src16 = buf;
117 			src32 = buf;
118 		} else {
119 			src16 = src;
120 			src32 = src;
121 		}
122 
123 		for (x = clip->x1; x < clip->x2; x++) {
124 			if (cpp == 4)
125 				*dst32++ = swab32(*src32++);
126 			else
127 				*dst16++ = swab16(*src16++);
128 		}
129 
130 		src += fb->pitches[0];
131 	}
132 
133 	kfree(buf);
134 }
135 EXPORT_SYMBOL(drm_fb_swab);
136 
drm_fb_xrgb8888_to_rgb565_line(u16 * dbuf,u32 * sbuf,unsigned int pixels,bool swab)137 static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf,
138 					   unsigned int pixels,
139 					   bool swab)
140 {
141 	unsigned int x;
142 	u16 val16;
143 
144 	for (x = 0; x < pixels; x++) {
145 		val16 = ((sbuf[x] & 0x00F80000) >> 8) |
146 			((sbuf[x] & 0x0000FC00) >> 5) |
147 			((sbuf[x] & 0x000000F8) >> 3);
148 		if (swab)
149 			dbuf[x] = swab16(val16);
150 		else
151 			dbuf[x] = val16;
152 	}
153 }
154 
155 /**
156  * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
157  * @dst: RGB565 destination buffer
158  * @vaddr: XRGB8888 source buffer
159  * @fb: DRM framebuffer
160  * @clip: Clip rectangle area to copy
161  * @swab: Swap bytes
162  *
163  * Drivers can use this function for RGB565 devices that don't natively
164  * support XRGB8888.
165  *
166  * This function does not apply clipping on dst, i.e. the destination
167  * is a small buffer containing the clip rect only.
168  */
drm_fb_xrgb8888_to_rgb565(void * dst,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip,bool swab)169 void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr,
170 			       struct drm_framebuffer *fb,
171 			       struct drm_rect *clip, bool swab)
172 {
173 	size_t linepixels = clip->x2 - clip->x1;
174 	size_t src_len = linepixels * sizeof(u32);
175 	size_t dst_len = linepixels * sizeof(u16);
176 	unsigned y, lines = clip->y2 - clip->y1;
177 	void *sbuf;
178 
179 	/*
180 	 * The cma memory is write-combined so reads are uncached.
181 	 * Speed up by fetching one line at a time.
182 	 */
183 	sbuf = kmalloc(src_len, GFP_KERNEL);
184 	if (!sbuf)
185 		return;
186 
187 	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
188 	for (y = 0; y < lines; y++) {
189 		memcpy(sbuf, vaddr, src_len);
190 		drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab);
191 		vaddr += fb->pitches[0];
192 		dst += dst_len;
193 	}
194 
195 	kfree(sbuf);
196 }
197 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
198 
199 /**
200  * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer
201  * @dst: RGB565 destination buffer (iomem)
202  * @dst_pitch: destination buffer pitch
203  * @vaddr: XRGB8888 source buffer
204  * @fb: DRM framebuffer
205  * @clip: Clip rectangle area to copy
206  * @swab: Swap bytes
207  *
208  * Drivers can use this function for RGB565 devices that don't natively
209  * support XRGB8888.
210  *
211  * This function applies clipping on dst, i.e. the destination is a
212  * full (iomem) framebuffer but only the clip rect content is copied over.
213  */
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)214 void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch,
215 				       void *vaddr, struct drm_framebuffer *fb,
216 				       struct drm_rect *clip, bool swab)
217 {
218 	size_t linepixels = clip->x2 - clip->x1;
219 	size_t dst_len = linepixels * sizeof(u16);
220 	unsigned y, lines = clip->y2 - clip->y1;
221 	void *dbuf;
222 
223 	dbuf = kmalloc(dst_len, GFP_KERNEL);
224 	if (!dbuf)
225 		return;
226 
227 	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
228 	dst += clip_offset(clip, dst_pitch, sizeof(u16));
229 	for (y = 0; y < lines; y++) {
230 		drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab);
231 		memcpy_toio(dst, dbuf, dst_len);
232 		vaddr += fb->pitches[0];
233 		dst += dst_len;
234 	}
235 
236 	kfree(dbuf);
237 }
238 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip);
239 
drm_fb_xrgb8888_to_rgb888_line(u8 * dbuf,u32 * sbuf,unsigned int pixels)240 static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf,
241 					   unsigned int pixels)
242 {
243 	unsigned int x;
244 
245 	for (x = 0; x < pixels; x++) {
246 		*dbuf++ = (sbuf[x] & 0x000000FF) >>  0;
247 		*dbuf++ = (sbuf[x] & 0x0000FF00) >>  8;
248 		*dbuf++ = (sbuf[x] & 0x00FF0000) >> 16;
249 	}
250 }
251 
252 /**
253  * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer
254  * @dst: RGB565 destination buffer (iomem)
255  * @dst_pitch: destination buffer pitch
256  * @vaddr: XRGB8888 source buffer
257  * @fb: DRM framebuffer
258  * @clip: Clip rectangle area to copy
259  *
260  * Drivers can use this function for RGB888 devices that don't natively
261  * support XRGB8888.
262  *
263  * This function applies clipping on dst, i.e. the destination is a
264  * full (iomem) framebuffer but only the clip rect content is copied over.
265  */
drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem * dst,unsigned int dst_pitch,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip)266 void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch,
267 				       void *vaddr, struct drm_framebuffer *fb,
268 				       struct drm_rect *clip)
269 {
270 	size_t linepixels = clip->x2 - clip->x1;
271 	size_t dst_len = linepixels * 3;
272 	unsigned y, lines = clip->y2 - clip->y1;
273 	void *dbuf;
274 
275 	dbuf = kmalloc(dst_len, GFP_KERNEL);
276 	if (!dbuf)
277 		return;
278 
279 	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
280 	dst += clip_offset(clip, dst_pitch, sizeof(u16));
281 	for (y = 0; y < lines; y++) {
282 		drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels);
283 		memcpy_toio(dst, dbuf, dst_len);
284 		vaddr += fb->pitches[0];
285 		dst += dst_len;
286 	}
287 
288 	kfree(dbuf);
289 }
290 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip);
291 
292 /**
293  * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
294  * @dst: 8-bit grayscale destination buffer
295  * @vaddr: XRGB8888 source buffer
296  * @fb: DRM framebuffer
297  * @clip: Clip rectangle area to copy
298  *
299  * Drm doesn't have native monochrome or grayscale support.
300  * Such drivers can announce the commonly supported XR24 format to userspace
301  * and use this function to convert to the native format.
302  *
303  * Monochrome drivers will use the most significant bit,
304  * where 1 means foreground color and 0 background color.
305  *
306  * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
307  */
drm_fb_xrgb8888_to_gray8(u8 * dst,void * vaddr,struct drm_framebuffer * fb,struct drm_rect * clip)308 void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
309 			       struct drm_rect *clip)
310 {
311 	unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
312 	unsigned int x, y;
313 	void *buf;
314 	u32 *src;
315 
316 	if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
317 		return;
318 	/*
319 	 * The cma memory is write-combined so reads are uncached.
320 	 * Speed up by fetching one line at a time.
321 	 */
322 	buf = kmalloc(len, GFP_KERNEL);
323 	if (!buf)
324 		return;
325 
326 	for (y = clip->y1; y < clip->y2; y++) {
327 		src = vaddr + (y * fb->pitches[0]);
328 		src += clip->x1;
329 		memcpy(buf, src, len);
330 		src = buf;
331 		for (x = clip->x1; x < clip->x2; x++) {
332 			u8 r = (*src & 0x00ff0000) >> 16;
333 			u8 g = (*src & 0x0000ff00) >> 8;
334 			u8 b =  *src & 0x000000ff;
335 
336 			/* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
337 			*dst++ = (3 * r + 6 * g + b) / 10;
338 			src++;
339 		}
340 	}
341 
342 	kfree(buf);
343 }
344 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
345 
346