diff mbox series

[v3,2/7] drm/format-helper: Add drm_fb_{xrgb8888,gray8}_to_mono_reversed()

Message ID 20220209090314.2511959-3-javierm@redhat.com (mailing list archive)
State Not Applicable
Headers show
Series drm: Add driver for Solomon SSD130X OLED displays | expand

Commit Message

Javier Martinez Canillas Feb. 9, 2022, 9:03 a.m. UTC
Add support to convert XR24 and 8-bit grayscale to reversed monochrome for
drivers that control monochromatic panels, that only have 1 bit per pixel.

The drm_fb_gray8_to_mono_reversed() helper was based on the function that
does the same in the drivers/gpu/drm/tiny/repaper.c driver.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---

Changes in v3:
- Also add a drm_fb_xrgb8888_to_mono_reversed() helper (Thomas Zimmermann)
- Split lines copy to drm_fb_gray8_to_mono_reversed_line() (Thomas Zimmermann)
- Handle case where the source buffer is not aligned to 8 (Thomas Zimmermann)

 drivers/gpu/drm/drm_format_helper.c | 157 ++++++++++++++++++++++++++++
 include/drm/drm_format_helper.h     |   8 ++
 2 files changed, 165 insertions(+)

Comments

Thomas Zimmermann Feb. 9, 2022, 12:51 p.m. UTC | #1
Hi

Am 09.02.22 um 10:03 schrieb Javier Martinez Canillas:
> Add support to convert XR24 and 8-bit grayscale to reversed monochrome for
> drivers that control monochromatic panels, that only have 1 bit per pixel.
> 
> The drm_fb_gray8_to_mono_reversed() helper was based on the function that
> does the same in the drivers/gpu/drm/tiny/repaper.c driver.
> 
> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
> ---
> 
> Changes in v3:
> - Also add a drm_fb_xrgb8888_to_mono_reversed() helper (Thomas Zimmermann)
> - Split lines copy to drm_fb_gray8_to_mono_reversed_line() (Thomas Zimmermann)
> - Handle case where the source buffer is not aligned to 8 (Thomas Zimmermann)
> 
>   drivers/gpu/drm/drm_format_helper.c | 157 ++++++++++++++++++++++++++++
>   include/drm/drm_format_helper.h     |   8 ++
>   2 files changed, 165 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c
> index b981712623d3..19710342c0de 100644
> --- a/drivers/gpu/drm/drm_format_helper.c
> +++ b/drivers/gpu/drm/drm_format_helper.c
> @@ -591,3 +591,160 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for
>   	return -EINVAL;
>   }
>   EXPORT_SYMBOL(drm_fb_blit_toio);
> +
> +static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, unsigned int pixels,
> +					       unsigned int start_offset, unsigned int end_offset)
> +{
> +	unsigned int xb, i;
> +
> +	for (xb = 0; xb < pixels; xb++) {
> +		unsigned int start = 0, end = 8;
> +		u8 byte = 0x00;
> +
> +		if (xb == 0 && start_offset)
> +			start = start_offset;
> +
> +		if (xb == pixels - 1 && end_offset)
> +			end = end_offset;

end_offset should be called end_len, because it is the number of bits in
the final byte; but not the offset of the final bit.

> +
> +		for (i = start; i < end; i++) {
> +			unsigned int x = xb * 8 + i;
> +
> +			byte >>= 1;
> +			if (src[x] >> 7)
> +				byte |= BIT(7);
> +		}
> +		*dst++ = byte;
> +	}
> +}
> +
> +/**
> + * drm_fb_gray8_to_mono_reversed - Convert grayscale to reversed monochrome
> + * @dst: reversed monochrome destination buffer
> + * @dst_pitch: Number of bytes between two consecutive scanlines within dst
> + * @src: 8-bit grayscale source buffer
> + * @fb: DRM framebuffer
> + * @clip: Clip rectangle area to copy
> + *
> + * DRM doesn't have native monochrome or grayscale support.
> + * Such drivers can announce the commonly supported XR24 format to userspace
> + * and use drm_fb_xrgb8888_to_gray8() to convert to grayscale and then this
> + * helper function to convert to the native format.
> + */
> +void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *vaddr,
> +				   const struct drm_framebuffer *fb,
> +				   const struct drm_rect *clip)
> +{
> +
> +	unsigned int linepixels = drm_rect_width(clip);
> +	unsigned int lines = drm_rect_height(clip);
> +	unsigned int start_offset, end_offset;
> +	unsigned int y;
> +	const u8 *gray8 = vaddr;
> +	u8 *mono = dst;
> +
> +	/*
> +	 * The reversed mono destination buffer contains 1 bit per pixel
> +	 * and destination scanlines have to be in multiple of 8 pixels.
> +	 */
> +	if (!dst_pitch)
> +		dst_pitch = DIV_ROUND_UP(linepixels, 8);
> +
> +	/*
> +	 * For damage handling, it is possible that only parts of the source
> +	 * buffer is copied and this could lead to start and end pixels that
> +	 * are not aligned to multiple of 8.
> +	 *
> +	 * Calculate if the start and end pixels are not aligned and set the
> +	 * offsets for the reversed mono line conversion function to adjust.
> +	 */
> +	start_offset = clip->x1 % 8;
> +	end_offset = clip->x2 % 8;
> +
> +	for (y = 0; y < lines; y++) {
> +		drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch,
> +						   start_offset, end_offset);
> +		gray8 += fb->pitches[0];
> +		mono += dst_pitch;
> +	}
> +}

Do you really need that function. It's not exported and if it's not 
otherwise used, I'd just remove it.  We don't keep unused interfaces around.

> +
> +/**
> + * drm_fb_xrgb8888_to_mono_reversed - Convert XRGB8888 to reversed monochrome
> + * @dst: reversed monochrome destination buffer
> + * @dst_pitch: Number of bytes between two consecutive scanlines within dst
> + * @src: XRGB8888 source buffer
> + * @fb: DRM framebuffer
> + * @clip: Clip rectangle area to copy
> + *
> + * DRM doesn't have native monochrome support.
> + * Such drivers can announce the commonly supported XR24 format to userspace
> + * and use this function to convert to the native format.
> + *
> + * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and
> + * then the result is converted from grayscale to reversed monohrome.
> + */
> +void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *vaddr,
> +				      const struct drm_framebuffer *fb, const struct drm_rect *clip)
> +{
> +	unsigned int linepixels = drm_rect_width(clip);
> +	unsigned int lines = clip->y2 - clip->y1;
> +	unsigned int cpp = fb->format->cpp[0];
> +	unsigned int len_src32 = linepixels * cpp;
> +	unsigned int start_offset, end_offset;
> +	unsigned int y;
> +	u8 *mono = dst, *gray8;
> +	u32 *src32;
> +
> +	if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
> +		return;
> +
> +	/*
> +	 * The reversed mono destination buffer contains 1 bit per pixel
> +	 * and destination scanlines have to be in multiple of 8 pixels.
> +	 */
> +	if (!dst_pitch)
> +		dst_pitch = DIV_ROUND_UP(linepixels, 8);

I'd do a warn_once if (dst_pitch % 8 != 0).


> +
> +	/*
> +	 * The cma memory is write-combined so reads are uncached.
> +	 * Speed up by fetching one line at a time.

I once had a patchset that adds caching information to struct 
dma_buf_map (soon to be named struct iosys_map).  Blitting helpers would 
be able to enable/disable this optimization as needed.

However, your driver doesn't use CMA. It's backed by SHMEM. Do you 
really want to keep that code in?


> +	 */
> +	src32 = kmalloc(len_src32, GFP_KERNEL);
> +	if (!src32)
> +		return;
> +
> +	/*
> +	 * Copies are done line-by-line, allocate an intermediate
> +	 * buffer to copy the gray8 lines and then convert to mono.
> +	 */
> +	gray8 = kmalloc(linepixels, GFP_KERNEL);
> +	if (!gray8)
> +		goto free_src32;

If might be faster to allocate both buffers in one step and set the 
pointers into the allocated buffer.

> +
> +	/*
> +	 * For damage handling, it is possible that only parts of the source
> +	 * buffer is copied and this could lead to start and end pixels that
> +	 * are not aligned to multiple of 8.
> +	 *
> +	 * Calculate if the start and end pixels are not aligned and set the
> +	 * offsets for the reversed mono line conversion function to adjust.
> +	 */
> +	start_offset = clip->x1 % 8;
> +	end_offset = clip->x2 % 8;

end_len, again. If you have 1 single bit set in the final byte, the
offset is 0, but the length is 1.

Best regards
Thomas

> +
> +	vaddr += clip_offset(clip, fb->pitches[0], cpp);
> +	for (y = 0; y < lines; y++) {
> +		src32 = memcpy(src32, vaddr, len_src32);
> +		drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels);
> +		drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch,
> +						   start_offset, end_offset);
> +		vaddr += fb->pitches[0];
> +		mono += dst_pitch;
> +	}
> +
> +	kfree(gray8);
> +free_src32:
> +	kfree(src32);
> +}
> +EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono_reversed);
> diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h
> index b30ed5de0a33..6638da9e9774 100644
> --- a/include/drm/drm_format_helper.h
> +++ b/include/drm/drm_format_helper.h
> @@ -43,4 +43,12 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for
>   		     const void *vmap, const struct drm_framebuffer *fb,
>   		     const struct drm_rect *rect);
>   
> +void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src,
> +				   const struct drm_framebuffer *fb,
> +				   const struct drm_rect *clip);
> +
> +void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src,
> +				      const struct drm_framebuffer *fb,
> +				      const struct drm_rect *clip);
> +
>   #endif /* __LINUX_DRM_FORMAT_HELPER_H */
Javier Martinez Canillas Feb. 9, 2022, 1:26 p.m. UTC | #2
Hello Thomas,

Thanks a lot for your feedback.

On 2/9/22 13:51, Thomas Zimmermann wrote:
> Hi
> 

[snip]

>> +
>> +		if (xb == pixels - 1 && end_offset)
>> +			end = end_offset;
> 
> end_offset should be called end_len, because it is the number of bits in
> the final byte; but not the offset of the final bit.
>

Indeed.

[snip]

>> +void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *vaddr,
>> +				   const struct drm_framebuffer *fb,
>> +				   const struct drm_rect *clip)

[snip]

> 
> Do you really need that function. It's not exported and if it's not 
> otherwise used, I'd just remove it.  We don't keep unused interfaces around.
>

At the end after your suggestion of doing line-per-line conversions it is not
needed, but since I already typed it and we were talking about adding other
formats besides the fake XRGB8888 as an optimization (R8 for grayscale and
Dx or something like that for reversed mono), I thought that would be useful
to have it as a helper.

Also other drivers that want to advertise a R8 format could just use it and
not having to add their own helper. But I'm happy to drop it in v4 if you
think that's better to not have unused helpers. 

It could be taken from this patch-set anyways if someone wants to wire the
needed support for R8.

[snip]

>> +
>> +	/*
>> +	 * The reversed mono destination buffer contains 1 bit per pixel
>> +	 * and destination scanlines have to be in multiple of 8 pixels.
>> +	 */
>> +	if (!dst_pitch)
>> +		dst_pitch = DIV_ROUND_UP(linepixels, 8);
> 
> I'd do a warn_once if (dst_pitch % 8 != 0).
>

Agreed. I'll add a warning an mention that will be rounded up.

> 
>> +
>> +	/*
>> +	 * The cma memory is write-combined so reads are uncached.
>> +	 * Speed up by fetching one line at a time.
> 
> I once had a patchset that adds caching information to struct 
> dma_buf_map (soon to be named struct iosys_map).  Blitting helpers would 
> be able to enable/disable this optimization as needed.
> 
> However, your driver doesn't use CMA. It's backed by SHMEM. Do you 
> really want to keep that code in?
>

It doesn't but the repaper does. And since the plan was to make that driver
to use the helper instead of having their own, I wanted to also make sure
that would work well with CMA.

> 
>> +	 */
>> +	src32 = kmalloc(len_src32, GFP_KERNEL);
>> +	if (!src32)
>> +		return;
>> +
>> +	/*
>> +	 * Copies are done line-by-line, allocate an intermediate
>> +	 * buffer to copy the gray8 lines and then convert to mono.
>> +	 */
>> +	gray8 = kmalloc(linepixels, GFP_KERNEL);
>> +	if (!gray8)
>> +		goto free_src32;
> 
> If might be faster to allocate both buffers in one step and set the 
> pointers into the allocated buffer.
>

Not sure I got this. Do you mean to have a single buffer with length
linepixels + len_src32 and point src32 and gray8 to the same buffer ?

>> +
>> +	/*
>> +	 * For damage handling, it is possible that only parts of the source
>> +	 * buffer is copied and this could lead to start and end pixels that
>> +	 * are not aligned to multiple of 8.
>> +	 *
>> +	 * Calculate if the start and end pixels are not aligned and set the
>> +	 * offsets for the reversed mono line conversion function to adjust.
>> +	 */
>> +	start_offset = clip->x1 % 8;
>> +	end_offset = clip->x2 % 8;
> 
> end_len, again. If you have 1 single bit set in the final byte, the
> offset is 0, but the length is 1.
>

Agreed, will change it too.

Best regards,
Thomas Zimmermann Feb. 9, 2022, 3:21 p.m. UTC | #3
Hi

Am 09.02.22 um 14:26 schrieb Javier Martinez Canillas:
> Hello Thomas,
> 
> Thanks a lot for your feedback.
> 
> On 2/9/22 13:51, Thomas Zimmermann wrote:
>> Hi
>>
> 
> [snip]
> 
>>> +
>>> +		if (xb == pixels - 1 && end_offset)
>>> +			end = end_offset;
>>
>> end_offset should be called end_len, because it is the number of bits in
>> the final byte; but not the offset of the final bit.
>>
> 
> Indeed.
> 
> [snip]
> 
>>> +void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *vaddr,
>>> +				   const struct drm_framebuffer *fb,
>>> +				   const struct drm_rect *clip)
> 
> [snip]
> 
>>
>> Do you really need that function. It's not exported and if it's not
>> otherwise used, I'd just remove it.  We don't keep unused interfaces around.
>>
> 
> At the end after your suggestion of doing line-per-line conversions it is not
> needed, but since I already typed it and we were talking about adding other
> formats besides the fake XRGB8888 as an optimization (R8 for grayscale and
> Dx or something like that for reversed mono), I thought that would be useful
> to have it as a helper.
> 
> Also other drivers that want to advertise a R8 format could just use it and
> not having to add their own helper. But I'm happy to drop it in v4 if you
> think that's better to not have unused helpers.
> 
> It could be taken from this patch-set anyways if someone wants to wire the
> needed support for R8.

I think, policy is to not keep unused code around.

> 
> [snip]
> 
>>> +
>>> +	/*
>>> +	 * The reversed mono destination buffer contains 1 bit per pixel
>>> +	 * and destination scanlines have to be in multiple of 8 pixels.
>>> +	 */
>>> +	if (!dst_pitch)
>>> +		dst_pitch = DIV_ROUND_UP(linepixels, 8);
>>
>> I'd do a warn_once if (dst_pitch % 8 != 0).
>>
> 
> Agreed. I'll add a warning an mention that will be rounded up.
> 
>>
>>> +
>>> +	/*
>>> +	 * The cma memory is write-combined so reads are uncached.
>>> +	 * Speed up by fetching one line at a time.
>>
>> I once had a patchset that adds caching information to struct
>> dma_buf_map (soon to be named struct iosys_map).  Blitting helpers would
>> be able to enable/disable this optimization as needed.
>>
>> However, your driver doesn't use CMA. It's backed by SHMEM. Do you
>> really want to keep that code in?
>>
> 
> It doesn't but the repaper does. And since the plan was to make that driver
> to use the helper instead of having their own, I wanted to also make sure
> that would work well with CMA.

That makes sense then.

> 
>>
>>> +	 */
>>> +	src32 = kmalloc(len_src32, GFP_KERNEL);
>>> +	if (!src32)
>>> +		return;
>>> +
>>> +	/*
>>> +	 * Copies are done line-by-line, allocate an intermediate
>>> +	 * buffer to copy the gray8 lines and then convert to mono.
>>> +	 */
>>> +	gray8 = kmalloc(linepixels, GFP_KERNEL);
>>> +	if (!gray8)
>>> +		goto free_src32;
>>
>> If might be faster to allocate both buffers in one step and set the
>> pointers into the allocated buffer.
>>
> 
> Not sure I got this. Do you mean to have a single buffer with length
> linepixels + len_src32 and point src32 and gray8 to the same buffer ?

That's the idea. I don't know the exact overhead for kalloc(), but at 
least the in userspace, malloc() in hot code paths is not a good idea. 
There's usually some searching for free space involved.

In the long term, we could add a field in struct drm_framebuffer to keep 
such buffers around for reuse.

> 
>>> +
>>> +	/*
>>> +	 * For damage handling, it is possible that only parts of the source
>>> +	 * buffer is copied and this could lead to start and end pixels that
>>> +	 * are not aligned to multiple of 8.
>>> +	 *
>>> +	 * Calculate if the start and end pixels are not aligned and set the
>>> +	 * offsets for the reversed mono line conversion function to adjust.
>>> +	 */
>>> +	start_offset = clip->x1 % 8;
>>> +	end_offset = clip->x2 % 8;
>>
>> end_len, again. If you have 1 single bit set in the final byte, the
>> offset is 0, but the length is 1.
>>
> 
> Agreed, will change it too.

Feel free to add my

Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>

to the next version.

Best regards
Thomas

> 
> Best regards,
Javier Martinez Canillas Feb. 9, 2022, 3:30 p.m. UTC | #4
On 2/9/22 16:21, Thomas Zimmermann wrote:

[snip]

>>
>> It could be taken from this patch-set anyways if someone wants to wire the
>> needed support for R8.
> 
> I think, policy is to not keep unused code around.
>

Ok, I'll drop it then. We can include it again when adding R8 formats.
 
[snip]

>>> If might be faster to allocate both buffers in one step and set the
>>> pointers into the allocated buffer.
>>>
>>
>> Not sure I got this. Do you mean to have a single buffer with length
>> linepixels + len_src32 and point src32 and gray8 to the same buffer ?
> 
> That's the idea. I don't know the exact overhead for kalloc(), but at 
> least the in userspace, malloc() in hot code paths is not a good idea. 
> There's usually some searching for free space involved.
>

Sure, let's do it in one allocation then and I'll add some comments to
make easier for people to follow the code.
 
> In the long term, we could add a field in struct drm_framebuffer to keep 
> such buffers around for reuse.
> 
>>
>>>> +
>>>> +	/*
>>>> +	 * For damage handling, it is possible that only parts of the source
>>>> +	 * buffer is copied and this could lead to start and end pixels that
>>>> +	 * are not aligned to multiple of 8.
>>>> +	 *
>>>> +	 * Calculate if the start and end pixels are not aligned and set the
>>>> +	 * offsets for the reversed mono line conversion function to adjust.
>>>> +	 */
>>>> +	start_offset = clip->x1 % 8;
>>>> +	end_offset = clip->x2 % 8;
>>>
>>> end_len, again. If you have 1 single bit set in the final byte, the
>>> offset is 0, but the length is 1.
>>>
>>
>> Agreed, will change it too.
> 
> Feel free to add my
> 
> Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
>

Thanks!

Best regards, -- 
Javier Martinez Canillas
Linux Engineering
Red Hat
diff mbox series

Patch

diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c
index b981712623d3..19710342c0de 100644
--- a/drivers/gpu/drm/drm_format_helper.c
+++ b/drivers/gpu/drm/drm_format_helper.c
@@ -591,3 +591,160 @@  int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for
 	return -EINVAL;
 }
 EXPORT_SYMBOL(drm_fb_blit_toio);
+
+static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, unsigned int pixels,
+					       unsigned int start_offset, unsigned int end_offset)
+{
+	unsigned int xb, i;
+
+	for (xb = 0; xb < pixels; xb++) {
+		unsigned int start = 0, end = 8;
+		u8 byte = 0x00;
+
+		if (xb == 0 && start_offset)
+			start = start_offset;
+
+		if (xb == pixels - 1 && end_offset)
+			end = end_offset;
+
+		for (i = start; i < end; i++) {
+			unsigned int x = xb * 8 + i;
+
+			byte >>= 1;
+			if (src[x] >> 7)
+				byte |= BIT(7);
+		}
+		*dst++ = byte;
+	}
+}
+
+/**
+ * drm_fb_gray8_to_mono_reversed - Convert grayscale to reversed monochrome
+ * @dst: reversed monochrome destination buffer
+ * @dst_pitch: Number of bytes between two consecutive scanlines within dst
+ * @src: 8-bit grayscale source buffer
+ * @fb: DRM framebuffer
+ * @clip: Clip rectangle area to copy
+ *
+ * DRM doesn't have native monochrome or grayscale support.
+ * Such drivers can announce the commonly supported XR24 format to userspace
+ * and use drm_fb_xrgb8888_to_gray8() to convert to grayscale and then this
+ * helper function to convert to the native format.
+ */
+void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *vaddr,
+				   const struct drm_framebuffer *fb,
+				   const struct drm_rect *clip)
+{
+
+	unsigned int linepixels = drm_rect_width(clip);
+	unsigned int lines = drm_rect_height(clip);
+	unsigned int start_offset, end_offset;
+	unsigned int y;
+	const u8 *gray8 = vaddr;
+	u8 *mono = dst;
+
+	/*
+	 * The reversed mono destination buffer contains 1 bit per pixel
+	 * and destination scanlines have to be in multiple of 8 pixels.
+	 */
+	if (!dst_pitch)
+		dst_pitch = DIV_ROUND_UP(linepixels, 8);
+
+	/*
+	 * For damage handling, it is possible that only parts of the source
+	 * buffer is copied and this could lead to start and end pixels that
+	 * are not aligned to multiple of 8.
+	 *
+	 * Calculate if the start and end pixels are not aligned and set the
+	 * offsets for the reversed mono line conversion function to adjust.
+	 */
+	start_offset = clip->x1 % 8;
+	end_offset = clip->x2 % 8;
+
+	for (y = 0; y < lines; y++) {
+		drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch,
+						   start_offset, end_offset);
+		gray8 += fb->pitches[0];
+		mono += dst_pitch;
+	}
+}
+
+/**
+ * drm_fb_xrgb8888_to_mono_reversed - Convert XRGB8888 to reversed monochrome
+ * @dst: reversed monochrome destination buffer
+ * @dst_pitch: Number of bytes between two consecutive scanlines within dst
+ * @src: XRGB8888 source buffer
+ * @fb: DRM framebuffer
+ * @clip: Clip rectangle area to copy
+ *
+ * DRM doesn't have native monochrome support.
+ * Such drivers can announce the commonly supported XR24 format to userspace
+ * and use this function to convert to the native format.
+ *
+ * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and
+ * then the result is converted from grayscale to reversed monohrome.
+ */
+void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *vaddr,
+				      const struct drm_framebuffer *fb, const struct drm_rect *clip)
+{
+	unsigned int linepixels = drm_rect_width(clip);
+	unsigned int lines = clip->y2 - clip->y1;
+	unsigned int cpp = fb->format->cpp[0];
+	unsigned int len_src32 = linepixels * cpp;
+	unsigned int start_offset, end_offset;
+	unsigned int y;
+	u8 *mono = dst, *gray8;
+	u32 *src32;
+
+	if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
+		return;
+
+	/*
+	 * The reversed mono destination buffer contains 1 bit per pixel
+	 * and destination scanlines have to be in multiple of 8 pixels.
+	 */
+	if (!dst_pitch)
+		dst_pitch = DIV_ROUND_UP(linepixels, 8);
+
+	/*
+	 * The cma memory is write-combined so reads are uncached.
+	 * Speed up by fetching one line at a time.
+	 */
+	src32 = kmalloc(len_src32, GFP_KERNEL);
+	if (!src32)
+		return;
+
+	/*
+	 * Copies are done line-by-line, allocate an intermediate
+	 * buffer to copy the gray8 lines and then convert to mono.
+	 */
+	gray8 = kmalloc(linepixels, GFP_KERNEL);
+	if (!gray8)
+		goto free_src32;
+
+	/*
+	 * For damage handling, it is possible that only parts of the source
+	 * buffer is copied and this could lead to start and end pixels that
+	 * are not aligned to multiple of 8.
+	 *
+	 * Calculate if the start and end pixels are not aligned and set the
+	 * offsets for the reversed mono line conversion function to adjust.
+	 */
+	start_offset = clip->x1 % 8;
+	end_offset = clip->x2 % 8;
+
+	vaddr += clip_offset(clip, fb->pitches[0], cpp);
+	for (y = 0; y < lines; y++) {
+		src32 = memcpy(src32, vaddr, len_src32);
+		drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels);
+		drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch,
+						   start_offset, end_offset);
+		vaddr += fb->pitches[0];
+		mono += dst_pitch;
+	}
+
+	kfree(gray8);
+free_src32:
+	kfree(src32);
+}
+EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono_reversed);
diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h
index b30ed5de0a33..6638da9e9774 100644
--- a/include/drm/drm_format_helper.h
+++ b/include/drm/drm_format_helper.h
@@ -43,4 +43,12 @@  int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for
 		     const void *vmap, const struct drm_framebuffer *fb,
 		     const struct drm_rect *rect);
 
+void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src,
+				   const struct drm_framebuffer *fb,
+				   const struct drm_rect *clip);
+
+void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src,
+				      const struct drm_framebuffer *fb,
+				      const struct drm_rect *clip);
+
 #endif /* __LINUX_DRM_FORMAT_HELPER_H */