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_rgb332_line(u8 *dbuf, __le32 *sbuf, unsigned int pixels) 139 { 140 unsigned int x; 141 u32 pix; 142 143 for (x = 0; x < pixels; x++) { 144 pix = le32_to_cpu(sbuf[x]); 145 dbuf[x] = ((pix & 0x00e00000) >> 16) | 146 ((pix & 0x0000e000) >> 11) | 147 ((pix & 0x000000c0) >> 6); 148 } 149 } 150 151 /** 152 * drm_fb_xrgb8888_to_rgb332 - Convert XRGB8888 to RGB332 clip buffer 153 * @dst: RGB332 destination buffer 154 * @src: XRGB8888 source buffer 155 * @fb: DRM framebuffer 156 * @clip: Clip rectangle area to copy 157 * 158 * Drivers can use this function for RGB332 devices that don't natively support XRGB8888. 159 * 160 * This function does not apply clipping on dst, i.e. the destination is a small buffer 161 * containing the clip rect only. 162 */ 163 void drm_fb_xrgb8888_to_rgb332(void *dst, void *src, struct drm_framebuffer *fb, 164 struct drm_rect *clip) 165 { 166 size_t width = drm_rect_width(clip); 167 size_t src_len = width * sizeof(u32); 168 unsigned int y; 169 void *sbuf; 170 171 /* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */ 172 sbuf = kmalloc(src_len, GFP_KERNEL); 173 if (!sbuf) 174 return; 175 176 src += clip_offset(clip, fb->pitches[0], sizeof(u32)); 177 for (y = 0; y < drm_rect_height(clip); y++) { 178 memcpy(sbuf, src, src_len); 179 drm_fb_xrgb8888_to_rgb332_line(dst, sbuf, width); 180 src += fb->pitches[0]; 181 dst += width; 182 } 183 184 kfree(sbuf); 185 } 186 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332); 187 188 static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf, 189 unsigned int pixels, 190 bool swab) 191 { 192 unsigned int x; 193 u16 val16; 194 195 for (x = 0; x < pixels; x++) { 196 val16 = ((sbuf[x] & 0x00F80000) >> 8) | 197 ((sbuf[x] & 0x0000FC00) >> 5) | 198 ((sbuf[x] & 0x000000F8) >> 3); 199 if (swab) 200 dbuf[x] = swab16(val16); 201 else 202 dbuf[x] = val16; 203 } 204 } 205 206 /** 207 * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer 208 * @dst: RGB565 destination buffer 209 * @vaddr: XRGB8888 source buffer 210 * @fb: DRM framebuffer 211 * @clip: Clip rectangle area to copy 212 * @swab: Swap bytes 213 * 214 * Drivers can use this function for RGB565 devices that don't natively 215 * support XRGB8888. 216 * 217 * This function does not apply clipping on dst, i.e. the destination 218 * is a small buffer containing the clip rect only. 219 */ 220 void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr, 221 struct drm_framebuffer *fb, 222 struct drm_rect *clip, bool swab) 223 { 224 size_t linepixels = clip->x2 - clip->x1; 225 size_t src_len = linepixels * sizeof(u32); 226 size_t dst_len = linepixels * sizeof(u16); 227 unsigned y, lines = clip->y2 - clip->y1; 228 void *sbuf; 229 230 /* 231 * The cma memory is write-combined so reads are uncached. 232 * Speed up by fetching one line at a time. 233 */ 234 sbuf = kmalloc(src_len, GFP_KERNEL); 235 if (!sbuf) 236 return; 237 238 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 239 for (y = 0; y < lines; y++) { 240 memcpy(sbuf, vaddr, src_len); 241 drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab); 242 vaddr += fb->pitches[0]; 243 dst += dst_len; 244 } 245 246 kfree(sbuf); 247 } 248 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); 249 250 /** 251 * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer 252 * @dst: RGB565 destination buffer (iomem) 253 * @dst_pitch: destination buffer pitch 254 * @vaddr: XRGB8888 source buffer 255 * @fb: DRM framebuffer 256 * @clip: Clip rectangle area to copy 257 * @swab: Swap bytes 258 * 259 * Drivers can use this function for RGB565 devices that don't natively 260 * support XRGB8888. 261 * 262 * This function applies clipping on dst, i.e. the destination is a 263 * full (iomem) framebuffer but only the clip rect content is copied over. 264 */ 265 void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch, 266 void *vaddr, struct drm_framebuffer *fb, 267 struct drm_rect *clip, bool swab) 268 { 269 size_t linepixels = clip->x2 - clip->x1; 270 size_t dst_len = linepixels * sizeof(u16); 271 unsigned y, lines = clip->y2 - clip->y1; 272 void *dbuf; 273 274 dbuf = kmalloc(dst_len, GFP_KERNEL); 275 if (!dbuf) 276 return; 277 278 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 279 dst += clip_offset(clip, dst_pitch, sizeof(u16)); 280 for (y = 0; y < lines; y++) { 281 drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab); 282 memcpy_toio(dst, dbuf, dst_len); 283 vaddr += fb->pitches[0]; 284 dst += dst_len; 285 } 286 287 kfree(dbuf); 288 } 289 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip); 290 291 static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf, 292 unsigned int pixels) 293 { 294 unsigned int x; 295 296 for (x = 0; x < pixels; x++) { 297 *dbuf++ = (sbuf[x] & 0x000000FF) >> 0; 298 *dbuf++ = (sbuf[x] & 0x0000FF00) >> 8; 299 *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16; 300 } 301 } 302 303 /** 304 * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer 305 * @dst: RGB888 destination buffer 306 * @src: XRGB8888 source buffer 307 * @fb: DRM framebuffer 308 * @clip: Clip rectangle area to copy 309 * 310 * Drivers can use this function for RGB888 devices that don't natively 311 * support XRGB8888. 312 * 313 * This function does not apply clipping on dst, i.e. the destination 314 * is a small buffer containing the clip rect only. 315 */ 316 void drm_fb_xrgb8888_to_rgb888(void *dst, void *src, struct drm_framebuffer *fb, 317 struct drm_rect *clip) 318 { 319 size_t width = drm_rect_width(clip); 320 size_t src_len = width * sizeof(u32); 321 unsigned int y; 322 void *sbuf; 323 324 /* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */ 325 sbuf = kmalloc(src_len, GFP_KERNEL); 326 if (!sbuf) 327 return; 328 329 src += clip_offset(clip, fb->pitches[0], sizeof(u32)); 330 for (y = 0; y < drm_rect_height(clip); y++) { 331 memcpy(sbuf, src, src_len); 332 drm_fb_xrgb8888_to_rgb888_line(dst, sbuf, width); 333 src += fb->pitches[0]; 334 dst += width * 3; 335 } 336 337 kfree(sbuf); 338 } 339 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); 340 341 /** 342 * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer 343 * @dst: RGB565 destination buffer (iomem) 344 * @dst_pitch: destination buffer pitch 345 * @vaddr: XRGB8888 source buffer 346 * @fb: DRM framebuffer 347 * @clip: Clip rectangle area to copy 348 * 349 * Drivers can use this function for RGB888 devices that don't natively 350 * support XRGB8888. 351 * 352 * This function applies clipping on dst, i.e. the destination is a 353 * full (iomem) framebuffer but only the clip rect content is copied over. 354 */ 355 void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch, 356 void *vaddr, struct drm_framebuffer *fb, 357 struct drm_rect *clip) 358 { 359 size_t linepixels = clip->x2 - clip->x1; 360 size_t dst_len = linepixels * 3; 361 unsigned y, lines = clip->y2 - clip->y1; 362 void *dbuf; 363 364 dbuf = kmalloc(dst_len, GFP_KERNEL); 365 if (!dbuf) 366 return; 367 368 vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); 369 dst += clip_offset(clip, dst_pitch, sizeof(u16)); 370 for (y = 0; y < lines; y++) { 371 drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels); 372 memcpy_toio(dst, dbuf, dst_len); 373 vaddr += fb->pitches[0]; 374 dst += dst_len; 375 } 376 377 kfree(dbuf); 378 } 379 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip); 380 381 /** 382 * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale 383 * @dst: 8-bit grayscale destination buffer 384 * @vaddr: XRGB8888 source buffer 385 * @fb: DRM framebuffer 386 * @clip: Clip rectangle area to copy 387 * 388 * Drm doesn't have native monochrome or grayscale support. 389 * Such drivers can announce the commonly supported XR24 format to userspace 390 * and use this function to convert to the native format. 391 * 392 * Monochrome drivers will use the most significant bit, 393 * where 1 means foreground color and 0 background color. 394 * 395 * ITU BT.601 is used for the RGB -> luma (brightness) conversion. 396 */ 397 void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb, 398 struct drm_rect *clip) 399 { 400 unsigned int len = (clip->x2 - clip->x1) * sizeof(u32); 401 unsigned int x, y; 402 void *buf; 403 u32 *src; 404 405 if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888)) 406 return; 407 /* 408 * The cma memory is write-combined so reads are uncached. 409 * Speed up by fetching one line at a time. 410 */ 411 buf = kmalloc(len, GFP_KERNEL); 412 if (!buf) 413 return; 414 415 for (y = clip->y1; y < clip->y2; y++) { 416 src = vaddr + (y * fb->pitches[0]); 417 src += clip->x1; 418 memcpy(buf, src, len); 419 src = buf; 420 for (x = clip->x1; x < clip->x2; x++) { 421 u8 r = (*src & 0x00ff0000) >> 16; 422 u8 g = (*src & 0x0000ff00) >> 8; 423 u8 b = *src & 0x000000ff; 424 425 /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ 426 *dst++ = (3 * r + 6 * g + b) / 10; 427 src++; 428 } 429 } 430 431 kfree(buf); 432 } 433 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); 434 435 /** 436 * drm_fb_blit_rect_dstclip - Copy parts of a framebuffer to display memory 437 * @dst: The display memory to copy to 438 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 439 * @dst_format: FOURCC code of the display's color format 440 * @vmap: The framebuffer memory to copy from 441 * @fb: The framebuffer to copy from 442 * @clip: Clip rectangle area to copy 443 * 444 * This function copies parts of a framebuffer to display memory. If the 445 * formats of the display and the framebuffer mismatch, the blit function 446 * will attempt to convert between them. 447 * 448 * Use drm_fb_blit_dstclip() to copy the full framebuffer. 449 * 450 * Returns: 451 * 0 on success, or 452 * -EINVAL if the color-format conversion failed, or 453 * a negative error code otherwise. 454 */ 455 int drm_fb_blit_rect_dstclip(void __iomem *dst, unsigned int dst_pitch, 456 uint32_t dst_format, void *vmap, 457 struct drm_framebuffer *fb, 458 struct drm_rect *clip) 459 { 460 uint32_t fb_format = fb->format->format; 461 462 /* treat alpha channel like filler bits */ 463 if (fb_format == DRM_FORMAT_ARGB8888) 464 fb_format = DRM_FORMAT_XRGB8888; 465 if (dst_format == DRM_FORMAT_ARGB8888) 466 dst_format = DRM_FORMAT_XRGB8888; 467 468 if (dst_format == fb_format) { 469 drm_fb_memcpy_dstclip(dst, dst_pitch, vmap, fb, clip); 470 return 0; 471 472 } else if (dst_format == DRM_FORMAT_RGB565) { 473 if (fb_format == DRM_FORMAT_XRGB8888) { 474 drm_fb_xrgb8888_to_rgb565_dstclip(dst, dst_pitch, 475 vmap, fb, clip, 476 false); 477 return 0; 478 } 479 } else if (dst_format == DRM_FORMAT_RGB888) { 480 if (fb_format == DRM_FORMAT_XRGB8888) { 481 drm_fb_xrgb8888_to_rgb888_dstclip(dst, dst_pitch, 482 vmap, fb, clip); 483 return 0; 484 } 485 } 486 487 return -EINVAL; 488 } 489 EXPORT_SYMBOL(drm_fb_blit_rect_dstclip); 490 491 /** 492 * drm_fb_blit_dstclip - Copy framebuffer to display memory 493 * @dst: The display memory to copy to 494 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 495 * @dst_format: FOURCC code of the display's color format 496 * @vmap: The framebuffer memory to copy from 497 * @fb: The framebuffer to copy from 498 * 499 * This function copies a full framebuffer to display memory. If the formats 500 * of the display and the framebuffer mismatch, the copy function will 501 * attempt to convert between them. 502 * 503 * See drm_fb_blit_rect_dstclip() for more information. 504 * 505 * Returns: 506 * 0 on success, or a negative error code otherwise. 507 */ 508 int drm_fb_blit_dstclip(void __iomem *dst, unsigned int dst_pitch, 509 uint32_t dst_format, void *vmap, 510 struct drm_framebuffer *fb) 511 { 512 struct drm_rect fullscreen = { 513 .x1 = 0, 514 .x2 = fb->width, 515 .y1 = 0, 516 .y2 = fb->height, 517 }; 518 return drm_fb_blit_rect_dstclip(dst, dst_pitch, dst_format, vmap, fb, 519 &fullscreen); 520 } 521 EXPORT_SYMBOL(drm_fb_blit_dstclip); 522