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(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp) 21 { 22 return clip->y1 * pitch + clip->x1 * cpp; 23 } 24 25 /** 26 * drm_fb_clip_offset - Returns the clipping rectangles byte-offset in a framebuffer 27 * @pitch: Framebuffer line pitch in byte 28 * @format: Framebuffer format 29 * @clip: Clip rectangle 30 * 31 * Returns: 32 * The byte offset of the clip rectangle's top-left corner within the framebuffer. 33 */ 34 unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format, 35 const struct drm_rect *clip) 36 { 37 return clip_offset(clip, pitch, format->cpp[0]); 38 } 39 EXPORT_SYMBOL(drm_fb_clip_offset); 40 41 /** 42 * drm_fb_memcpy - Copy clip buffer 43 * @dst: Destination buffer 44 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 45 * @vaddr: Source buffer 46 * @fb: DRM framebuffer 47 * @clip: Clip rectangle area to copy 48 * 49 * This function does not apply clipping on dst, i.e. the destination 50 * is at the top-left corner. 51 */ 52 void drm_fb_memcpy(void *dst, unsigned int dst_pitch, const void *vaddr, 53 const struct drm_framebuffer *fb, const struct drm_rect *clip) 54 { 55 unsigned int cpp = fb->format->cpp[0]; 56 size_t len = (clip->x2 - clip->x1) * cpp; 57 unsigned int y, lines = clip->y2 - clip->y1; 58 59 if (!dst_pitch) 60 dst_pitch = len; 61 62 vaddr += clip_offset(clip, fb->pitches[0], cpp); 63 for (y = 0; y < lines; y++) { 64 memcpy(dst, vaddr, len); 65 vaddr += fb->pitches[0]; 66 dst += dst_pitch; 67 } 68 } 69 EXPORT_SYMBOL(drm_fb_memcpy); 70 71 /** 72 * drm_fb_memcpy_toio - Copy clip buffer 73 * @dst: Destination buffer (iomem) 74 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 75 * @vaddr: Source buffer 76 * @fb: DRM framebuffer 77 * @clip: Clip rectangle area to copy 78 * 79 * This function does not apply clipping on dst, i.e. the destination 80 * is at the top-left corner. 81 */ 82 void drm_fb_memcpy_toio(void __iomem *dst, unsigned int dst_pitch, const void *vaddr, 83 const struct drm_framebuffer *fb, const struct drm_rect *clip) 84 { 85 unsigned int cpp = fb->format->cpp[0]; 86 size_t len = (clip->x2 - clip->x1) * cpp; 87 unsigned int y, lines = clip->y2 - clip->y1; 88 89 if (!dst_pitch) 90 dst_pitch = len; 91 92 vaddr += clip_offset(clip, fb->pitches[0], cpp); 93 for (y = 0; y < lines; y++) { 94 memcpy_toio(dst, vaddr, len); 95 vaddr += fb->pitches[0]; 96 dst += dst_pitch; 97 } 98 } 99 EXPORT_SYMBOL(drm_fb_memcpy_toio); 100 101 /** 102 * drm_fb_swab - Swap bytes into clip buffer 103 * @dst: Destination buffer 104 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 105 * @src: Source buffer 106 * @fb: DRM framebuffer 107 * @clip: Clip rectangle area to copy 108 * @cached: Source buffer is mapped cached (eg. not write-combined) 109 * 110 * If @cached is false a temporary buffer is used to cache one pixel line at a 111 * time to speed up slow uncached reads. 112 * 113 * This function does not apply clipping on dst, i.e. the destination 114 * is at the top-left corner. 115 */ 116 void drm_fb_swab(void *dst, unsigned int dst_pitch, const void *src, 117 const struct drm_framebuffer *fb, const struct drm_rect *clip, 118 bool cached) 119 { 120 u8 cpp = fb->format->cpp[0]; 121 size_t len = drm_rect_width(clip) * cpp; 122 const u16 *src16; 123 const u32 *src32; 124 u16 *dst16; 125 u32 *dst32; 126 unsigned int x, y; 127 void *buf = NULL; 128 129 if (WARN_ON_ONCE(cpp != 2 && cpp != 4)) 130 return; 131 132 if (!dst_pitch) 133 dst_pitch = len; 134 135 if (!cached) 136 buf = kmalloc(len, GFP_KERNEL); 137 138 src += clip_offset(clip, fb->pitches[0], cpp); 139 140 for (y = clip->y1; y < clip->y2; y++) { 141 if (buf) { 142 memcpy(buf, src, len); 143 src16 = buf; 144 src32 = buf; 145 } else { 146 src16 = src; 147 src32 = src; 148 } 149 150 dst16 = dst; 151 dst32 = dst; 152 153 for (x = clip->x1; x < clip->x2; x++) { 154 if (cpp == 4) 155 *dst32++ = swab32(*src32++); 156 else 157 *dst16++ = swab16(*src16++); 158 } 159 160 src += fb->pitches[0]; 161 dst += dst_pitch; 162 } 163 164 kfree(buf); 165 } 166 EXPORT_SYMBOL(drm_fb_swab); 167 168 static void drm_fb_xrgb8888_to_rgb332_line(u8 *dbuf, const __le32 *sbuf, unsigned int pixels) 169 { 170 unsigned int x; 171 u32 pix; 172 173 for (x = 0; x < pixels; x++) { 174 pix = le32_to_cpu(sbuf[x]); 175 dbuf[x] = ((pix & 0x00e00000) >> 16) | 176 ((pix & 0x0000e000) >> 11) | 177 ((pix & 0x000000c0) >> 6); 178 } 179 } 180 181 /** 182 * drm_fb_xrgb8888_to_rgb332 - Convert XRGB8888 to RGB332 clip buffer 183 * @dst: RGB332 destination buffer 184 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 185 * @src: XRGB8888 source buffer 186 * @fb: DRM framebuffer 187 * @clip: Clip rectangle area to copy 188 * 189 * Drivers can use this function for RGB332 devices that don't natively support XRGB8888. 190 */ 191 void drm_fb_xrgb8888_to_rgb332(void *dst, unsigned int dst_pitch, const void *src, 192 const struct drm_framebuffer *fb, const struct drm_rect *clip) 193 { 194 size_t width = drm_rect_width(clip); 195 size_t src_len = width * sizeof(u32); 196 unsigned int y; 197 void *sbuf; 198 199 if (!dst_pitch) 200 dst_pitch = width; 201 202 /* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */ 203 sbuf = kmalloc(src_len, GFP_KERNEL); 204 if (!sbuf) 205 return; 206 207 src += clip_offset(clip, fb->pitches[0], sizeof(u32)); 208 for (y = 0; y < drm_rect_height(clip); y++) { 209 memcpy(sbuf, src, src_len); 210 drm_fb_xrgb8888_to_rgb332_line(dst, sbuf, width); 211 src += fb->pitches[0]; 212 dst += dst_pitch; 213 } 214 215 kfree(sbuf); 216 } 217 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332); 218 219 static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, const u32 *sbuf, 220 unsigned int pixels, 221 bool swab) 222 { 223 unsigned int x; 224 u16 val16; 225 226 for (x = 0; x < pixels; x++) { 227 val16 = ((sbuf[x] & 0x00F80000) >> 8) | 228 ((sbuf[x] & 0x0000FC00) >> 5) | 229 ((sbuf[x] & 0x000000F8) >> 3); 230 if (swab) 231 dbuf[x] = swab16(val16); 232 else 233 dbuf[x] = val16; 234 } 235 } 236 237 /** 238 * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer 239 * @dst: RGB565 destination buffer 240 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 241 * @vaddr: XRGB8888 source buffer 242 * @fb: DRM framebuffer 243 * @clip: Clip rectangle area to copy 244 * @swab: Swap bytes 245 * 246 * Drivers can use this function for RGB565 devices that don't natively 247 * support XRGB8888. 248 */ 249 void drm_fb_xrgb8888_to_rgb565(void *dst, unsigned int dst_pitch, const void *vaddr, 250 const struct drm_framebuffer *fb, const struct drm_rect *clip, 251 bool swab) 252 { 253 size_t linepixels = clip->x2 - clip->x1; 254 size_t src_len = linepixels * sizeof(u32); 255 size_t dst_len = linepixels * sizeof(u16); 256 unsigned y, lines = clip->y2 - clip->y1; 257 void *sbuf; 258 259 if (!dst_pitch) 260 dst_pitch = dst_len; 261 262 /* 263 * The cma memory is write-combined so reads are uncached. 264 * Speed up by fetching one line at a time. 265 */ 266 sbuf = kmalloc(src_len, GFP_KERNEL); 267 if (!sbuf) 268 return; 269 270 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 271 for (y = 0; y < lines; y++) { 272 memcpy(sbuf, vaddr, src_len); 273 drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab); 274 vaddr += fb->pitches[0]; 275 dst += dst_pitch; 276 } 277 278 kfree(sbuf); 279 } 280 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); 281 282 /** 283 * drm_fb_xrgb8888_to_rgb565_toio - Convert XRGB8888 to RGB565 clip buffer 284 * @dst: RGB565 destination buffer (iomem) 285 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 286 * @vaddr: XRGB8888 source buffer 287 * @fb: DRM framebuffer 288 * @clip: Clip rectangle area to copy 289 * @swab: Swap bytes 290 * 291 * Drivers can use this function for RGB565 devices that don't natively 292 * support XRGB8888. 293 */ 294 void drm_fb_xrgb8888_to_rgb565_toio(void __iomem *dst, unsigned int dst_pitch, 295 const void *vaddr, const struct drm_framebuffer *fb, 296 const struct drm_rect *clip, bool swab) 297 { 298 size_t linepixels = clip->x2 - clip->x1; 299 size_t dst_len = linepixels * sizeof(u16); 300 unsigned y, lines = clip->y2 - clip->y1; 301 void *dbuf; 302 303 if (!dst_pitch) 304 dst_pitch = dst_len; 305 306 dbuf = kmalloc(dst_len, GFP_KERNEL); 307 if (!dbuf) 308 return; 309 310 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 311 for (y = 0; y < lines; y++) { 312 drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab); 313 memcpy_toio(dst, dbuf, dst_len); 314 vaddr += fb->pitches[0]; 315 dst += dst_pitch; 316 } 317 318 kfree(dbuf); 319 } 320 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_toio); 321 322 static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, const u32 *sbuf, 323 unsigned int pixels) 324 { 325 unsigned int x; 326 327 for (x = 0; x < pixels; x++) { 328 *dbuf++ = (sbuf[x] & 0x000000FF) >> 0; 329 *dbuf++ = (sbuf[x] & 0x0000FF00) >> 8; 330 *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16; 331 } 332 } 333 334 /** 335 * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer 336 * @dst: RGB888 destination buffer 337 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 338 * @src: XRGB8888 source buffer 339 * @fb: DRM framebuffer 340 * @clip: Clip rectangle area to copy 341 * 342 * Drivers can use this function for RGB888 devices that don't natively 343 * support XRGB8888. 344 */ 345 void drm_fb_xrgb8888_to_rgb888(void *dst, unsigned int dst_pitch, const void *src, 346 const struct drm_framebuffer *fb, const struct drm_rect *clip) 347 { 348 size_t width = drm_rect_width(clip); 349 size_t src_len = width * sizeof(u32); 350 unsigned int y; 351 void *sbuf; 352 353 if (!dst_pitch) 354 dst_pitch = width * 3; 355 356 /* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */ 357 sbuf = kmalloc(src_len, GFP_KERNEL); 358 if (!sbuf) 359 return; 360 361 src += clip_offset(clip, fb->pitches[0], sizeof(u32)); 362 for (y = 0; y < drm_rect_height(clip); y++) { 363 memcpy(sbuf, src, src_len); 364 drm_fb_xrgb8888_to_rgb888_line(dst, sbuf, width); 365 src += fb->pitches[0]; 366 dst += dst_pitch; 367 } 368 369 kfree(sbuf); 370 } 371 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); 372 373 /** 374 * drm_fb_xrgb8888_to_rgb888_toio - Convert XRGB8888 to RGB888 clip buffer 375 * @dst: RGB565 destination buffer (iomem) 376 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 377 * @vaddr: XRGB8888 source buffer 378 * @fb: DRM framebuffer 379 * @clip: Clip rectangle area to copy 380 * 381 * Drivers can use this function for RGB888 devices that don't natively 382 * support XRGB8888. 383 */ 384 void drm_fb_xrgb8888_to_rgb888_toio(void __iomem *dst, unsigned int dst_pitch, 385 const void *vaddr, const struct drm_framebuffer *fb, 386 const struct drm_rect *clip) 387 { 388 size_t linepixels = clip->x2 - clip->x1; 389 size_t dst_len = linepixels * 3; 390 unsigned y, lines = clip->y2 - clip->y1; 391 void *dbuf; 392 393 if (!dst_pitch) 394 dst_pitch = dst_len; 395 396 dbuf = kmalloc(dst_len, GFP_KERNEL); 397 if (!dbuf) 398 return; 399 400 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 401 for (y = 0; y < lines; y++) { 402 drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels); 403 memcpy_toio(dst, dbuf, dst_len); 404 vaddr += fb->pitches[0]; 405 dst += dst_pitch; 406 } 407 408 kfree(dbuf); 409 } 410 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_toio); 411 412 static void drm_fb_xrgb8888_to_xrgb2101010_line(u32 *dbuf, const u32 *sbuf, 413 unsigned int pixels) 414 { 415 unsigned int x; 416 u32 val32; 417 418 for (x = 0; x < pixels; x++) { 419 val32 = ((sbuf[x] & 0x000000FF) << 2) | 420 ((sbuf[x] & 0x0000FF00) << 4) | 421 ((sbuf[x] & 0x00FF0000) << 6); 422 *dbuf++ = val32 | ((val32 >> 8) & 0x00300C03); 423 } 424 } 425 426 /** 427 * drm_fb_xrgb8888_to_xrgb2101010_toio - Convert XRGB8888 to XRGB2101010 clip 428 * buffer 429 * @dst: XRGB2101010 destination buffer (iomem) 430 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 431 * @vaddr: XRGB8888 source buffer 432 * @fb: DRM framebuffer 433 * @clip: Clip rectangle area to copy 434 * 435 * Drivers can use this function for XRGB2101010 devices that don't natively 436 * support XRGB8888. 437 */ 438 void drm_fb_xrgb8888_to_xrgb2101010_toio(void __iomem *dst, 439 unsigned int dst_pitch, const void *vaddr, 440 const struct drm_framebuffer *fb, 441 const struct drm_rect *clip) 442 { 443 size_t linepixels = clip->x2 - clip->x1; 444 size_t dst_len = linepixels * sizeof(u32); 445 unsigned int y, lines = clip->y2 - clip->y1; 446 void *dbuf; 447 448 if (!dst_pitch) 449 dst_pitch = dst_len; 450 451 dbuf = kmalloc(dst_len, GFP_KERNEL); 452 if (!dbuf) 453 return; 454 455 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 456 for (y = 0; y < lines; y++) { 457 drm_fb_xrgb8888_to_xrgb2101010_line(dbuf, vaddr, linepixels); 458 memcpy_toio(dst, dbuf, dst_len); 459 vaddr += fb->pitches[0]; 460 dst += dst_pitch; 461 } 462 463 kfree(dbuf); 464 } 465 EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010_toio); 466 467 /** 468 * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale 469 * @dst: 8-bit grayscale destination buffer 470 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 471 * @vaddr: XRGB8888 source buffer 472 * @fb: DRM framebuffer 473 * @clip: Clip rectangle area to copy 474 * 475 * Drm doesn't have native monochrome or grayscale support. 476 * Such drivers can announce the commonly supported XR24 format to userspace 477 * and use this function to convert to the native format. 478 * 479 * Monochrome drivers will use the most significant bit, 480 * where 1 means foreground color and 0 background color. 481 * 482 * ITU BT.601 is used for the RGB -> luma (brightness) conversion. 483 */ 484 void drm_fb_xrgb8888_to_gray8(void *dst, unsigned int dst_pitch, const void *vaddr, 485 const struct drm_framebuffer *fb, const struct drm_rect *clip) 486 { 487 unsigned int len = (clip->x2 - clip->x1) * sizeof(u32); 488 unsigned int x, y; 489 void *buf; 490 u8 *dst8; 491 u32 *src32; 492 493 if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888)) 494 return; 495 496 if (!dst_pitch) 497 dst_pitch = drm_rect_width(clip); 498 499 /* 500 * The cma memory is write-combined so reads are uncached. 501 * Speed up by fetching one line at a time. 502 */ 503 buf = kmalloc(len, GFP_KERNEL); 504 if (!buf) 505 return; 506 507 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 508 for (y = clip->y1; y < clip->y2; y++) { 509 dst8 = dst; 510 src32 = memcpy(buf, vaddr, len); 511 for (x = clip->x1; x < clip->x2; x++) { 512 u8 r = (*src32 & 0x00ff0000) >> 16; 513 u8 g = (*src32 & 0x0000ff00) >> 8; 514 u8 b = *src32 & 0x000000ff; 515 516 /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ 517 *dst8++ = (3 * r + 6 * g + b) / 10; 518 src32++; 519 } 520 521 vaddr += fb->pitches[0]; 522 dst += dst_pitch; 523 } 524 525 kfree(buf); 526 } 527 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); 528 529 /** 530 * drm_fb_blit_toio - Copy parts of a framebuffer to display memory 531 * @dst: The display memory to copy to 532 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 533 * @dst_format: FOURCC code of the display's color format 534 * @vmap: The framebuffer memory to copy from 535 * @fb: The framebuffer to copy from 536 * @clip: Clip rectangle area to copy 537 * 538 * This function copies parts of a framebuffer to display memory. If the 539 * formats of the display and the framebuffer mismatch, the blit function 540 * will attempt to convert between them. 541 * 542 * Returns: 543 * 0 on success, or 544 * -EINVAL if the color-format conversion failed, or 545 * a negative error code otherwise. 546 */ 547 int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_format, 548 const void *vmap, const struct drm_framebuffer *fb, 549 const struct drm_rect *clip) 550 { 551 uint32_t fb_format = fb->format->format; 552 553 /* treat alpha channel like filler bits */ 554 if (fb_format == DRM_FORMAT_ARGB8888) 555 fb_format = DRM_FORMAT_XRGB8888; 556 if (dst_format == DRM_FORMAT_ARGB8888) 557 dst_format = DRM_FORMAT_XRGB8888; 558 if (fb_format == DRM_FORMAT_ARGB2101010) 559 fb_format = DRM_FORMAT_XRGB2101010; 560 if (dst_format == DRM_FORMAT_ARGB2101010) 561 dst_format = DRM_FORMAT_XRGB2101010; 562 563 if (dst_format == fb_format) { 564 drm_fb_memcpy_toio(dst, dst_pitch, vmap, fb, clip); 565 return 0; 566 567 } else if (dst_format == DRM_FORMAT_RGB565) { 568 if (fb_format == DRM_FORMAT_XRGB8888) { 569 drm_fb_xrgb8888_to_rgb565_toio(dst, dst_pitch, vmap, fb, clip, false); 570 return 0; 571 } 572 } else if (dst_format == DRM_FORMAT_RGB888) { 573 if (fb_format == DRM_FORMAT_XRGB8888) { 574 drm_fb_xrgb8888_to_rgb888_toio(dst, dst_pitch, vmap, fb, clip); 575 return 0; 576 } 577 } else if (dst_format == DRM_FORMAT_XRGB2101010) { 578 if (fb_format == DRM_FORMAT_XRGB8888) { 579 drm_fb_xrgb8888_to_xrgb2101010_toio(dst, dst_pitch, vmap, fb, clip); 580 return 0; 581 } 582 } 583 584 return -EINVAL; 585 } 586 EXPORT_SYMBOL(drm_fb_blit_toio); 587