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_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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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