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 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 56 * @vaddr: Source buffer 57 * @fb: DRM framebuffer 58 * @clip: Clip rectangle area to copy 59 * 60 * This function applies clipping on dst, i.e. the destination is a 61 * full (iomem) framebuffer but only the clip rect content is copied over. 62 */ 63 void drm_fb_memcpy_dstclip(void __iomem *dst, unsigned int dst_pitch, 64 void *vaddr, struct drm_framebuffer *fb, 65 struct drm_rect *clip) 66 { 67 unsigned int cpp = fb->format->cpp[0]; 68 unsigned int offset = clip_offset(clip, dst_pitch, cpp); 69 size_t len = (clip->x2 - clip->x1) * cpp; 70 unsigned int y, lines = clip->y2 - clip->y1; 71 72 vaddr += offset; 73 dst += offset; 74 for (y = 0; y < lines; y++) { 75 memcpy_toio(dst, vaddr, len); 76 vaddr += fb->pitches[0]; 77 dst += dst_pitch; 78 } 79 } 80 EXPORT_SYMBOL(drm_fb_memcpy_dstclip); 81 82 /** 83 * drm_fb_swab - Swap bytes into clip buffer 84 * @dst: Destination buffer 85 * @src: Source buffer 86 * @fb: DRM framebuffer 87 * @clip: Clip rectangle area to copy 88 * @cached: Source buffer is mapped cached (eg. not write-combined) 89 * 90 * If @cached is false a temporary buffer is used to cache one pixel line at a 91 * time to speed up slow uncached reads. 92 * 93 * This function does not apply clipping on dst, i.e. the destination 94 * is a small buffer containing the clip rect only. 95 */ 96 void drm_fb_swab(void *dst, void *src, struct drm_framebuffer *fb, 97 struct drm_rect *clip, bool cached) 98 { 99 u8 cpp = fb->format->cpp[0]; 100 size_t len = drm_rect_width(clip) * cpp; 101 u16 *src16, *dst16 = dst; 102 u32 *src32, *dst32 = dst; 103 unsigned int x, y; 104 void *buf = NULL; 105 106 if (WARN_ON_ONCE(cpp != 2 && cpp != 4)) 107 return; 108 109 if (!cached) 110 buf = kmalloc(len, GFP_KERNEL); 111 112 src += clip_offset(clip, fb->pitches[0], cpp); 113 114 for (y = clip->y1; y < clip->y2; y++) { 115 if (buf) { 116 memcpy(buf, src, len); 117 src16 = buf; 118 src32 = buf; 119 } else { 120 src16 = src; 121 src32 = src; 122 } 123 124 for (x = clip->x1; x < clip->x2; x++) { 125 if (cpp == 4) 126 *dst32++ = swab32(*src32++); 127 else 128 *dst16++ = swab16(*src16++); 129 } 130 131 src += fb->pitches[0]; 132 } 133 134 kfree(buf); 135 } 136 EXPORT_SYMBOL(drm_fb_swab); 137 138 static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf, 139 unsigned int pixels, 140 bool swab) 141 { 142 unsigned int x; 143 u16 val16; 144 145 for (x = 0; x < pixels; x++) { 146 val16 = ((sbuf[x] & 0x00F80000) >> 8) | 147 ((sbuf[x] & 0x0000FC00) >> 5) | 148 ((sbuf[x] & 0x000000F8) >> 3); 149 if (swab) 150 dbuf[x] = swab16(val16); 151 else 152 dbuf[x] = val16; 153 } 154 } 155 156 /** 157 * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer 158 * @dst: RGB565 destination buffer 159 * @vaddr: XRGB8888 source buffer 160 * @fb: DRM framebuffer 161 * @clip: Clip rectangle area to copy 162 * @swab: Swap bytes 163 * 164 * Drivers can use this function for RGB565 devices that don't natively 165 * support XRGB8888. 166 * 167 * This function does not apply clipping on dst, i.e. the destination 168 * is a small buffer containing the clip rect only. 169 */ 170 void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr, 171 struct drm_framebuffer *fb, 172 struct drm_rect *clip, bool swab) 173 { 174 size_t linepixels = clip->x2 - clip->x1; 175 size_t src_len = linepixels * sizeof(u32); 176 size_t dst_len = linepixels * sizeof(u16); 177 unsigned y, lines = clip->y2 - clip->y1; 178 void *sbuf; 179 180 /* 181 * The cma memory is write-combined so reads are uncached. 182 * Speed up by fetching one line at a time. 183 */ 184 sbuf = kmalloc(src_len, GFP_KERNEL); 185 if (!sbuf) 186 return; 187 188 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 189 for (y = 0; y < lines; y++) { 190 memcpy(sbuf, vaddr, src_len); 191 drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab); 192 vaddr += fb->pitches[0]; 193 dst += dst_len; 194 } 195 196 kfree(sbuf); 197 } 198 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); 199 200 /** 201 * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer 202 * @dst: RGB565 destination buffer (iomem) 203 * @dst_pitch: destination buffer pitch 204 * @vaddr: XRGB8888 source buffer 205 * @fb: DRM framebuffer 206 * @clip: Clip rectangle area to copy 207 * @swab: Swap bytes 208 * 209 * Drivers can use this function for RGB565 devices that don't natively 210 * support XRGB8888. 211 * 212 * This function applies clipping on dst, i.e. the destination is a 213 * full (iomem) framebuffer but only the clip rect content is copied over. 214 */ 215 void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch, 216 void *vaddr, struct drm_framebuffer *fb, 217 struct drm_rect *clip, bool swab) 218 { 219 size_t linepixels = clip->x2 - clip->x1; 220 size_t dst_len = linepixels * sizeof(u16); 221 unsigned y, lines = clip->y2 - clip->y1; 222 void *dbuf; 223 224 dbuf = kmalloc(dst_len, GFP_KERNEL); 225 if (!dbuf) 226 return; 227 228 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 229 dst += clip_offset(clip, dst_pitch, sizeof(u16)); 230 for (y = 0; y < lines; y++) { 231 drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab); 232 memcpy_toio(dst, dbuf, dst_len); 233 vaddr += fb->pitches[0]; 234 dst += dst_len; 235 } 236 237 kfree(dbuf); 238 } 239 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip); 240 241 static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf, 242 unsigned int pixels) 243 { 244 unsigned int x; 245 246 for (x = 0; x < pixels; x++) { 247 *dbuf++ = (sbuf[x] & 0x000000FF) >> 0; 248 *dbuf++ = (sbuf[x] & 0x0000FF00) >> 8; 249 *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16; 250 } 251 } 252 253 /** 254 * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer 255 * @dst: RGB565 destination buffer (iomem) 256 * @dst_pitch: destination buffer pitch 257 * @vaddr: XRGB8888 source buffer 258 * @fb: DRM framebuffer 259 * @clip: Clip rectangle area to copy 260 * 261 * Drivers can use this function for RGB888 devices that don't natively 262 * support XRGB8888. 263 * 264 * This function applies clipping on dst, i.e. the destination is a 265 * full (iomem) framebuffer but only the clip rect content is copied over. 266 */ 267 void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch, 268 void *vaddr, struct drm_framebuffer *fb, 269 struct drm_rect *clip) 270 { 271 size_t linepixels = clip->x2 - clip->x1; 272 size_t dst_len = linepixels * 3; 273 unsigned y, lines = clip->y2 - clip->y1; 274 void *dbuf; 275 276 dbuf = kmalloc(dst_len, GFP_KERNEL); 277 if (!dbuf) 278 return; 279 280 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 281 dst += clip_offset(clip, dst_pitch, sizeof(u16)); 282 for (y = 0; y < lines; y++) { 283 drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels); 284 memcpy_toio(dst, dbuf, dst_len); 285 vaddr += fb->pitches[0]; 286 dst += dst_len; 287 } 288 289 kfree(dbuf); 290 } 291 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip); 292 293 /** 294 * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale 295 * @dst: 8-bit grayscale destination buffer 296 * @vaddr: XRGB8888 source buffer 297 * @fb: DRM framebuffer 298 * @clip: Clip rectangle area to copy 299 * 300 * Drm doesn't have native monochrome or grayscale support. 301 * Such drivers can announce the commonly supported XR24 format to userspace 302 * and use this function to convert to the native format. 303 * 304 * Monochrome drivers will use the most significant bit, 305 * where 1 means foreground color and 0 background color. 306 * 307 * ITU BT.601 is used for the RGB -> luma (brightness) conversion. 308 */ 309 void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb, 310 struct drm_rect *clip) 311 { 312 unsigned int len = (clip->x2 - clip->x1) * sizeof(u32); 313 unsigned int x, y; 314 void *buf; 315 u32 *src; 316 317 if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888)) 318 return; 319 /* 320 * The cma memory is write-combined so reads are uncached. 321 * Speed up by fetching one line at a time. 322 */ 323 buf = kmalloc(len, GFP_KERNEL); 324 if (!buf) 325 return; 326 327 for (y = clip->y1; y < clip->y2; y++) { 328 src = vaddr + (y * fb->pitches[0]); 329 src += clip->x1; 330 memcpy(buf, src, len); 331 src = buf; 332 for (x = clip->x1; x < clip->x2; x++) { 333 u8 r = (*src & 0x00ff0000) >> 16; 334 u8 g = (*src & 0x0000ff00) >> 8; 335 u8 b = *src & 0x000000ff; 336 337 /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ 338 *dst++ = (3 * r + 6 * g + b) / 10; 339 src++; 340 } 341 } 342 343 kfree(buf); 344 } 345 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); 346 347 /** 348 * drm_fb_blit_rect_dstclip - Copy parts of a framebuffer to display memory 349 * @dst: The display memory to copy to 350 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 351 * @dst_format: FOURCC code of the display's color format 352 * @vmap: The framebuffer memory to copy from 353 * @fb: The framebuffer to copy from 354 * @clip: Clip rectangle area to copy 355 * 356 * This function copies parts of a framebuffer to display memory. If the 357 * formats of the display and the framebuffer mismatch, the blit function 358 * will attempt to convert between them. 359 * 360 * Use drm_fb_blit_dstclip() to copy the full framebuffer. 361 * 362 * Returns: 363 * 0 on success, or 364 * -EINVAL if the color-format conversion failed, or 365 * a negative error code otherwise. 366 */ 367 int drm_fb_blit_rect_dstclip(void __iomem *dst, unsigned int dst_pitch, 368 uint32_t dst_format, void *vmap, 369 struct drm_framebuffer *fb, 370 struct drm_rect *clip) 371 { 372 uint32_t fb_format = fb->format->format; 373 374 /* treat alpha channel like filler bits */ 375 if (fb_format == DRM_FORMAT_ARGB8888) 376 fb_format = DRM_FORMAT_XRGB8888; 377 if (dst_format == DRM_FORMAT_ARGB8888) 378 dst_format = DRM_FORMAT_XRGB8888; 379 380 if (dst_format == fb_format) { 381 drm_fb_memcpy_dstclip(dst, dst_pitch, vmap, fb, clip); 382 return 0; 383 384 } else if (dst_format == DRM_FORMAT_RGB565) { 385 if (fb_format == DRM_FORMAT_XRGB8888) { 386 drm_fb_xrgb8888_to_rgb565_dstclip(dst, dst_pitch, 387 vmap, fb, clip, 388 false); 389 return 0; 390 } 391 } else if (dst_format == DRM_FORMAT_RGB888) { 392 if (fb_format == DRM_FORMAT_XRGB8888) { 393 drm_fb_xrgb8888_to_rgb888_dstclip(dst, dst_pitch, 394 vmap, fb, clip); 395 return 0; 396 } 397 } 398 399 return -EINVAL; 400 } 401 EXPORT_SYMBOL(drm_fb_blit_rect_dstclip); 402 403 /** 404 * drm_fb_blit_dstclip - Copy framebuffer to display memory 405 * @dst: The display memory to copy to 406 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 407 * @dst_format: FOURCC code of the display's color format 408 * @vmap: The framebuffer memory to copy from 409 * @fb: The framebuffer to copy from 410 * 411 * This function copies a full framebuffer to display memory. If the formats 412 * of the display and the framebuffer mismatch, the copy function will 413 * attempt to convert between them. 414 * 415 * See drm_fb_blit_rect_dstclip() for more information. 416 * 417 * Returns: 418 * 0 on success, or a negative error code otherwise. 419 */ 420 int drm_fb_blit_dstclip(void __iomem *dst, unsigned int dst_pitch, 421 uint32_t dst_format, void *vmap, 422 struct drm_framebuffer *fb) 423 { 424 struct drm_rect fullscreen = { 425 .x1 = 0, 426 .x2 = fb->width, 427 .y1 = 0, 428 .y2 = fb->height, 429 }; 430 return drm_fb_blit_rect_dstclip(dst, dst_pitch, dst_format, vmap, fb, 431 &fullscreen); 432 } 433 EXPORT_SYMBOL(drm_fb_blit_dstclip); 434