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