drm_format_helper.c (21163B)
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_device.h> 16#include <drm/drm_format_helper.h> 17#include <drm/drm_framebuffer.h> 18#include <drm/drm_fourcc.h> 19#include <drm/drm_print.h> 20#include <drm/drm_rect.h> 21 22static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp) 23{ 24 return clip->y1 * pitch + clip->x1 * cpp; 25} 26 27/** 28 * drm_fb_clip_offset - Returns the clipping rectangles byte-offset in a framebuffer 29 * @pitch: Framebuffer line pitch in byte 30 * @format: Framebuffer format 31 * @clip: Clip rectangle 32 * 33 * Returns: 34 * The byte offset of the clip rectangle's top-left corner within the framebuffer. 35 */ 36unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format, 37 const struct drm_rect *clip) 38{ 39 return clip_offset(clip, pitch, format->cpp[0]); 40} 41EXPORT_SYMBOL(drm_fb_clip_offset); 42 43/* TODO: Make this functon work with multi-plane formats. */ 44static int drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_pixsize, 45 const void *vaddr, const struct drm_framebuffer *fb, 46 const struct drm_rect *clip, bool vaddr_cached_hint, 47 void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels)) 48{ 49 unsigned long linepixels = drm_rect_width(clip); 50 unsigned long lines = drm_rect_height(clip); 51 size_t sbuf_len = linepixels * fb->format->cpp[0]; 52 void *stmp = NULL; 53 unsigned long i; 54 const void *sbuf; 55 56 /* 57 * Some source buffers, such as CMA memory, use write-combine 58 * caching, so reads are uncached. Speed up access by fetching 59 * one line at a time. 60 */ 61 if (!vaddr_cached_hint) { 62 stmp = kmalloc(sbuf_len, GFP_KERNEL); 63 if (!stmp) 64 return -ENOMEM; 65 } 66 67 if (!dst_pitch) 68 dst_pitch = drm_rect_width(clip) * dst_pixsize; 69 vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]); 70 71 for (i = 0; i < lines; ++i) { 72 if (stmp) 73 sbuf = memcpy(stmp, vaddr, sbuf_len); 74 else 75 sbuf = vaddr; 76 xfrm_line(dst, sbuf, linepixels); 77 vaddr += fb->pitches[0]; 78 dst += dst_pitch; 79 } 80 81 kfree(stmp); 82 83 return 0; 84} 85 86/* TODO: Make this functon work with multi-plane formats. */ 87static int drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsigned long dst_pixsize, 88 const void *vaddr, const struct drm_framebuffer *fb, 89 const struct drm_rect *clip, bool vaddr_cached_hint, 90 void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels)) 91{ 92 unsigned long linepixels = drm_rect_width(clip); 93 unsigned long lines = drm_rect_height(clip); 94 size_t dbuf_len = linepixels * dst_pixsize; 95 size_t stmp_off = round_up(dbuf_len, ARCH_KMALLOC_MINALIGN); /* for sbuf alignment */ 96 size_t sbuf_len = linepixels * fb->format->cpp[0]; 97 void *stmp = NULL; 98 unsigned long i; 99 const void *sbuf; 100 void *dbuf; 101 102 if (vaddr_cached_hint) { 103 dbuf = kmalloc(dbuf_len, GFP_KERNEL); 104 } else { 105 dbuf = kmalloc(stmp_off + sbuf_len, GFP_KERNEL); 106 stmp = dbuf + stmp_off; 107 } 108 if (!dbuf) 109 return -ENOMEM; 110 111 if (!dst_pitch) 112 dst_pitch = linepixels * dst_pixsize; 113 vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]); 114 115 for (i = 0; i < lines; ++i) { 116 if (stmp) 117 sbuf = memcpy(stmp, vaddr, sbuf_len); 118 else 119 sbuf = vaddr; 120 xfrm_line(dbuf, sbuf, linepixels); 121 memcpy_toio(dst, dbuf, dbuf_len); 122 vaddr += fb->pitches[0]; 123 dst += dst_pitch; 124 } 125 126 kfree(dbuf); 127 128 return 0; 129} 130 131/** 132 * drm_fb_memcpy - Copy clip buffer 133 * @dst: Destination buffer 134 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 135 * @vaddr: Source buffer 136 * @fb: DRM framebuffer 137 * @clip: Clip rectangle area to copy 138 * 139 * This function does not apply clipping on dst, i.e. the destination 140 * is at the top-left corner. 141 */ 142void drm_fb_memcpy(void *dst, unsigned int dst_pitch, const void *vaddr, 143 const struct drm_framebuffer *fb, const struct drm_rect *clip) 144{ 145 unsigned int cpp = fb->format->cpp[0]; 146 size_t len = (clip->x2 - clip->x1) * cpp; 147 unsigned int y, lines = clip->y2 - clip->y1; 148 149 if (!dst_pitch) 150 dst_pitch = len; 151 152 vaddr += clip_offset(clip, fb->pitches[0], cpp); 153 for (y = 0; y < lines; y++) { 154 memcpy(dst, vaddr, len); 155 vaddr += fb->pitches[0]; 156 dst += dst_pitch; 157 } 158} 159EXPORT_SYMBOL(drm_fb_memcpy); 160 161/** 162 * drm_fb_memcpy_toio - Copy clip buffer 163 * @dst: Destination buffer (iomem) 164 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 165 * @vaddr: Source buffer 166 * @fb: DRM framebuffer 167 * @clip: Clip rectangle area to copy 168 * 169 * This function does not apply clipping on dst, i.e. the destination 170 * is at the top-left corner. 171 */ 172void drm_fb_memcpy_toio(void __iomem *dst, unsigned int dst_pitch, const void *vaddr, 173 const struct drm_framebuffer *fb, const struct drm_rect *clip) 174{ 175 unsigned int cpp = fb->format->cpp[0]; 176 size_t len = (clip->x2 - clip->x1) * cpp; 177 unsigned int y, lines = clip->y2 - clip->y1; 178 179 if (!dst_pitch) 180 dst_pitch = len; 181 182 vaddr += clip_offset(clip, fb->pitches[0], cpp); 183 for (y = 0; y < lines; y++) { 184 memcpy_toio(dst, vaddr, len); 185 vaddr += fb->pitches[0]; 186 dst += dst_pitch; 187 } 188} 189EXPORT_SYMBOL(drm_fb_memcpy_toio); 190 191static void drm_fb_swab16_line(void *dbuf, const void *sbuf, unsigned int pixels) 192{ 193 u16 *dbuf16 = dbuf; 194 const u16 *sbuf16 = sbuf; 195 const u16 *send16 = sbuf16 + pixels; 196 197 while (sbuf16 < send16) 198 *dbuf16++ = swab16(*sbuf16++); 199} 200 201static void drm_fb_swab32_line(void *dbuf, const void *sbuf, unsigned int pixels) 202{ 203 u32 *dbuf32 = dbuf; 204 const u32 *sbuf32 = sbuf; 205 const u32 *send32 = sbuf32 + pixels; 206 207 while (sbuf32 < send32) 208 *dbuf32++ = swab32(*sbuf32++); 209} 210 211/** 212 * drm_fb_swab - Swap bytes into clip buffer 213 * @dst: Destination buffer 214 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 215 * @src: Source buffer 216 * @fb: DRM framebuffer 217 * @clip: Clip rectangle area to copy 218 * @cached: Source buffer is mapped cached (eg. not write-combined) 219 * 220 * If @cached is false a temporary buffer is used to cache one pixel line at a 221 * time to speed up slow uncached reads. 222 * 223 * This function does not apply clipping on dst, i.e. the destination 224 * is at the top-left corner. 225 */ 226void drm_fb_swab(void *dst, unsigned int dst_pitch, const void *src, 227 const struct drm_framebuffer *fb, const struct drm_rect *clip, 228 bool cached) 229{ 230 u8 cpp = fb->format->cpp[0]; 231 232 switch (cpp) { 233 case 4: 234 drm_fb_xfrm(dst, dst_pitch, cpp, src, fb, clip, cached, drm_fb_swab32_line); 235 break; 236 case 2: 237 drm_fb_xfrm(dst, dst_pitch, cpp, src, fb, clip, cached, drm_fb_swab16_line); 238 break; 239 default: 240 drm_warn_once(fb->dev, "Format %p4cc has unsupported pixel size.\n", 241 &fb->format->format); 242 break; 243 } 244} 245EXPORT_SYMBOL(drm_fb_swab); 246 247static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigned int pixels) 248{ 249 u8 *dbuf8 = dbuf; 250 const __le32 *sbuf32 = sbuf; 251 unsigned int x; 252 u32 pix; 253 254 for (x = 0; x < pixels; x++) { 255 pix = le32_to_cpu(sbuf32[x]); 256 dbuf8[x] = ((pix & 0x00e00000) >> 16) | 257 ((pix & 0x0000e000) >> 11) | 258 ((pix & 0x000000c0) >> 6); 259 } 260} 261 262/** 263 * drm_fb_xrgb8888_to_rgb332 - Convert XRGB8888 to RGB332 clip buffer 264 * @dst: RGB332 destination buffer 265 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 266 * @src: XRGB8888 source buffer 267 * @fb: DRM framebuffer 268 * @clip: Clip rectangle area to copy 269 * 270 * Drivers can use this function for RGB332 devices that don't natively support XRGB8888. 271 */ 272void drm_fb_xrgb8888_to_rgb332(void *dst, unsigned int dst_pitch, const void *src, 273 const struct drm_framebuffer *fb, const struct drm_rect *clip) 274{ 275 drm_fb_xfrm(dst, dst_pitch, 1, src, fb, clip, false, drm_fb_xrgb8888_to_rgb332_line); 276} 277EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332); 278 279static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigned int pixels) 280{ 281 u16 *dbuf16 = dbuf; 282 const u32 *sbuf32 = sbuf; 283 unsigned int x; 284 u16 val16; 285 286 for (x = 0; x < pixels; x++) { 287 val16 = ((sbuf32[x] & 0x00F80000) >> 8) | 288 ((sbuf32[x] & 0x0000FC00) >> 5) | 289 ((sbuf32[x] & 0x000000F8) >> 3); 290 dbuf16[x] = val16; 291 } 292} 293 294static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf, 295 unsigned int pixels) 296{ 297 u16 *dbuf16 = dbuf; 298 const u32 *sbuf32 = sbuf; 299 unsigned int x; 300 u16 val16; 301 302 for (x = 0; x < pixels; x++) { 303 val16 = ((sbuf32[x] & 0x00F80000) >> 8) | 304 ((sbuf32[x] & 0x0000FC00) >> 5) | 305 ((sbuf32[x] & 0x000000F8) >> 3); 306 dbuf16[x] = swab16(val16); 307 } 308} 309 310/** 311 * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer 312 * @dst: RGB565 destination buffer 313 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 314 * @vaddr: XRGB8888 source buffer 315 * @fb: DRM framebuffer 316 * @clip: Clip rectangle area to copy 317 * @swab: Swap bytes 318 * 319 * Drivers can use this function for RGB565 devices that don't natively 320 * support XRGB8888. 321 */ 322void drm_fb_xrgb8888_to_rgb565(void *dst, unsigned int dst_pitch, const void *vaddr, 323 const struct drm_framebuffer *fb, const struct drm_rect *clip, 324 bool swab) 325{ 326 if (swab) 327 drm_fb_xfrm(dst, dst_pitch, 2, vaddr, fb, clip, false, 328 drm_fb_xrgb8888_to_rgb565_swab_line); 329 else 330 drm_fb_xfrm(dst, dst_pitch, 2, vaddr, fb, clip, false, 331 drm_fb_xrgb8888_to_rgb565_line); 332} 333EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); 334 335/** 336 * drm_fb_xrgb8888_to_rgb565_toio - Convert XRGB8888 to RGB565 clip buffer 337 * @dst: RGB565 destination buffer (iomem) 338 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 339 * @vaddr: XRGB8888 source buffer 340 * @fb: DRM framebuffer 341 * @clip: Clip rectangle area to copy 342 * @swab: Swap bytes 343 * 344 * Drivers can use this function for RGB565 devices that don't natively 345 * support XRGB8888. 346 */ 347void drm_fb_xrgb8888_to_rgb565_toio(void __iomem *dst, unsigned int dst_pitch, 348 const void *vaddr, const struct drm_framebuffer *fb, 349 const struct drm_rect *clip, bool swab) 350{ 351 if (swab) 352 drm_fb_xfrm_toio(dst, dst_pitch, 2, vaddr, fb, clip, false, 353 drm_fb_xrgb8888_to_rgb565_swab_line); 354 else 355 drm_fb_xfrm_toio(dst, dst_pitch, 2, vaddr, fb, clip, false, 356 drm_fb_xrgb8888_to_rgb565_line); 357} 358EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_toio); 359 360static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigned int pixels) 361{ 362 u8 *dbuf8 = dbuf; 363 const u32 *sbuf32 = sbuf; 364 unsigned int x; 365 366 for (x = 0; x < pixels; x++) { 367 *dbuf8++ = (sbuf32[x] & 0x000000FF) >> 0; 368 *dbuf8++ = (sbuf32[x] & 0x0000FF00) >> 8; 369 *dbuf8++ = (sbuf32[x] & 0x00FF0000) >> 16; 370 } 371} 372 373/** 374 * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer 375 * @dst: RGB888 destination buffer 376 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 377 * @src: 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 */ 384void drm_fb_xrgb8888_to_rgb888(void *dst, unsigned int dst_pitch, const void *src, 385 const struct drm_framebuffer *fb, const struct drm_rect *clip) 386{ 387 drm_fb_xfrm(dst, dst_pitch, 3, src, fb, clip, false, drm_fb_xrgb8888_to_rgb888_line); 388} 389EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); 390 391/** 392 * drm_fb_xrgb8888_to_rgb888_toio - Convert XRGB8888 to RGB888 clip buffer 393 * @dst: RGB565 destination buffer (iomem) 394 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 395 * @vaddr: XRGB8888 source buffer 396 * @fb: DRM framebuffer 397 * @clip: Clip rectangle area to copy 398 * 399 * Drivers can use this function for RGB888 devices that don't natively 400 * support XRGB8888. 401 */ 402void drm_fb_xrgb8888_to_rgb888_toio(void __iomem *dst, unsigned int dst_pitch, 403 const void *vaddr, const struct drm_framebuffer *fb, 404 const struct drm_rect *clip) 405{ 406 drm_fb_xfrm_toio(dst, dst_pitch, 3, vaddr, fb, clip, false, 407 drm_fb_xrgb8888_to_rgb888_line); 408} 409EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_toio); 410 411static void drm_fb_rgb565_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels) 412{ 413 u32 *dbuf32 = dbuf; 414 const u16 *sbuf16 = sbuf; 415 unsigned int x; 416 417 for (x = 0; x < pixels; x++, ++sbuf16, ++dbuf32) { 418 u32 val32 = ((*sbuf16 & 0xf800) << 8) | 419 ((*sbuf16 & 0x07e0) << 5) | 420 ((*sbuf16 & 0x001f) << 3); 421 *dbuf32 = 0xff000000 | val32 | 422 ((val32 >> 3) & 0x00070007) | 423 ((val32 >> 2) & 0x00000300); 424 } 425} 426 427static void drm_fb_rgb565_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_pitch, 428 const void *vaddr, const struct drm_framebuffer *fb, 429 const struct drm_rect *clip) 430{ 431 drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false, 432 drm_fb_rgb565_to_xrgb8888_line); 433} 434 435static void drm_fb_rgb888_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels) 436{ 437 u32 *dbuf32 = dbuf; 438 const u8 *sbuf8 = sbuf; 439 unsigned int x; 440 441 for (x = 0; x < pixels; x++) { 442 u8 r = *sbuf8++; 443 u8 g = *sbuf8++; 444 u8 b = *sbuf8++; 445 *dbuf32++ = 0xff000000 | (r << 16) | (g << 8) | b; 446 } 447} 448 449static void drm_fb_rgb888_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_pitch, 450 const void *vaddr, const struct drm_framebuffer *fb, 451 const struct drm_rect *clip) 452{ 453 drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false, 454 drm_fb_rgb888_to_xrgb8888_line); 455} 456 457static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels) 458{ 459 u32 *dbuf32 = dbuf; 460 const u32 *sbuf32 = sbuf; 461 unsigned int x; 462 u32 val32; 463 464 for (x = 0; x < pixels; x++) { 465 val32 = ((sbuf32[x] & 0x000000FF) << 2) | 466 ((sbuf32[x] & 0x0000FF00) << 4) | 467 ((sbuf32[x] & 0x00FF0000) << 6); 468 *dbuf32++ = val32 | ((val32 >> 8) & 0x00300C03); 469 } 470} 471 472/** 473 * drm_fb_xrgb8888_to_xrgb2101010_toio - Convert XRGB8888 to XRGB2101010 clip 474 * buffer 475 * @dst: XRGB2101010 destination buffer (iomem) 476 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 477 * @vaddr: XRGB8888 source buffer 478 * @fb: DRM framebuffer 479 * @clip: Clip rectangle area to copy 480 * 481 * Drivers can use this function for XRGB2101010 devices that don't natively 482 * support XRGB8888. 483 */ 484void drm_fb_xrgb8888_to_xrgb2101010_toio(void __iomem *dst, 485 unsigned int dst_pitch, const void *vaddr, 486 const struct drm_framebuffer *fb, 487 const struct drm_rect *clip) 488{ 489 drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false, 490 drm_fb_xrgb8888_to_xrgb2101010_line); 491} 492EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010_toio); 493 494static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned int pixels) 495{ 496 u8 *dbuf8 = dbuf; 497 const u32 *sbuf32 = sbuf; 498 unsigned int x; 499 500 for (x = 0; x < pixels; x++) { 501 u8 r = (*sbuf32 & 0x00ff0000) >> 16; 502 u8 g = (*sbuf32 & 0x0000ff00) >> 8; 503 u8 b = *sbuf32 & 0x000000ff; 504 505 /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ 506 *dbuf8++ = (3 * r + 6 * g + b) / 10; 507 sbuf32++; 508 } 509} 510 511/** 512 * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale 513 * @dst: 8-bit grayscale destination buffer 514 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 515 * @vaddr: XRGB8888 source buffer 516 * @fb: DRM framebuffer 517 * @clip: Clip rectangle area to copy 518 * 519 * Drm doesn't have native monochrome or grayscale support. 520 * Such drivers can announce the commonly supported XR24 format to userspace 521 * and use this function to convert to the native format. 522 * 523 * Monochrome drivers will use the most significant bit, 524 * where 1 means foreground color and 0 background color. 525 * 526 * ITU BT.601 is used for the RGB -> luma (brightness) conversion. 527 */ 528void drm_fb_xrgb8888_to_gray8(void *dst, unsigned int dst_pitch, const void *vaddr, 529 const struct drm_framebuffer *fb, const struct drm_rect *clip) 530{ 531 drm_fb_xfrm(dst, dst_pitch, 1, vaddr, fb, clip, false, drm_fb_xrgb8888_to_gray8_line); 532} 533EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); 534 535/** 536 * drm_fb_blit_toio - Copy parts of a framebuffer to display memory 537 * @dst: The display memory to copy to 538 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 539 * @dst_format: FOURCC code of the display's color format 540 * @vmap: The framebuffer memory to copy from 541 * @fb: The framebuffer to copy from 542 * @clip: Clip rectangle area to copy 543 * 544 * This function copies parts of a framebuffer to display memory. If the 545 * formats of the display and the framebuffer mismatch, the blit function 546 * will attempt to convert between them. 547 * 548 * Returns: 549 * 0 on success, or 550 * -EINVAL if the color-format conversion failed, or 551 * a negative error code otherwise. 552 */ 553int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_format, 554 const void *vmap, const struct drm_framebuffer *fb, 555 const struct drm_rect *clip) 556{ 557 uint32_t fb_format = fb->format->format; 558 559 /* treat alpha channel like filler bits */ 560 if (fb_format == DRM_FORMAT_ARGB8888) 561 fb_format = DRM_FORMAT_XRGB8888; 562 if (dst_format == DRM_FORMAT_ARGB8888) 563 dst_format = DRM_FORMAT_XRGB8888; 564 if (fb_format == DRM_FORMAT_ARGB2101010) 565 fb_format = DRM_FORMAT_XRGB2101010; 566 if (dst_format == DRM_FORMAT_ARGB2101010) 567 dst_format = DRM_FORMAT_XRGB2101010; 568 569 if (dst_format == fb_format) { 570 drm_fb_memcpy_toio(dst, dst_pitch, vmap, fb, clip); 571 return 0; 572 573 } else if (dst_format == DRM_FORMAT_RGB565) { 574 if (fb_format == DRM_FORMAT_XRGB8888) { 575 drm_fb_xrgb8888_to_rgb565_toio(dst, dst_pitch, vmap, fb, clip, false); 576 return 0; 577 } 578 } else if (dst_format == DRM_FORMAT_RGB888) { 579 if (fb_format == DRM_FORMAT_XRGB8888) { 580 drm_fb_xrgb8888_to_rgb888_toio(dst, dst_pitch, vmap, fb, clip); 581 return 0; 582 } 583 } else if (dst_format == DRM_FORMAT_XRGB8888) { 584 if (fb_format == DRM_FORMAT_RGB888) { 585 drm_fb_rgb888_to_xrgb8888_toio(dst, dst_pitch, vmap, fb, clip); 586 return 0; 587 } else if (fb_format == DRM_FORMAT_RGB565) { 588 drm_fb_rgb565_to_xrgb8888_toio(dst, dst_pitch, vmap, fb, clip); 589 return 0; 590 } 591 } else if (dst_format == DRM_FORMAT_XRGB2101010) { 592 if (fb_format == DRM_FORMAT_XRGB8888) { 593 drm_fb_xrgb8888_to_xrgb2101010_toio(dst, dst_pitch, vmap, fb, clip); 594 return 0; 595 } 596 } 597 598 drm_warn_once(fb->dev, "No conversion helper from %p4cc to %p4cc found.\n", 599 &fb_format, &dst_format); 600 601 return -EINVAL; 602} 603EXPORT_SYMBOL(drm_fb_blit_toio); 604 605 606static void drm_fb_gray8_to_mono_line(void *dbuf, const void *sbuf, unsigned int pixels) 607{ 608 u8 *dbuf8 = dbuf; 609 const u8 *sbuf8 = sbuf; 610 611 while (pixels) { 612 unsigned int i, bits = min(pixels, 8U); 613 u8 byte = 0; 614 615 for (i = 0; i < bits; i++, pixels--) { 616 if (*sbuf8++ >= 128) 617 byte |= BIT(i); 618 } 619 *dbuf8++ = byte; 620 } 621} 622 623/** 624 * drm_fb_xrgb8888_to_mono - Convert XRGB8888 to monochrome 625 * @dst: monochrome destination buffer (0=black, 1=white) 626 * @dst_pitch: Number of bytes between two consecutive scanlines within dst 627 * @vaddr: XRGB8888 source buffer 628 * @fb: DRM framebuffer 629 * @clip: Clip rectangle area to copy 630 * 631 * DRM doesn't have native monochrome support. 632 * Such drivers can announce the commonly supported XR24 format to userspace 633 * and use this function to convert to the native format. 634 * 635 * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and 636 * then the result is converted from grayscale to monochrome. 637 * 638 * The first pixel (upper left corner of the clip rectangle) will be converted 639 * and copied to the first bit (LSB) in the first byte of the monochrome 640 * destination buffer. 641 * If the caller requires that the first pixel in a byte must be located at an 642 * x-coordinate that is a multiple of 8, then the caller must take care itself 643 * of supplying a suitable clip rectangle. 644 */ 645void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *vaddr, 646 const struct drm_framebuffer *fb, const struct drm_rect *clip) 647{ 648 unsigned int linepixels = drm_rect_width(clip); 649 unsigned int lines = drm_rect_height(clip); 650 unsigned int cpp = fb->format->cpp[0]; 651 unsigned int len_src32 = linepixels * cpp; 652 struct drm_device *dev = fb->dev; 653 unsigned int y; 654 u8 *mono = dst, *gray8; 655 u32 *src32; 656 657 if (drm_WARN_ON(dev, fb->format->format != DRM_FORMAT_XRGB8888)) 658 return; 659 660 /* 661 * The mono destination buffer contains 1 bit per pixel 662 */ 663 if (!dst_pitch) 664 dst_pitch = DIV_ROUND_UP(linepixels, 8); 665 666 /* 667 * The cma memory is write-combined so reads are uncached. 668 * Speed up by fetching one line at a time. 669 * 670 * Also, format conversion from XR24 to monochrome are done 671 * line-by-line but are converted to 8-bit grayscale as an 672 * intermediate step. 673 * 674 * Allocate a buffer to be used for both copying from the cma 675 * memory and to store the intermediate grayscale line pixels. 676 */ 677 src32 = kmalloc(len_src32 + linepixels, GFP_KERNEL); 678 if (!src32) 679 return; 680 681 gray8 = (u8 *)src32 + len_src32; 682 683 vaddr += clip_offset(clip, fb->pitches[0], cpp); 684 for (y = 0; y < lines; y++) { 685 src32 = memcpy(src32, vaddr, len_src32); 686 drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels); 687 drm_fb_gray8_to_mono_line(mono, gray8, linepixels); 688 vaddr += fb->pitches[0]; 689 mono += dst_pitch; 690 } 691 692 kfree(src32); 693} 694EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono);