diff mbox series

[v7,1/9] drm/format-helper: Add drm_fb_blit_from_r1 and drm_fb_fill

Message ID 20240104160301.185915-2-jfalempe@redhat.com (mailing list archive)
State New, archived
Headers show
Series drm/panic: Add a drm panic handler | expand

Commit Message

Jocelyn Falempe Jan. 4, 2024, 4 p.m. UTC
This is needed for drm_panic, to draw the font, and fill
the background color, in multiple color format.

v5:
 * Change the drawing API, use drm_fb_blit_from_r1() to draw the font.
 * Also add drm_fb_fill() to fill area with background color.
v6:
 * fix __le32 conversion warning found by the kernel test bot

Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
---
 drivers/gpu/drm/drm_format_helper.c | 432 ++++++++++++++++++++++------
 include/drm/drm_format_helper.h     |   9 +
 2 files changed, 360 insertions(+), 81 deletions(-)

Comments

Thomas Zimmermann Jan. 17, 2024, 3:06 p.m. UTC | #1
Hi

Am 04.01.24 um 17:00 schrieb Jocelyn Falempe:
> This is needed for drm_panic, to draw the font, and fill
> the background color, in multiple color format.

TBH, I don't like this patch at all. It looks like you're reimplementing 
existing functionality for a single use case; specifically drm_fb_blit().

What's wrong with the existing interfaces?

Best regards
Thomas

> 
> v5:
>   * Change the drawing API, use drm_fb_blit_from_r1() to draw the font.
>   * Also add drm_fb_fill() to fill area with background color.
> v6:
>   * fix __le32 conversion warning found by the kernel test bot
> 
> Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
> ---
>   drivers/gpu/drm/drm_format_helper.c | 432 ++++++++++++++++++++++------
>   include/drm/drm_format_helper.h     |   9 +
>   2 files changed, 360 insertions(+), 81 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c
> index b1be458ed4dd..8cbc2d747cff 100644
> --- a/drivers/gpu/drm/drm_format_helper.c
> +++ b/drivers/gpu/drm/drm_format_helper.c
> @@ -111,6 +111,153 @@ void drm_format_conv_state_release(struct drm_format_conv_state *state)
>   }
>   EXPORT_SYMBOL(drm_format_conv_state_release);
>   
> +static inline __le16 drm_format_xrgb8888_to_rgb565(__le32 val32)
> +{
> +	u16 val16;
> +	u32 pix;
> +
> +	pix = le32_to_cpu(val32);
> +	val16 = ((pix & 0x00F80000) >> 8) |
> +		((pix & 0x0000FC00) >> 5) |
> +		((pix & 0x000000F8) >> 3);
> +	return cpu_to_le16(val16);
> +}
> +
> +static inline __le16 drm_format_xrgb8888_to_rgba5551(__le32 val32)
> +{
> +	u16 val16;
> +	u32 pix;
> +
> +	pix = le32_to_cpu(val32);
> +	val16 = ((pix & 0x00f80000) >> 8) |
> +		((pix & 0x0000f800) >> 5) |
> +		((pix & 0x000000f8) >> 2) |
> +		BIT(0); /* set alpha bit */
> +	return cpu_to_le16(val16);
> +}
> +
> +static inline __le16 drm_format_xrgb8888_to_xrgb1555(__le32 val32)
> +{
> +	u16 val16;
> +	u32 pix;
> +
> +	pix = le32_to_cpu(val32);
> +	val16 = ((pix & 0x00f80000) >> 9) |
> +		((pix & 0x0000f800) >> 6) |
> +		((pix & 0x000000f8) >> 3);
> +	return cpu_to_le16(val16);
> +}
> +
> +static inline __le16 drm_format_xrgb8888_to_argb1555(__le32 val32)
> +{
> +	u16 val16;
> +	u32 pix;
> +
> +	pix = le32_to_cpu(val32);
> +	val16 = BIT(15) | /* set alpha bit */
> +		((pix & 0x00f80000) >> 9) |
> +		((pix & 0x0000f800) >> 6) |
> +		((pix & 0x000000f8) >> 3);
> +	return cpu_to_le16(val16);
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_argb8888(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 |= GENMASK(31, 24); /* fill alpha bits */
> +	return cpu_to_le32(val32);
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_xbgr8888(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 = ((val32 & 0x00ff0000) >> 16) <<  0 |
> +		((val32 & 0x0000ff00) >>  8) <<  8 |
> +		((val32 & 0x000000ff) >>  0) << 16 |
> +		((val32 & 0xff000000) >> 24) << 24;
> +	return cpu_to_le32(val32);
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_abgr8888(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 = ((val32 & 0x00ff0000) >> 16) <<  0 |
> +		((val32 & 0x0000ff00) >>  8) <<  8 |
> +		((val32 & 0x000000ff) >>  0) << 16 |
> +		GENMASK(31, 24); /* fill alpha bits */
> +	return cpu_to_le32(val32);
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_xrgb2101010(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 = ((val32 & 0x000000FF) << 2) |
> +		((val32 & 0x0000FF00) << 4) |
> +		((val32 & 0x00FF0000) << 6);
> +	return cpu_to_le32(val32 | ((val32 >> 8) & 0x00300C03));
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_argb2101010(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 = ((val32 & 0x000000FF) << 2) |
> +		((val32 & 0x0000FF00) << 4) |
> +		((val32 & 0x00FF0000) << 6);
> +	val32 = GENMASK(31, 30) | /* set alpha bits */
> +	      val32 | ((val32 >> 8) & 0x00300c03);
> +	return cpu_to_le32(val32);
> +}
> +
> +/**
> + * drm_fb_convert_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format
> + * @color: input color, in xrgb8888 format
> + * @format: output format
> + *
> + * Returns:
> + * Color in the format specified, casted to u32.
> + * Or 0 if the format is unknown.
> + */
> +u32 drm_fb_convert_from_xrgb8888(u32 color, u32 format)
> +{
> +	__le32 pix = cpu_to_le32(color);
> +
> +	switch (format) {
> +	case DRM_FORMAT_RGB565:
> +		return le16_to_cpu(drm_format_xrgb8888_to_rgb565(pix));
> +	case DRM_FORMAT_RGBA5551:
> +		return le16_to_cpu(drm_format_xrgb8888_to_rgba5551(pix));
> +	case DRM_FORMAT_XRGB1555:
> +		return le16_to_cpu(drm_format_xrgb8888_to_xrgb1555(pix));
> +	case DRM_FORMAT_ARGB1555:
> +		return le16_to_cpu(drm_format_xrgb8888_to_argb1555(pix));
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_XRGB8888:
> +		return le32_to_cpu(pix);
> +	case DRM_FORMAT_ARGB8888:
> +		return le32_to_cpu(drm_format_xrgb8888_to_argb8888(pix));
> +	case DRM_FORMAT_XBGR8888:
> +		return le32_to_cpu(drm_format_xrgb8888_to_xbgr8888(pix));
> +	case DRM_FORMAT_XRGB2101010:
> +		return le32_to_cpu(drm_format_xrgb8888_to_xrgb2101010(pix));
> +	case DRM_FORMAT_ARGB2101010:
> +		return le32_to_cpu(drm_format_xrgb8888_to_argb2101010(pix));
> +	default:
> +		WARN_ONCE(1, "Can't convert to %p4cc\n", &format);
> +		return 0;
> +	}
> +}
> +EXPORT_SYMBOL(drm_fb_convert_from_xrgb8888);
> +
>   static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp)
>   {
>   	return clip->y1 * pitch + clip->x1 * cpp;
> @@ -366,6 +513,193 @@ void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch,
>   }
>   EXPORT_SYMBOL(drm_fb_swab);
>   
> +static void drm_fb_r1_to_16bit(struct iosys_map *dmap, unsigned int dpitch,
> +			       const u8 *sbuf8, unsigned int spitch,
> +			       unsigned int height, unsigned int width,
> +			       __le16 fg16, __le16 bg16)
> +{
> +	unsigned int l, x;
> +	__le16 val16;
> +
> +	for (l = 0; l < height; l++) {
> +		for (x = 0; x < width; x++) {
> +			val16 = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg16 : bg16;
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u16), u16, le16_to_cpu(val16));
> +		}
> +	}
> +}
> +
> +static void drm_fb_r1_to_24bit(struct iosys_map *dmap, unsigned int dpitch,
> +			       const u8 *sbuf8, unsigned int spitch,
> +			       unsigned int height, unsigned int width,
> +			       __le32 fg32, __le32 bg32)
> +{
> +	unsigned int l, x;
> +	__le32 color;
> +	u32 val32;
> +
> +	for (l = 0; l < height; l++) {
> +		for (x = 0; x < width; x++) {
> +			u32 off = l * dpitch + x * 3;
> +
> +			color = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32;
> +			val32 = le32_to_cpu(color);
> +
> +			/* write blue-green-red to output in little endianness */
> +			iosys_map_wr(dmap, off, u8, (val32 & 0x000000FF) >> 0);
> +			iosys_map_wr(dmap, off + 1, u8, (val32 & 0x0000FF00) >> 8);
> +			iosys_map_wr(dmap, off + 2, u8, (val32 & 0x00FF0000) >> 16);
> +		}
> +	}
> +}
> +
> +static void drm_fb_r1_to_32bit(struct iosys_map *dmap, unsigned int dpitch,
> +			       const u8 *sbuf8, unsigned int spitch,
> +			       unsigned int height, unsigned int width,
> +			       __le32 fg32, __le32 bg32)
> +{
> +	unsigned int l, x;
> +	__le32 val32;
> +
> +	for (l = 0; l < height; l++) {
> +		for (x = 0; x < width; x++) {
> +			val32 = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32;
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u32), u32, le32_to_cpu(val32));
> +		}
> +	}
> +}
> +
> +/**
> + * drm_fb_blit_from_r1 - convert a monochrome image to a linear framebuffer
> + * @dmap: destination iosys_map
> + * @dpitch: destination pitch in bytes
> + * @sbuf8: source buffer, in monochrome format, 8 pixels per byte.
> + * @spitch: source pitch in bytes
> + * @height: height of the image to copy, in pixels
> + * @width: width of the image to copy, in pixels
> + * @fg_color: foreground color, in destination format
> + * @bg_color: background color, in destination format
> + * @pixel_width: pixel width in bytes.
> + *
> + * This can be used to draw font which are monochrome images, to a framebuffer
> + * in other supported format.
> + */
> +void drm_fb_blit_from_r1(struct iosys_map *dmap, unsigned int dpitch,
> +			 const u8 *sbuf8, unsigned int spitch,
> +			 unsigned int height, unsigned int width,
> +			 u32 fg_color, u32 bg_color,
> +			 unsigned int pixel_width)
> +{
> +	switch (pixel_width) {
> +	case 2:
> +		drm_fb_r1_to_16bit(dmap, dpitch, sbuf8, spitch,
> +				   height, width,
> +				   cpu_to_le16(fg_color),
> +				   cpu_to_le16(bg_color));
> +	break;
> +	case 3:
> +		drm_fb_r1_to_24bit(dmap, dpitch, sbuf8, spitch,
> +				   height, width,
> +				   cpu_to_le32(fg_color),
> +				   cpu_to_le32(bg_color));
> +	break;
> +	case 4:
> +		drm_fb_r1_to_32bit(dmap, dpitch, sbuf8, spitch,
> +				   height, width,
> +				   cpu_to_le32(fg_color),
> +				   cpu_to_le32(bg_color));
> +	break;
> +	default:
> +		WARN_ONCE(1, "Can't blit with pixel width %d\n", pixel_width);
> +	}
> +}
> +EXPORT_SYMBOL(drm_fb_blit_from_r1);
> +
> +static void drm_fb_fill8(struct iosys_map *dmap, unsigned int dpitch,
> +			 unsigned int height, unsigned int width,
> +			 u8 color)
> +{
> +	unsigned int l, x;
> +
> +	for (l = 0; l < height; l++)
> +		for (x = 0; x < width; x++)
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u8), u8, color);
> +}
> +
> +static void drm_fb_fill16(struct iosys_map *dmap, unsigned int dpitch,
> +			  unsigned int height, unsigned int width,
> +			  u16 color)
> +{
> +	unsigned int l, x;
> +
> +	for (l = 0; l < height; l++)
> +		for (x = 0; x < width; x++)
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u16), u16, color);
> +}
> +
> +static void drm_fb_fill24(struct iosys_map *dmap, unsigned int dpitch,
> +			  unsigned int height, unsigned int width,
> +			  u32 color)
> +{
> +	unsigned int l, x;
> +
> +	for (l = 0; l < height; l++) {
> +		for (x = 0; x < width; x++) {
> +			unsigned int off = l * dpitch + x * 3;
> +
> +			/* write blue-green-red to output in little endianness */
> +			iosys_map_wr(dmap, off, u8, (color & 0x000000FF) >> 0);
> +			iosys_map_wr(dmap, off + 1, u8, (color & 0x0000FF00) >> 8);
> +			iosys_map_wr(dmap, off + 2, u8, (color & 0x00FF0000) >> 16);
> +		}
> +	}
> +}
> +
> +static void drm_fb_fill32(struct iosys_map *dmap, unsigned int dpitch,
> +			  unsigned int height, unsigned int width,
> +			  u32 color)
> +{
> +	unsigned int l, x;
> +
> +	for (l = 0; l < height; l++)
> +		for (x = 0; x < width; x++)
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u32), u32, color);
> +}
> +
> +/**
> + * drm_fb_fill - Fill a rectangle with a color
> + * @dmap: destination iosys_map, pointing to the top left corner of the rectangle
> + * @dpitch: destination pitch in bytes
> + * @height: height of the rectangle, in pixels
> + * @width: width of the rectangle, in pixels
> + * @color: color to fill the rectangle.
> + * @pixel_width: pixel width in bytes
> + *
> + * Fill a rectangle with a color, in a linear framebuffer.
> + */
> +void drm_fb_fill(struct iosys_map *dmap, unsigned int dpitch,
> +			 unsigned int height, unsigned int width,
> +			 u32 color, unsigned int pixel_width)
> +{
> +	switch (pixel_width) {
> +	case 1:
> +		drm_fb_fill8(dmap, dpitch, height, width, color);
> +	break;
> +	case 2:
> +		drm_fb_fill16(dmap, dpitch, height, width, color);
> +	break;
> +	case 3:
> +		drm_fb_fill24(dmap, dpitch, height, width, color);
> +	break;
> +	case 4:
> +		drm_fb_fill32(dmap, dpitch, height, width, color);
> +	break;
> +	default:
> +		WARN_ONCE(1, "Can't fill with pixel width %d\n", pixel_width);
> +	}
> +}
> +EXPORT_SYMBOL(drm_fb_fill);
> +
>   static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigned int pixels)
>   {
>   	u8 *dbuf8 = dbuf;
> @@ -420,15 +754,9 @@ static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigne
>   	__le16 *dbuf16 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u16 val16;
> -	u32 pix;
>   
>   	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val16 = ((pix & 0x00F80000) >> 8) |
> -			((pix & 0x0000FC00) >> 5) |
> -			((pix & 0x000000F8) >> 3);
> -		dbuf16[x] = cpu_to_le16(val16);
> +		dbuf16[x] = drm_format_xrgb8888_to_rgb565(sbuf32[x]);
>   	}
>   }
>   
> @@ -498,16 +826,9 @@ static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsig
>   	__le16 *dbuf16 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u16 val16;
> -	u32 pix;
>   
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val16 = ((pix & 0x00f80000) >> 9) |
> -			((pix & 0x0000f800) >> 6) |
> -			((pix & 0x000000f8) >> 3);
> -		dbuf16[x] = cpu_to_le16(val16);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		dbuf16[x] = drm_format_xrgb8888_to_xrgb1555(sbuf32[x]);
>   }
>   
>   /**
> @@ -550,17 +871,9 @@ static void drm_fb_xrgb8888_to_argb1555_line(void *dbuf, const void *sbuf, unsig
>   	__le16 *dbuf16 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u16 val16;
> -	u32 pix;
>   
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val16 = BIT(15) | /* set alpha bit */
> -			((pix & 0x00f80000) >> 9) |
> -			((pix & 0x0000f800) >> 6) |
> -			((pix & 0x000000f8) >> 3);
> -		dbuf16[x] = cpu_to_le16(val16);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		dbuf16[x] = drm_format_xrgb8888_to_argb1555(sbuf32[x]);
>   }
>   
>   /**
> @@ -603,17 +916,9 @@ static void drm_fb_xrgb8888_to_rgba5551_line(void *dbuf, const void *sbuf, unsig
>   	__le16 *dbuf16 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u16 val16;
> -	u32 pix;
>   
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val16 = ((pix & 0x00f80000) >> 8) |
> -			((pix & 0x0000f800) >> 5) |
> -			((pix & 0x000000f8) >> 2) |
> -			BIT(0); /* set alpha bit */
> -		dbuf16[x] = cpu_to_le16(val16);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		dbuf16[x] = drm_format_xrgb8888_to_rgba5551(sbuf32[x]);
>   }
>   
>   /**
> @@ -707,13 +1012,9 @@ static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsig
>   	__le32 *dbuf32 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u32 pix;
>   
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		pix |= GENMASK(31, 24); /* fill alpha bits */
> -		dbuf32[x] = cpu_to_le32(pix);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		dbuf32[x] = drm_format_xrgb8888_to_argb8888(sbuf32[x]);
>   }
>   
>   /**
> @@ -756,16 +1057,9 @@ static void drm_fb_xrgb8888_to_abgr8888_line(void *dbuf, const void *sbuf, unsig
>   	__le32 *dbuf32 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u32 pix;
>   
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		pix = ((pix & 0x00ff0000) >> 16) <<  0 |
> -		      ((pix & 0x0000ff00) >>  8) <<  8 |
> -		      ((pix & 0x000000ff) >>  0) << 16 |
> -		      GENMASK(31, 24); /* fill alpha bits */
> -		*dbuf32++ = cpu_to_le32(pix);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		*dbuf32++ = drm_format_xrgb8888_to_abgr8888(sbuf32[x]);
>   }
>   
>   static void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned int *dst_pitch,
> @@ -787,16 +1081,9 @@ static void drm_fb_xrgb8888_to_xbgr8888_line(void *dbuf, const void *sbuf, unsig
>   	__le32 *dbuf32 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u32 pix;
>   
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		pix = ((pix & 0x00ff0000) >> 16) <<  0 |
> -		      ((pix & 0x0000ff00) >>  8) <<  8 |
> -		      ((pix & 0x000000ff) >>  0) << 16 |
> -		      ((pix & 0xff000000) >> 24) << 24;
> -		*dbuf32++ = cpu_to_le32(pix);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		*dbuf32++ = drm_format_xrgb8888_to_xbgr8888(sbuf32[x]);
>   }
>   
>   static void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned int *dst_pitch,
> @@ -818,17 +1105,9 @@ static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, un
>   	__le32 *dbuf32 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u32 val32;
> -	u32 pix;
>   
> -	for (x = 0; x < pixels; x++) {
> -		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);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		*dbuf32++ = drm_format_xrgb8888_to_xrgb2101010(sbuf32[x]);
>   }
>   
>   /**
> @@ -872,18 +1151,9 @@ static void drm_fb_xrgb8888_to_argb2101010_line(void *dbuf, const void *sbuf, un
>   	__le32 *dbuf32 = dbuf;
>   	const __le32 *sbuf32 = sbuf;
>   	unsigned int x;
> -	u32 val32;
> -	u32 pix;
>   
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val32 = ((pix & 0x000000ff) << 2) |
> -			((pix & 0x0000ff00) << 4) |
> -			((pix & 0x00ff0000) << 6);
> -		pix = GENMASK(31, 30) | /* set alpha bits */
> -		      val32 | ((val32 >> 8) & 0x00300c03);
> -		*dbuf32++ = cpu_to_le32(pix);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		*dbuf32++ = drm_format_xrgb8888_to_argb2101010(sbuf32[x]);
>   }
>   
>   /**
> diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h
> index f13b34e0b752..f416f0bef52d 100644
> --- a/include/drm/drm_format_helper.h
> +++ b/include/drm/drm_format_helper.h
> @@ -66,6 +66,7 @@ void *drm_format_conv_state_reserve(struct drm_format_conv_state *state,
>   				    size_t new_size, gfp_t flags);
>   void drm_format_conv_state_release(struct drm_format_conv_state *state);
>   
> +u32 drm_fb_convert_from_xrgb8888(u32 color, u32 format);
>   unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format,
>   				const struct drm_rect *clip);
>   
> @@ -76,6 +77,14 @@ void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch,
>   		 const struct iosys_map *src, const struct drm_framebuffer *fb,
>   		 const struct drm_rect *clip, bool cached,
>   		 struct drm_format_conv_state *state);
> +void drm_fb_blit_from_r1(struct iosys_map *dmap, unsigned int dpitch,
> +			 const u8 *sbuf8, unsigned int spitch,
> +			 unsigned int height, unsigned int width,
> +			 u32 fg_color, u32 bg_color,
> +			 unsigned int pixel_width);
> +void drm_fb_fill(struct iosys_map *dmap, unsigned int dpitch,
> +		 unsigned int height, unsigned int width,
> +		 u32 color, unsigned int pixel_width);
>   void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pitch,
>   			       const struct iosys_map *src, const struct drm_framebuffer *fb,
>   			       const struct drm_rect *clip, struct drm_format_conv_state *state);
Jani Nikula Jan. 17, 2024, 3:26 p.m. UTC | #2
On Thu, 04 Jan 2024, Jocelyn Falempe <jfalempe@redhat.com> wrote:
> This is needed for drm_panic, to draw the font, and fill
> the background color, in multiple color format.
>
> v5:
>  * Change the drawing API, use drm_fb_blit_from_r1() to draw the font.
>  * Also add drm_fb_fill() to fill area with background color.
> v6:
>  * fix __le32 conversion warning found by the kernel test bot
>
> Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
> ---
>  drivers/gpu/drm/drm_format_helper.c | 432 ++++++++++++++++++++++------
>  include/drm/drm_format_helper.h     |   9 +
>  2 files changed, 360 insertions(+), 81 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c
> index b1be458ed4dd..8cbc2d747cff 100644
> --- a/drivers/gpu/drm/drm_format_helper.c
> +++ b/drivers/gpu/drm/drm_format_helper.c
> @@ -111,6 +111,153 @@ void drm_format_conv_state_release(struct drm_format_conv_state *state)
>  }
>  EXPORT_SYMBOL(drm_format_conv_state_release);
>  
> +static inline __le16 drm_format_xrgb8888_to_rgb565(__le32 val32)

Please don't use inline in C files. Just let the compiler do its job.

BR,
Jani.

> +{
> +	u16 val16;
> +	u32 pix;
> +
> +	pix = le32_to_cpu(val32);
> +	val16 = ((pix & 0x00F80000) >> 8) |
> +		((pix & 0x0000FC00) >> 5) |
> +		((pix & 0x000000F8) >> 3);
> +	return cpu_to_le16(val16);
> +}
> +
> +static inline __le16 drm_format_xrgb8888_to_rgba5551(__le32 val32)
> +{
> +	u16 val16;
> +	u32 pix;
> +
> +	pix = le32_to_cpu(val32);
> +	val16 = ((pix & 0x00f80000) >> 8) |
> +		((pix & 0x0000f800) >> 5) |
> +		((pix & 0x000000f8) >> 2) |
> +		BIT(0); /* set alpha bit */
> +	return cpu_to_le16(val16);
> +}
> +
> +static inline __le16 drm_format_xrgb8888_to_xrgb1555(__le32 val32)
> +{
> +	u16 val16;
> +	u32 pix;
> +
> +	pix = le32_to_cpu(val32);
> +	val16 = ((pix & 0x00f80000) >> 9) |
> +		((pix & 0x0000f800) >> 6) |
> +		((pix & 0x000000f8) >> 3);
> +	return cpu_to_le16(val16);
> +}
> +
> +static inline __le16 drm_format_xrgb8888_to_argb1555(__le32 val32)
> +{
> +	u16 val16;
> +	u32 pix;
> +
> +	pix = le32_to_cpu(val32);
> +	val16 = BIT(15) | /* set alpha bit */
> +		((pix & 0x00f80000) >> 9) |
> +		((pix & 0x0000f800) >> 6) |
> +		((pix & 0x000000f8) >> 3);
> +	return cpu_to_le16(val16);
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_argb8888(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 |= GENMASK(31, 24); /* fill alpha bits */
> +	return cpu_to_le32(val32);
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_xbgr8888(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 = ((val32 & 0x00ff0000) >> 16) <<  0 |
> +		((val32 & 0x0000ff00) >>  8) <<  8 |
> +		((val32 & 0x000000ff) >>  0) << 16 |
> +		((val32 & 0xff000000) >> 24) << 24;
> +	return cpu_to_le32(val32);
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_abgr8888(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 = ((val32 & 0x00ff0000) >> 16) <<  0 |
> +		((val32 & 0x0000ff00) >>  8) <<  8 |
> +		((val32 & 0x000000ff) >>  0) << 16 |
> +		GENMASK(31, 24); /* fill alpha bits */
> +	return cpu_to_le32(val32);
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_xrgb2101010(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 = ((val32 & 0x000000FF) << 2) |
> +		((val32 & 0x0000FF00) << 4) |
> +		((val32 & 0x00FF0000) << 6);
> +	return cpu_to_le32(val32 | ((val32 >> 8) & 0x00300C03));
> +}
> +
> +static inline __le32 drm_format_xrgb8888_to_argb2101010(__le32 pix)
> +{
> +	u32 val32;
> +
> +	val32 = le32_to_cpu(pix);
> +	val32 = ((val32 & 0x000000FF) << 2) |
> +		((val32 & 0x0000FF00) << 4) |
> +		((val32 & 0x00FF0000) << 6);
> +	val32 = GENMASK(31, 30) | /* set alpha bits */
> +	      val32 | ((val32 >> 8) & 0x00300c03);
> +	return cpu_to_le32(val32);
> +}
> +
> +/**
> + * drm_fb_convert_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format
> + * @color: input color, in xrgb8888 format
> + * @format: output format
> + *
> + * Returns:
> + * Color in the format specified, casted to u32.
> + * Or 0 if the format is unknown.
> + */
> +u32 drm_fb_convert_from_xrgb8888(u32 color, u32 format)
> +{
> +	__le32 pix = cpu_to_le32(color);
> +
> +	switch (format) {
> +	case DRM_FORMAT_RGB565:
> +		return le16_to_cpu(drm_format_xrgb8888_to_rgb565(pix));
> +	case DRM_FORMAT_RGBA5551:
> +		return le16_to_cpu(drm_format_xrgb8888_to_rgba5551(pix));
> +	case DRM_FORMAT_XRGB1555:
> +		return le16_to_cpu(drm_format_xrgb8888_to_xrgb1555(pix));
> +	case DRM_FORMAT_ARGB1555:
> +		return le16_to_cpu(drm_format_xrgb8888_to_argb1555(pix));
> +	case DRM_FORMAT_RGB888:
> +	case DRM_FORMAT_XRGB8888:
> +		return le32_to_cpu(pix);
> +	case DRM_FORMAT_ARGB8888:
> +		return le32_to_cpu(drm_format_xrgb8888_to_argb8888(pix));
> +	case DRM_FORMAT_XBGR8888:
> +		return le32_to_cpu(drm_format_xrgb8888_to_xbgr8888(pix));
> +	case DRM_FORMAT_XRGB2101010:
> +		return le32_to_cpu(drm_format_xrgb8888_to_xrgb2101010(pix));
> +	case DRM_FORMAT_ARGB2101010:
> +		return le32_to_cpu(drm_format_xrgb8888_to_argb2101010(pix));
> +	default:
> +		WARN_ONCE(1, "Can't convert to %p4cc\n", &format);
> +		return 0;
> +	}
> +}
> +EXPORT_SYMBOL(drm_fb_convert_from_xrgb8888);
> +
>  static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp)
>  {
>  	return clip->y1 * pitch + clip->x1 * cpp;
> @@ -366,6 +513,193 @@ void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch,
>  }
>  EXPORT_SYMBOL(drm_fb_swab);
>  
> +static void drm_fb_r1_to_16bit(struct iosys_map *dmap, unsigned int dpitch,
> +			       const u8 *sbuf8, unsigned int spitch,
> +			       unsigned int height, unsigned int width,
> +			       __le16 fg16, __le16 bg16)
> +{
> +	unsigned int l, x;
> +	__le16 val16;
> +
> +	for (l = 0; l < height; l++) {
> +		for (x = 0; x < width; x++) {
> +			val16 = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg16 : bg16;
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u16), u16, le16_to_cpu(val16));
> +		}
> +	}
> +}
> +
> +static void drm_fb_r1_to_24bit(struct iosys_map *dmap, unsigned int dpitch,
> +			       const u8 *sbuf8, unsigned int spitch,
> +			       unsigned int height, unsigned int width,
> +			       __le32 fg32, __le32 bg32)
> +{
> +	unsigned int l, x;
> +	__le32 color;
> +	u32 val32;
> +
> +	for (l = 0; l < height; l++) {
> +		for (x = 0; x < width; x++) {
> +			u32 off = l * dpitch + x * 3;
> +
> +			color = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32;
> +			val32 = le32_to_cpu(color);
> +
> +			/* write blue-green-red to output in little endianness */
> +			iosys_map_wr(dmap, off, u8, (val32 & 0x000000FF) >> 0);
> +			iosys_map_wr(dmap, off + 1, u8, (val32 & 0x0000FF00) >> 8);
> +			iosys_map_wr(dmap, off + 2, u8, (val32 & 0x00FF0000) >> 16);
> +		}
> +	}
> +}
> +
> +static void drm_fb_r1_to_32bit(struct iosys_map *dmap, unsigned int dpitch,
> +			       const u8 *sbuf8, unsigned int spitch,
> +			       unsigned int height, unsigned int width,
> +			       __le32 fg32, __le32 bg32)
> +{
> +	unsigned int l, x;
> +	__le32 val32;
> +
> +	for (l = 0; l < height; l++) {
> +		for (x = 0; x < width; x++) {
> +			val32 = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32;
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u32), u32, le32_to_cpu(val32));
> +		}
> +	}
> +}
> +
> +/**
> + * drm_fb_blit_from_r1 - convert a monochrome image to a linear framebuffer
> + * @dmap: destination iosys_map
> + * @dpitch: destination pitch in bytes
> + * @sbuf8: source buffer, in monochrome format, 8 pixels per byte.
> + * @spitch: source pitch in bytes
> + * @height: height of the image to copy, in pixels
> + * @width: width of the image to copy, in pixels
> + * @fg_color: foreground color, in destination format
> + * @bg_color: background color, in destination format
> + * @pixel_width: pixel width in bytes.
> + *
> + * This can be used to draw font which are monochrome images, to a framebuffer
> + * in other supported format.
> + */
> +void drm_fb_blit_from_r1(struct iosys_map *dmap, unsigned int dpitch,
> +			 const u8 *sbuf8, unsigned int spitch,
> +			 unsigned int height, unsigned int width,
> +			 u32 fg_color, u32 bg_color,
> +			 unsigned int pixel_width)
> +{
> +	switch (pixel_width) {
> +	case 2:
> +		drm_fb_r1_to_16bit(dmap, dpitch, sbuf8, spitch,
> +				   height, width,
> +				   cpu_to_le16(fg_color),
> +				   cpu_to_le16(bg_color));
> +	break;
> +	case 3:
> +		drm_fb_r1_to_24bit(dmap, dpitch, sbuf8, spitch,
> +				   height, width,
> +				   cpu_to_le32(fg_color),
> +				   cpu_to_le32(bg_color));
> +	break;
> +	case 4:
> +		drm_fb_r1_to_32bit(dmap, dpitch, sbuf8, spitch,
> +				   height, width,
> +				   cpu_to_le32(fg_color),
> +				   cpu_to_le32(bg_color));
> +	break;
> +	default:
> +		WARN_ONCE(1, "Can't blit with pixel width %d\n", pixel_width);
> +	}
> +}
> +EXPORT_SYMBOL(drm_fb_blit_from_r1);
> +
> +static void drm_fb_fill8(struct iosys_map *dmap, unsigned int dpitch,
> +			 unsigned int height, unsigned int width,
> +			 u8 color)
> +{
> +	unsigned int l, x;
> +
> +	for (l = 0; l < height; l++)
> +		for (x = 0; x < width; x++)
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u8), u8, color);
> +}
> +
> +static void drm_fb_fill16(struct iosys_map *dmap, unsigned int dpitch,
> +			  unsigned int height, unsigned int width,
> +			  u16 color)
> +{
> +	unsigned int l, x;
> +
> +	for (l = 0; l < height; l++)
> +		for (x = 0; x < width; x++)
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u16), u16, color);
> +}
> +
> +static void drm_fb_fill24(struct iosys_map *dmap, unsigned int dpitch,
> +			  unsigned int height, unsigned int width,
> +			  u32 color)
> +{
> +	unsigned int l, x;
> +
> +	for (l = 0; l < height; l++) {
> +		for (x = 0; x < width; x++) {
> +			unsigned int off = l * dpitch + x * 3;
> +
> +			/* write blue-green-red to output in little endianness */
> +			iosys_map_wr(dmap, off, u8, (color & 0x000000FF) >> 0);
> +			iosys_map_wr(dmap, off + 1, u8, (color & 0x0000FF00) >> 8);
> +			iosys_map_wr(dmap, off + 2, u8, (color & 0x00FF0000) >> 16);
> +		}
> +	}
> +}
> +
> +static void drm_fb_fill32(struct iosys_map *dmap, unsigned int dpitch,
> +			  unsigned int height, unsigned int width,
> +			  u32 color)
> +{
> +	unsigned int l, x;
> +
> +	for (l = 0; l < height; l++)
> +		for (x = 0; x < width; x++)
> +			iosys_map_wr(dmap, l * dpitch + x * sizeof(u32), u32, color);
> +}
> +
> +/**
> + * drm_fb_fill - Fill a rectangle with a color
> + * @dmap: destination iosys_map, pointing to the top left corner of the rectangle
> + * @dpitch: destination pitch in bytes
> + * @height: height of the rectangle, in pixels
> + * @width: width of the rectangle, in pixels
> + * @color: color to fill the rectangle.
> + * @pixel_width: pixel width in bytes
> + *
> + * Fill a rectangle with a color, in a linear framebuffer.
> + */
> +void drm_fb_fill(struct iosys_map *dmap, unsigned int dpitch,
> +			 unsigned int height, unsigned int width,
> +			 u32 color, unsigned int pixel_width)
> +{
> +	switch (pixel_width) {
> +	case 1:
> +		drm_fb_fill8(dmap, dpitch, height, width, color);
> +	break;
> +	case 2:
> +		drm_fb_fill16(dmap, dpitch, height, width, color);
> +	break;
> +	case 3:
> +		drm_fb_fill24(dmap, dpitch, height, width, color);
> +	break;
> +	case 4:
> +		drm_fb_fill32(dmap, dpitch, height, width, color);
> +	break;
> +	default:
> +		WARN_ONCE(1, "Can't fill with pixel width %d\n", pixel_width);
> +	}
> +}
> +EXPORT_SYMBOL(drm_fb_fill);
> +
>  static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigned int pixels)
>  {
>  	u8 *dbuf8 = dbuf;
> @@ -420,15 +754,9 @@ static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigne
>  	__le16 *dbuf16 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u16 val16;
> -	u32 pix;
>  
>  	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val16 = ((pix & 0x00F80000) >> 8) |
> -			((pix & 0x0000FC00) >> 5) |
> -			((pix & 0x000000F8) >> 3);
> -		dbuf16[x] = cpu_to_le16(val16);
> +		dbuf16[x] = drm_format_xrgb8888_to_rgb565(sbuf32[x]);
>  	}
>  }
>  
> @@ -498,16 +826,9 @@ static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsig
>  	__le16 *dbuf16 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u16 val16;
> -	u32 pix;
>  
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val16 = ((pix & 0x00f80000) >> 9) |
> -			((pix & 0x0000f800) >> 6) |
> -			((pix & 0x000000f8) >> 3);
> -		dbuf16[x] = cpu_to_le16(val16);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		dbuf16[x] = drm_format_xrgb8888_to_xrgb1555(sbuf32[x]);
>  }
>  
>  /**
> @@ -550,17 +871,9 @@ static void drm_fb_xrgb8888_to_argb1555_line(void *dbuf, const void *sbuf, unsig
>  	__le16 *dbuf16 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u16 val16;
> -	u32 pix;
>  
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val16 = BIT(15) | /* set alpha bit */
> -			((pix & 0x00f80000) >> 9) |
> -			((pix & 0x0000f800) >> 6) |
> -			((pix & 0x000000f8) >> 3);
> -		dbuf16[x] = cpu_to_le16(val16);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		dbuf16[x] = drm_format_xrgb8888_to_argb1555(sbuf32[x]);
>  }
>  
>  /**
> @@ -603,17 +916,9 @@ static void drm_fb_xrgb8888_to_rgba5551_line(void *dbuf, const void *sbuf, unsig
>  	__le16 *dbuf16 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u16 val16;
> -	u32 pix;
>  
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val16 = ((pix & 0x00f80000) >> 8) |
> -			((pix & 0x0000f800) >> 5) |
> -			((pix & 0x000000f8) >> 2) |
> -			BIT(0); /* set alpha bit */
> -		dbuf16[x] = cpu_to_le16(val16);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		dbuf16[x] = drm_format_xrgb8888_to_rgba5551(sbuf32[x]);
>  }
>  
>  /**
> @@ -707,13 +1012,9 @@ static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsig
>  	__le32 *dbuf32 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u32 pix;
>  
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		pix |= GENMASK(31, 24); /* fill alpha bits */
> -		dbuf32[x] = cpu_to_le32(pix);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		dbuf32[x] = drm_format_xrgb8888_to_argb8888(sbuf32[x]);
>  }
>  
>  /**
> @@ -756,16 +1057,9 @@ static void drm_fb_xrgb8888_to_abgr8888_line(void *dbuf, const void *sbuf, unsig
>  	__le32 *dbuf32 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u32 pix;
>  
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		pix = ((pix & 0x00ff0000) >> 16) <<  0 |
> -		      ((pix & 0x0000ff00) >>  8) <<  8 |
> -		      ((pix & 0x000000ff) >>  0) << 16 |
> -		      GENMASK(31, 24); /* fill alpha bits */
> -		*dbuf32++ = cpu_to_le32(pix);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		*dbuf32++ = drm_format_xrgb8888_to_abgr8888(sbuf32[x]);
>  }
>  
>  static void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned int *dst_pitch,
> @@ -787,16 +1081,9 @@ static void drm_fb_xrgb8888_to_xbgr8888_line(void *dbuf, const void *sbuf, unsig
>  	__le32 *dbuf32 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u32 pix;
>  
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		pix = ((pix & 0x00ff0000) >> 16) <<  0 |
> -		      ((pix & 0x0000ff00) >>  8) <<  8 |
> -		      ((pix & 0x000000ff) >>  0) << 16 |
> -		      ((pix & 0xff000000) >> 24) << 24;
> -		*dbuf32++ = cpu_to_le32(pix);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		*dbuf32++ = drm_format_xrgb8888_to_xbgr8888(sbuf32[x]);
>  }
>  
>  static void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned int *dst_pitch,
> @@ -818,17 +1105,9 @@ static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, un
>  	__le32 *dbuf32 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u32 val32;
> -	u32 pix;
>  
> -	for (x = 0; x < pixels; x++) {
> -		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);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		*dbuf32++ = drm_format_xrgb8888_to_xrgb2101010(sbuf32[x]);
>  }
>  
>  /**
> @@ -872,18 +1151,9 @@ static void drm_fb_xrgb8888_to_argb2101010_line(void *dbuf, const void *sbuf, un
>  	__le32 *dbuf32 = dbuf;
>  	const __le32 *sbuf32 = sbuf;
>  	unsigned int x;
> -	u32 val32;
> -	u32 pix;
>  
> -	for (x = 0; x < pixels; x++) {
> -		pix = le32_to_cpu(sbuf32[x]);
> -		val32 = ((pix & 0x000000ff) << 2) |
> -			((pix & 0x0000ff00) << 4) |
> -			((pix & 0x00ff0000) << 6);
> -		pix = GENMASK(31, 30) | /* set alpha bits */
> -		      val32 | ((val32 >> 8) & 0x00300c03);
> -		*dbuf32++ = cpu_to_le32(pix);
> -	}
> +	for (x = 0; x < pixels; x++)
> +		*dbuf32++ = drm_format_xrgb8888_to_argb2101010(sbuf32[x]);
>  }
>  
>  /**
> diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h
> index f13b34e0b752..f416f0bef52d 100644
> --- a/include/drm/drm_format_helper.h
> +++ b/include/drm/drm_format_helper.h
> @@ -66,6 +66,7 @@ void *drm_format_conv_state_reserve(struct drm_format_conv_state *state,
>  				    size_t new_size, gfp_t flags);
>  void drm_format_conv_state_release(struct drm_format_conv_state *state);
>  
> +u32 drm_fb_convert_from_xrgb8888(u32 color, u32 format);
>  unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format,
>  				const struct drm_rect *clip);
>  
> @@ -76,6 +77,14 @@ void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch,
>  		 const struct iosys_map *src, const struct drm_framebuffer *fb,
>  		 const struct drm_rect *clip, bool cached,
>  		 struct drm_format_conv_state *state);
> +void drm_fb_blit_from_r1(struct iosys_map *dmap, unsigned int dpitch,
> +			 const u8 *sbuf8, unsigned int spitch,
> +			 unsigned int height, unsigned int width,
> +			 u32 fg_color, u32 bg_color,
> +			 unsigned int pixel_width);
> +void drm_fb_fill(struct iosys_map *dmap, unsigned int dpitch,
> +		 unsigned int height, unsigned int width,
> +		 u32 color, unsigned int pixel_width);
>  void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pitch,
>  			       const struct iosys_map *src, const struct drm_framebuffer *fb,
>  			       const struct drm_rect *clip, struct drm_format_conv_state *state);
Jocelyn Falempe Jan. 17, 2024, 4:40 p.m. UTC | #3
On 17/01/2024 16:06, Thomas Zimmermann wrote:
> Hi
> 
> Am 04.01.24 um 17:00 schrieb Jocelyn Falempe:
>> This is needed for drm_panic, to draw the font, and fill
>> the background color, in multiple color format.
> 
> TBH, I don't like this patch at all. It looks like you're reimplementing 
> existing functionality for a single use case; specifically drm_fb_blit().
> 
> What's wrong with the existing interfaces?

drm_fb_blit() is good to copy a framebuffer to another, but is clearly 
unoptimal to draw font.
It handles xrgb8888 to any rgb format, and I need monochrome to any rgb 
format.
I need to convert foreground and background color to the destination 
format, but using drm_fb_blit() to convert 1 pixel is tedious.

It also requires an additional memory buffer, and do an additional 
memory copy that we don't need at all.

It also has no way to fill a region with the background color.

The last thing, is if I plan to add YUV support, with this 
implementation, I only need to write one function that convert one 
pixel. Otherwise I would need to add the drm_fb_r1_to_yuvxxx_line() and 
drm_fb_r1_to_yuvxxxx() boilerplate.

Best regards,
Jocelyn Falempe Jan. 17, 2024, 4:43 p.m. UTC | #4
On 17/01/2024 16:26, Jani Nikula wrote:
> On Thu, 04 Jan 2024, Jocelyn Falempe <jfalempe@redhat.com> wrote:
>> This is needed for drm_panic, to draw the font, and fill
>> the background color, in multiple color format.
>>
>> v5:
>>   * Change the drawing API, use drm_fb_blit_from_r1() to draw the font.
>>   * Also add drm_fb_fill() to fill area with background color.
>> v6:
>>   * fix __le32 conversion warning found by the kernel test bot
>>
>> Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
>> ---
>>   drivers/gpu/drm/drm_format_helper.c | 432 ++++++++++++++++++++++------
>>   include/drm/drm_format_helper.h     |   9 +
>>   2 files changed, 360 insertions(+), 81 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c
>> index b1be458ed4dd..8cbc2d747cff 100644
>> --- a/drivers/gpu/drm/drm_format_helper.c
>> +++ b/drivers/gpu/drm/drm_format_helper.c
>> @@ -111,6 +111,153 @@ void drm_format_conv_state_release(struct drm_format_conv_state *state)
>>   }
>>   EXPORT_SYMBOL(drm_format_conv_state_release);
>>   
>> +static inline __le16 drm_format_xrgb8888_to_rgb565(__le32 val32)
> 
> Please don't use inline in C files. Just let the compiler do its job.

Sure, I will remove those inline in next version.

Thanks,
Thomas Zimmermann Jan. 19, 2024, 10:58 a.m. UTC | #5
Hi

Am 17.01.24 um 17:40 schrieb Jocelyn Falempe:
> 
> 
> On 17/01/2024 16:06, Thomas Zimmermann wrote:
>> Hi
>>
>> Am 04.01.24 um 17:00 schrieb Jocelyn Falempe:
>>> This is needed for drm_panic, to draw the font, and fill
>>> the background color, in multiple color format.
>>
>> TBH, I don't like this patch at all. It looks like you're 
>> reimplementing existing functionality for a single use case; 
>> specifically drm_fb_blit().
>>
>> What's wrong with the existing interfaces?

I've spend considerable time to clean up the format-helper code and get 
it into shape. It's not there yet, but on its way. So I'd rather prefer 
to update the existing code for new use cases. Adding a new interface 
for a single use case is something like a leap backwards.

So let's see if we can work out something.

> 
> drm_fb_blit() is good to copy a framebuffer to another, but is clearly 
> unoptimal to draw font.

1) The framebuffer data structure is only there for historical reasons. 
It should be removed from the internal implementation entirely. A first 
patch should go into this in any case. It didn't happened so far, as 
I've been busy with other work.

2) For the public API, I've long wanted to replace framebuffers with 
something more flexible, let's call it drm_pixmap

   struct drm_pixmap {
     struct drm_format_info *format
     unsigned int width, height
     unsigned int pitches[DRM_FORMAT_MAX_PLANES]
     iomap vaddr[DRM_FORMAT_MAX_PLANES]
   };

It's the essence of drm_framebuffer. Let's say there's also an init 
helper drm_pixmap_init_from_framebuffer(pix, fb) that sets up 
everything. The implementation of drm_fb_blit() would look like this:

   drm_fb_blit(...) {

	drm_pixmap pix;
	drm_pixmap_init_from_framebuffer(pix, fb)
	__drm_fb_blit_pixmap( <like drm_fb_blit() but with a pixmap> )
   }

That would require some changes to drivers, but it's only simple 
refactoring.

3) When looking at your patch, there's

   src = font->data + (msg->txt[i] * font->height) * src_stride;

which should be in a helper that sets up the drm_pixmap for a font 
character:

   drm_pixmap_init_from_char(pixmap, c, font_data)

where 'c' equals msg->txt[i]

The text drawing in the panic handler would do something like

   for (msg->txt[i]) {
	drm_pixmap_init_from_char(pixmap, ...)
       	drm_fb_blit_pixmap(...)
   }


> It handles xrgb8888 to any rgb format, and I need monochrome to any rgb 
> format.

4) You're free to add any conversion to drm_fb_blit(). It's supposed to 
handle all available format conversion. With the pixmap-related changes 
outlined above and the actual conversion code, I think that would 
already put characters on the screen.

> I need to convert foreground and background color to the destination 
> format, but using drm_fb_blit() to convert 1 pixel is tedious.

5) I've recently added drm_format_conv_state to the API. It is supposed 
to hold state that is required for the conversion process. I 
specifically had color palettes in mind. Please use the data structure. 
Something like that:

   struct drm_format_conv_state {
	...
	const drm_color_lut *palette;
   }

and in the conversion code:

   void r1_to_rgb() {
	for (x < pixels) {
		rgb = state->palette[r1]
	}
   }

> 
> It also requires an additional memory buffer, and do an additional 
> memory copy that we don't need at all.

6) That memcpy_to_io() not a big deal. You should pre-allocate that 
memory buffer in the panic handler and init the drm_format_conv_state 
with DRM_FORMAT_CONV_STATE_INIT_PREALLOCATED().

> 
> It also has no way to fill a region with the background color.

7) Please add a separate drm_fb_fill() implementation. If you have a 
palette in struct drm_format_conf_state, you can add a helper for each 
destination format that takes a drm_color_lut value as input.

This point is probably worth a separate discussion.

> 
> The last thing, is if I plan to add YUV support, with this 
> implementation, I only need to write one function that convert one 
> pixel. Otherwise I would need to add the drm_fb_r1_to_yuvxxx_line() and 
> drm_fb_r1_to_yuvxxxx() boilerplate.

8) YUVs are multi-plane formats IIRC. So it's likely a bit more 
complicated.And I'm not aware of any current use case for YUV. If the 
framebuffer console doesn't support it, the panic helper probably won't 
either.

Best regards
Thomas

> 
> Best regards,
>
Pekka Paalanen Jan. 19, 2024, 12:25 p.m. UTC | #6
On Fri, 19 Jan 2024 11:58:38 +0100
Thomas Zimmermann <tzimmermann@suse.de> wrote:

> Hi
> 
> Am 17.01.24 um 17:40 schrieb Jocelyn Falempe:

...

> > The last thing, is if I plan to add YUV support, with this 
> > implementation, I only need to write one function that convert one 
> > pixel. Otherwise I would need to add the drm_fb_r1_to_yuvxxx_line() and 
> > drm_fb_r1_to_yuvxxxx() boilerplate.  
> 
> 8) YUVs are multi-plane formats IIRC. So it's likely a bit more 
> complicated.And I'm not aware of any current use case for YUV. If the 
> framebuffer console doesn't support it, the panic helper probably won't 
> either.

Kernel panic during a fullscreen video playback, maybe?

That use case is likely to have an YUV FB as the only visible KMS plane
FB, either primary or overlay plane depending on which is capable of
displaying it. Sub-titles might not exist, or might be in a fairly
small RGB overlay.

I don't know if such case is important enough to handle.


Thanks,
pq
Thomas Zimmermann Jan. 19, 2024, 1:31 p.m. UTC | #7
Hi

Am 19.01.24 um 13:25 schrieb Pekka Paalanen:
> On Fri, 19 Jan 2024 11:58:38 +0100
> Thomas Zimmermann <tzimmermann@suse.de> wrote:
> 
>> Hi
>>
>> Am 17.01.24 um 17:40 schrieb Jocelyn Falempe:
> 
> ...
> 
>>> The last thing, is if I plan to add YUV support, with this
>>> implementation, I only need to write one function that convert one
>>> pixel. Otherwise I would need to add the drm_fb_r1_to_yuvxxx_line() and
>>> drm_fb_r1_to_yuvxxxx() boilerplate.
>>
>> 8) YUVs are multi-plane formats IIRC. So it's likely a bit more
>> complicated.And I'm not aware of any current use case for YUV. If the
>> framebuffer console doesn't support it, the panic helper probably won't
>> either.
> 
> Kernel panic during a fullscreen video playback, maybe?
> 
> That use case is likely to have an YUV FB as the only visible KMS plane
> FB, either primary or overlay plane depending on which is capable of
> displaying it. Sub-titles might not exist, or might be in a fairly
> small RGB overlay.
> 
> I don't know if such case is important enough to handle.

That's at least a possible use case AFAICT.

Each conversion is implemented in a helper drm_fb_<src format>_to_<dst 
format>(). drm_fb_blit() is just a big switch statement to call the 
correct helper. Something like drm_r1_to_yuv422() would be no different 
and would integrate with the existing drm_fb_blit() nicely.

So it appears to me as if it's better to either extend the current code 
for multi-plane formats, or write custom helpers 
drm_fb_r1_to_yuv<whatever>().

Best regards
Thomas

> 
> 
> Thanks,
> pq
Thomas Zimmermann Jan. 23, 2024, 12:56 p.m. UTC | #8
Hi,

FYI for points 1 and 2, I'm typing up a patchset that introduces 
drm_pixmap for the source buffer. I'll post it when I have something ready.

Best regards
Thomas

Am 19.01.24 um 11:58 schrieb Thomas Zimmermann:
> Hi
> 
> Am 17.01.24 um 17:40 schrieb Jocelyn Falempe:
>>
>>
>> On 17/01/2024 16:06, Thomas Zimmermann wrote:
>>> Hi
>>>
>>> Am 04.01.24 um 17:00 schrieb Jocelyn Falempe:
>>>> This is needed for drm_panic, to draw the font, and fill
>>>> the background color, in multiple color format.
>>>
>>> TBH, I don't like this patch at all. It looks like you're 
>>> reimplementing existing functionality for a single use case; 
>>> specifically drm_fb_blit().
>>>
>>> What's wrong with the existing interfaces?
> 
> I've spend considerable time to clean up the format-helper code and get 
> it into shape. It's not there yet, but on its way. So I'd rather prefer 
> to update the existing code for new use cases. Adding a new interface 
> for a single use case is something like a leap backwards.
> 
> So let's see if we can work out something.
> 
>>
>> drm_fb_blit() is good to copy a framebuffer to another, but is clearly 
>> unoptimal to draw font.
> 
> 1) The framebuffer data structure is only there for historical reasons. 
> It should be removed from the internal implementation entirely. A first 
> patch should go into this in any case. It didn't happened so far, as 
> I've been busy with other work.
> 
> 2) For the public API, I've long wanted to replace framebuffers with 
> something more flexible, let's call it drm_pixmap
> 
>    struct drm_pixmap {
>      struct drm_format_info *format
>      unsigned int width, height
>      unsigned int pitches[DRM_FORMAT_MAX_PLANES]
>      iomap vaddr[DRM_FORMAT_MAX_PLANES]
>    };
> 
> It's the essence of drm_framebuffer. Let's say there's also an init 
> helper drm_pixmap_init_from_framebuffer(pix, fb) that sets up 
> everything. The implementation of drm_fb_blit() would look like this:
> 
>    drm_fb_blit(...) {
> 
>      drm_pixmap pix;
>      drm_pixmap_init_from_framebuffer(pix, fb)
>      __drm_fb_blit_pixmap( <like drm_fb_blit() but with a pixmap> )
>    }
> 
> That would require some changes to drivers, but it's only simple 
> refactoring.
> 
> 3) When looking at your patch, there's
> 
>    src = font->data + (msg->txt[i] * font->height) * src_stride;
> 
> which should be in a helper that sets up the drm_pixmap for a font 
> character:
> 
>    drm_pixmap_init_from_char(pixmap, c, font_data)
> 
> where 'c' equals msg->txt[i]
> 
> The text drawing in the panic handler would do something like
> 
>    for (msg->txt[i]) {
>      drm_pixmap_init_from_char(pixmap, ...)
>            drm_fb_blit_pixmap(...)
>    }
> 
> 
>> It handles xrgb8888 to any rgb format, and I need monochrome to any 
>> rgb format.
> 
> 4) You're free to add any conversion to drm_fb_blit(). It's supposed to 
> handle all available format conversion. With the pixmap-related changes 
> outlined above and the actual conversion code, I think that would 
> already put characters on the screen.
> 
>> I need to convert foreground and background color to the destination 
>> format, but using drm_fb_blit() to convert 1 pixel is tedious.
> 
> 5) I've recently added drm_format_conv_state to the API. It is supposed 
> to hold state that is required for the conversion process. I 
> specifically had color palettes in mind. Please use the data structure. 
> Something like that:
> 
>    struct drm_format_conv_state {
>      ...
>      const drm_color_lut *palette;
>    }
> 
> and in the conversion code:
> 
>    void r1_to_rgb() {
>      for (x < pixels) {
>          rgb = state->palette[r1]
>      }
>    }
> 
>>
>> It also requires an additional memory buffer, and do an additional 
>> memory copy that we don't need at all.
> 
> 6) That memcpy_to_io() not a big deal. You should pre-allocate that 
> memory buffer in the panic handler and init the drm_format_conv_state 
> with DRM_FORMAT_CONV_STATE_INIT_PREALLOCATED().
> 
>>
>> It also has no way to fill a region with the background color.
> 
> 7) Please add a separate drm_fb_fill() implementation. If you have a 
> palette in struct drm_format_conf_state, you can add a helper for each 
> destination format that takes a drm_color_lut value as input.
> 
> This point is probably worth a separate discussion.
> 
>>
>> The last thing, is if I plan to add YUV support, with this 
>> implementation, I only need to write one function that convert one 
>> pixel. Otherwise I would need to add the drm_fb_r1_to_yuvxxx_line() 
>> and drm_fb_r1_to_yuvxxxx() boilerplate.
> 
> 8) YUVs are multi-plane formats IIRC. So it's likely a bit more 
> complicated.And I'm not aware of any current use case for YUV. If the 
> framebuffer console doesn't support it, the panic helper probably won't 
> either.
> 
> Best regards
> Thomas
> 
>>
>> Best regards,
>>
>
Jocelyn Falempe Jan. 23, 2024, 2:56 p.m. UTC | #9
On 23/01/2024 13:56, Thomas Zimmermann wrote:
> Hi,
> 
> FYI for points 1 and 2, I'm typing up a patchset that introduces 
> drm_pixmap for the source buffer. I'll post it when I have something ready.

Thanks, I didn't have time to look into this yet.

Best regards,
Thomas Zimmermann Jan. 30, 2024, 11:20 a.m. UTC | #10
Hi

Am 23.01.24 um 15:56 schrieb Jocelyn Falempe:
> 
> 
> On 23/01/2024 13:56, Thomas Zimmermann wrote:
>> Hi,
>>
>> FYI for points 1 and 2, I'm typing up a patchset that introduces 
>> drm_pixmap for the source buffer. I'll post it when I have something 
>> ready.
> 
> Thanks, I didn't have time to look into this yet.

You can find my RFC series at [1].

Best regards
Thomas

[1] https://patchwork.freedesktop.org/series/129301/

> 
> Best regards,
>
diff mbox series

Patch

diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c
index b1be458ed4dd..8cbc2d747cff 100644
--- a/drivers/gpu/drm/drm_format_helper.c
+++ b/drivers/gpu/drm/drm_format_helper.c
@@ -111,6 +111,153 @@  void drm_format_conv_state_release(struct drm_format_conv_state *state)
 }
 EXPORT_SYMBOL(drm_format_conv_state_release);
 
+static inline __le16 drm_format_xrgb8888_to_rgb565(__le32 val32)
+{
+	u16 val16;
+	u32 pix;
+
+	pix = le32_to_cpu(val32);
+	val16 = ((pix & 0x00F80000) >> 8) |
+		((pix & 0x0000FC00) >> 5) |
+		((pix & 0x000000F8) >> 3);
+	return cpu_to_le16(val16);
+}
+
+static inline __le16 drm_format_xrgb8888_to_rgba5551(__le32 val32)
+{
+	u16 val16;
+	u32 pix;
+
+	pix = le32_to_cpu(val32);
+	val16 = ((pix & 0x00f80000) >> 8) |
+		((pix & 0x0000f800) >> 5) |
+		((pix & 0x000000f8) >> 2) |
+		BIT(0); /* set alpha bit */
+	return cpu_to_le16(val16);
+}
+
+static inline __le16 drm_format_xrgb8888_to_xrgb1555(__le32 val32)
+{
+	u16 val16;
+	u32 pix;
+
+	pix = le32_to_cpu(val32);
+	val16 = ((pix & 0x00f80000) >> 9) |
+		((pix & 0x0000f800) >> 6) |
+		((pix & 0x000000f8) >> 3);
+	return cpu_to_le16(val16);
+}
+
+static inline __le16 drm_format_xrgb8888_to_argb1555(__le32 val32)
+{
+	u16 val16;
+	u32 pix;
+
+	pix = le32_to_cpu(val32);
+	val16 = BIT(15) | /* set alpha bit */
+		((pix & 0x00f80000) >> 9) |
+		((pix & 0x0000f800) >> 6) |
+		((pix & 0x000000f8) >> 3);
+	return cpu_to_le16(val16);
+}
+
+static inline __le32 drm_format_xrgb8888_to_argb8888(__le32 pix)
+{
+	u32 val32;
+
+	val32 = le32_to_cpu(pix);
+	val32 |= GENMASK(31, 24); /* fill alpha bits */
+	return cpu_to_le32(val32);
+}
+
+static inline __le32 drm_format_xrgb8888_to_xbgr8888(__le32 pix)
+{
+	u32 val32;
+
+	val32 = le32_to_cpu(pix);
+	val32 = ((val32 & 0x00ff0000) >> 16) <<  0 |
+		((val32 & 0x0000ff00) >>  8) <<  8 |
+		((val32 & 0x000000ff) >>  0) << 16 |
+		((val32 & 0xff000000) >> 24) << 24;
+	return cpu_to_le32(val32);
+}
+
+static inline __le32 drm_format_xrgb8888_to_abgr8888(__le32 pix)
+{
+	u32 val32;
+
+	val32 = le32_to_cpu(pix);
+	val32 = ((val32 & 0x00ff0000) >> 16) <<  0 |
+		((val32 & 0x0000ff00) >>  8) <<  8 |
+		((val32 & 0x000000ff) >>  0) << 16 |
+		GENMASK(31, 24); /* fill alpha bits */
+	return cpu_to_le32(val32);
+}
+
+static inline __le32 drm_format_xrgb8888_to_xrgb2101010(__le32 pix)
+{
+	u32 val32;
+
+	val32 = le32_to_cpu(pix);
+	val32 = ((val32 & 0x000000FF) << 2) |
+		((val32 & 0x0000FF00) << 4) |
+		((val32 & 0x00FF0000) << 6);
+	return cpu_to_le32(val32 | ((val32 >> 8) & 0x00300C03));
+}
+
+static inline __le32 drm_format_xrgb8888_to_argb2101010(__le32 pix)
+{
+	u32 val32;
+
+	val32 = le32_to_cpu(pix);
+	val32 = ((val32 & 0x000000FF) << 2) |
+		((val32 & 0x0000FF00) << 4) |
+		((val32 & 0x00FF0000) << 6);
+	val32 = GENMASK(31, 30) | /* set alpha bits */
+	      val32 | ((val32 >> 8) & 0x00300c03);
+	return cpu_to_le32(val32);
+}
+
+/**
+ * drm_fb_convert_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format
+ * @color: input color, in xrgb8888 format
+ * @format: output format
+ *
+ * Returns:
+ * Color in the format specified, casted to u32.
+ * Or 0 if the format is unknown.
+ */
+u32 drm_fb_convert_from_xrgb8888(u32 color, u32 format)
+{
+	__le32 pix = cpu_to_le32(color);
+
+	switch (format) {
+	case DRM_FORMAT_RGB565:
+		return le16_to_cpu(drm_format_xrgb8888_to_rgb565(pix));
+	case DRM_FORMAT_RGBA5551:
+		return le16_to_cpu(drm_format_xrgb8888_to_rgba5551(pix));
+	case DRM_FORMAT_XRGB1555:
+		return le16_to_cpu(drm_format_xrgb8888_to_xrgb1555(pix));
+	case DRM_FORMAT_ARGB1555:
+		return le16_to_cpu(drm_format_xrgb8888_to_argb1555(pix));
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_XRGB8888:
+		return le32_to_cpu(pix);
+	case DRM_FORMAT_ARGB8888:
+		return le32_to_cpu(drm_format_xrgb8888_to_argb8888(pix));
+	case DRM_FORMAT_XBGR8888:
+		return le32_to_cpu(drm_format_xrgb8888_to_xbgr8888(pix));
+	case DRM_FORMAT_XRGB2101010:
+		return le32_to_cpu(drm_format_xrgb8888_to_xrgb2101010(pix));
+	case DRM_FORMAT_ARGB2101010:
+		return le32_to_cpu(drm_format_xrgb8888_to_argb2101010(pix));
+	default:
+		WARN_ONCE(1, "Can't convert to %p4cc\n", &format);
+		return 0;
+	}
+}
+EXPORT_SYMBOL(drm_fb_convert_from_xrgb8888);
+
 static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp)
 {
 	return clip->y1 * pitch + clip->x1 * cpp;
@@ -366,6 +513,193 @@  void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch,
 }
 EXPORT_SYMBOL(drm_fb_swab);
 
+static void drm_fb_r1_to_16bit(struct iosys_map *dmap, unsigned int dpitch,
+			       const u8 *sbuf8, unsigned int spitch,
+			       unsigned int height, unsigned int width,
+			       __le16 fg16, __le16 bg16)
+{
+	unsigned int l, x;
+	__le16 val16;
+
+	for (l = 0; l < height; l++) {
+		for (x = 0; x < width; x++) {
+			val16 = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg16 : bg16;
+			iosys_map_wr(dmap, l * dpitch + x * sizeof(u16), u16, le16_to_cpu(val16));
+		}
+	}
+}
+
+static void drm_fb_r1_to_24bit(struct iosys_map *dmap, unsigned int dpitch,
+			       const u8 *sbuf8, unsigned int spitch,
+			       unsigned int height, unsigned int width,
+			       __le32 fg32, __le32 bg32)
+{
+	unsigned int l, x;
+	__le32 color;
+	u32 val32;
+
+	for (l = 0; l < height; l++) {
+		for (x = 0; x < width; x++) {
+			u32 off = l * dpitch + x * 3;
+
+			color = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32;
+			val32 = le32_to_cpu(color);
+
+			/* write blue-green-red to output in little endianness */
+			iosys_map_wr(dmap, off, u8, (val32 & 0x000000FF) >> 0);
+			iosys_map_wr(dmap, off + 1, u8, (val32 & 0x0000FF00) >> 8);
+			iosys_map_wr(dmap, off + 2, u8, (val32 & 0x00FF0000) >> 16);
+		}
+	}
+}
+
+static void drm_fb_r1_to_32bit(struct iosys_map *dmap, unsigned int dpitch,
+			       const u8 *sbuf8, unsigned int spitch,
+			       unsigned int height, unsigned int width,
+			       __le32 fg32, __le32 bg32)
+{
+	unsigned int l, x;
+	__le32 val32;
+
+	for (l = 0; l < height; l++) {
+		for (x = 0; x < width; x++) {
+			val32 = (sbuf8[(l * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32;
+			iosys_map_wr(dmap, l * dpitch + x * sizeof(u32), u32, le32_to_cpu(val32));
+		}
+	}
+}
+
+/**
+ * drm_fb_blit_from_r1 - convert a monochrome image to a linear framebuffer
+ * @dmap: destination iosys_map
+ * @dpitch: destination pitch in bytes
+ * @sbuf8: source buffer, in monochrome format, 8 pixels per byte.
+ * @spitch: source pitch in bytes
+ * @height: height of the image to copy, in pixels
+ * @width: width of the image to copy, in pixels
+ * @fg_color: foreground color, in destination format
+ * @bg_color: background color, in destination format
+ * @pixel_width: pixel width in bytes.
+ *
+ * This can be used to draw font which are monochrome images, to a framebuffer
+ * in other supported format.
+ */
+void drm_fb_blit_from_r1(struct iosys_map *dmap, unsigned int dpitch,
+			 const u8 *sbuf8, unsigned int spitch,
+			 unsigned int height, unsigned int width,
+			 u32 fg_color, u32 bg_color,
+			 unsigned int pixel_width)
+{
+	switch (pixel_width) {
+	case 2:
+		drm_fb_r1_to_16bit(dmap, dpitch, sbuf8, spitch,
+				   height, width,
+				   cpu_to_le16(fg_color),
+				   cpu_to_le16(bg_color));
+	break;
+	case 3:
+		drm_fb_r1_to_24bit(dmap, dpitch, sbuf8, spitch,
+				   height, width,
+				   cpu_to_le32(fg_color),
+				   cpu_to_le32(bg_color));
+	break;
+	case 4:
+		drm_fb_r1_to_32bit(dmap, dpitch, sbuf8, spitch,
+				   height, width,
+				   cpu_to_le32(fg_color),
+				   cpu_to_le32(bg_color));
+	break;
+	default:
+		WARN_ONCE(1, "Can't blit with pixel width %d\n", pixel_width);
+	}
+}
+EXPORT_SYMBOL(drm_fb_blit_from_r1);
+
+static void drm_fb_fill8(struct iosys_map *dmap, unsigned int dpitch,
+			 unsigned int height, unsigned int width,
+			 u8 color)
+{
+	unsigned int l, x;
+
+	for (l = 0; l < height; l++)
+		for (x = 0; x < width; x++)
+			iosys_map_wr(dmap, l * dpitch + x * sizeof(u8), u8, color);
+}
+
+static void drm_fb_fill16(struct iosys_map *dmap, unsigned int dpitch,
+			  unsigned int height, unsigned int width,
+			  u16 color)
+{
+	unsigned int l, x;
+
+	for (l = 0; l < height; l++)
+		for (x = 0; x < width; x++)
+			iosys_map_wr(dmap, l * dpitch + x * sizeof(u16), u16, color);
+}
+
+static void drm_fb_fill24(struct iosys_map *dmap, unsigned int dpitch,
+			  unsigned int height, unsigned int width,
+			  u32 color)
+{
+	unsigned int l, x;
+
+	for (l = 0; l < height; l++) {
+		for (x = 0; x < width; x++) {
+			unsigned int off = l * dpitch + x * 3;
+
+			/* write blue-green-red to output in little endianness */
+			iosys_map_wr(dmap, off, u8, (color & 0x000000FF) >> 0);
+			iosys_map_wr(dmap, off + 1, u8, (color & 0x0000FF00) >> 8);
+			iosys_map_wr(dmap, off + 2, u8, (color & 0x00FF0000) >> 16);
+		}
+	}
+}
+
+static void drm_fb_fill32(struct iosys_map *dmap, unsigned int dpitch,
+			  unsigned int height, unsigned int width,
+			  u32 color)
+{
+	unsigned int l, x;
+
+	for (l = 0; l < height; l++)
+		for (x = 0; x < width; x++)
+			iosys_map_wr(dmap, l * dpitch + x * sizeof(u32), u32, color);
+}
+
+/**
+ * drm_fb_fill - Fill a rectangle with a color
+ * @dmap: destination iosys_map, pointing to the top left corner of the rectangle
+ * @dpitch: destination pitch in bytes
+ * @height: height of the rectangle, in pixels
+ * @width: width of the rectangle, in pixels
+ * @color: color to fill the rectangle.
+ * @pixel_width: pixel width in bytes
+ *
+ * Fill a rectangle with a color, in a linear framebuffer.
+ */
+void drm_fb_fill(struct iosys_map *dmap, unsigned int dpitch,
+			 unsigned int height, unsigned int width,
+			 u32 color, unsigned int pixel_width)
+{
+	switch (pixel_width) {
+	case 1:
+		drm_fb_fill8(dmap, dpitch, height, width, color);
+	break;
+	case 2:
+		drm_fb_fill16(dmap, dpitch, height, width, color);
+	break;
+	case 3:
+		drm_fb_fill24(dmap, dpitch, height, width, color);
+	break;
+	case 4:
+		drm_fb_fill32(dmap, dpitch, height, width, color);
+	break;
+	default:
+		WARN_ONCE(1, "Can't fill with pixel width %d\n", pixel_width);
+	}
+}
+EXPORT_SYMBOL(drm_fb_fill);
+
 static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigned int pixels)
 {
 	u8 *dbuf8 = dbuf;
@@ -420,15 +754,9 @@  static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigne
 	__le16 *dbuf16 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u16 val16;
-	u32 pix;
 
 	for (x = 0; x < pixels; x++) {
-		pix = le32_to_cpu(sbuf32[x]);
-		val16 = ((pix & 0x00F80000) >> 8) |
-			((pix & 0x0000FC00) >> 5) |
-			((pix & 0x000000F8) >> 3);
-		dbuf16[x] = cpu_to_le16(val16);
+		dbuf16[x] = drm_format_xrgb8888_to_rgb565(sbuf32[x]);
 	}
 }
 
@@ -498,16 +826,9 @@  static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsig
 	__le16 *dbuf16 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u16 val16;
-	u32 pix;
 
-	for (x = 0; x < pixels; x++) {
-		pix = le32_to_cpu(sbuf32[x]);
-		val16 = ((pix & 0x00f80000) >> 9) |
-			((pix & 0x0000f800) >> 6) |
-			((pix & 0x000000f8) >> 3);
-		dbuf16[x] = cpu_to_le16(val16);
-	}
+	for (x = 0; x < pixels; x++)
+		dbuf16[x] = drm_format_xrgb8888_to_xrgb1555(sbuf32[x]);
 }
 
 /**
@@ -550,17 +871,9 @@  static void drm_fb_xrgb8888_to_argb1555_line(void *dbuf, const void *sbuf, unsig
 	__le16 *dbuf16 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u16 val16;
-	u32 pix;
 
-	for (x = 0; x < pixels; x++) {
-		pix = le32_to_cpu(sbuf32[x]);
-		val16 = BIT(15) | /* set alpha bit */
-			((pix & 0x00f80000) >> 9) |
-			((pix & 0x0000f800) >> 6) |
-			((pix & 0x000000f8) >> 3);
-		dbuf16[x] = cpu_to_le16(val16);
-	}
+	for (x = 0; x < pixels; x++)
+		dbuf16[x] = drm_format_xrgb8888_to_argb1555(sbuf32[x]);
 }
 
 /**
@@ -603,17 +916,9 @@  static void drm_fb_xrgb8888_to_rgba5551_line(void *dbuf, const void *sbuf, unsig
 	__le16 *dbuf16 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u16 val16;
-	u32 pix;
 
-	for (x = 0; x < pixels; x++) {
-		pix = le32_to_cpu(sbuf32[x]);
-		val16 = ((pix & 0x00f80000) >> 8) |
-			((pix & 0x0000f800) >> 5) |
-			((pix & 0x000000f8) >> 2) |
-			BIT(0); /* set alpha bit */
-		dbuf16[x] = cpu_to_le16(val16);
-	}
+	for (x = 0; x < pixels; x++)
+		dbuf16[x] = drm_format_xrgb8888_to_rgba5551(sbuf32[x]);
 }
 
 /**
@@ -707,13 +1012,9 @@  static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsig
 	__le32 *dbuf32 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u32 pix;
 
-	for (x = 0; x < pixels; x++) {
-		pix = le32_to_cpu(sbuf32[x]);
-		pix |= GENMASK(31, 24); /* fill alpha bits */
-		dbuf32[x] = cpu_to_le32(pix);
-	}
+	for (x = 0; x < pixels; x++)
+		dbuf32[x] = drm_format_xrgb8888_to_argb8888(sbuf32[x]);
 }
 
 /**
@@ -756,16 +1057,9 @@  static void drm_fb_xrgb8888_to_abgr8888_line(void *dbuf, const void *sbuf, unsig
 	__le32 *dbuf32 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u32 pix;
 
-	for (x = 0; x < pixels; x++) {
-		pix = le32_to_cpu(sbuf32[x]);
-		pix = ((pix & 0x00ff0000) >> 16) <<  0 |
-		      ((pix & 0x0000ff00) >>  8) <<  8 |
-		      ((pix & 0x000000ff) >>  0) << 16 |
-		      GENMASK(31, 24); /* fill alpha bits */
-		*dbuf32++ = cpu_to_le32(pix);
-	}
+	for (x = 0; x < pixels; x++)
+		*dbuf32++ = drm_format_xrgb8888_to_abgr8888(sbuf32[x]);
 }
 
 static void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned int *dst_pitch,
@@ -787,16 +1081,9 @@  static void drm_fb_xrgb8888_to_xbgr8888_line(void *dbuf, const void *sbuf, unsig
 	__le32 *dbuf32 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u32 pix;
 
-	for (x = 0; x < pixels; x++) {
-		pix = le32_to_cpu(sbuf32[x]);
-		pix = ((pix & 0x00ff0000) >> 16) <<  0 |
-		      ((pix & 0x0000ff00) >>  8) <<  8 |
-		      ((pix & 0x000000ff) >>  0) << 16 |
-		      ((pix & 0xff000000) >> 24) << 24;
-		*dbuf32++ = cpu_to_le32(pix);
-	}
+	for (x = 0; x < pixels; x++)
+		*dbuf32++ = drm_format_xrgb8888_to_xbgr8888(sbuf32[x]);
 }
 
 static void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned int *dst_pitch,
@@ -818,17 +1105,9 @@  static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, un
 	__le32 *dbuf32 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u32 val32;
-	u32 pix;
 
-	for (x = 0; x < pixels; x++) {
-		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);
-	}
+	for (x = 0; x < pixels; x++)
+		*dbuf32++ = drm_format_xrgb8888_to_xrgb2101010(sbuf32[x]);
 }
 
 /**
@@ -872,18 +1151,9 @@  static void drm_fb_xrgb8888_to_argb2101010_line(void *dbuf, const void *sbuf, un
 	__le32 *dbuf32 = dbuf;
 	const __le32 *sbuf32 = sbuf;
 	unsigned int x;
-	u32 val32;
-	u32 pix;
 
-	for (x = 0; x < pixels; x++) {
-		pix = le32_to_cpu(sbuf32[x]);
-		val32 = ((pix & 0x000000ff) << 2) |
-			((pix & 0x0000ff00) << 4) |
-			((pix & 0x00ff0000) << 6);
-		pix = GENMASK(31, 30) | /* set alpha bits */
-		      val32 | ((val32 >> 8) & 0x00300c03);
-		*dbuf32++ = cpu_to_le32(pix);
-	}
+	for (x = 0; x < pixels; x++)
+		*dbuf32++ = drm_format_xrgb8888_to_argb2101010(sbuf32[x]);
 }
 
 /**
diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h
index f13b34e0b752..f416f0bef52d 100644
--- a/include/drm/drm_format_helper.h
+++ b/include/drm/drm_format_helper.h
@@ -66,6 +66,7 @@  void *drm_format_conv_state_reserve(struct drm_format_conv_state *state,
 				    size_t new_size, gfp_t flags);
 void drm_format_conv_state_release(struct drm_format_conv_state *state);
 
+u32 drm_fb_convert_from_xrgb8888(u32 color, u32 format);
 unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format,
 				const struct drm_rect *clip);
 
@@ -76,6 +77,14 @@  void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch,
 		 const struct iosys_map *src, const struct drm_framebuffer *fb,
 		 const struct drm_rect *clip, bool cached,
 		 struct drm_format_conv_state *state);
+void drm_fb_blit_from_r1(struct iosys_map *dmap, unsigned int dpitch,
+			 const u8 *sbuf8, unsigned int spitch,
+			 unsigned int height, unsigned int width,
+			 u32 fg_color, u32 bg_color,
+			 unsigned int pixel_width);
+void drm_fb_fill(struct iosys_map *dmap, unsigned int dpitch,
+		 unsigned int height, unsigned int width,
+		 u32 color, unsigned int pixel_width);
 void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pitch,
 			       const struct iosys_map *src, const struct drm_framebuffer *fb,
 			       const struct drm_rect *clip, struct drm_format_conv_state *state);