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