18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or MIT
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Noralf Trønnes
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
68c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
78c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
88c2ecf20Sopenharmony_ci * (at your option) any later version.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <drm/drm_format_helper.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_framebuffer.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_rect.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic unsigned int clip_offset(struct drm_rect *clip,
218c2ecf20Sopenharmony_ci				unsigned int pitch, unsigned int cpp)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	return clip->y1 * pitch + clip->x1 * cpp;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * drm_fb_memcpy - Copy clip buffer
288c2ecf20Sopenharmony_ci * @dst: Destination buffer
298c2ecf20Sopenharmony_ci * @vaddr: Source buffer
308c2ecf20Sopenharmony_ci * @fb: DRM framebuffer
318c2ecf20Sopenharmony_ci * @clip: Clip rectangle area to copy
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * This function does not apply clipping on dst, i.e. the destination
348c2ecf20Sopenharmony_ci * is a small buffer containing the clip rect only.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_civoid drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
378c2ecf20Sopenharmony_ci		   struct drm_rect *clip)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	unsigned int cpp = fb->format->cpp[0];
408c2ecf20Sopenharmony_ci	size_t len = (clip->x2 - clip->x1) * cpp;
418c2ecf20Sopenharmony_ci	unsigned int y, lines = clip->y2 - clip->y1;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	vaddr += clip_offset(clip, fb->pitches[0], cpp);
448c2ecf20Sopenharmony_ci	for (y = 0; y < lines; y++) {
458c2ecf20Sopenharmony_ci		memcpy(dst, vaddr, len);
468c2ecf20Sopenharmony_ci		vaddr += fb->pitches[0];
478c2ecf20Sopenharmony_ci		dst += len;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_memcpy);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/**
538c2ecf20Sopenharmony_ci * drm_fb_memcpy_dstclip - Copy clip buffer
548c2ecf20Sopenharmony_ci * @dst: Destination buffer (iomem)
558c2ecf20Sopenharmony_ci * @vaddr: Source buffer
568c2ecf20Sopenharmony_ci * @fb: DRM framebuffer
578c2ecf20Sopenharmony_ci * @clip: Clip rectangle area to copy
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * This function applies clipping on dst, i.e. the destination is a
608c2ecf20Sopenharmony_ci * full (iomem) framebuffer but only the clip rect content is copied over.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_civoid drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr,
638c2ecf20Sopenharmony_ci			   struct drm_framebuffer *fb,
648c2ecf20Sopenharmony_ci			   struct drm_rect *clip)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	unsigned int cpp = fb->format->cpp[0];
678c2ecf20Sopenharmony_ci	unsigned int offset = clip_offset(clip, fb->pitches[0], cpp);
688c2ecf20Sopenharmony_ci	size_t len = (clip->x2 - clip->x1) * cpp;
698c2ecf20Sopenharmony_ci	unsigned int y, lines = clip->y2 - clip->y1;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	vaddr += offset;
728c2ecf20Sopenharmony_ci	dst += offset;
738c2ecf20Sopenharmony_ci	for (y = 0; y < lines; y++) {
748c2ecf20Sopenharmony_ci		memcpy_toio(dst, vaddr, len);
758c2ecf20Sopenharmony_ci		vaddr += fb->pitches[0];
768c2ecf20Sopenharmony_ci		dst += fb->pitches[0];
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_memcpy_dstclip);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/**
828c2ecf20Sopenharmony_ci * drm_fb_swab - Swap bytes into clip buffer
838c2ecf20Sopenharmony_ci * @dst: Destination buffer
848c2ecf20Sopenharmony_ci * @src: Source buffer
858c2ecf20Sopenharmony_ci * @fb: DRM framebuffer
868c2ecf20Sopenharmony_ci * @clip: Clip rectangle area to copy
878c2ecf20Sopenharmony_ci * @cached: Source buffer is mapped cached (eg. not write-combined)
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci * If @cached is false a temporary buffer is used to cache one pixel line at a
908c2ecf20Sopenharmony_ci * time to speed up slow uncached reads.
918c2ecf20Sopenharmony_ci *
928c2ecf20Sopenharmony_ci * This function does not apply clipping on dst, i.e. the destination
938c2ecf20Sopenharmony_ci * is a small buffer containing the clip rect only.
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_civoid drm_fb_swab(void *dst, void *src, struct drm_framebuffer *fb,
968c2ecf20Sopenharmony_ci		 struct drm_rect *clip, bool cached)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	u8 cpp = fb->format->cpp[0];
998c2ecf20Sopenharmony_ci	size_t len = drm_rect_width(clip) * cpp;
1008c2ecf20Sopenharmony_ci	u16 *src16, *dst16 = dst;
1018c2ecf20Sopenharmony_ci	u32 *src32, *dst32 = dst;
1028c2ecf20Sopenharmony_ci	unsigned int x, y;
1038c2ecf20Sopenharmony_ci	void *buf = NULL;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(cpp != 2 && cpp != 4))
1068c2ecf20Sopenharmony_ci		return;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (!cached)
1098c2ecf20Sopenharmony_ci		buf = kmalloc(len, GFP_KERNEL);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	src += clip_offset(clip, fb->pitches[0], cpp);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	for (y = clip->y1; y < clip->y2; y++) {
1148c2ecf20Sopenharmony_ci		if (buf) {
1158c2ecf20Sopenharmony_ci			memcpy(buf, src, len);
1168c2ecf20Sopenharmony_ci			src16 = buf;
1178c2ecf20Sopenharmony_ci			src32 = buf;
1188c2ecf20Sopenharmony_ci		} else {
1198c2ecf20Sopenharmony_ci			src16 = src;
1208c2ecf20Sopenharmony_ci			src32 = src;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		for (x = clip->x1; x < clip->x2; x++) {
1248c2ecf20Sopenharmony_ci			if (cpp == 4)
1258c2ecf20Sopenharmony_ci				*dst32++ = swab32(*src32++);
1268c2ecf20Sopenharmony_ci			else
1278c2ecf20Sopenharmony_ci				*dst16++ = swab16(*src16++);
1288c2ecf20Sopenharmony_ci		}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		src += fb->pitches[0];
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	kfree(buf);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_swab);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf,
1388c2ecf20Sopenharmony_ci					   unsigned int pixels,
1398c2ecf20Sopenharmony_ci					   bool swab)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	unsigned int x;
1428c2ecf20Sopenharmony_ci	u16 val16;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	for (x = 0; x < pixels; x++) {
1458c2ecf20Sopenharmony_ci		val16 = ((sbuf[x] & 0x00F80000) >> 8) |
1468c2ecf20Sopenharmony_ci			((sbuf[x] & 0x0000FC00) >> 5) |
1478c2ecf20Sopenharmony_ci			((sbuf[x] & 0x000000F8) >> 3);
1488c2ecf20Sopenharmony_ci		if (swab)
1498c2ecf20Sopenharmony_ci			dbuf[x] = swab16(val16);
1508c2ecf20Sopenharmony_ci		else
1518c2ecf20Sopenharmony_ci			dbuf[x] = val16;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/**
1568c2ecf20Sopenharmony_ci * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
1578c2ecf20Sopenharmony_ci * @dst: RGB565 destination buffer
1588c2ecf20Sopenharmony_ci * @vaddr: XRGB8888 source buffer
1598c2ecf20Sopenharmony_ci * @fb: DRM framebuffer
1608c2ecf20Sopenharmony_ci * @clip: Clip rectangle area to copy
1618c2ecf20Sopenharmony_ci * @swab: Swap bytes
1628c2ecf20Sopenharmony_ci *
1638c2ecf20Sopenharmony_ci * Drivers can use this function for RGB565 devices that don't natively
1648c2ecf20Sopenharmony_ci * support XRGB8888.
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci * This function does not apply clipping on dst, i.e. the destination
1678c2ecf20Sopenharmony_ci * is a small buffer containing the clip rect only.
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_civoid drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr,
1708c2ecf20Sopenharmony_ci			       struct drm_framebuffer *fb,
1718c2ecf20Sopenharmony_ci			       struct drm_rect *clip, bool swab)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	size_t linepixels = clip->x2 - clip->x1;
1748c2ecf20Sopenharmony_ci	size_t src_len = linepixels * sizeof(u32);
1758c2ecf20Sopenharmony_ci	size_t dst_len = linepixels * sizeof(u16);
1768c2ecf20Sopenharmony_ci	unsigned y, lines = clip->y2 - clip->y1;
1778c2ecf20Sopenharmony_ci	void *sbuf;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/*
1808c2ecf20Sopenharmony_ci	 * The cma memory is write-combined so reads are uncached.
1818c2ecf20Sopenharmony_ci	 * Speed up by fetching one line at a time.
1828c2ecf20Sopenharmony_ci	 */
1838c2ecf20Sopenharmony_ci	sbuf = kmalloc(src_len, GFP_KERNEL);
1848c2ecf20Sopenharmony_ci	if (!sbuf)
1858c2ecf20Sopenharmony_ci		return;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
1888c2ecf20Sopenharmony_ci	for (y = 0; y < lines; y++) {
1898c2ecf20Sopenharmony_ci		memcpy(sbuf, vaddr, src_len);
1908c2ecf20Sopenharmony_ci		drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab);
1918c2ecf20Sopenharmony_ci		vaddr += fb->pitches[0];
1928c2ecf20Sopenharmony_ci		dst += dst_len;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	kfree(sbuf);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer
2018c2ecf20Sopenharmony_ci * @dst: RGB565 destination buffer (iomem)
2028c2ecf20Sopenharmony_ci * @dst_pitch: destination buffer pitch
2038c2ecf20Sopenharmony_ci * @vaddr: XRGB8888 source buffer
2048c2ecf20Sopenharmony_ci * @fb: DRM framebuffer
2058c2ecf20Sopenharmony_ci * @clip: Clip rectangle area to copy
2068c2ecf20Sopenharmony_ci * @swab: Swap bytes
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci * Drivers can use this function for RGB565 devices that don't natively
2098c2ecf20Sopenharmony_ci * support XRGB8888.
2108c2ecf20Sopenharmony_ci *
2118c2ecf20Sopenharmony_ci * This function applies clipping on dst, i.e. the destination is a
2128c2ecf20Sopenharmony_ci * full (iomem) framebuffer but only the clip rect content is copied over.
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_civoid drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch,
2158c2ecf20Sopenharmony_ci				       void *vaddr, struct drm_framebuffer *fb,
2168c2ecf20Sopenharmony_ci				       struct drm_rect *clip, bool swab)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	size_t linepixels = clip->x2 - clip->x1;
2198c2ecf20Sopenharmony_ci	size_t dst_len = linepixels * sizeof(u16);
2208c2ecf20Sopenharmony_ci	unsigned y, lines = clip->y2 - clip->y1;
2218c2ecf20Sopenharmony_ci	void *dbuf;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	dbuf = kmalloc(dst_len, GFP_KERNEL);
2248c2ecf20Sopenharmony_ci	if (!dbuf)
2258c2ecf20Sopenharmony_ci		return;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
2288c2ecf20Sopenharmony_ci	dst += clip_offset(clip, dst_pitch, sizeof(u16));
2298c2ecf20Sopenharmony_ci	for (y = 0; y < lines; y++) {
2308c2ecf20Sopenharmony_ci		drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab);
2318c2ecf20Sopenharmony_ci		memcpy_toio(dst, dbuf, dst_len);
2328c2ecf20Sopenharmony_ci		vaddr += fb->pitches[0];
2338c2ecf20Sopenharmony_ci		dst += dst_len;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	kfree(dbuf);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf,
2418c2ecf20Sopenharmony_ci					   unsigned int pixels)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	unsigned int x;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	for (x = 0; x < pixels; x++) {
2468c2ecf20Sopenharmony_ci		*dbuf++ = (sbuf[x] & 0x000000FF) >>  0;
2478c2ecf20Sopenharmony_ci		*dbuf++ = (sbuf[x] & 0x0000FF00) >>  8;
2488c2ecf20Sopenharmony_ci		*dbuf++ = (sbuf[x] & 0x00FF0000) >> 16;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/**
2538c2ecf20Sopenharmony_ci * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer
2548c2ecf20Sopenharmony_ci * @dst: RGB565 destination buffer (iomem)
2558c2ecf20Sopenharmony_ci * @dst_pitch: destination buffer pitch
2568c2ecf20Sopenharmony_ci * @vaddr: XRGB8888 source buffer
2578c2ecf20Sopenharmony_ci * @fb: DRM framebuffer
2588c2ecf20Sopenharmony_ci * @clip: Clip rectangle area to copy
2598c2ecf20Sopenharmony_ci *
2608c2ecf20Sopenharmony_ci * Drivers can use this function for RGB888 devices that don't natively
2618c2ecf20Sopenharmony_ci * support XRGB8888.
2628c2ecf20Sopenharmony_ci *
2638c2ecf20Sopenharmony_ci * This function applies clipping on dst, i.e. the destination is a
2648c2ecf20Sopenharmony_ci * full (iomem) framebuffer but only the clip rect content is copied over.
2658c2ecf20Sopenharmony_ci */
2668c2ecf20Sopenharmony_civoid drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch,
2678c2ecf20Sopenharmony_ci				       void *vaddr, struct drm_framebuffer *fb,
2688c2ecf20Sopenharmony_ci				       struct drm_rect *clip)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	size_t linepixels = clip->x2 - clip->x1;
2718c2ecf20Sopenharmony_ci	size_t dst_len = linepixels * 3;
2728c2ecf20Sopenharmony_ci	unsigned y, lines = clip->y2 - clip->y1;
2738c2ecf20Sopenharmony_ci	void *dbuf;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	dbuf = kmalloc(dst_len, GFP_KERNEL);
2768c2ecf20Sopenharmony_ci	if (!dbuf)
2778c2ecf20Sopenharmony_ci		return;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
2808c2ecf20Sopenharmony_ci	dst += clip_offset(clip, dst_pitch, sizeof(u16));
2818c2ecf20Sopenharmony_ci	for (y = 0; y < lines; y++) {
2828c2ecf20Sopenharmony_ci		drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels);
2838c2ecf20Sopenharmony_ci		memcpy_toio(dst, dbuf, dst_len);
2848c2ecf20Sopenharmony_ci		vaddr += fb->pitches[0];
2858c2ecf20Sopenharmony_ci		dst += dst_len;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	kfree(dbuf);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/**
2938c2ecf20Sopenharmony_ci * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
2948c2ecf20Sopenharmony_ci * @dst: 8-bit grayscale destination buffer
2958c2ecf20Sopenharmony_ci * @vaddr: XRGB8888 source buffer
2968c2ecf20Sopenharmony_ci * @fb: DRM framebuffer
2978c2ecf20Sopenharmony_ci * @clip: Clip rectangle area to copy
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * Drm doesn't have native monochrome or grayscale support.
3008c2ecf20Sopenharmony_ci * Such drivers can announce the commonly supported XR24 format to userspace
3018c2ecf20Sopenharmony_ci * and use this function to convert to the native format.
3028c2ecf20Sopenharmony_ci *
3038c2ecf20Sopenharmony_ci * Monochrome drivers will use the most significant bit,
3048c2ecf20Sopenharmony_ci * where 1 means foreground color and 0 background color.
3058c2ecf20Sopenharmony_ci *
3068c2ecf20Sopenharmony_ci * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
3078c2ecf20Sopenharmony_ci */
3088c2ecf20Sopenharmony_civoid drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
3098c2ecf20Sopenharmony_ci			       struct drm_rect *clip)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
3128c2ecf20Sopenharmony_ci	unsigned int x, y;
3138c2ecf20Sopenharmony_ci	void *buf;
3148c2ecf20Sopenharmony_ci	u32 *src;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
3178c2ecf20Sopenharmony_ci		return;
3188c2ecf20Sopenharmony_ci	/*
3198c2ecf20Sopenharmony_ci	 * The cma memory is write-combined so reads are uncached.
3208c2ecf20Sopenharmony_ci	 * Speed up by fetching one line at a time.
3218c2ecf20Sopenharmony_ci	 */
3228c2ecf20Sopenharmony_ci	buf = kmalloc(len, GFP_KERNEL);
3238c2ecf20Sopenharmony_ci	if (!buf)
3248c2ecf20Sopenharmony_ci		return;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	for (y = clip->y1; y < clip->y2; y++) {
3278c2ecf20Sopenharmony_ci		src = vaddr + (y * fb->pitches[0]);
3288c2ecf20Sopenharmony_ci		src += clip->x1;
3298c2ecf20Sopenharmony_ci		memcpy(buf, src, len);
3308c2ecf20Sopenharmony_ci		src = buf;
3318c2ecf20Sopenharmony_ci		for (x = clip->x1; x < clip->x2; x++) {
3328c2ecf20Sopenharmony_ci			u8 r = (*src & 0x00ff0000) >> 16;
3338c2ecf20Sopenharmony_ci			u8 g = (*src & 0x0000ff00) >> 8;
3348c2ecf20Sopenharmony_ci			u8 b =  *src & 0x000000ff;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci			/* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
3378c2ecf20Sopenharmony_ci			*dst++ = (3 * r + 6 * g + b) / 10;
3388c2ecf20Sopenharmony_ci			src++;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	kfree(buf);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
3458c2ecf20Sopenharmony_ci
346