Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 or MIT
0002 /*
0003  * Copyright (C) 2016 Noralf Trønnes
0004  *
0005  * This program is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation; either version 2 of the License, or
0008  * (at your option) any later version.
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  * drm_fb_clip_offset - Returns the clipping rectangles byte-offset in a framebuffer
0029  * @pitch: Framebuffer line pitch in byte
0030  * @format: Framebuffer format
0031  * @clip: Clip rectangle
0032  *
0033  * Returns:
0034  * The byte offset of the clip rectangle's top-left corner within the framebuffer.
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 /* TODO: Make this functon work with multi-plane formats. */
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      * Some source buffers, such as CMA memory, use write-combine
0058      * caching, so reads are uncached. Speed up access by fetching
0059      * one line at a time.
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 /* TODO: Make this functon work with multi-plane formats. */
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); /* for sbuf alignment */
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  * drm_fb_memcpy - Copy clip buffer
0133  * @dst: Destination buffer
0134  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0135  * @vaddr: Source buffer
0136  * @fb: DRM framebuffer
0137  * @clip: Clip rectangle area to copy
0138  *
0139  * This function does not apply clipping on dst, i.e. the destination
0140  * is at the top-left corner.
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  * drm_fb_memcpy_toio - Copy clip buffer
0163  * @dst: Destination buffer (iomem)
0164  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0165  * @vaddr: Source buffer
0166  * @fb: DRM framebuffer
0167  * @clip: Clip rectangle area to copy
0168  *
0169  * This function does not apply clipping on dst, i.e. the destination
0170  * is at the top-left corner.
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  * drm_fb_swab - Swap bytes into clip buffer
0213  * @dst: Destination buffer
0214  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0215  * @src: Source buffer
0216  * @fb: DRM framebuffer
0217  * @clip: Clip rectangle area to copy
0218  * @cached: Source buffer is mapped cached (eg. not write-combined)
0219  *
0220  * If @cached is false a temporary buffer is used to cache one pixel line at a
0221  * time to speed up slow uncached reads.
0222  *
0223  * This function does not apply clipping on dst, i.e. the destination
0224  * is at the top-left corner.
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  * drm_fb_xrgb8888_to_rgb332 - Convert XRGB8888 to RGB332 clip buffer
0264  * @dst: RGB332 destination buffer
0265  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0266  * @src: XRGB8888 source buffer
0267  * @fb: DRM framebuffer
0268  * @clip: Clip rectangle area to copy
0269  *
0270  * Drivers can use this function for RGB332 devices that don't natively support XRGB8888.
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  * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
0312  * @dst: RGB565 destination buffer
0313  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0314  * @vaddr: XRGB8888 source buffer
0315  * @fb: DRM framebuffer
0316  * @clip: Clip rectangle area to copy
0317  * @swab: Swap bytes
0318  *
0319  * Drivers can use this function for RGB565 devices that don't natively
0320  * support XRGB8888.
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  * drm_fb_xrgb8888_to_rgb565_toio - Convert XRGB8888 to RGB565 clip buffer
0337  * @dst: RGB565 destination buffer (iomem)
0338  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0339  * @vaddr: XRGB8888 source buffer
0340  * @fb: DRM framebuffer
0341  * @clip: Clip rectangle area to copy
0342  * @swab: Swap bytes
0343  *
0344  * Drivers can use this function for RGB565 devices that don't natively
0345  * support XRGB8888.
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  * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer
0375  * @dst: RGB888 destination buffer
0376  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0377  * @src: XRGB8888 source buffer
0378  * @fb: DRM framebuffer
0379  * @clip: Clip rectangle area to copy
0380  *
0381  * Drivers can use this function for RGB888 devices that don't natively
0382  * support XRGB8888.
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  * drm_fb_xrgb8888_to_rgb888_toio - Convert XRGB8888 to RGB888 clip buffer
0393  * @dst: RGB565 destination buffer (iomem)
0394  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0395  * @vaddr: XRGB8888 source buffer
0396  * @fb: DRM framebuffer
0397  * @clip: Clip rectangle area to copy
0398  *
0399  * Drivers can use this function for RGB888 devices that don't natively
0400  * support XRGB8888.
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  * drm_fb_xrgb8888_to_xrgb2101010_toio - Convert XRGB8888 to XRGB2101010 clip
0474  * buffer
0475  * @dst: XRGB2101010 destination buffer (iomem)
0476  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0477  * @vaddr: XRGB8888 source buffer
0478  * @fb: DRM framebuffer
0479  * @clip: Clip rectangle area to copy
0480  *
0481  * Drivers can use this function for XRGB2101010 devices that don't natively
0482  * support XRGB8888.
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         /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
0506         *dbuf8++ = (3 * r + 6 * g + b) / 10;
0507         sbuf32++;
0508     }
0509 }
0510 
0511 /**
0512  * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
0513  * @dst: 8-bit grayscale destination buffer
0514  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0515  * @vaddr: XRGB8888 source buffer
0516  * @fb: DRM framebuffer
0517  * @clip: Clip rectangle area to copy
0518  *
0519  * Drm doesn't have native monochrome or grayscale support.
0520  * Such drivers can announce the commonly supported XR24 format to userspace
0521  * and use this function to convert to the native format.
0522  *
0523  * Monochrome drivers will use the most significant bit,
0524  * where 1 means foreground color and 0 background color.
0525  *
0526  * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
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  * drm_fb_blit_toio - Copy parts of a framebuffer to display memory
0537  * @dst:    The display memory to copy to
0538  * @dst_pitch:  Number of bytes between two consecutive scanlines within dst
0539  * @dst_format: FOURCC code of the display's color format
0540  * @vmap:   The framebuffer memory to copy from
0541  * @fb:     The framebuffer to copy from
0542  * @clip:   Clip rectangle area to copy
0543  *
0544  * This function copies parts of a framebuffer to display memory. If the
0545  * formats of the display and the framebuffer mismatch, the blit function
0546  * will attempt to convert between them.
0547  *
0548  * Returns:
0549  * 0 on success, or
0550  * -EINVAL if the color-format conversion failed, or
0551  * a negative error code otherwise.
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     /* treat alpha channel like filler bits */
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  * drm_fb_xrgb8888_to_mono - Convert XRGB8888 to monochrome
0625  * @dst: monochrome destination buffer (0=black, 1=white)
0626  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
0627  * @vaddr: XRGB8888 source buffer
0628  * @fb: DRM framebuffer
0629  * @clip: Clip rectangle area to copy
0630  *
0631  * DRM doesn't have native monochrome support.
0632  * Such drivers can announce the commonly supported XR24 format to userspace
0633  * and use this function to convert to the native format.
0634  *
0635  * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and
0636  * then the result is converted from grayscale to monochrome.
0637  *
0638  * The first pixel (upper left corner of the clip rectangle) will be converted
0639  * and copied to the first bit (LSB) in the first byte of the monochrome
0640  * destination buffer.
0641  * If the caller requires that the first pixel in a byte must be located at an
0642  * x-coordinate that is a multiple of 8, then the caller must take care itself
0643  * of supplying a suitable clip rectangle.
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      * The mono destination buffer contains 1 bit per pixel
0662      */
0663     if (!dst_pitch)
0664         dst_pitch = DIV_ROUND_UP(linepixels, 8);
0665 
0666     /*
0667      * The cma memory is write-combined so reads are uncached.
0668      * Speed up by fetching one line at a time.
0669      *
0670      * Also, format conversion from XR24 to monochrome are done
0671      * line-by-line but are converted to 8-bit grayscale as an
0672      * intermediate step.
0673      *
0674      * Allocate a buffer to be used for both copying from the cma
0675      * memory and to store the intermediate grayscale line pixels.
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);