drm_format_helper.c 27.9 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0 or MIT
2 3 4 5 6 7 8 9 10
/*
 * Copyright (C) 2016 Noralf Trønnes
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

11 12
#include <linux/io.h>
#include <linux/iosys-map.h>
13 14 15
#include <linux/module.h>
#include <linux/slab.h>

16
#include <drm/drm_device.h>
17 18 19
#include <drm/drm_format_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_fourcc.h>
20
#include <drm/drm_print.h>
21 22
#include <drm/drm_rect.h>

23
static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp)
24
{
25
	return clip->y1 * pitch + clip->x1 * cpp;
26 27
}

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/**
 * drm_fb_clip_offset - Returns the clipping rectangles byte-offset in a framebuffer
 * @pitch: Framebuffer line pitch in byte
 * @format: Framebuffer format
 * @clip: Clip rectangle
 *
 * Returns:
 * The byte offset of the clip rectangle's top-left corner within the framebuffer.
 */
unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format,
				const struct drm_rect *clip)
{
	return clip_offset(clip, pitch, format->cpp[0]);
}
EXPORT_SYMBOL(drm_fb_clip_offset);

44 45 46 47 48 49 50 51 52 53 54 55 56 57
/* TODO: Make this functon work with multi-plane formats. */
static int drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_pixsize,
		       const void *vaddr, const struct drm_framebuffer *fb,
		       const struct drm_rect *clip, bool vaddr_cached_hint,
		       void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels))
{
	unsigned long linepixels = drm_rect_width(clip);
	unsigned long lines = drm_rect_height(clip);
	size_t sbuf_len = linepixels * fb->format->cpp[0];
	void *stmp = NULL;
	unsigned long i;
	const void *sbuf;

	/*
58
	 * Some source buffers, such as DMA memory, use write-combine
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	 * caching, so reads are uncached. Speed up access by fetching
	 * one line at a time.
	 */
	if (!vaddr_cached_hint) {
		stmp = kmalloc(sbuf_len, GFP_KERNEL);
		if (!stmp)
			return -ENOMEM;
	}

	if (!dst_pitch)
		dst_pitch = drm_rect_width(clip) * dst_pixsize;
	vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]);

	for (i = 0; i < lines; ++i) {
		if (stmp)
			sbuf = memcpy(stmp, vaddr, sbuf_len);
		else
			sbuf = vaddr;
		xfrm_line(dst, sbuf, linepixels);
		vaddr += fb->pitches[0];
		dst += dst_pitch;
	}

	kfree(stmp);

	return 0;
}

/* TODO: Make this functon work with multi-plane formats. */
static int drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsigned long dst_pixsize,
			    const void *vaddr, const struct drm_framebuffer *fb,
			    const struct drm_rect *clip, bool vaddr_cached_hint,
			    void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels))
{
	unsigned long linepixels = drm_rect_width(clip);
	unsigned long lines = drm_rect_height(clip);
	size_t dbuf_len = linepixels * dst_pixsize;
	size_t stmp_off = round_up(dbuf_len, ARCH_KMALLOC_MINALIGN); /* for sbuf alignment */
	size_t sbuf_len = linepixels * fb->format->cpp[0];
	void *stmp = NULL;
	unsigned long i;
	const void *sbuf;
	void *dbuf;

	if (vaddr_cached_hint) {
		dbuf = kmalloc(dbuf_len, GFP_KERNEL);
	} else {
		dbuf = kmalloc(stmp_off + sbuf_len, GFP_KERNEL);
		stmp = dbuf + stmp_off;
	}
	if (!dbuf)
		return -ENOMEM;

	if (!dst_pitch)
		dst_pitch = linepixels * dst_pixsize;
	vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]);

	for (i = 0; i < lines; ++i) {
		if (stmp)
			sbuf = memcpy(stmp, vaddr, sbuf_len);
		else
			sbuf = vaddr;
		xfrm_line(dbuf, sbuf, linepixels);
		memcpy_toio(dst, dbuf, dbuf_len);
		vaddr += fb->pitches[0];
		dst += dst_pitch;
	}

	kfree(dbuf);

	return 0;
}

132 133
/**
 * drm_fb_memcpy - Copy clip buffer
134 135 136 137
 * @dst: Array of destination buffers
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
 * @vmap: Array of source buffers
138 139
 * @fb: DRM framebuffer
 * @clip: Clip rectangle area to copy
140
 *
141 142 143 144 145
 * This function copies parts of a framebuffer to display memory. Destination and
 * framebuffer formats must match. No conversion takes place. The parameters @dst,
 * @dst_pitch and @vmap refer to arrays. Each array must have at least as many entries
 * as there are planes in @fb's format. Each entry stores the value for the format's
 * respective color plane at the same index.
146
 *
147 148
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner).
149
 */
150 151 152
void drm_fb_memcpy(struct iosys_map *dst, const unsigned int *dst_pitch,
		   const struct iosys_map *vmap, const struct drm_framebuffer *fb,
		   const struct drm_rect *clip)
153
{
154 155 156
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};
157

158 159
	const struct drm_format_info *format = fb->format;
	unsigned int i, y, lines = drm_rect_height(clip);
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	for (i = 0; i < format->num_planes; ++i) {
		unsigned int bpp_i = drm_format_info_bpp(format, i);
		unsigned int cpp_i = DIV_ROUND_UP(bpp_i, 8);
		size_t len_i = DIV_ROUND_UP(drm_rect_width(clip) * bpp_i, 8);
		unsigned int dst_pitch_i = dst_pitch[i];
		struct iosys_map dst_i = dst[i];
		struct iosys_map vmap_i = vmap[i];

		if (!dst_pitch_i)
			dst_pitch_i = len_i;

		iosys_map_incr(&vmap_i, clip_offset(clip, fb->pitches[i], cpp_i));
		for (y = 0; y < lines; y++) {
			/* TODO: handle vmap_i in I/O memory here */
			iosys_map_memcpy_to(&dst_i, 0, vmap_i.vaddr, len_i);
			iosys_map_incr(&vmap_i, fb->pitches[i]);
			iosys_map_incr(&dst_i, dst_pitch_i);
		}
182
	}
183
}
184
EXPORT_SYMBOL(drm_fb_memcpy);
185

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
static void drm_fb_swab16_line(void *dbuf, const void *sbuf, unsigned int pixels)
{
	u16 *dbuf16 = dbuf;
	const u16 *sbuf16 = sbuf;
	const u16 *send16 = sbuf16 + pixels;

	while (sbuf16 < send16)
		*dbuf16++ = swab16(*sbuf16++);
}

static void drm_fb_swab32_line(void *dbuf, const void *sbuf, unsigned int pixels)
{
	u32 *dbuf32 = dbuf;
	const u32 *sbuf32 = sbuf;
	const u32 *send32 = sbuf32 + pixels;

	while (sbuf32 < send32)
		*dbuf32++ = swab32(*sbuf32++);
}

206
/**
207
 * drm_fb_swab - Swap bytes into clip buffer
208 209 210 211
 * @dst: Array of destination buffers
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
 * @vmap: Array of source buffers
212 213
 * @fb: DRM framebuffer
 * @clip: Clip rectangle area to copy
214 215
 * @cached: Source buffer is mapped cached (eg. not write-combined)
 *
216 217 218 219 220 221 222
 * This function copies parts of a framebuffer to display memory and swaps per-pixel
 * bytes during the process. Destination and framebuffer formats must match. The
 * parameters @dst, @dst_pitch and @vmap refer to arrays. Each array must have at
 * least as many entries as there are planes in @fb's format. Each entry stores the
 * value for the format's respective color plane at the same index. If @cached is
 * false a temporary buffer is used to cache one pixel line at a time to speed up
 * slow uncached reads.
223
 *
224 225
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner).
226
 */
227 228 229
void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch,
		 const struct iosys_map *vmap, const struct drm_framebuffer *fb,
		 const struct drm_rect *clip, bool cached)
230
{
231 232 233 234 235 236
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};
	const struct drm_format_info *format = fb->format;
	u8 cpp = DIV_ROUND_UP(drm_format_info_bpp(format, 0), 8);
	void (*swab_line)(void *dbuf, const void *sbuf, unsigned int npixels);
237

238 239
	switch (cpp) {
	case 4:
240
		swab_line = drm_fb_swab32_line;
241 242
		break;
	case 2:
243
		swab_line = drm_fb_swab16_line;
244 245 246
		break;
	default:
		drm_warn_once(fb->dev, "Format %p4cc has unsupported pixel size.\n",
247 248
			      &format->format);
		swab_line = NULL;
249
		break;
250
	}
251 252 253 254 255 256 257 258 259 260 261 262 263
	if (!swab_line)
		return;

	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	/* TODO: handle vmap in I/O memory here */
	if (dst->is_iomem)
		drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], cpp,
				 vmap[0].vaddr, fb, clip, cached, swab_line);
	else
		drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], cpp, vmap[0].vaddr, fb,
			    clip, cached, swab_line);
264
}
265
EXPORT_SYMBOL(drm_fb_swab);
266

267
static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigned int pixels)
268
{
269 270
	u8 *dbuf8 = dbuf;
	const __le32 *sbuf32 = sbuf;
271 272 273 274
	unsigned int x;
	u32 pix;

	for (x = 0; x < pixels; x++) {
275 276 277 278
		pix = le32_to_cpu(sbuf32[x]);
		dbuf8[x] = ((pix & 0x00e00000) >> 16) |
			   ((pix & 0x0000e000) >> 11) |
			   ((pix & 0x000000c0) >> 6);
279 280 281 282 283
	}
}

/**
 * drm_fb_xrgb8888_to_rgb332 - Convert XRGB8888 to RGB332 clip buffer
284 285 286 287
 * @dst: Array of RGB332 destination buffers
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
 * @vmap: Array of XRGB8888 source buffers
288 289 290
 * @fb: DRM framebuffer
 * @clip: Clip rectangle area to copy
 *
291 292 293 294 295 296 297 298 299 300
 * This function copies parts of a framebuffer to display memory and converts the
 * color format during the process. Destination and framebuffer formats must match. The
 * parameters @dst, @dst_pitch and @vmap refer to arrays. Each array must have at
 * least as many entries as there are planes in @fb's format. Each entry stores the
 * value for the format's respective color plane at the same index.
 *
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner).
 *
 * Drivers can use this function for RGB332 devices that don't support XRGB8888 natively.
301
 */
302 303 304
void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pitch,
			       const struct iosys_map *vmap, const struct drm_framebuffer *fb,
			       const struct drm_rect *clip)
305
{
306 307 308 309 310 311 312 313 314 315 316 317 318 319
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};

	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	/* TODO: handle vmap in I/O memory here */
	if (dst[0].is_iomem)
		drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], 1, vmap[0].vaddr, fb, clip,
				 false, drm_fb_xrgb8888_to_rgb332_line);
	else
		drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], 1, vmap[0].vaddr, fb, clip,
			    false, drm_fb_xrgb8888_to_rgb332_line);
320 321 322
}
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332);

323
static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigned int pixels)
324
{
325
	u16 *dbuf16 = dbuf;
326
	const __le32 *sbuf32 = sbuf;
327 328
	unsigned int x;
	u16 val16;
329
	u32 pix;
330 331

	for (x = 0; x < pixels; x++) {
332 333 334 335
		pix = le32_to_cpu(sbuf32[x]);
		val16 = ((pix & 0x00F80000) >> 8) |
			((pix & 0x0000FC00) >> 5) |
			((pix & 0x000000F8) >> 3);
336
		dbuf16[x] = val16;
337 338 339
	}
}

340
static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf,
341 342
						unsigned int pixels)
{
343
	u16 *dbuf16 = dbuf;
344
	const __le32 *sbuf32 = sbuf;
345 346
	unsigned int x;
	u16 val16;
347
	u32 pix;
348 349

	for (x = 0; x < pixels; x++) {
350 351 352 353
		pix = le32_to_cpu(sbuf32[x]);
		val16 = ((pix & 0x00F80000) >> 8) |
			((pix & 0x0000FC00) >> 5) |
			((pix & 0x000000F8) >> 3);
354
		dbuf16[x] = swab16(val16);
355
	}
356 357 358 359
}

/**
 * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
360 361 362 363
 * @dst: Array of RGB565 destination buffers
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
 * @vmap: Array of XRGB8888 source buffer
364 365
 * @fb: DRM framebuffer
 * @clip: Clip rectangle area to copy
366
 * @swab: Swap bytes
367
 *
368 369 370 371 372 373 374 375 376 377
 * This function copies parts of a framebuffer to display memory and converts the
 * color format during the process. Destination and framebuffer formats must match. The
 * parameters @dst, @dst_pitch and @vmap refer to arrays. Each array must have at
 * least as many entries as there are planes in @fb's format. Each entry stores the
 * value for the format's respective color plane at the same index.
 *
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner).
 *
 * Drivers can use this function for RGB565 devices that don't support XRGB8888 natively.
378
 */
379 380 381
void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch,
			       const struct iosys_map *vmap, const struct drm_framebuffer *fb,
			       const struct drm_rect *clip, bool swab)
382
{
383 384 385 386 387
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};
	void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels);

388
	if (swab)
389
		xfrm_line = drm_fb_xrgb8888_to_rgb565_swab_line;
390
	else
391
		xfrm_line = drm_fb_xrgb8888_to_rgb565_line;
392

393 394 395 396 397 398 399
	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	/* TODO: handle vmap in I/O memory here */
	if (dst[0].is_iomem)
		drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], 2, vmap[0].vaddr, fb, clip,
				 false, xfrm_line);
400
	else
401 402
		drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], 2, vmap[0].vaddr, fb, clip,
			    false, xfrm_line);
403
}
404
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
405

406
static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigned int pixels)
407
{
408
	u8 *dbuf8 = dbuf;
409
	const __le32 *sbuf32 = sbuf;
410
	unsigned int x;
411
	u32 pix;
412

413
	for (x = 0; x < pixels; x++) {
414 415 416 417
		pix = le32_to_cpu(sbuf32[x]);
		*dbuf8++ = (pix & 0x000000FF) >>  0;
		*dbuf8++ = (pix & 0x0000FF00) >>  8;
		*dbuf8++ = (pix & 0x00FF0000) >> 16;
418 419 420
	}
}

421 422
/**
 * drm_fb_xrgb8888_to_rgb888 - Convert XRGB8888 to RGB888 clip buffer
423 424 425 426
 * @dst: Array of RGB888 destination buffers
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
 * @vmap: Array of XRGB8888 source buffers
427 428 429
 * @fb: DRM framebuffer
 * @clip: Clip rectangle area to copy
 *
430 431 432 433 434 435 436 437
 * This function copies parts of a framebuffer to display memory and converts the
 * color format during the process. Destination and framebuffer formats must match. The
 * parameters @dst, @dst_pitch and @vmap refer to arrays. Each array must have at
 * least as many entries as there are planes in @fb's format. Each entry stores the
 * value for the format's respective color plane at the same index.
 *
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner).
438 439 440 441
 *
 * Drivers can use this function for RGB888 devices that don't natively
 * support XRGB8888.
 */
442 443 444
void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch,
			       const struct iosys_map *vmap, const struct drm_framebuffer *fb,
			       const struct drm_rect *clip)
445
{
446 447 448 449 450 451 452 453 454 455 456 457 458 459
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};

	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	/* TODO: handle vmap in I/O memory here */
	if (dst[0].is_iomem)
		drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], 3, vmap[0].vaddr, fb,
				 clip, false, drm_fb_xrgb8888_to_rgb888_line);
	else
		drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], 3, vmap[0].vaddr, fb,
			    clip, false, drm_fb_xrgb8888_to_rgb888_line);
460
}
461
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888);
462

463 464
static void drm_fb_rgb565_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
{
465 466
	__le32 *dbuf32 = dbuf;
	const __le16 *sbuf16 = sbuf;
467 468
	unsigned int x;

469 470 471 472 473 474 475 476 477
	for (x = 0; x < pixels; x++) {
		u16 val16 = le16_to_cpu(sbuf16[x]);
		u32 val32 = ((val16 & 0xf800) << 8) |
			    ((val16 & 0x07e0) << 5) |
			    ((val16 & 0x001f) << 3);
		val32 = 0xff000000 | val32 |
			((val32 >> 3) & 0x00070007) |
			((val32 >> 2) & 0x00000300);
		dbuf32[x] = cpu_to_le32(val32);
478 479 480
	}
}

481 482 483 484
static void drm_fb_rgb565_to_xrgb8888(struct iosys_map *dst, const unsigned int *dst_pitch,
				      const struct iosys_map *vmap,
				      const struct drm_framebuffer *fb,
				      const struct drm_rect *clip)
485
{
486 487 488 489 490 491 492 493 494 495 496 497 498 499
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};

	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	/* TODO: handle vmap in I/O memory here */
	if (dst[0].is_iomem)
		drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], 4, vmap[0].vaddr, fb,
				 clip, false, drm_fb_rgb565_to_xrgb8888_line);
	else
		drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], 4, vmap[0].vaddr, fb,
			    clip, false, drm_fb_rgb565_to_xrgb8888_line);
500 501
}

502 503
static void drm_fb_rgb888_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
{
504
	__le32 *dbuf32 = dbuf;
505 506 507 508 509 510 511
	const u8 *sbuf8 = sbuf;
	unsigned int x;

	for (x = 0; x < pixels; x++) {
		u8 r = *sbuf8++;
		u8 g = *sbuf8++;
		u8 b = *sbuf8++;
512 513
		u32 pix = 0xff000000 | (r << 16) | (g << 8) | b;
		dbuf32[x] = cpu_to_le32(pix);
514 515 516
	}
}

517 518 519 520
static void drm_fb_rgb888_to_xrgb8888(struct iosys_map *dst, const unsigned int *dst_pitch,
				      const struct iosys_map *vmap,
				      const struct drm_framebuffer *fb,
				      const struct drm_rect *clip)
521
{
522 523 524 525 526 527 528 529 530 531 532 533 534 535
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};

	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	/* TODO: handle vmap in I/O memory here */
	if (dst[0].is_iomem)
		drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], 4, vmap[0].vaddr, fb,
				 clip, false, drm_fb_rgb888_to_xrgb8888_line);
	else
		drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], 4, vmap[0].vaddr, fb,
			    clip, false, drm_fb_rgb888_to_xrgb8888_line);
536 537
}

538
static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels)
539
{
540 541
	__le32 *dbuf32 = dbuf;
	const __le32 *sbuf32 = sbuf;
542 543
	unsigned int x;
	u32 val32;
544
	u32 pix;
545 546

	for (x = 0; x < pixels; x++) {
547 548 549 550 551 552
		pix = le32_to_cpu(sbuf32[x]);
		val32 = ((pix & 0x000000FF) << 2) |
			((pix & 0x0000FF00) << 4) |
			((pix & 0x00FF0000) << 6);
		pix = val32 | ((val32 >> 8) & 0x00300C03);
		*dbuf32++ = cpu_to_le32(pix);
553 554 555 556
	}
}

/**
557 558 559 560 561
 * drm_fb_xrgb8888_to_xrgb2101010 - Convert XRGB8888 to XRGB2101010 clip buffer
 * @dst: Array of XRGB2101010 destination buffers
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
 * @vmap: Array of XRGB8888 source buffers
562 563 564
 * @fb: DRM framebuffer
 * @clip: Clip rectangle area to copy
 *
565 566 567 568 569 570 571 572 573 574 575
 * This function copies parts of a framebuffer to display memory and converts the
 * color format during the process. Destination and framebuffer formats must match. The
 * parameters @dst, @dst_pitch and @vmap refer to arrays. Each array must have at
 * least as many entries as there are planes in @fb's format. Each entry stores the
 * value for the format's respective color plane at the same index.
 *
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner).
 *
 * Drivers can use this function for XRGB2101010 devices that don't support XRGB8888
 * natively.
576
 */
577 578 579
void drm_fb_xrgb8888_to_xrgb2101010(struct iosys_map *dst, const unsigned int *dst_pitch,
				    const struct iosys_map *vmap, const struct drm_framebuffer *fb,
				    const struct drm_rect *clip)
580
{
581 582 583 584 585 586 587 588 589 590 591 592 593 594
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};

	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	/* TODO: handle vmap in I/O memory here */
	if (dst[0].is_iomem)
		drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], 4, vmap[0].vaddr, fb,
				 clip, false, drm_fb_xrgb8888_to_xrgb2101010_line);
	else
		drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], 4, vmap[0].vaddr, fb,
			    clip, false, drm_fb_xrgb8888_to_xrgb2101010_line);
595 596
}

597
static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned int pixels)
598
{
599
	u8 *dbuf8 = dbuf;
600
	const __le32 *sbuf32 = sbuf;
601 602 603
	unsigned int x;

	for (x = 0; x < pixels; x++) {
604 605 606 607
		u32 pix = le32_to_cpu(sbuf32[x]);
		u8 r = (pix & 0x00ff0000) >> 16;
		u8 g = (pix & 0x0000ff00) >> 8;
		u8 b =  pix & 0x000000ff;
608 609

		/* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
610
		*dbuf8++ = (3 * r + 6 * g + b) / 10;
611 612 613
	}
}

614 615
/**
 * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
616 617 618 619
 * @dst: Array of 8-bit grayscale destination buffers
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
 * @vmap: Array of XRGB8888 source buffers
620 621 622
 * @fb: DRM framebuffer
 * @clip: Clip rectangle area to copy
 *
623 624 625 626 627
 * This function copies parts of a framebuffer to display memory and converts the
 * color format during the process. Destination and framebuffer formats must match. The
 * parameters @dst, @dst_pitch and @vmap refer to arrays. Each array must have at
 * least as many entries as there are planes in @fb's format. Each entry stores the
 * value for the format's respective color plane at the same index.
628
 *
629 630
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner).
631
 *
632 633 634 635 636 637
 * DRM doesn't have native monochrome or grayscale support. Drivers can use this
 * function for grayscale devices that don't support XRGB8888 natively.Such
 * drivers can announce the commonly supported XR24 format to userspace and use
 * this function to convert to the native format. Monochrome drivers will use the
 * most significant bit, where 1 means foreground color and 0 background color.
 * ITU BT.601 is being used for the RGB -> luma (brightness) conversion.
638
 */
639 640 641
void drm_fb_xrgb8888_to_gray8(struct iosys_map *dst, const unsigned int *dst_pitch,
			      const struct iosys_map *vmap, const struct drm_framebuffer *fb,
			      const struct drm_rect *clip)
642
{
643 644 645 646 647 648 649 650 651 652 653 654 655 656
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};

	if (!dst_pitch)
		dst_pitch = default_dst_pitch;

	/* TODO: handle vmap in I/O memory here */
	if (dst[0].is_iomem)
		drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], 1, vmap[0].vaddr, fb,
				 clip, false, drm_fb_xrgb8888_to_gray8_line);
	else
		drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], 1, vmap[0].vaddr, fb,
			    clip, false, drm_fb_xrgb8888_to_gray8_line);
657 658 659
}
EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);

660
/**
661 662 663 664
 * drm_fb_blit - Copy parts of a framebuffer to display memory
 * @dst:	Array of display-memory addresses to copy to
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
665 666 667 668 669 670 671
 * @dst_format:	FOURCC code of the display's color format
 * @vmap:	The framebuffer memory to copy from
 * @fb:		The framebuffer to copy from
 * @clip:	Clip rectangle area to copy
 *
 * This function copies parts of a framebuffer to display memory. If the
 * formats of the display and the framebuffer mismatch, the blit function
672 673 674 675 676 677 678
 * will attempt to convert between them during the process. The parameters @dst,
 * @dst_pitch and @vmap refer to arrays. Each array must have at least as many
 * entries as there are planes in @dst_format's format. Each entry stores the
 * value for the format's respective color plane at the same index.
 *
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner).
679 680 681 682 683 684
 *
 * Returns:
 * 0 on success, or
 * -EINVAL if the color-format conversion failed, or
 * a negative error code otherwise.
 */
685 686 687
int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t dst_format,
		const struct iosys_map *vmap, const struct drm_framebuffer *fb,
		const struct drm_rect *clip)
688 689 690 691 692 693 694 695
{
	uint32_t fb_format = fb->format->format;

	/* treat alpha channel like filler bits */
	if (fb_format == DRM_FORMAT_ARGB8888)
		fb_format = DRM_FORMAT_XRGB8888;
	if (dst_format == DRM_FORMAT_ARGB8888)
		dst_format = DRM_FORMAT_XRGB8888;
696 697 698 699
	if (fb_format == DRM_FORMAT_ARGB2101010)
		fb_format = DRM_FORMAT_XRGB2101010;
	if (dst_format == DRM_FORMAT_ARGB2101010)
		dst_format = DRM_FORMAT_XRGB2101010;
700 701

	if (dst_format == fb_format) {
702
		drm_fb_memcpy(dst, dst_pitch, vmap, fb, clip);
703 704 705 706
		return 0;

	} else if (dst_format == DRM_FORMAT_RGB565) {
		if (fb_format == DRM_FORMAT_XRGB8888) {
707
			drm_fb_xrgb8888_to_rgb565(dst, dst_pitch, vmap, fb, clip, false);
708 709 710 711
			return 0;
		}
	} else if (dst_format == DRM_FORMAT_RGB888) {
		if (fb_format == DRM_FORMAT_XRGB8888) {
712
			drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, vmap, fb, clip);
713 714
			return 0;
		}
715 716
	} else if (dst_format == DRM_FORMAT_XRGB8888) {
		if (fb_format == DRM_FORMAT_RGB888) {
717
			drm_fb_rgb888_to_xrgb8888(dst, dst_pitch, vmap, fb, clip);
718
			return 0;
719
		} else if (fb_format == DRM_FORMAT_RGB565) {
720
			drm_fb_rgb565_to_xrgb8888(dst, dst_pitch, vmap, fb, clip);
721
			return 0;
722
		}
723 724
	} else if (dst_format == DRM_FORMAT_XRGB2101010) {
		if (fb_format == DRM_FORMAT_XRGB8888) {
725
			drm_fb_xrgb8888_to_xrgb2101010(dst, dst_pitch, vmap, fb, clip);
726 727
			return 0;
		}
728 729
	}

730 731 732
	drm_warn_once(fb->dev, "No conversion helper from %p4cc to %p4cc found.\n",
		      &fb_format, &dst_format);

733 734
	return -EINVAL;
}
735
EXPORT_SYMBOL(drm_fb_blit);
736

737
static void drm_fb_gray8_to_mono_line(void *dbuf, const void *sbuf, unsigned int pixels)
738
{
739 740 741
	u8 *dbuf8 = dbuf;
	const u8 *sbuf8 = sbuf;

742 743 744
	while (pixels) {
		unsigned int i, bits = min(pixels, 8U);
		u8 byte = 0;
745

746
		for (i = 0; i < bits; i++, pixels--) {
747
			if (*sbuf8++ >= 128)
748
				byte |= BIT(i);
749
		}
750
		*dbuf8++ = byte;
751 752 753 754
	}
}

/**
755
 * drm_fb_xrgb8888_to_mono - Convert XRGB8888 to monochrome
756 757 758 759
 * @dst: Array of monochrome destination buffers (0=black, 1=white)
 * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
 *             within @dst; can be NULL if scanlines are stored next to each other.
 * @vmap: Array of XRGB8888 source buffers
760 761 762
 * @fb: DRM framebuffer
 * @clip: Clip rectangle area to copy
 *
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
 * This function copies parts of a framebuffer to display memory and converts the
 * color format during the process. Destination and framebuffer formats must match. The
 * parameters @dst, @dst_pitch and @vmap refer to arrays. Each array must have at
 * least as many entries as there are planes in @fb's format. Each entry stores the
 * value for the format's respective color plane at the same index.
 *
 * This function does not apply clipping on @dst (i.e. the destination is at the
 * top-left corner). The first pixel (upper left corner of the clip rectangle) will
 * be converted and copied to the first bit (LSB) in the first byte of the monochrome
 * destination buffer. If the caller requires that the first pixel in a byte must
 * be located at an x-coordinate that is a multiple of 8, then the caller must take
 * care itself of supplying a suitable clip rectangle.
 *
 * DRM doesn't have native monochrome support. Drivers can use this function for
 * monochrome devices that don't support XRGB8888 natively. Such drivers can
 * announce the commonly supported XR24 format to userspace and use this function
 * to convert to the native format.
780 781
 *
 * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and
782
 * then the result is converted from grayscale to monochrome.
783
 */
784 785 786
void drm_fb_xrgb8888_to_mono(struct iosys_map *dst, const unsigned int *dst_pitch,
			     const struct iosys_map *vmap, const struct drm_framebuffer *fb,
			     const struct drm_rect *clip)
787
{
788 789 790
	static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
		0, 0, 0, 0
	};
791
	unsigned int linepixels = drm_rect_width(clip);
792
	unsigned int lines = drm_rect_height(clip);
793 794 795
	unsigned int cpp = fb->format->cpp[0];
	unsigned int len_src32 = linepixels * cpp;
	struct drm_device *dev = fb->dev;
796 797
	void *vaddr = vmap[0].vaddr;
	unsigned int dst_pitch_0;
798
	unsigned int y;
799
	u8 *mono = dst[0].vaddr, *gray8;
800 801 802 803 804
	u32 *src32;

	if (drm_WARN_ON(dev, fb->format->format != DRM_FORMAT_XRGB8888))
		return;

805 806 807 808
	if (!dst_pitch)
		dst_pitch = default_dst_pitch;
	dst_pitch_0 = dst_pitch[0];

809
	/*
810
	 * The mono destination buffer contains 1 bit per pixel
811
	 */
812 813
	if (!dst_pitch_0)
		dst_pitch_0 = DIV_ROUND_UP(linepixels, 8);
814 815

	/*
816
	 * The dma memory is write-combined so reads are uncached.
817 818
	 * Speed up by fetching one line at a time.
	 *
819 820 821
	 * Also, format conversion from XR24 to monochrome are done
	 * line-by-line but are converted to 8-bit grayscale as an
	 * intermediate step.
822 823 824 825 826 827 828 829 830 831 832 833 834 835
	 *
	 * Allocate a buffer to be used for both copying from the cma
	 * memory and to store the intermediate grayscale line pixels.
	 */
	src32 = kmalloc(len_src32 + linepixels, GFP_KERNEL);
	if (!src32)
		return;

	gray8 = (u8 *)src32 + len_src32;

	vaddr += clip_offset(clip, fb->pitches[0], cpp);
	for (y = 0; y < lines; y++) {
		src32 = memcpy(src32, vaddr, len_src32);
		drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels);
836
		drm_fb_gray8_to_mono_line(mono, gray8, linepixels);
837
		vaddr += fb->pitches[0];
838
		mono += dst_pitch_0;
839 840 841 842
	}

	kfree(src32);
}
843
EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono);