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 * @vaddr: Source buffer
  56 * @fb: DRM framebuffer
  57 * @clip: Clip rectangle area to copy
  58 *
  59 * This function applies clipping on dst, i.e. the destination is a
  60 * full (iomem) framebuffer but only the clip rect content is copied over.
  61 */
  62void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr,
  63                           struct drm_framebuffer *fb,
  64                           struct drm_rect *clip)
  65{
  66        unsigned int cpp = fb->format->cpp[0];
  67        unsigned int offset = clip_offset(clip, fb->pitches[0], cpp);
  68        size_t len = (clip->x2 - clip->x1) * cpp;
  69        unsigned int y, lines = clip->y2 - clip->y1;
  70
  71        vaddr += offset;
  72        dst += offset;
  73        for (y = 0; y < lines; y++) {
  74                memcpy_toio(dst, vaddr, len);
  75                vaddr += fb->pitches[0];
  76                dst += fb->pitches[0];
  77        }
  78}
  79EXPORT_SYMBOL(drm_fb_memcpy_dstclip);
  80
  81/**
  82 * drm_fb_swab - Swap bytes into clip buffer
  83 * @dst: Destination buffer
  84 * @src: Source buffer
  85 * @fb: DRM framebuffer
  86 * @clip: Clip rectangle area to copy
  87 * @cached: Source buffer is mapped cached (eg. not write-combined)
  88 *
  89 * If @cached is false a temporary buffer is used to cache one pixel line at a
  90 * time to speed up slow uncached reads.
  91 *
  92 * This function does not apply clipping on dst, i.e. the destination
  93 * is a small buffer containing the clip rect only.
  94 */
  95void drm_fb_swab(void *dst, void *src, struct drm_framebuffer *fb,
  96                 struct drm_rect *clip, bool cached)
  97{
  98        u8 cpp = fb->format->cpp[0];
  99        size_t len = drm_rect_width(clip) * cpp;
 100        u16 *src16, *dst16 = dst;
 101        u32 *src32, *dst32 = dst;
 102        unsigned int x, y;
 103        void *buf = NULL;
 104
 105        if (WARN_ON_ONCE(cpp != 2 && cpp != 4))
 106                return;
 107
 108        if (!cached)
 109                buf = kmalloc(len, GFP_KERNEL);
 110
 111        src += clip_offset(clip, fb->pitches[0], cpp);
 112
 113        for (y = clip->y1; y < clip->y2; y++) {
 114                if (buf) {
 115                        memcpy(buf, src, len);
 116                        src16 = buf;
 117                        src32 = buf;
 118                } else {
 119                        src16 = src;
 120                        src32 = src;
 121                }
 122
 123                for (x = clip->x1; x < clip->x2; x++) {
 124                        if (cpp == 4)
 125                                *dst32++ = swab32(*src32++);
 126                        else
 127                                *dst16++ = swab16(*src16++);
 128                }
 129
 130                src += fb->pitches[0];
 131        }
 132
 133        kfree(buf);
 134}
 135EXPORT_SYMBOL(drm_fb_swab);
 136
 137static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf,
 138                                           unsigned int pixels,
 139                                           bool swab)
 140{
 141        unsigned int x;
 142        u16 val16;
 143
 144        for (x = 0; x < pixels; x++) {
 145                val16 = ((sbuf[x] & 0x00F80000) >> 8) |
 146                        ((sbuf[x] & 0x0000FC00) >> 5) |
 147                        ((sbuf[x] & 0x000000F8) >> 3);
 148                if (swab)
 149                        dbuf[x] = swab16(val16);
 150                else
 151                        dbuf[x] = val16;
 152        }
 153}
 154
 155/**
 156 * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
 157 * @dst: RGB565 destination buffer
 158 * @vaddr: XRGB8888 source buffer
 159 * @fb: DRM framebuffer
 160 * @clip: Clip rectangle area to copy
 161 * @swab: Swap bytes
 162 *
 163 * Drivers can use this function for RGB565 devices that don't natively
 164 * support XRGB8888.
 165 *
 166 * This function does not apply clipping on dst, i.e. the destination
 167 * is a small buffer containing the clip rect only.
 168 */
 169void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr,
 170                               struct drm_framebuffer *fb,
 171                               struct drm_rect *clip, bool swab)
 172{
 173        size_t linepixels = clip->x2 - clip->x1;
 174        size_t src_len = linepixels * sizeof(u32);
 175        size_t dst_len = linepixels * sizeof(u16);
 176        unsigned y, lines = clip->y2 - clip->y1;
 177        void *sbuf;
 178
 179        /*
 180         * The cma memory is write-combined so reads are uncached.
 181         * Speed up by fetching one line at a time.
 182         */
 183        sbuf = kmalloc(src_len, GFP_KERNEL);
 184        if (!sbuf)
 185                return;
 186
 187        vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
 188        for (y = 0; y < lines; y++) {
 189                memcpy(sbuf, vaddr, src_len);
 190                drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab);
 191                vaddr += fb->pitches[0];
 192                dst += dst_len;
 193        }
 194
 195        kfree(sbuf);
 196}
 197EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
 198
 199/**
 200 * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer
 201 * @dst: RGB565 destination buffer (iomem)
 202 * @dst_pitch: destination buffer pitch
 203 * @vaddr: XRGB8888 source buffer
 204 * @fb: DRM framebuffer
 205 * @clip: Clip rectangle area to copy
 206 * @swab: Swap bytes
 207 *
 208 * Drivers can use this function for RGB565 devices that don't natively
 209 * support XRGB8888.
 210 *
 211 * This function applies clipping on dst, i.e. the destination is a
 212 * full (iomem) framebuffer but only the clip rect content is copied over.
 213 */
 214void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch,
 215                                       void *vaddr, struct drm_framebuffer *fb,
 216                                       struct drm_rect *clip, bool swab)
 217{
 218        size_t linepixels = clip->x2 - clip->x1;
 219        size_t dst_len = linepixels * sizeof(u16);
 220        unsigned y, lines = clip->y2 - clip->y1;
 221        void *dbuf;
 222
 223        dbuf = kmalloc(dst_len, GFP_KERNEL);
 224        if (!dbuf)
 225                return;
 226
 227        vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
 228        dst += clip_offset(clip, dst_pitch, sizeof(u16));
 229        for (y = 0; y < lines; y++) {
 230                drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab);
 231                memcpy_toio(dst, dbuf, dst_len);
 232                vaddr += fb->pitches[0];
 233                dst += dst_len;
 234        }
 235
 236        kfree(dbuf);
 237}
 238EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip);
 239
 240static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf,
 241                                           unsigned int pixels)
 242{
 243        unsigned int x;
 244
 245        for (x = 0; x < pixels; x++) {
 246                *dbuf++ = (sbuf[x] & 0x000000FF) >>  0;
 247                *dbuf++ = (sbuf[x] & 0x0000FF00) >>  8;
 248                *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16;
 249        }
 250}
 251
 252/**
 253 * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer
 254 * @dst: RGB565 destination buffer (iomem)
 255 * @dst_pitch: destination buffer pitch
 256 * @vaddr: XRGB8888 source buffer
 257 * @fb: DRM framebuffer
 258 * @clip: Clip rectangle area to copy
 259 *
 260 * Drivers can use this function for RGB888 devices that don't natively
 261 * support XRGB8888.
 262 *
 263 * This function applies clipping on dst, i.e. the destination is a
 264 * full (iomem) framebuffer but only the clip rect content is copied over.
 265 */
 266void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch,
 267                                       void *vaddr, struct drm_framebuffer *fb,
 268                                       struct drm_rect *clip)
 269{
 270        size_t linepixels = clip->x2 - clip->x1;
 271        size_t dst_len = linepixels * 3;
 272        unsigned y, lines = clip->y2 - clip->y1;
 273        void *dbuf;
 274
 275        dbuf = kmalloc(dst_len, GFP_KERNEL);
 276        if (!dbuf)
 277                return;
 278
 279        vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
 280        dst += clip_offset(clip, dst_pitch, sizeof(u16));
 281        for (y = 0; y < lines; y++) {
 282                drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels);
 283                memcpy_toio(dst, dbuf, dst_len);
 284                vaddr += fb->pitches[0];
 285                dst += dst_len;
 286        }
 287
 288        kfree(dbuf);
 289}
 290EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip);
 291
 292/**
 293 * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
 294 * @dst: 8-bit grayscale destination buffer
 295 * @vaddr: XRGB8888 source buffer
 296 * @fb: DRM framebuffer
 297 * @clip: Clip rectangle area to copy
 298 *
 299 * Drm doesn't have native monochrome or grayscale support.
 300 * Such drivers can announce the commonly supported XR24 format to userspace
 301 * and use this function to convert to the native format.
 302 *
 303 * Monochrome drivers will use the most significant bit,
 304 * where 1 means foreground color and 0 background color.
 305 *
 306 * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
 307 */
 308void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
 309                               struct drm_rect *clip)
 310{
 311        unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
 312        unsigned int x, y;
 313        void *buf;
 314        u32 *src;
 315
 316        if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
 317                return;
 318        /*
 319         * The cma memory is write-combined so reads are uncached.
 320         * Speed up by fetching one line at a time.
 321         */
 322        buf = kmalloc(len, GFP_KERNEL);
 323        if (!buf)
 324                return;
 325
 326        for (y = clip->y1; y < clip->y2; y++) {
 327                src = vaddr + (y * fb->pitches[0]);
 328                src += clip->x1;
 329                memcpy(buf, src, len);
 330                src = buf;
 331                for (x = clip->x1; x < clip->x2; x++) {
 332                        u8 r = (*src & 0x00ff0000) >> 16;
 333                        u8 g = (*src & 0x0000ff00) >> 8;
 334                        u8 b =  *src & 0x000000ff;
 335
 336                        /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
 337                        *dst++ = (3 * r + 6 * g + b) / 10;
 338                        src++;
 339                }
 340        }
 341
 342        kfree(buf);
 343}
 344EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
 345
 346