Commit | Line | Data |
---|---|---|
0bd65bd8 | 1 | // SPDX-License-Identifier: GPL-2.0 or MIT |
7415287e GH |
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> | |
bf4f6d16 | 13 | #include <linux/io.h> |
7415287e GH |
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 | ||
bf4f6d16 GH |
20 | static unsigned int clip_offset(struct drm_rect *clip, |
21 | unsigned int pitch, unsigned int cpp) | |
26f024f5 | 22 | { |
bf4f6d16 | 23 | return clip->y1 * pitch + clip->x1 * cpp; |
26f024f5 GH |
24 | } |
25 | ||
7415287e GH |
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 | |
26f024f5 GH |
32 | * |
33 | * This function does not apply clipping on dst, i.e. the destination | |
34 | * is a small buffer containing the clip rect only. | |
7415287e GH |
35 | */ |
36 | void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, | |
37 | struct drm_rect *clip) | |
38 | { | |
b0f986b4 | 39 | unsigned int cpp = fb->format->cpp[0]; |
7415287e | 40 | size_t len = (clip->x2 - clip->x1) * cpp; |
bf4f6d16 | 41 | unsigned int y, lines = clip->y2 - clip->y1; |
7415287e | 42 | |
bf4f6d16 GH |
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 | } | |
7415287e GH |
49 | } |
50 | EXPORT_SYMBOL(drm_fb_memcpy); | |
51 | ||
26f024f5 GH |
52 | /** |
53 | * drm_fb_memcpy_dstclip - Copy clip buffer | |
bf4f6d16 | 54 | * @dst: Destination buffer (iomem) |
26f024f5 GH |
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 | |
bf4f6d16 | 60 | * full (iomem) framebuffer but only the clip rect content is copied over. |
26f024f5 | 61 | */ |
bf4f6d16 GH |
62 | void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr, |
63 | struct drm_framebuffer *fb, | |
26f024f5 GH |
64 | struct drm_rect *clip) |
65 | { | |
b0f986b4 | 66 | unsigned int cpp = fb->format->cpp[0]; |
bf4f6d16 | 67 | unsigned int offset = clip_offset(clip, fb->pitches[0], cpp); |
26f024f5 | 68 | size_t len = (clip->x2 - clip->x1) * cpp; |
bf4f6d16 | 69 | unsigned int y, lines = clip->y2 - clip->y1; |
26f024f5 | 70 | |
bf4f6d16 GH |
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 | } | |
26f024f5 GH |
78 | } |
79 | EXPORT_SYMBOL(drm_fb_memcpy_dstclip); | |
80 | ||
7415287e | 81 | /** |
bd34cea2 NT |
82 | * drm_fb_swab - Swap bytes into clip buffer |
83 | * @dst: Destination buffer | |
84 | * @src: Source buffer | |
7415287e GH |
85 | * @fb: DRM framebuffer |
86 | * @clip: Clip rectangle area to copy | |
bd34cea2 NT |
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. | |
7415287e | 94 | */ |
bd34cea2 NT |
95 | void drm_fb_swab(void *dst, void *src, struct drm_framebuffer *fb, |
96 | struct drm_rect *clip, bool cached) | |
7415287e | 97 | { |
bd34cea2 NT |
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; | |
7415287e | 102 | unsigned int x, y; |
bd34cea2 NT |
103 | void *buf = NULL; |
104 | ||
105 | if (WARN_ON_ONCE(cpp != 2 && cpp != 4)) | |
7415287e GH |
106 | return; |
107 | ||
bd34cea2 NT |
108 | if (!cached) |
109 | buf = kmalloc(len, GFP_KERNEL); | |
110 | ||
111 | src += clip_offset(clip, fb->pitches[0], cpp); | |
112 | ||
7415287e | 113 | for (y = clip->y1; y < clip->y2; y++) { |
bd34cea2 NT |
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]; | |
7415287e GH |
131 | } |
132 | ||
133 | kfree(buf); | |
134 | } | |
bd34cea2 | 135 | EXPORT_SYMBOL(drm_fb_swab); |
7415287e | 136 | |
d653bd39 GH |
137 | static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf, |
138 | unsigned int pixels, | |
139 | bool swab) | |
7415287e | 140 | { |
d653bd39 GH |
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; | |
7415287e | 152 | } |
bcc44420 GH |
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 | |
d653bd39 | 161 | * @swab: Swap bytes |
bcc44420 GH |
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 | */ | |
169 | void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr, | |
170 | struct drm_framebuffer *fb, | |
d653bd39 | 171 | struct drm_rect *clip, bool swab) |
bcc44420 | 172 | { |
d653bd39 GH |
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); | |
7415287e GH |
196 | } |
197 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); | |
198 | ||
bcc44420 GH |
199 | /** |
200 | * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer | |
d653bd39 | 201 | * @dst: RGB565 destination buffer (iomem) |
bcc44420 GH |
202 | * @dst_pitch: destination buffer pitch |
203 | * @vaddr: XRGB8888 source buffer | |
204 | * @fb: DRM framebuffer | |
205 | * @clip: Clip rectangle area to copy | |
d653bd39 | 206 | * @swab: Swap bytes |
bcc44420 GH |
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 | |
d653bd39 | 212 | * full (iomem) framebuffer but only the clip rect content is copied over. |
bcc44420 | 213 | */ |
d653bd39 | 214 | void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch, |
bcc44420 | 215 | void *vaddr, struct drm_framebuffer *fb, |
d653bd39 | 216 | struct drm_rect *clip, bool swab) |
bcc44420 | 217 | { |
d653bd39 GH |
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); | |
bcc44420 GH |
237 | } |
238 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip); | |
239 | ||
5c5373b5 GH |
240 | static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf, |
241 | unsigned int pixels) | |
ec3de7a4 | 242 | { |
5c5373b5 | 243 | unsigned int x; |
ec3de7a4 | 244 | |
5c5373b5 GH |
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; | |
ec3de7a4 | 249 | } |
ec3de7a4 GH |
250 | } |
251 | ||
252 | /** | |
253 | * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer | |
79b97973 | 254 | * @dst: RGB565 destination buffer (iomem) |
ec3de7a4 GH |
255 | * @dst_pitch: destination buffer pitch |
256 | * @vaddr: XRGB8888 source buffer | |
257 | * @fb: DRM framebuffer | |
258 | * @clip: Clip rectangle area to copy | |
ec3de7a4 GH |
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 | |
79b97973 | 264 | * full (iomem) framebuffer but only the clip rect content is copied over. |
ec3de7a4 | 265 | */ |
79b97973 | 266 | void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch, |
ec3de7a4 GH |
267 | void *vaddr, struct drm_framebuffer *fb, |
268 | struct drm_rect *clip) | |
269 | { | |
5c5373b5 GH |
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; | |
ec3de7a4 | 274 | |
5c5373b5 GH |
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); | |
ec3de7a4 GH |
289 | } |
290 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip); | |
291 | ||
7415287e GH |
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 | */ | |
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 |