0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/module.h>
0012 #include <linux/slab.h>
0013 #include <linux/io.h>
0014
0015 #include <drm/drm_device.h>
0016 #include <drm/drm_format_helper.h>
0017 #include <drm/drm_framebuffer.h>
0018 #include <drm/drm_fourcc.h>
0019 #include <drm/drm_print.h>
0020 #include <drm/drm_rect.h>
0021
0022 static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp)
0023 {
0024 return clip->y1 * pitch + clip->x1 * cpp;
0025 }
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036 unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format,
0037 const struct drm_rect *clip)
0038 {
0039 return clip_offset(clip, pitch, format->cpp[0]);
0040 }
0041 EXPORT_SYMBOL(drm_fb_clip_offset);
0042
0043
0044 static int drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_pixsize,
0045 const void *vaddr, const struct drm_framebuffer *fb,
0046 const struct drm_rect *clip, bool vaddr_cached_hint,
0047 void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels))
0048 {
0049 unsigned long linepixels = drm_rect_width(clip);
0050 unsigned long lines = drm_rect_height(clip);
0051 size_t sbuf_len = linepixels * fb->format->cpp[0];
0052 void *stmp = NULL;
0053 unsigned long i;
0054 const void *sbuf;
0055
0056
0057
0058
0059
0060
0061 if (!vaddr_cached_hint) {
0062 stmp = kmalloc(sbuf_len, GFP_KERNEL);
0063 if (!stmp)
0064 return -ENOMEM;
0065 }
0066
0067 if (!dst_pitch)
0068 dst_pitch = drm_rect_width(clip) * dst_pixsize;
0069 vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]);
0070
0071 for (i = 0; i < lines; ++i) {
0072 if (stmp)
0073 sbuf = memcpy(stmp, vaddr, sbuf_len);
0074 else
0075 sbuf = vaddr;
0076 xfrm_line(dst, sbuf, linepixels);
0077 vaddr += fb->pitches[0];
0078 dst += dst_pitch;
0079 }
0080
0081 kfree(stmp);
0082
0083 return 0;
0084 }
0085
0086
0087 static int drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsigned long dst_pixsize,
0088 const void *vaddr, const struct drm_framebuffer *fb,
0089 const struct drm_rect *clip, bool vaddr_cached_hint,
0090 void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels))
0091 {
0092 unsigned long linepixels = drm_rect_width(clip);
0093 unsigned long lines = drm_rect_height(clip);
0094 size_t dbuf_len = linepixels * dst_pixsize;
0095 size_t stmp_off = round_up(dbuf_len, ARCH_KMALLOC_MINALIGN);
0096 size_t sbuf_len = linepixels * fb->format->cpp[0];
0097 void *stmp = NULL;
0098 unsigned long i;
0099 const void *sbuf;
0100 void *dbuf;
0101
0102 if (vaddr_cached_hint) {
0103 dbuf = kmalloc(dbuf_len, GFP_KERNEL);
0104 } else {
0105 dbuf = kmalloc(stmp_off + sbuf_len, GFP_KERNEL);
0106 stmp = dbuf + stmp_off;
0107 }
0108 if (!dbuf)
0109 return -ENOMEM;
0110
0111 if (!dst_pitch)
0112 dst_pitch = linepixels * dst_pixsize;
0113 vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]);
0114
0115 for (i = 0; i < lines; ++i) {
0116 if (stmp)
0117 sbuf = memcpy(stmp, vaddr, sbuf_len);
0118 else
0119 sbuf = vaddr;
0120 xfrm_line(dbuf, sbuf, linepixels);
0121 memcpy_toio(dst, dbuf, dbuf_len);
0122 vaddr += fb->pitches[0];
0123 dst += dst_pitch;
0124 }
0125
0126 kfree(dbuf);
0127
0128 return 0;
0129 }
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142 void drm_fb_memcpy(void *dst, unsigned int dst_pitch, const void *vaddr,
0143 const struct drm_framebuffer *fb, const struct drm_rect *clip)
0144 {
0145 unsigned int cpp = fb->format->cpp[0];
0146 size_t len = (clip->x2 - clip->x1) * cpp;
0147 unsigned int y, lines = clip->y2 - clip->y1;
0148
0149 if (!dst_pitch)
0150 dst_pitch = len;
0151
0152 vaddr += clip_offset(clip, fb->pitches[0], cpp);
0153 for (y = 0; y < lines; y++) {
0154 memcpy(dst, vaddr, len);
0155 vaddr += fb->pitches[0];
0156 dst += dst_pitch;
0157 }
0158 }
0159 EXPORT_SYMBOL(drm_fb_memcpy);
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172 void drm_fb_memcpy_toio(void __iomem *dst, unsigned int dst_pitch, const void *vaddr,
0173 const struct drm_framebuffer *fb, const struct drm_rect *clip)
0174 {
0175 unsigned int cpp = fb->format->cpp[0];
0176 size_t len = (clip->x2 - clip->x1) * cpp;
0177 unsigned int y, lines = clip->y2 - clip->y1;
0178
0179 if (!dst_pitch)
0180 dst_pitch = len;
0181
0182 vaddr += clip_offset(clip, fb->pitches[0], cpp);
0183 for (y = 0; y < lines; y++) {
0184 memcpy_toio(dst, vaddr, len);
0185 vaddr += fb->pitches[0];
0186 dst += dst_pitch;
0187 }
0188 }
0189 EXPORT_SYMBOL(drm_fb_memcpy_toio);
0190
0191 static void drm_fb_swab16_line(void *dbuf, const void *sbuf, unsigned int pixels)
0192 {
0193 u16 *dbuf16 = dbuf;
0194 const u16 *sbuf16 = sbuf;
0195 const u16 *send16 = sbuf16 + pixels;
0196
0197 while (sbuf16 < send16)
0198 *dbuf16++ = swab16(*sbuf16++);
0199 }
0200
0201 static void drm_fb_swab32_line(void *dbuf, const void *sbuf, unsigned int pixels)
0202 {
0203 u32 *dbuf32 = dbuf;
0204 const u32 *sbuf32 = sbuf;
0205 const u32 *send32 = sbuf32 + pixels;
0206
0207 while (sbuf32 < send32)
0208 *dbuf32++ = swab32(*sbuf32++);
0209 }
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 void drm_fb_swab(void *dst, unsigned int dst_pitch, const void *src,
0227 const struct drm_framebuffer *fb, const struct drm_rect *clip,
0228 bool cached)
0229 {
0230 u8 cpp = fb->format->cpp[0];
0231
0232 switch (cpp) {
0233 case 4:
0234 drm_fb_xfrm(dst, dst_pitch, cpp, src, fb, clip, cached, drm_fb_swab32_line);
0235 break;
0236 case 2:
0237 drm_fb_xfrm(dst, dst_pitch, cpp, src, fb, clip, cached, drm_fb_swab16_line);
0238 break;
0239 default:
0240 drm_warn_once(fb->dev, "Format %p4cc has unsupported pixel size.\n",
0241 &fb->format->format);
0242 break;
0243 }
0244 }
0245 EXPORT_SYMBOL(drm_fb_swab);
0246
0247 static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigned int pixels)
0248 {
0249 u8 *dbuf8 = dbuf;
0250 const __le32 *sbuf32 = sbuf;
0251 unsigned int x;
0252 u32 pix;
0253
0254 for (x = 0; x < pixels; x++) {
0255 pix = le32_to_cpu(sbuf32[x]);
0256 dbuf8[x] = ((pix & 0x00e00000) >> 16) |
0257 ((pix & 0x0000e000) >> 11) |
0258 ((pix & 0x000000c0) >> 6);
0259 }
0260 }
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272 void drm_fb_xrgb8888_to_rgb332(void *dst, unsigned int dst_pitch, const void *src,
0273 const struct drm_framebuffer *fb, const struct drm_rect *clip)
0274 {
0275 drm_fb_xfrm(dst, dst_pitch, 1, src, fb, clip, false, drm_fb_xrgb8888_to_rgb332_line);
0276 }
0277 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332);
0278
0279 static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigned int pixels)
0280 {
0281 u16 *dbuf16 = dbuf;
0282 const u32 *sbuf32 = sbuf;
0283 unsigned int x;
0284 u16 val16;
0285
0286 for (x = 0; x < pixels; x++) {
0287 val16 = ((sbuf32[x] & 0x00F80000) >> 8) |
0288 ((sbuf32[x] & 0x0000FC00) >> 5) |
0289 ((sbuf32[x] & 0x000000F8) >> 3);
0290 dbuf16[x] = val16;
0291 }
0292 }
0293
0294 static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf,
0295 unsigned int pixels)
0296 {
0297 u16 *dbuf16 = dbuf;
0298 const u32 *sbuf32 = sbuf;
0299 unsigned int x;
0300 u16 val16;
0301
0302 for (x = 0; x < pixels; x++) {
0303 val16 = ((sbuf32[x] & 0x00F80000) >> 8) |
0304 ((sbuf32[x] & 0x0000FC00) >> 5) |
0305 ((sbuf32[x] & 0x000000F8) >> 3);
0306 dbuf16[x] = swab16(val16);
0307 }
0308 }
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322 void drm_fb_xrgb8888_to_rgb565(void *dst, unsigned int dst_pitch, const void *vaddr,
0323 const struct drm_framebuffer *fb, const struct drm_rect *clip,
0324 bool swab)
0325 {
0326 if (swab)
0327 drm_fb_xfrm(dst, dst_pitch, 2, vaddr, fb, clip, false,
0328 drm_fb_xrgb8888_to_rgb565_swab_line);
0329 else
0330 drm_fb_xfrm(dst, dst_pitch, 2, vaddr, fb, clip, false,
0331 drm_fb_xrgb8888_to_rgb565_line);
0332 }
0333 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347 void drm_fb_xrgb8888_to_rgb565_toio(void __iomem *dst, unsigned int dst_pitch,
0348 const void *vaddr, const struct drm_framebuffer *fb,
0349 const struct drm_rect *clip, bool swab)
0350 {
0351 if (swab)
0352 drm_fb_xfrm_toio(dst, dst_pitch, 2, vaddr, fb, clip, false,
0353 drm_fb_xrgb8888_to_rgb565_swab_line);
0354 else
0355 drm_fb_xfrm_toio(dst, dst_pitch, 2, vaddr, fb, clip, false,
0356 drm_fb_xrgb8888_to_rgb565_line);
0357 }
0358 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_toio);
0359
0360 static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigned int pixels)
0361 {
0362 u8 *dbuf8 = dbuf;
0363 const u32 *sbuf32 = sbuf;
0364 unsigned int x;
0365
0366 for (x = 0; x < pixels; x++) {
0367 *dbuf8++ = (sbuf32[x] & 0x000000FF) >> 0;
0368 *dbuf8++ = (sbuf32[x] & 0x0000FF00) >> 8;
0369 *dbuf8++ = (sbuf32[x] & 0x00FF0000) >> 16;
0370 }
0371 }
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384 void drm_fb_xrgb8888_to_rgb888(void *dst, unsigned int dst_pitch, const void *src,
0385 const struct drm_framebuffer *fb, const struct drm_rect *clip)
0386 {
0387 drm_fb_xfrm(dst, dst_pitch, 3, src, fb, clip, false, drm_fb_xrgb8888_to_rgb888_line);
0388 }
0389 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888);
0390
0391
0392
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402 void drm_fb_xrgb8888_to_rgb888_toio(void __iomem *dst, unsigned int dst_pitch,
0403 const void *vaddr, const struct drm_framebuffer *fb,
0404 const struct drm_rect *clip)
0405 {
0406 drm_fb_xfrm_toio(dst, dst_pitch, 3, vaddr, fb, clip, false,
0407 drm_fb_xrgb8888_to_rgb888_line);
0408 }
0409 EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_toio);
0410
0411 static void drm_fb_rgb565_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
0412 {
0413 u32 *dbuf32 = dbuf;
0414 const u16 *sbuf16 = sbuf;
0415 unsigned int x;
0416
0417 for (x = 0; x < pixels; x++, ++sbuf16, ++dbuf32) {
0418 u32 val32 = ((*sbuf16 & 0xf800) << 8) |
0419 ((*sbuf16 & 0x07e0) << 5) |
0420 ((*sbuf16 & 0x001f) << 3);
0421 *dbuf32 = 0xff000000 | val32 |
0422 ((val32 >> 3) & 0x00070007) |
0423 ((val32 >> 2) & 0x00000300);
0424 }
0425 }
0426
0427 static void drm_fb_rgb565_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_pitch,
0428 const void *vaddr, const struct drm_framebuffer *fb,
0429 const struct drm_rect *clip)
0430 {
0431 drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false,
0432 drm_fb_rgb565_to_xrgb8888_line);
0433 }
0434
0435 static void drm_fb_rgb888_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
0436 {
0437 u32 *dbuf32 = dbuf;
0438 const u8 *sbuf8 = sbuf;
0439 unsigned int x;
0440
0441 for (x = 0; x < pixels; x++) {
0442 u8 r = *sbuf8++;
0443 u8 g = *sbuf8++;
0444 u8 b = *sbuf8++;
0445 *dbuf32++ = 0xff000000 | (r << 16) | (g << 8) | b;
0446 }
0447 }
0448
0449 static void drm_fb_rgb888_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_pitch,
0450 const void *vaddr, const struct drm_framebuffer *fb,
0451 const struct drm_rect *clip)
0452 {
0453 drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false,
0454 drm_fb_rgb888_to_xrgb8888_line);
0455 }
0456
0457 static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels)
0458 {
0459 u32 *dbuf32 = dbuf;
0460 const u32 *sbuf32 = sbuf;
0461 unsigned int x;
0462 u32 val32;
0463
0464 for (x = 0; x < pixels; x++) {
0465 val32 = ((sbuf32[x] & 0x000000FF) << 2) |
0466 ((sbuf32[x] & 0x0000FF00) << 4) |
0467 ((sbuf32[x] & 0x00FF0000) << 6);
0468 *dbuf32++ = val32 | ((val32 >> 8) & 0x00300C03);
0469 }
0470 }
0471
0472
0473
0474
0475
0476
0477
0478
0479
0480
0481
0482
0483
0484 void drm_fb_xrgb8888_to_xrgb2101010_toio(void __iomem *dst,
0485 unsigned int dst_pitch, const void *vaddr,
0486 const struct drm_framebuffer *fb,
0487 const struct drm_rect *clip)
0488 {
0489 drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false,
0490 drm_fb_xrgb8888_to_xrgb2101010_line);
0491 }
0492 EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010_toio);
0493
0494 static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned int pixels)
0495 {
0496 u8 *dbuf8 = dbuf;
0497 const u32 *sbuf32 = sbuf;
0498 unsigned int x;
0499
0500 for (x = 0; x < pixels; x++) {
0501 u8 r = (*sbuf32 & 0x00ff0000) >> 16;
0502 u8 g = (*sbuf32 & 0x0000ff00) >> 8;
0503 u8 b = *sbuf32 & 0x000000ff;
0504
0505
0506 *dbuf8++ = (3 * r + 6 * g + b) / 10;
0507 sbuf32++;
0508 }
0509 }
0510
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528 void drm_fb_xrgb8888_to_gray8(void *dst, unsigned int dst_pitch, const void *vaddr,
0529 const struct drm_framebuffer *fb, const struct drm_rect *clip)
0530 {
0531 drm_fb_xfrm(dst, dst_pitch, 1, vaddr, fb, clip, false, drm_fb_xrgb8888_to_gray8_line);
0532 }
0533 EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552
0553 int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_format,
0554 const void *vmap, const struct drm_framebuffer *fb,
0555 const struct drm_rect *clip)
0556 {
0557 uint32_t fb_format = fb->format->format;
0558
0559
0560 if (fb_format == DRM_FORMAT_ARGB8888)
0561 fb_format = DRM_FORMAT_XRGB8888;
0562 if (dst_format == DRM_FORMAT_ARGB8888)
0563 dst_format = DRM_FORMAT_XRGB8888;
0564 if (fb_format == DRM_FORMAT_ARGB2101010)
0565 fb_format = DRM_FORMAT_XRGB2101010;
0566 if (dst_format == DRM_FORMAT_ARGB2101010)
0567 dst_format = DRM_FORMAT_XRGB2101010;
0568
0569 if (dst_format == fb_format) {
0570 drm_fb_memcpy_toio(dst, dst_pitch, vmap, fb, clip);
0571 return 0;
0572
0573 } else if (dst_format == DRM_FORMAT_RGB565) {
0574 if (fb_format == DRM_FORMAT_XRGB8888) {
0575 drm_fb_xrgb8888_to_rgb565_toio(dst, dst_pitch, vmap, fb, clip, false);
0576 return 0;
0577 }
0578 } else if (dst_format == DRM_FORMAT_RGB888) {
0579 if (fb_format == DRM_FORMAT_XRGB8888) {
0580 drm_fb_xrgb8888_to_rgb888_toio(dst, dst_pitch, vmap, fb, clip);
0581 return 0;
0582 }
0583 } else if (dst_format == DRM_FORMAT_XRGB8888) {
0584 if (fb_format == DRM_FORMAT_RGB888) {
0585 drm_fb_rgb888_to_xrgb8888_toio(dst, dst_pitch, vmap, fb, clip);
0586 return 0;
0587 } else if (fb_format == DRM_FORMAT_RGB565) {
0588 drm_fb_rgb565_to_xrgb8888_toio(dst, dst_pitch, vmap, fb, clip);
0589 return 0;
0590 }
0591 } else if (dst_format == DRM_FORMAT_XRGB2101010) {
0592 if (fb_format == DRM_FORMAT_XRGB8888) {
0593 drm_fb_xrgb8888_to_xrgb2101010_toio(dst, dst_pitch, vmap, fb, clip);
0594 return 0;
0595 }
0596 }
0597
0598 drm_warn_once(fb->dev, "No conversion helper from %p4cc to %p4cc found.\n",
0599 &fb_format, &dst_format);
0600
0601 return -EINVAL;
0602 }
0603 EXPORT_SYMBOL(drm_fb_blit_toio);
0604
0605
0606 static void drm_fb_gray8_to_mono_line(void *dbuf, const void *sbuf, unsigned int pixels)
0607 {
0608 u8 *dbuf8 = dbuf;
0609 const u8 *sbuf8 = sbuf;
0610
0611 while (pixels) {
0612 unsigned int i, bits = min(pixels, 8U);
0613 u8 byte = 0;
0614
0615 for (i = 0; i < bits; i++, pixels--) {
0616 if (*sbuf8++ >= 128)
0617 byte |= BIT(i);
0618 }
0619 *dbuf8++ = byte;
0620 }
0621 }
0622
0623
0624
0625
0626
0627
0628
0629
0630
0631
0632
0633
0634
0635
0636
0637
0638
0639
0640
0641
0642
0643
0644
0645 void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *vaddr,
0646 const struct drm_framebuffer *fb, const struct drm_rect *clip)
0647 {
0648 unsigned int linepixels = drm_rect_width(clip);
0649 unsigned int lines = drm_rect_height(clip);
0650 unsigned int cpp = fb->format->cpp[0];
0651 unsigned int len_src32 = linepixels * cpp;
0652 struct drm_device *dev = fb->dev;
0653 unsigned int y;
0654 u8 *mono = dst, *gray8;
0655 u32 *src32;
0656
0657 if (drm_WARN_ON(dev, fb->format->format != DRM_FORMAT_XRGB8888))
0658 return;
0659
0660
0661
0662
0663 if (!dst_pitch)
0664 dst_pitch = DIV_ROUND_UP(linepixels, 8);
0665
0666
0667
0668
0669
0670
0671
0672
0673
0674
0675
0676
0677 src32 = kmalloc(len_src32 + linepixels, GFP_KERNEL);
0678 if (!src32)
0679 return;
0680
0681 gray8 = (u8 *)src32 + len_src32;
0682
0683 vaddr += clip_offset(clip, fb->pitches[0], cpp);
0684 for (y = 0; y < lines; y++) {
0685 src32 = memcpy(src32, vaddr, len_src32);
0686 drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels);
0687 drm_fb_gray8_to_mono_line(mono, gray8, linepixels);
0688 vaddr += fb->pitches[0];
0689 mono += dst_pitch;
0690 }
0691
0692 kfree(src32);
0693 }
0694 EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono);