[PATCH 2/5] fbdev/core: Export framebuffer read and write code as cfb_ function

Thomas Zimmermann tzimmermann at suse.de
Mon Aug 3 06:46:34 UTC 2020


Hi

Am 02.08.20 um 22:01 schrieb Sam Ravnborg:
> On Fri, Jul 31, 2020 at 11:20:33AM +0200, daniel at ffwll.ch wrote:
>> On Wed, Jul 29, 2020 at 06:36:03PM +0200, Sam Ravnborg wrote:
>>> Hi Daniel.
>>>
>>> On Wed, Jul 29, 2020 at 03:53:28PM +0200, daniel at ffwll.ch wrote:
>>>> On Wed, Jul 29, 2020 at 03:41:45PM +0200, Thomas Zimmermann wrote:
>>>>> DRM fb helpers require read and write functions for framebuffer
>>>>> memory. Export the existing code from fbdev.
>>>>>
>>>>> Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de>
>>>>
>>>> Hm I'm not super sure whether we want to actually reuse this stuff ... We
>>>> kinda don't care about the sparc special case, and just having an fbdev
>>>> implementation witch has the switch between memcpy and memcpy_to/from_io
>>>> in one single place sounds a lot simpler ...
>>>>
>>>> This way we can have a clean split between the old horrors of real fbdev
>>>> drivers, and a much cleaner world in drm. It would mean a bit of
>>>> copypasting, but I think that's actually a good thing.
>>>>
>>>> In general my idea for drm fbdev emulation is that for any area we have a
>>>> problem we just ignore the entire fbmem.c code and write our own: mmap,
>>>> backlight handling (still unsolved, and horrible), cfb vs sys here. This
>>>> entire fbmem.c stuff is pretty bad midlayer, trying to avoid code
>>>> duplication here doesn't seem worth it imo.
>>>>
>>>> Thoughts?
>>>
>>>
>>> I can see that fbmem is a mix of ioctl support and other stuff.
>>> We could factor out all the ioctl parts of fbmem.c to a new file
>>> named fbioctl.c.
>>>
>>> And then let the ioctl parts call down into drm stuff and avoid reusing
>>> the fbdev code when we first reach drm code.
>>> This would require local copies of:
>>> sys_read, sys_write, sys_fillrect, sys_copyarea, sys_imageblit
>>> and more I think which I missed.
>>>
>>> With local copies we could avoid some of the special cases and trim the
>>> unctions to what is required by drm only.
>>> And then no more fbmem dependencies and no dependencies to several of
>>> the small helper functions. So less entanglement with fbdev core.
>>>
>>> This all sounds simple so I am surely missing a lot a ugly details here.
>>>
>>> And should we touch this anyway we need a test suite to verify not too
>>> much breaks. To the best of my knowledge there is not yet such a test
>>> suite :-( Maybe because people caring about fbdev are limited.
>>
>> Well my idea was to not refactor anything, but just have drm copies of the
>> various fb_ops callbacks. Definitely not even more refactoring :-)

Thanks for making a prototype, Sam.

But do we really want to duplicate alls this code from fbdev? It's not
actually pretty and there's little value in rewritting it. If anything,
I can see us extending drm_format_helpers.c and building upon this.

Best regards
Thomas

> 
> $ wc -l drivers/gpu/drm/drm_fb_fbdev_helper.c
> 1212 drivers/gpu/drm/drm_fb_fbdev_helper.c
> 
> This is a straight copy of:
> $ grep EXPORT drivers/gpu/drm/drm_fb_fbdev_helper.c
> EXPORT_SYMBOL(drm_copyarea);
> EXPORT_SYMBOL(drm_fillrect);
> EXPORT_SYMBOL(drm_imageblit);
> EXPORT_SYMBOL_GPL(drm_fb_read);
> EXPORT_SYMBOL_GPL(drm_fb_write);
> 
> copyarea, fillrect and imageblit are a copy of the sys variants.
> read and write are from fb_sys_fops.c
> 
> Patch builds but I am not a big fan yet - right now it gains us very
> little. What would the next step be?
> 
> I looked briefly at the few drivers that have not migrated to
> the generic fbdev emulation (yet?).
>   msm
>   omapdrm
>   gma500
> 
> all uses sys_ variants - they already uses the migrated variants as
> they call the drm_fb helper.
> 
> 
>   exynos
>   gma500
>   i915
>   nouveau
>   radeon
>   rockchip
>   vmwgfx
> 
> all uses the cfb variants and cannot use the migrated variants as-is.
> 
> 	Sam
> 
> From c554e8b9ec96e06a5933ed5bd834a928c977bcdf Mon Sep 17 00:00:00 2001
> From: Sam Ravnborg <sam at ravnborg.org>
> Date: Fri, 31 Jul 2020 23:36:01 +0200
> Subject: [PATCH 1/3] drm: drm_fb_helper: copy fb_ops functions to drm
> 
> Copy the fbdev functions used by fb_ops to drm.
> Having local copies enable the possibility
> to adjust the functions so then are tailored to the need of DRM.
> At the same time there is one less dependency between DRM and fbdev.
> 
> The files were copied verbatim and only minimal changes was made.
> 
> As some drivers uses the fbdev functions they continue to be build but
> the generic fbdev emulation no longer uses the fbdev functions.
> 
> Signed-off-by: Sam Ravnborg <sam at ravnborg.org>
> Suggested-by: Daniel Vetter <daniel.vetter at ffwll.ch>
> ---
>  drivers/gpu/drm/Kconfig               |    1 -
>  drivers/gpu/drm/Makefile              |    1 +
>  drivers/gpu/drm/drm_fb_fbdev_helper.c | 1212 +++++++++++++++++++++++++
>  drivers/gpu/drm/drm_fb_helper.c       |   30 +-
>  include/drm/drm_fb_helper.h           |    6 +
>  5 files changed, 1234 insertions(+), 16 deletions(-)
>  create mode 100644 drivers/gpu/drm/drm_fb_fbdev_helper.c
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index c4fd57d8b717..11f41bc31f05 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -83,7 +83,6 @@ config DRM_KMS_FB_HELPER
>  	select FB
>  	select FRAMEBUFFER_CONSOLE if !EXPERT
>  	select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE
> -	select FB_SYS_FOPS
>  	select FB_SYS_FILLRECT
>  	select FB_SYS_COPYAREA
>  	select FB_SYS_IMAGEBLIT
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 02ee5faf1a92..398cb4746762 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -50,6 +50,7 @@ drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o drm_dp_helper.o \
>  
>  drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
>  drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
> +drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_fbdev_helper.o
>  drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
>  drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
>  drm_kms_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o
> diff --git a/drivers/gpu/drm/drm_fb_fbdev_helper.c b/drivers/gpu/drm/drm_fb_fbdev_helper.c
> new file mode 100644
> index 000000000000..dda60f85b9d5
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_fb_fbdev_helper.c
> @@ -0,0 +1,1212 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive for
> + * more details.
> + *
> + * Copyright (C)  2007 Antonino Daplas <adaplas at pol.net>
> + *
> + * drm_copyarea:
> + *   Based almost entirely from cfbcopyarea.c (which is based almost entirely
> + *   on Geert Uytterhoeven's copyarea routine)
> + *
> + *  drm_fillrect:
> + *    Based almost entirely from cfbfillrect.c (which is based almost entirely
> + *    on Geert Uytterhoeven's fillrect routine)
> + *
> + *  drm_imageblit:
> + *    Based almost entirely on cfbimgblt.c
> + *
> + */
> +
> +#include <linux/export.h>
> +#include <linux/fb.h>
> +#include <linux/io.h>
> +
> +
> +/*
> +*  Compose two values, using a bitmask as decision value
> +*  This is equivalent to (a & mask) | (b & ~mask)
> +*/
> +static inline unsigned long
> +comp(unsigned long a, unsigned long b, unsigned long mask)
> +{
> +    return ((a ^ b) & mask) ^ b;
> +}
> +
> +/*
> + *  Create a pattern with the given pixel's color
> + */
> +#if BITS_PER_LONG == 64
> +static inline unsigned long
> +pixel_to_pat( u32 bpp, u32 pixel)
> +{
> +	switch (bpp) {
> +	case 1:
> +		return 0xfffffffffffffffful*pixel;
> +	case 2:
> +		return 0x5555555555555555ul*pixel;
> +	case 4:
> +		return 0x1111111111111111ul*pixel;
> +	case 8:
> +		return 0x0101010101010101ul*pixel;
> +	case 12:
> +		return 0x1001001001001001ul*pixel;
> +	case 16:
> +		return 0x0001000100010001ul*pixel;
> +	case 24:
> +		return 0x0001000001000001ul*pixel;
> +	case 32:
> +		return 0x0000000100000001ul*pixel;
> +	default:
> +		WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
> +		return 0;
> +    }
> +}
> +#else
> +static inline unsigned long
> +pixel_to_pat( u32 bpp, u32 pixel)
> +{
> +	switch (bpp) {
> +	case 1:
> +		return 0xfffffffful*pixel;
> +	case 2:
> +		return 0x55555555ul*pixel;
> +	case 4:
> +		return 0x11111111ul*pixel;
> +	case 8:
> +		return 0x01010101ul*pixel;
> +	case 12:
> +		return 0x01001001ul*pixel;
> +	case 16:
> +		return 0x00010001ul*pixel;
> +	case 24:
> +		return 0x01000001ul*pixel;
> +	case 32:
> +		return 0x00000001ul*pixel;
> +	default:
> +		WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
> +		return 0;
> +    }
> +}
> +#endif
> +
> +#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE
> +#if BITS_PER_LONG == 64
> +#define REV_PIXELS_MASK1 0x5555555555555555ul
> +#define REV_PIXELS_MASK2 0x3333333333333333ul
> +#define REV_PIXELS_MASK4 0x0f0f0f0f0f0f0f0ful
> +#else
> +#define REV_PIXELS_MASK1 0x55555555ul
> +#define REV_PIXELS_MASK2 0x33333333ul
> +#define REV_PIXELS_MASK4 0x0f0f0f0ful
> +#endif
> +
> +static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
> +						  u32 bswapmask)
> +{
> +	if (bswapmask & 1)
> +		val = comp(val >> 1, val << 1, REV_PIXELS_MASK1);
> +	if (bswapmask & 2)
> +		val = comp(val >> 2, val << 2, REV_PIXELS_MASK2);
> +	if (bswapmask & 3)
> +		val = comp(val >> 4, val << 4, REV_PIXELS_MASK4);
> +	return val;
> +}
> +
> +static inline u32 fb_shifted_pixels_mask_u32(struct fb_info *p, u32 index,
> +					     u32 bswapmask)
> +{
> +	u32 mask;
> +
> +	if (!bswapmask) {
> +		mask = FB_SHIFT_HIGH(p, ~(u32)0, index);
> +	} else {
> +		mask = 0xff << FB_LEFT_POS(p, 8);
> +		mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
> +		mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
> +#if defined(__i386__) || defined(__x86_64__)
> +		/* Shift argument is limited to 0 - 31 on x86 based CPU's */
> +		if(index + bswapmask < 32)
> +#endif
> +			mask |= FB_SHIFT_HIGH(p, ~(u32)0,
> +					(index + bswapmask) & ~(bswapmask));
> +	}
> +	return mask;
> +}
> +
> +static inline unsigned long fb_shifted_pixels_mask_long(struct fb_info *p,
> +							u32 index,
> +							u32 bswapmask)
> +{
> +	unsigned long mask;
> +
> +	if (!bswapmask) {
> +		mask = FB_SHIFT_HIGH(p, ~0UL, index);
> +	} else {
> +		mask = 0xff << FB_LEFT_POS(p, 8);
> +		mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
> +		mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
> +#if defined(__i386__) || defined(__x86_64__)
> +		/* Shift argument is limited to 0 - 31 on x86 based CPU's */
> +		if(index + bswapmask < BITS_PER_LONG)
> +#endif
> +			mask |= FB_SHIFT_HIGH(p, ~0UL,
> +					(index + bswapmask) & ~(bswapmask));
> +	}
> +	return mask;
> +}
> +
> +
> +static inline u32 fb_compute_bswapmask(struct fb_info *info)
> +{
> +	u32 bswapmask = 0;
> +	unsigned bpp = info->var.bits_per_pixel;
> +
> +	if ((bpp < 8) && (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B)) {
> +		/*
> +		 * Reversed order of pixel layout in bytes
> +		 * works only for 1, 2 and 4 bpp
> +		 */
> +		bswapmask = 7 - bpp + 1;
> +	}
> +	return bswapmask;
> +}
> +
> +#else /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
> +
> +static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
> +						  u32 bswapmask)
> +{
> +	return val;
> +}
> +
> +#define fb_shifted_pixels_mask_u32(p, i, b) FB_SHIFT_HIGH((p), ~(u32)0, (i))
> +#define fb_shifted_pixels_mask_long(p, i, b) FB_SHIFT_HIGH((p), ~0UL, (i))
> +#define fb_compute_bswapmask(...) 0
> +
> +#endif  /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
> +
> +#define cpu_to_le_long _cpu_to_le_long(BITS_PER_LONG)
> +#define _cpu_to_le_long(x) __cpu_to_le_long(x)
> +#define __cpu_to_le_long(x) cpu_to_le##x
> +
> +#define le_long_to_cpu _le_long_to_cpu(BITS_PER_LONG)
> +#define _le_long_to_cpu(x) __le_long_to_cpu(x)
> +#define __le_long_to_cpu(x) le##x##_to_cpu
> +
> +static inline unsigned long rolx(unsigned long word, unsigned int shift, unsigned int x)
> +{
> +	return (word << shift) | (word >> (x - shift));
> +}
> +
> +
> +/*
> + *  Generic bitwise copy algorithm
> + */
> +static void
> +bitcpy(struct fb_info *p, unsigned long *dst, unsigned dst_idx,
> +	const unsigned long *src, unsigned src_idx, int bits, unsigned n)
> +{
> +	unsigned long first, last;
> +	int const shift = dst_idx-src_idx;
> +	int left, right;
> +
> +	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
> +	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
> +
> +	if (!shift) {
> +		/* Same alignment for source and dest */
> +		if (dst_idx+n <= bits) {
> +			/* Single word */
> +			if (last)
> +				first &= last;
> +			*dst = comp(*src, *dst, first);
> +		} else {
> +			/* Multiple destination words */
> +			/* Leading bits */
> + 			if (first != ~0UL) {
> +				*dst = comp(*src, *dst, first);
> +				dst++;
> +				src++;
> +				n -= bits - dst_idx;
> +			}
> +
> +			/* Main chunk */
> +			n /= bits;
> +			while (n >= 8) {
> +				*dst++ = *src++;
> +				*dst++ = *src++;
> +				*dst++ = *src++;
> +				*dst++ = *src++;
> +				*dst++ = *src++;
> +				*dst++ = *src++;
> +				*dst++ = *src++;
> +				*dst++ = *src++;
> +				n -= 8;
> +			}
> +			while (n--)
> +				*dst++ = *src++;
> +
> +			/* Trailing bits */
> +			if (last)
> +				*dst = comp(*src, *dst, last);
> +		}
> +	} else {
> +		unsigned long d0, d1;
> +		int m;
> +
> +		/* Different alignment for source and dest */
> +		right = shift & (bits - 1);
> +		left = -shift & (bits - 1);
> +
> +		if (dst_idx+n <= bits) {
> +			/* Single destination word */
> +			if (last)
> +				first &= last;
> +			if (shift > 0) {
> +				/* Single source word */
> +				*dst = comp(*src << left, *dst, first);
> +			} else if (src_idx+n <= bits) {
> +				/* Single source word */
> +				*dst = comp(*src >> right, *dst, first);
> +			} else {
> +				/* 2 source words */
> +				d0 = *src++;
> +				d1 = *src;
> +				*dst = comp(d0 >> right | d1 << left, *dst,
> +					    first);
> +			}
> +		} else {
> +			/* Multiple destination words */
> +			/** We must always remember the last value read,
> +			    because in case SRC and DST overlap bitwise (e.g.
> +			    when moving just one pixel in 1bpp), we always
> +			    collect one full long for DST and that might
> +			    overlap with the current long from SRC. We store
> +			    this value in 'd0'. */
> +			d0 = *src++;
> +			/* Leading bits */
> +			if (shift > 0) {
> +				/* Single source word */
> +				*dst = comp(d0 << left, *dst, first);
> +				dst++;
> +				n -= bits - dst_idx;
> +			} else {
> +				/* 2 source words */
> +				d1 = *src++;
> +				*dst = comp(d0 >> right | d1 << left, *dst,
> +					    first);
> +				d0 = d1;
> +				dst++;
> +				n -= bits - dst_idx;
> +			}
> +
> +			/* Main chunk */
> +			m = n % bits;
> +			n /= bits;
> +			while (n >= 4) {
> +				d1 = *src++;
> +				*dst++ = d0 >> right | d1 << left;
> +				d0 = d1;
> +				d1 = *src++;
> +				*dst++ = d0 >> right | d1 << left;
> +				d0 = d1;
> +				d1 = *src++;
> +				*dst++ = d0 >> right | d1 << left;
> +				d0 = d1;
> +				d1 = *src++;
> +				*dst++ = d0 >> right | d1 << left;
> +				d0 = d1;
> +				n -= 4;
> +			}
> +			while (n--) {
> +				d1 = *src++;
> +				*dst++ = d0 >> right | d1 << left;
> +				d0 = d1;
> +			}
> +
> +			/* Trailing bits */
> +			if (m) {
> +				if (m <= bits - right) {
> +					/* Single source word */
> +					d0 >>= right;
> +				} else {
> +					/* 2 source words */
> + 					d1 = *src;
> +					d0 = d0 >> right | d1 << left;
> +				}
> +				*dst = comp(d0, *dst, last);
> +			}
> +		}
> +	}
> +}
> +
> +/*
> + *  Generic bitwise copy algorithm, operating backward
> + */
> +static void
> +bitcpy_rev(struct fb_info *p, unsigned long *dst, unsigned dst_idx,
> +	   const unsigned long *src, unsigned src_idx, unsigned bits,
> +	   unsigned n)
> +{
> +	unsigned long first, last;
> +	int shift;
> +
> +	dst += (dst_idx + n - 1) / bits;
> +	src += (src_idx + n - 1) / bits;
> +	dst_idx = (dst_idx + n - 1) % bits;
> +	src_idx = (src_idx + n - 1) % bits;
> +
> +	shift = dst_idx-src_idx;
> +
> +	first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits);
> +	last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits);
> +
> +	if (!shift) {
> +		/* Same alignment for source and dest */
> +		if ((unsigned long)dst_idx+1 >= n) {
> +			/* Single word */
> +			if (first)
> +				last &= first;
> +			*dst = comp(*src, *dst, last);
> +		} else {
> +			/* Multiple destination words */
> +
> +			/* Leading bits */
> +			if (first) {
> +				*dst = comp(*src, *dst, first);
> +				dst--;
> +				src--;
> +				n -= dst_idx+1;
> +			}
> +
> +			/* Main chunk */
> +			n /= bits;
> +			while (n >= 8) {
> +				*dst-- = *src--;
> +				*dst-- = *src--;
> +				*dst-- = *src--;
> +				*dst-- = *src--;
> +				*dst-- = *src--;
> +				*dst-- = *src--;
> +				*dst-- = *src--;
> +				*dst-- = *src--;
> +				n -= 8;
> +			}
> +			while (n--)
> +				*dst-- = *src--;
> +			/* Trailing bits */
> +			if (last != -1UL)
> +				*dst = comp(*src, *dst, last);
> +		}
> +	} else {
> +		/* Different alignment for source and dest */
> +
> +		int const left = shift & (bits-1);
> +		int const right = -shift & (bits-1);
> +
> +		if ((unsigned long)dst_idx+1 >= n) {
> +			/* Single destination word */
> +			if (first)
> +				last &= first;
> +			if (shift < 0) {
> +				/* Single source word */
> +				*dst = comp(*src >> right, *dst, last);
> +			} else if (1+(unsigned long)src_idx >= n) {
> +				/* Single source word */
> +				*dst = comp(*src << left, *dst, last);
> +			} else {
> +				/* 2 source words */
> +				*dst = comp(*src << left | *(src-1) >> right,
> +					    *dst, last);
> +			}
> +		} else {
> +			/* Multiple destination words */
> +			/** We must always remember the last value read,
> +			    because in case SRC and DST overlap bitwise (e.g.
> +			    when moving just one pixel in 1bpp), we always
> +			    collect one full long for DST and that might
> +			    overlap with the current long from SRC. We store
> +			    this value in 'd0'. */
> +			unsigned long d0, d1;
> +			int m;
> +
> +			d0 = *src--;
> +			/* Leading bits */
> +			if (shift < 0) {
> +				/* Single source word */
> +				d1 = d0;
> +				d0 >>= right;
> +			} else {
> +				/* 2 source words */
> +				d1 = *src--;
> +				d0 = d0 << left | d1 >> right;
> +			}
> +			if (!first)
> +				*dst = d0;
> +			else
> +				*dst = comp(d0, *dst, first);
> +			d0 = d1;
> +			dst--;
> +			n -= dst_idx+1;
> +
> +			/* Main chunk */
> +			m = n % bits;
> +			n /= bits;
> +			while (n >= 4) {
> +				d1 = *src--;
> +				*dst-- = d0 << left | d1 >> right;
> +				d0 = d1;
> +				d1 = *src--;
> +				*dst-- = d0 << left | d1 >> right;
> +				d0 = d1;
> +				d1 = *src--;
> +				*dst-- = d0 << left | d1 >> right;
> +				d0 = d1;
> +				d1 = *src--;
> +				*dst-- = d0 << left | d1 >> right;
> +				d0 = d1;
> +				n -= 4;
> +			}
> +			while (n--) {
> +				d1 = *src--;
> +				*dst-- = d0 << left | d1 >> right;
> +				d0 = d1;
> +			}
> +
> +			/* Trailing bits */
> +			if (m) {
> +				if (m <= bits - left) {
> +					/* Single source word */
> +					d0 <<= left;
> +				} else {
> +					/* 2 source words */
> +					d1 = *src;
> +					d0 = d0 << left | d1 >> right;
> +				}
> +				*dst = comp(d0, *dst, last);
> +			}
> +		}
> +	}
> +}
> +
> +/*
> + * Generic Bit Block Transfer for frame buffers located in system RAM with
> + * packed pixels of any depth.
> + */
> +void drm_copyarea(struct fb_info *p, const struct fb_copyarea *area)
> +{
> +	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
> +	u32 height = area->height, width = area->width;
> +	unsigned long const bits_per_line = p->fix.line_length*8u;
> +	unsigned long *base = NULL;
> +	int bits = BITS_PER_LONG, bytes = bits >> 3;
> +	unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
> +
> +	if (p->state != FBINFO_STATE_RUNNING)
> +		return;
> +
> +	/* if the beginning of the target area might overlap with the end of
> +	the source area, be have to copy the area reverse. */
> +	if ((dy == sy && dx > sx) || (dy > sy)) {
> +		dy += height;
> +		sy += height;
> +		rev_copy = 1;
> +	}
> +
> +	/* split the base of the framebuffer into a long-aligned address and
> +	   the index of the first bit */
> +	base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
> +	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
> +	/* add offset of source and target area */
> +	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
> +	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
> +
> +	if (p->fbops->fb_sync)
> +		p->fbops->fb_sync(p);
> +
> +	if (rev_copy) {
> +		while (height--) {
> +			dst_idx -= bits_per_line;
> +			src_idx -= bits_per_line;
> +			bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
> +				base + (src_idx / bits), src_idx % bits, bits,
> +				width*p->var.bits_per_pixel);
> +		}
> +	} else {
> +		while (height--) {
> +			bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
> +				base + (src_idx / bits), src_idx % bits, bits,
> +				width*p->var.bits_per_pixel);
> +			dst_idx += bits_per_line;
> +			src_idx += bits_per_line;
> +		}
> +	}
> +}
> +EXPORT_SYMBOL(drm_copyarea);
> +
> +/*
> + *  Aligned pattern fill using 32/64-bit memory accesses
> + */
> +static void
> +bitfill_aligned(struct fb_info *p, unsigned long *dst, int dst_idx,
> +		unsigned long pat, unsigned n, int bits)
> +{
> +	unsigned long first, last;
> +
> +	if (!n)
> +		return;
> +
> +	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
> +	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
> +
> +	if (dst_idx+n <= bits) {
> +		/* Single word */
> +		if (last)
> +			first &= last;
> +		*dst = comp(pat, *dst, first);
> +	} else {
> +		/* Multiple destination words */
> +
> +		/* Leading bits */
> + 		if (first!= ~0UL) {
> +			*dst = comp(pat, *dst, first);
> +			dst++;
> +			n -= bits - dst_idx;
> +		}
> +
> +		/* Main chunk */
> +		n /= bits;
> +		while (n >= 8) {
> +			*dst++ = pat;
> +			*dst++ = pat;
> +			*dst++ = pat;
> +			*dst++ = pat;
> +			*dst++ = pat;
> +			*dst++ = pat;
> +			*dst++ = pat;
> +			*dst++ = pat;
> +			n -= 8;
> +		}
> +		while (n--)
> +			*dst++ = pat;
> +		/* Trailing bits */
> +		if (last)
> +			*dst = comp(pat, *dst, last);
> +	}
> +}
> +
> +
> +/*
> + *  Unaligned generic pattern fill using 32/64-bit memory accesses
> + *  The pattern must have been expanded to a full 32/64-bit value
> + *  Left/right are the appropriate shifts to convert to the pattern to be
> + *  used for the next 32/64-bit word
> + */
> +static void
> +bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx,
> +		  unsigned long pat, int left, int right, unsigned n, int bits)
> +{
> +	unsigned long first, last;
> +
> +	if (!n)
> +		return;
> +
> +	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
> +	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
> +
> +	if (dst_idx+n <= bits) {
> +		/* Single word */
> +		if (last)
> +			first &= last;
> +		*dst = comp(pat, *dst, first);
> +	} else {
> +		/* Multiple destination words */
> +		/* Leading bits */
> +		if (first) {
> +			*dst = comp(pat, *dst, first);
> +			dst++;
> +			pat = pat << left | pat >> right;
> +			n -= bits - dst_idx;
> +		}
> +
> +		/* Main chunk */
> +		n /= bits;
> +		while (n >= 4) {
> +			*dst++ = pat;
> +			pat = pat << left | pat >> right;
> +			*dst++ = pat;
> +			pat = pat << left | pat >> right;
> +			*dst++ = pat;
> +			pat = pat << left | pat >> right;
> +			*dst++ = pat;
> +			pat = pat << left | pat >> right;
> +			n -= 4;
> +		}
> +		while (n--) {
> +			*dst++ = pat;
> +			pat = pat << left | pat >> right;
> +		}
> +
> +		/* Trailing bits */
> +		if (last)
> +			*dst = comp(pat, *dst, last);
> +	}
> +}
> +
> +/*
> + *  Aligned pattern invert using 32/64-bit memory accesses
> + */
> +static void
> +bitfill_aligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
> +		    unsigned long pat, unsigned n, int bits)
> +{
> +	unsigned long val = pat;
> +	unsigned long first, last;
> +
> +	if (!n)
> +		return;
> +
> +	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
> +	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
> +
> +	if (dst_idx+n <= bits) {
> +		/* Single word */
> +		if (last)
> +			first &= last;
> +		*dst = comp(*dst ^ val, *dst, first);
> +	} else {
> +		/* Multiple destination words */
> +		/* Leading bits */
> +		if (first!=0UL) {
> +			*dst = comp(*dst ^ val, *dst, first);
> +			dst++;
> +			n -= bits - dst_idx;
> +		}
> +
> +		/* Main chunk */
> +		n /= bits;
> +		while (n >= 8) {
> +			*dst++ ^= val;
> +			*dst++ ^= val;
> +			*dst++ ^= val;
> +			*dst++ ^= val;
> +			*dst++ ^= val;
> +			*dst++ ^= val;
> +			*dst++ ^= val;
> +			*dst++ ^= val;
> +			n -= 8;
> +		}
> +		while (n--)
> +			*dst++ ^= val;
> +		/* Trailing bits */
> +		if (last)
> +			*dst = comp(*dst ^ val, *dst, last);
> +	}
> +}
> +
> +
> +/*
> + *  Unaligned generic pattern invert using 32/64-bit memory accesses
> + *  The pattern must have been expanded to a full 32/64-bit value
> + *  Left/right are the appropriate shifts to convert to the pattern to be
> + *  used for the next 32/64-bit word
> + */
> +static void
> +bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
> +		      unsigned long pat, int left, int right, unsigned n,
> +		      int bits)
> +{
> +	unsigned long first, last;
> +
> +	if (!n)
> +		return;
> +
> +	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
> +	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
> +
> +	if (dst_idx+n <= bits) {
> +		/* Single word */
> +		if (last)
> +			first &= last;
> +		*dst = comp(*dst ^ pat, *dst, first);
> +	} else {
> +		/* Multiple destination words */
> +
> +		/* Leading bits */
> +		if (first != 0UL) {
> +			*dst = comp(*dst ^ pat, *dst, first);
> +			dst++;
> +			pat = pat << left | pat >> right;
> +			n -= bits - dst_idx;
> +		}
> +
> +		/* Main chunk */
> +		n /= bits;
> +		while (n >= 4) {
> +			*dst++ ^= pat;
> +			pat = pat << left | pat >> right;
> +			*dst++ ^= pat;
> +			pat = pat << left | pat >> right;
> +			*dst++ ^= pat;
> +			pat = pat << left | pat >> right;
> +			*dst++ ^= pat;
> +			pat = pat << left | pat >> right;
> +			n -= 4;
> +		}
> +		while (n--) {
> +			*dst ^= pat;
> +			pat = pat << left | pat >> right;
> +		}
> +
> +		/* Trailing bits */
> +		if (last)
> +			*dst = comp(*dst ^ pat, *dst, last);
> +	}
> +}
> +
> +/*
> + * Generic fillrect for frame buffers in system RAM with packed pixels of
> + * any depth.
> + */
> +void drm_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
> +{
> +	unsigned long pat, pat2, fg;
> +	unsigned long width = rect->width, height = rect->height;
> +	int bits = BITS_PER_LONG, bytes = bits >> 3;
> +	u32 bpp = p->var.bits_per_pixel;
> +	unsigned long *dst;
> +	int dst_idx, left;
> +
> +	if (p->state != FBINFO_STATE_RUNNING)
> +		return;
> +
> +	if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
> +	    p->fix.visual == FB_VISUAL_DIRECTCOLOR )
> +		fg = ((u32 *) (p->pseudo_palette))[rect->color];
> +	else
> +		fg = rect->color;
> +
> +	pat = pixel_to_pat( bpp, fg);
> +
> +	dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
> +	dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
> +	dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
> +	/* FIXME For now we support 1-32 bpp only */
> +	left = bits % bpp;
> +	if (p->fbops->fb_sync)
> +		p->fbops->fb_sync(p);
> +	if (!left) {
> +		void (*fill_op32)(struct fb_info *p, unsigned long *dst,
> +				  int dst_idx, unsigned long pat, unsigned n,
> +				  int bits) = NULL;
> +
> +		switch (rect->rop) {
> +		case ROP_XOR:
> +			fill_op32 = bitfill_aligned_rev;
> +			break;
> +		case ROP_COPY:
> +			fill_op32 = bitfill_aligned;
> +			break;
> +		default:
> +			printk( KERN_ERR "cfb_fillrect(): unknown rop, "
> +				"defaulting to ROP_COPY\n");
> +			fill_op32 = bitfill_aligned;
> +			break;
> +		}
> +		while (height--) {
> +			dst += dst_idx >> (ffs(bits) - 1);
> +			dst_idx &= (bits - 1);
> +			fill_op32(p, dst, dst_idx, pat, width*bpp, bits);
> +			dst_idx += p->fix.line_length*8;
> +		}
> +	} else {
> +		int right, r;
> +		void (*fill_op)(struct fb_info *p, unsigned long *dst,
> +				int dst_idx, unsigned long pat, int left,
> +				int right, unsigned n, int bits) = NULL;
> +#ifdef __LITTLE_ENDIAN
> +		right = left;
> +		left = bpp - right;
> +#else
> +		right = bpp - left;
> +#endif
> +		switch (rect->rop) {
> +		case ROP_XOR:
> +			fill_op = bitfill_unaligned_rev;
> +			break;
> +		case ROP_COPY:
> +			fill_op = bitfill_unaligned;
> +			break;
> +		default:
> +			printk(KERN_ERR "sys_fillrect(): unknown rop, "
> +				"defaulting to ROP_COPY\n");
> +			fill_op = bitfill_unaligned;
> +			break;
> +		}
> +		while (height--) {
> +			dst += dst_idx / bits;
> +			dst_idx &= (bits - 1);
> +			r = dst_idx % bpp;
> +			/* rotate pattern to the correct start position */
> +			pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
> +			fill_op(p, dst, dst_idx, pat2, left, right,
> +				width*bpp, bits);
> +			dst_idx += p->fix.line_length*8;
> +		}
> +	}
> +}
> +EXPORT_SYMBOL(drm_fillrect);
> +
> +static const u32 cfb_tab8_be[] = {
> +    0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
> +    0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
> +    0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
> +    0xffff0000,0xffff00ff,0xffffff00,0xffffffff
> +};
> +
> +static const u32 cfb_tab8_le[] = {
> +    0x00000000,0xff000000,0x00ff0000,0xffff0000,
> +    0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
> +    0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
> +    0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
> +};
> +
> +static const u32 cfb_tab16_be[] = {
> +    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
> +};
> +
> +static const u32 cfb_tab16_le[] = {
> +    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
> +};
> +
> +static const u32 cfb_tab32[] = {
> +	0x00000000, 0xffffffff
> +};
> +
> +static void color_imageblit(const struct fb_image *image, struct fb_info *p,
> +			    void *dst1, u32 start_index, u32 pitch_index)
> +{
> +	/* Draw the penguin */
> +	u32 *dst, *dst2;
> +	u32 color = 0, val, shift;
> +	int i, n, bpp = p->var.bits_per_pixel;
> +	u32 null_bits = 32 - bpp;
> +	u32 *palette = (u32 *) p->pseudo_palette;
> +	const u8 *src = image->data;
> +
> +	dst2 = dst1;
> +	for (i = image->height; i--; ) {
> +		n = image->width;
> +		dst = dst1;
> +		shift = 0;
> +		val = 0;
> +
> +		if (start_index) {
> +			u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
> +							 start_index));
> +			val = *dst & start_mask;
> +			shift = start_index;
> +		}
> +		while (n--) {
> +			if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
> +			    p->fix.visual == FB_VISUAL_DIRECTCOLOR )
> +				color = palette[*src];
> +			else
> +				color = *src;
> +			color <<= FB_LEFT_POS(p, bpp);
> +			val |= FB_SHIFT_HIGH(p, color, shift);
> +			if (shift >= null_bits) {
> +				*dst++ = val;
> +
> +				val = (shift == null_bits) ? 0 :
> +					FB_SHIFT_LOW(p, color, 32 - shift);
> +			}
> +			shift += bpp;
> +			shift &= (32 - 1);
> +			src++;
> +		}
> +		if (shift) {
> +			u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
> +
> +			*dst &= end_mask;
> +			*dst |= val;
> +		}
> +		dst1 += p->fix.line_length;
> +		if (pitch_index) {
> +			dst2 += p->fix.line_length;
> +			dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
> +
> +			start_index += pitch_index;
> +			start_index &= 32 - 1;
> +		}
> +	}
> +}
> +
> +static void slow_imageblit(const struct fb_image *image, struct fb_info *p,
> +				  void *dst1, u32 fgcolor, u32 bgcolor,
> +				  u32 start_index, u32 pitch_index)
> +{
> +	u32 shift, color = 0, bpp = p->var.bits_per_pixel;
> +	u32 *dst, *dst2;
> +	u32 val, pitch = p->fix.line_length;
> +	u32 null_bits = 32 - bpp;
> +	u32 spitch = (image->width+7)/8;
> +	const u8 *src = image->data, *s;
> +	u32 i, j, l;
> +
> +	dst2 = dst1;
> +	fgcolor <<= FB_LEFT_POS(p, bpp);
> +	bgcolor <<= FB_LEFT_POS(p, bpp);
> +
> +	for (i = image->height; i--; ) {
> +		shift = val = 0;
> +		l = 8;
> +		j = image->width;
> +		dst = dst1;
> +		s = src;
> +
> +		/* write leading bits */
> +		if (start_index) {
> +			u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
> +							 start_index));
> +			val = *dst & start_mask;
> +			shift = start_index;
> +		}
> +
> +		while (j--) {
> +			l--;
> +			color = (*s & (1 << l)) ? fgcolor : bgcolor;
> +			val |= FB_SHIFT_HIGH(p, color, shift);
> +
> +			/* Did the bitshift spill bits to the next long? */
> +			if (shift >= null_bits) {
> +				*dst++ = val;
> +				val = (shift == null_bits) ? 0 :
> +					FB_SHIFT_LOW(p, color, 32 - shift);
> +			}
> +			shift += bpp;
> +			shift &= (32 - 1);
> +			if (!l) { l = 8; s++; }
> +		}
> +
> +		/* write trailing bits */
> + 		if (shift) {
> +			u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
> +
> +			*dst &= end_mask;
> +			*dst |= val;
> +		}
> +
> +		dst1 += pitch;
> +		src += spitch;
> +		if (pitch_index) {
> +			dst2 += pitch;
> +			dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
> +			start_index += pitch_index;
> +			start_index &= 32 - 1;
> +		}
> +
> +	}
> +}
> +
> +/*
> + * fast_imageblit - optimized monochrome color expansion
> + *
> + * Only if:  bits_per_pixel == 8, 16, or 32
> + *           image->width is divisible by pixel/dword (ppw);
> + *           fix->line_legth is divisible by 4;
> + *           beginning and end of a scanline is dword aligned
> + */
> +static void fast_imageblit(const struct fb_image *image, struct fb_info *p,
> +				  void *dst1, u32 fgcolor, u32 bgcolor)
> +{
> +	u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
> +	u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
> +	u32 bit_mask, end_mask, eorx, shift;
> +	const char *s = image->data, *src;
> +	u32 *dst;
> +	const u32 *tab = NULL;
> +	int i, j, k;
> +
> +	switch (bpp) {
> +	case 8:
> +		tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
> +		break;
> +	case 16:
> +		tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
> +		break;
> +	case 32:
> +	default:
> +		tab = cfb_tab32;
> +		break;
> +	}
> +
> +	for (i = ppw-1; i--; ) {
> +		fgx <<= bpp;
> +		bgx <<= bpp;
> +		fgx |= fgcolor;
> +		bgx |= bgcolor;
> +	}
> +
> +	bit_mask = (1 << ppw) - 1;
> +	eorx = fgx ^ bgx;
> +	k = image->width/ppw;
> +
> +	for (i = image->height; i--; ) {
> +		dst = dst1;
> +		shift = 8;
> +		src = s;
> +
> +		for (j = k; j--; ) {
> +			shift -= ppw;
> +			end_mask = tab[(*src >> shift) & bit_mask];
> +			*dst++ = (end_mask & eorx) ^ bgx;
> +			if (!shift) {
> +				shift = 8;
> +				src++;
> +			}
> +		}
> +		dst1 += p->fix.line_length;
> +		s += spitch;
> +	}
> +}
> +
> +/*
> + * Generic 1-bit or 8-bit source to 1-32 bit destination expansion
> + * for frame buffer located in system RAM with packed pixels of any depth.
> + */
> +void drm_imageblit(struct fb_info *p, const struct fb_image *image)
> +{
> +	u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
> +	u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
> +	u32 width = image->width;
> +	u32 dx = image->dx, dy = image->dy;
> +	void *dst1;
> +
> +	if (p->state != FBINFO_STATE_RUNNING)
> +		return;
> +
> +	bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
> +	start_index = bitstart & (32 - 1);
> +	pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
> +
> +	bitstart /= 8;
> +	bitstart &= ~(bpl - 1);
> +	dst1 = (void __force *)p->screen_base + bitstart;
> +
> +	if (p->fbops->fb_sync)
> +		p->fbops->fb_sync(p);
> +
> +	if (image->depth == 1) {
> +		if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
> +		    p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
> +			fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
> +			bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
> +		} else {
> +			fgcolor = image->fg_color;
> +			bgcolor = image->bg_color;
> +		}
> +
> +		if (32 % bpp == 0 && !start_index && !pitch_index &&
> +		    ((width & (32/bpp-1)) == 0) &&
> +		    bpp >= 8 && bpp <= 32)
> +			fast_imageblit(image, p, dst1, fgcolor, bgcolor);
> +		else
> +			slow_imageblit(image, p, dst1, fgcolor, bgcolor,
> +					start_index, pitch_index);
> +	} else
> +		color_imageblit(image, p, dst1, start_index, pitch_index);
> +}
> +EXPORT_SYMBOL(drm_imageblit);
> +
> +/*
> + * Generic file operations where framebuffer is in system RAM
> + */
> +ssize_t drm_fb_read(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos)
> +{
> +	unsigned long p = *ppos;
> +	void *src;
> +	int err = 0;
> +	unsigned long total_size;
> +
> +	if (info->state != FBINFO_STATE_RUNNING)
> +		return -EPERM;
> +
> +	total_size = info->screen_size;
> +
> +	if (total_size == 0)
> +		total_size = info->fix.smem_len;
> +
> +	if (p >= total_size)
> +		return 0;
> +
> +	if (count >= total_size)
> +		count = total_size;
> +
> +	if (count + p > total_size)
> +		count = total_size - p;
> +
> +	src = (void __force *)(info->screen_base + p);
> +
> +	if (info->fbops->fb_sync)
> +		info->fbops->fb_sync(info);
> +
> +	if (copy_to_user(buf, src, count))
> +		err = -EFAULT;
> +
> +	if  (!err)
> +		*ppos += count;
> +
> +	return (err) ? err : count;
> +}
> +EXPORT_SYMBOL_GPL(drm_fb_read);
> +
> +/*
> + * Generic file operations where framebuffer is in system RAM
> + */
> +ssize_t drm_fb_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos)
> +{
> +	unsigned long p = *ppos;
> +	void *dst;
> +	int err = 0;
> +	unsigned long total_size;
> +
> +	if (info->state != FBINFO_STATE_RUNNING)
> +		return -EPERM;
> +
> +	total_size = info->screen_size;
> +
> +	if (total_size == 0)
> +		total_size = info->fix.smem_len;
> +
> +	if (p > total_size)
> +		return -EFBIG;
> +
> +	if (count > total_size) {
> +		err = -EFBIG;
> +		count = total_size;
> +	}
> +
> +	if (count + p > total_size) {
> +		if (!err)
> +			err = -ENOSPC;
> +
> +		count = total_size - p;
> +	}
> +
> +	dst = (void __force *) (info->screen_base + p);
> +
> +	if (info->fbops->fb_sync)
> +		info->fbops->fb_sync(info);
> +
> +	if (copy_from_user(dst, buf, count))
> +		err = -EFAULT;
> +
> +	if  (!err)
> +		*ppos += count;
> +
> +	return (err) ? err : count;
> +}
> +EXPORT_SYMBOL_GPL(drm_fb_write);
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index da0d96a69570..18fba9818635 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -664,36 +664,36 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
>  EXPORT_SYMBOL(drm_fb_helper_deferred_io);
>  
>  /**
> - * drm_fb_helper_sys_read - wrapper around fb_sys_read
> + * drm_fb_helper_sys_read - wrapper around drm_fb_read
>   * @info: fb_info struct pointer
>   * @buf: userspace buffer to read from framebuffer memory
>   * @count: number of bytes to read from framebuffer memory
>   * @ppos: read offset within framebuffer memory
>   *
> - * A wrapper around fb_sys_read implemented by fbdev core
> + * A wrapper around drm_fb_read
>   */
>  ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
>  			       size_t count, loff_t *ppos)
>  {
> -	return fb_sys_read(info, buf, count, ppos);
> +	return drm_fb_read(info, buf, count, ppos);
>  }
>  EXPORT_SYMBOL(drm_fb_helper_sys_read);
>  
>  /**
> - * drm_fb_helper_sys_write - wrapper around fb_sys_write
> + * drm_fb_helper_sys_write - wrapper around drm_fb_write
>   * @info: fb_info struct pointer
>   * @buf: userspace buffer to write to framebuffer memory
>   * @count: number of bytes to write to framebuffer memory
>   * @ppos: write offset within framebuffer memory
>   *
> - * A wrapper around fb_sys_write implemented by fbdev core
> + * A wrapper around drm_fb_write
>   */
>  ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
>  				size_t count, loff_t *ppos)
>  {
>  	ssize_t ret;
>  
> -	ret = fb_sys_write(info, buf, count, ppos);
> +	ret = drm_fb_write(info, buf, count, ppos);
>  	if (ret > 0)
>  		drm_fb_helper_dirty(info, 0, 0, info->var.xres,
>  				    info->var.yres);
> @@ -703,48 +703,48 @@ ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
>  EXPORT_SYMBOL(drm_fb_helper_sys_write);
>  
>  /**
> - * drm_fb_helper_sys_fillrect - wrapper around sys_fillrect
> + * drm_fb_helper_sys_fillrect - wrapper around drm_fillrect
>   * @info: fbdev registered by the helper
>   * @rect: info about rectangle to fill
>   *
> - * A wrapper around sys_fillrect implemented by fbdev core
> + * A wrapper around drm_fillrect
>   */
>  void drm_fb_helper_sys_fillrect(struct fb_info *info,
>  				const struct fb_fillrect *rect)
>  {
> -	sys_fillrect(info, rect);
> +	drm_fillrect(info, rect);
>  	drm_fb_helper_dirty(info, rect->dx, rect->dy,
>  			    rect->width, rect->height);
>  }
>  EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
>  
>  /**
> - * drm_fb_helper_sys_copyarea - wrapper around sys_copyarea
> + * drm_fb_helper_sys_copyarea - wrapper around drm_copyarea
>   * @info: fbdev registered by the helper
>   * @area: info about area to copy
>   *
> - * A wrapper around sys_copyarea implemented by fbdev core
> + * A wrapper around drm_copyarea
>   */
>  void drm_fb_helper_sys_copyarea(struct fb_info *info,
>  				const struct fb_copyarea *area)
>  {
> -	sys_copyarea(info, area);
> +	drm_copyarea(info, area);
>  	drm_fb_helper_dirty(info, area->dx, area->dy,
>  			    area->width, area->height);
>  }
>  EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
>  
>  /**
> - * drm_fb_helper_sys_imageblit - wrapper around sys_imageblit
> + * drm_fb_helper_sys_imageblit - wrapper around drm_imageblit
>   * @info: fbdev registered by the helper
>   * @image: info about image to blit
>   *
> - * A wrapper around sys_imageblit implemented by fbdev core
> + * A wrapper around drm_imageblit
>   */
>  void drm_fb_helper_sys_imageblit(struct fb_info *info,
>  				 const struct fb_image *image)
>  {
> -	sys_imageblit(info, image);
> +	drm_imageblit(info, image);
>  	drm_fb_helper_dirty(info, image->dx, image->dy,
>  			    image->width, image->height);
>  }
> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
> index 306aa3a60be9..d38005f6128a 100644
> --- a/include/drm/drm_fb_helper.h
> +++ b/include/drm/drm_fb_helper.h
> @@ -271,6 +271,12 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev);
>  
>  void drm_fbdev_generic_setup(struct drm_device *dev,
>  			     unsigned int preferred_bpp);
> +
> +void drm_copyarea(struct fb_info *p, const struct fb_copyarea *area);
> +void drm_fillrect(struct fb_info *p, const struct fb_fillrect *rect);
> +void drm_imageblit(struct fb_info *p, const struct fb_image *image);
> +ssize_t drm_fb_read(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos);
> +ssize_t drm_fb_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos);
>  #else
>  static inline void drm_fb_helper_prepare(struct drm_device *dev,
>  					struct drm_fb_helper *helper,
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 516 bytes
Desc: OpenPGP digital signature
URL: <https://lists.freedesktop.org/archives/dri-devel/attachments/20200803/3f4dbe8f/attachment-0001.sig>


More information about the dri-devel mailing list