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