[PATCH RFC 3/6] drm/mediatek: ovl: Fix misaligned layer source size on AFBC mode

Nícolas F. R. A. Prado posted 6 patches 1 month, 1 week ago
[PATCH RFC 3/6] drm/mediatek: ovl: Fix misaligned layer source size on AFBC mode
Posted by Nícolas F. R. A. Prado 1 month, 1 week ago
From: Ariel D'Alessandro <ariel.dalessandro@collabora.com>

In AFBC mode, OVL_SRC_SIZE must be block aligned. Due to this limitation
of the AFBC format, OVL_CLIP needs to be used to achieve the desired
output size of the layer while still meeting the alignment constraints.
Failure to do this will result in vblank timeouts and no rendered output
when the AFBC data source isn't aligned to the AFBC block (32x8).

Configure OVL_CLIP so unaligned AFBC layers can be displayed.

The following illustrates how the alignment is achieved through the clip
settings for the horizontal coordinates, the vertical coordinates are
analogous:

/------------------------------------------------\
|                                                |
|            ........................            |
|            ........................            |
|            ........................            |
|            ........................            |
|                                                |
\------------------------------------------------/
     |       |                      |       |
     |       src.x1                 src.x2  |
     |       |                      |       |
     |       |<-------------------->|       |
     |              src_width               |
     |                                      |
     N * AFBC_DATA_BLOCK_WIDTH              M * AFBC_DATA_BLOCK_WIDTH
     |                                      |
     |<----->|                      |<----->|
      clip_left                      clip_right

Signed-off-by: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
Co-developed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c | 35 ++++++++++++++++++++++++++++-----
 drivers/gpu/drm/mediatek/mtk_plane.c    | 21 ++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_plane.h    |  4 ++++
 3 files changed, 55 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index 8e20b45411fc..c6a00c2256dd 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -39,6 +39,11 @@
 #define OVL_PITCH_MSB_2ND_SUBBUF			BIT(16)
 #define DISP_REG_OVL_PITCH(n)			(0x0044 + 0x20 * (n))
 #define OVL_CONST_BLEND					BIT(28)
+#define DISP_REG_OVL_CLIP(n)			(0x004C + 0x20 * (n))
+#define OVL_CLIP_LEFT					GENMASK(7, 0)
+#define OVL_CLIP_RIGHT					GENMASK(15, 8)
+#define OVL_CLIP_TOP					GENMASK(23, 16)
+#define OVL_CLIP_BOTTOM					GENMASK(31, 24)
 #define DISP_REG_OVL_RDMA_CTRL(n)		(0x00c0 + 0x20 * (n))
 #define DISP_REG_OVL_RDMA_GMC(n)		(0x00c8 + 0x20 * (n))
 #define DISP_REG_OVL_ADDR_MT2701		0x0040
@@ -499,13 +504,14 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
 	struct mtk_plane_pending_state *pending = &state->pending;
 	unsigned int addr = pending->addr;
 	unsigned int pitch_lsb = pending->pitch & GENMASK(15, 0);
+	unsigned long long modifier = pending->modifier;
 	unsigned int fmt = pending->format;
 	unsigned int rotation = pending->rotation;
 	unsigned int offset = (pending->y << 16) | pending->x;
-	unsigned int src_size = (pending->height << 16) | pending->width;
 	unsigned int blend_mode = state->base.pixel_blend_mode;
 	unsigned int ignore_pixel_alpha = 0;
-	unsigned int con;
+	unsigned int src_size, con, src_width, src_height;
+	unsigned int clip = 0;
 
 	if (!pending->enable) {
 		mtk_ovl_layer_off(dev, idx, cmdq_pkt);
@@ -550,9 +556,26 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
 		addr += pending->pitch - 1;
 	}
 
-	if (ovl->data->supports_afbc)
-		mtk_ovl_set_afbc(ovl, cmdq_pkt, idx,
-				 pending->modifier != DRM_FORMAT_MOD_LINEAR);
+	if (ovl->data->supports_afbc && (modifier != DRM_FORMAT_MOD_LINEAR)) {
+		/*
+		 * In AFBC mode, OVL_SRC_SIZE must be block aligned. Due to this
+		 * limitation of the AFBC format, OVL_CLIP is used to adjust the
+		 * output size of the layer.
+		 */
+		clip = FIELD_PREP(OVL_CLIP_BOTTOM, pending->clip_bottom) |
+		       FIELD_PREP(OVL_CLIP_TOP, pending->clip_top) |
+		       FIELD_PREP(OVL_CLIP_RIGHT, pending->clip_right) |
+		       FIELD_PREP(OVL_CLIP_LEFT, pending->clip_left);
+		src_height = pending->height + pending->clip_top + pending->clip_bottom;
+		src_width = pending->width + pending->clip_left + pending->clip_right;
+		mtk_ovl_set_afbc(ovl, cmdq_pkt, idx, true);
+	} else {
+		src_height = pending->height;
+		src_width = pending->width;
+		mtk_ovl_set_afbc(ovl, cmdq_pkt, idx, false);
+	}
+
+	src_size = (src_height << 16) | src_width;
 
 	mtk_ddp_write_relaxed(cmdq_pkt, con, &ovl->cmdq_reg, ovl->regs,
 			      DISP_REG_OVL_CON(idx));
@@ -560,6 +583,8 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
 			      &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH(idx));
 	mtk_ddp_write_relaxed(cmdq_pkt, src_size, &ovl->cmdq_reg, ovl->regs,
 			      DISP_REG_OVL_SRC_SIZE(idx));
+	mtk_ddp_write_relaxed(cmdq_pkt, clip, &ovl->cmdq_reg, ovl->regs,
+			      DISP_REG_OVL_CLIP(idx));
 	mtk_ddp_write_relaxed(cmdq_pkt, offset, &ovl->cmdq_reg, ovl->regs,
 			      DISP_REG_OVL_OFFSET(idx));
 	mtk_ddp_write_relaxed(cmdq_pkt, addr, &ovl->cmdq_reg, ovl->regs,
diff --git a/drivers/gpu/drm/mediatek/mtk_plane.c b/drivers/gpu/drm/mediatek/mtk_plane.c
index 1214f623859e..8fb08768e8ce 100644
--- a/drivers/gpu/drm/mediatek/mtk_plane.c
+++ b/drivers/gpu/drm/mediatek/mtk_plane.c
@@ -114,6 +114,7 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
 				       struct mtk_plane_state *mtk_plane_state)
 {
 	struct drm_framebuffer *fb = new_state->fb;
+	unsigned int clip_left = 0, clip_top = 0, clip_right = 0, clip_bottom = 0;
 	struct drm_gem_object *gem;
 	struct mtk_gem_obj *mtk_gem;
 	unsigned int pitch, format;
@@ -148,6 +149,22 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
 		int x_offset_in_blocks = (new_state->src.x1 >> 16) / AFBC_DATA_BLOCK_WIDTH;
 		int y_offset_in_blocks = (new_state->src.y1 >> 16) / AFBC_DATA_BLOCK_HEIGHT;
 		int hdr_size, hdr_offset;
+		int src_width = drm_rect_width(&new_state->src) >> 16;
+		int src_height = drm_rect_height(&new_state->src) >> 16;
+		unsigned int remainder_right, remainder_bottom;
+
+		/*
+		 * In AFBC mode, the source size configured needs to be a
+		 * multiple of the AFBC data block size. Compute and save the
+		 * necessary clips so the indeded x, y, width and height are
+		 * obtained in the output despite this constraint.
+		 */
+		clip_left = (new_state->src.x1 >> 16) % AFBC_DATA_BLOCK_WIDTH;
+		clip_top = (new_state->src.y1 >> 16) % AFBC_DATA_BLOCK_HEIGHT;
+		remainder_right = (src_width + clip_left) % AFBC_DATA_BLOCK_WIDTH;
+		clip_right = remainder_right ? AFBC_DATA_BLOCK_WIDTH - remainder_right : 0;
+		remainder_bottom = (src_height + clip_top) % AFBC_DATA_BLOCK_HEIGHT;
+		clip_bottom = remainder_bottom ? AFBC_DATA_BLOCK_HEIGHT - remainder_bottom : 0;
 
 		hdr_pitch = width_in_blocks * AFBC_HEADER_BLOCK_SIZE;
 		pitch = width_in_blocks * AFBC_DATA_BLOCK_WIDTH *
@@ -187,6 +204,10 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
 	mtk_plane_state->pending.y = new_state->dst.y1;
 	mtk_plane_state->pending.width = drm_rect_width(&new_state->dst);
 	mtk_plane_state->pending.height = drm_rect_height(&new_state->dst);
+	mtk_plane_state->pending.clip_left = clip_left;
+	mtk_plane_state->pending.clip_top = clip_top;
+	mtk_plane_state->pending.clip_right = clip_right;
+	mtk_plane_state->pending.clip_bottom = clip_bottom;
 	mtk_plane_state->pending.rotation = new_state->rotation;
 	mtk_plane_state->pending.color_encoding = new_state->color_encoding;
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_plane.h b/drivers/gpu/drm/mediatek/mtk_plane.h
index 46be4454bc92..a9cfb2ee5859 100644
--- a/drivers/gpu/drm/mediatek/mtk_plane.h
+++ b/drivers/gpu/drm/mediatek/mtk_plane.h
@@ -28,6 +28,10 @@ struct mtk_plane_pending_state {
 	unsigned int			y;
 	unsigned int			width;
 	unsigned int			height;
+	unsigned int			clip_left;
+	unsigned int			clip_top;
+	unsigned int			clip_right;
+	unsigned int			clip_bottom;
 	unsigned int			rotation;
 	bool				dirty;
 	bool				async_dirty;

-- 
2.51.0

Re: [PATCH RFC 3/6] drm/mediatek: ovl: Fix misaligned layer source size on AFBC mode
Posted by CK Hu (胡俊光) 6 days, 13 hours ago
On Tue, 2025-12-30 at 11:03 -0300, Nícolas F. R. A. Prado wrote:
> From: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
> 
> In AFBC mode, OVL_SRC_SIZE must be block aligned. Due to this limitation
> of the AFBC format, OVL_CLIP needs to be used to achieve the desired
> output size of the layer while still meeting the alignment constraints.
> Failure to do this will result in vblank timeouts and no rendered output
> when the AFBC data source isn't aligned to the AFBC block (32x8).
> 
> Configure OVL_CLIP so unaligned AFBC layers can be displayed.
> 
> The following illustrates how the alignment is achieved through the clip
> settings for the horizontal coordinates, the vertical coordinates are
> analogous:
> 
> /------------------------------------------------\
> >                                                |
> >            ........................            |
> >            ........................            |
> >            ........................            |
> >            ........................            |
> >                                                |
> \------------------------------------------------/
>      |       |                      |       |
>      |       src.x1                 src.x2  |
>      |       |                      |       |
>      |       |<-------------------->|       |
>      |              src_width               |
>      |                                      |
>      N * AFBC_DATA_BLOCK_WIDTH              M * AFBC_DATA_BLOCK_WIDTH
>      |                                      |
>      |<----->|                      |<----->|
>       clip_left                      clip_right

As I know, crop is used to drop pixel data.
From the name of 'clip_left', I think it would drop the left part of this image.
But usually the image is aligned to the left (start from axis 0) and append garbage data in right part.
If so, clip_left should be zero and all the clip would be clip_right.
This is the normal behavior.
If OVL_CROP does behave as this, add comment to describe that clip_left does not drop pixel data.

Regards,
CK

> 
> Signed-off-by: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
> Co-developed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> ---
> 

Re: [PATCH RFC 3/6] drm/mediatek: ovl: Fix misaligned layer source size on AFBC mode
Posted by Nícolas F. R. A. Prado 3 days, 20 hours ago
On Tue, 2026-02-03 at 02:01 +0000, CK Hu (胡俊光) wrote:
> On Tue, 2025-12-30 at 11:03 -0300, Nícolas F. R. A. Prado wrote:
> > From: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
> > 
> > In AFBC mode, OVL_SRC_SIZE must be block aligned. Due to this
> > limitation
> > of the AFBC format, OVL_CLIP needs to be used to achieve the
> > desired
> > output size of the layer while still meeting the alignment
> > constraints.
> > Failure to do this will result in vblank timeouts and no rendered
> > output
> > when the AFBC data source isn't aligned to the AFBC block (32x8).
> > 
> > Configure OVL_CLIP so unaligned AFBC layers can be displayed.
> > 
> > The following illustrates how the alignment is achieved through the
> > clip
> > settings for the horizontal coordinates, the vertical coordinates
> > are
> > analogous:
> > 
> > /------------------------------------------------\
> > >                                                |
> > >            ........................            |
> > >            ........................            |
> > >            ........................            |
> > >            ........................            |
> > >                                                |
> > \------------------------------------------------/
> >      |       |                      |       |
> >      |       src.x1                 src.x2  |
> >      |       |                      |       |
> >      |       |<-------------------->|       |
> >      |              src_width               |
> >      |                                      |
> >      N * AFBC_DATA_BLOCK_WIDTH              M *
> > AFBC_DATA_BLOCK_WIDTH
> >      |                                      |
> >      |<----->|                      |<----->|
> >       clip_left                      clip_right
> 
> As I know, crop is used to drop pixel data.
> From the name of 'clip_left', I think it would drop the left part of
> this image.
> But usually the image is aligned to the left (start from axis 0) and
> append garbage data in right part.
> If so, clip_left should be zero and all the clip would be clip_right.
> This is the normal behavior.
> If OVL_CROP does behave as this, add comment to describe that
> clip_left does not drop pixel data.

Both clip_left and clip_right work in the same way, by discarding that
many pixels, on the left and right, respectively, of the plane's
framebuffer when compositing the plane on the final image.

In the simplest case, when the image to be displayed is left-aligned,
ie src.x1 = 0, then yes, only clip_right will be used to make sure that
the plane's width aligns with the AFBC_DATA_BLOCK_WIDTH.

However if only a sub-region of the image is to be displayed, then
src.x1 will be non-zero. If that x offset coordinate aligns with the
AFBC_DATA_BLOCK_WIDTH, then again clip_left will be 0, and we're back
at the simplest case.

But if it doesn't align, then clip_left will need to be used to ensure
only the intended sub-region is displayed, even though it starts in the
middle of an AFBC data block.

This is because not only the width and height in DISP_REG_OVL_SRC_SIZE
need to be aligned to the AFBC data block, but also the starting
address in DISP_REG_OVL_ADDR, while src.x1 and src.x2 supplied by
userspace are arbitrary and won't necessarily align, hence we use both
clips as needed to achieve the intended display outcome respecting the
hardware constraints.

-- 
Thanks,

Nícolas
Re: [PATCH RFC 3/6] drm/mediatek: ovl: Fix misaligned layer source size on AFBC mode
Posted by CK Hu (胡俊光) 1 week ago
On Tue, 2025-12-30 at 11:03 -0300, Nícolas F. R. A. Prado wrote:
> From: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
> 
> In AFBC mode, OVL_SRC_SIZE must be block aligned. Due to this limitation
> of the AFBC format, OVL_CLIP needs to be used to achieve the desired
> output size of the layer while still meeting the alignment constraints.
> Failure to do this will result in vblank timeouts and no rendered output
> when the AFBC data source isn't aligned to the AFBC block (32x8).
> 
> Configure OVL_CLIP so unaligned AFBC layers can be displayed.
> 
> The following illustrates how the alignment is achieved through the clip
> settings for the horizontal coordinates, the vertical coordinates are
> analogous:
> 
> /------------------------------------------------\
> >                                                |
> >            ........................            |
> >            ........................            |
> >            ........................            |
> >            ........................            |
> >                                                |
> \------------------------------------------------/
>      |       |                      |       |
>      |       src.x1                 src.x2  |
>      |       |                      |       |
>      |       |<-------------------->|       |
>      |              src_width               |

This patch looks to me.
But "In AFBC mode, OVL_SRC_SIZE must be block aligned", so this graph should show as:

     |       src.x1                 src.x2  |
     |       |                      |       |
     |       |                      |       |
     N * AFBC_DATA_BLOCK_WIDTH      |       M * AFBC_DATA_BLOCK_WIDTH
     |       |                      |       |
     |<----->|                      |<----->|
     |clip_left                      clip_right
     |                                      |
     |<------------------------------------>|
     |              src_width               |

Regards,
CK

>      |                                      |
>      N * AFBC_DATA_BLOCK_WIDTH              M * AFBC_DATA_BLOCK_WIDTH
>      |                                      |
>      |<----->|                      |<----->|
>       clip_left                      clip_right
> 
> Signed-off-by: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
> Co-developed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_disp_ovl.c | 35 ++++++++++++++++++++++++++++-----
>  drivers/gpu/drm/mediatek/mtk_plane.c    | 21 ++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_plane.h    |  4 ++++
>  3 files changed, 55 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> index 8e20b45411fc..c6a00c2256dd 100644
> --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> @@ -39,6 +39,11 @@
>  #define OVL_PITCH_MSB_2ND_SUBBUF			BIT(16)
>  #define DISP_REG_OVL_PITCH(n)			(0x0044 + 0x20 * (n))
>  #define OVL_CONST_BLEND					BIT(28)
> +#define DISP_REG_OVL_CLIP(n)			(0x004C + 0x20 * (n))
> +#define OVL_CLIP_LEFT					GENMASK(7, 0)
> +#define OVL_CLIP_RIGHT					GENMASK(15, 8)
> +#define OVL_CLIP_TOP					GENMASK(23, 16)
> +#define OVL_CLIP_BOTTOM					GENMASK(31, 24)
>  #define DISP_REG_OVL_RDMA_CTRL(n)		(0x00c0 + 0x20 * (n))
>  #define DISP_REG_OVL_RDMA_GMC(n)		(0x00c8 + 0x20 * (n))
>  #define DISP_REG_OVL_ADDR_MT2701		0x0040
> @@ -499,13 +504,14 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
>  	struct mtk_plane_pending_state *pending = &state->pending;
>  	unsigned int addr = pending->addr;
>  	unsigned int pitch_lsb = pending->pitch & GENMASK(15, 0);
> +	unsigned long long modifier = pending->modifier;
>  	unsigned int fmt = pending->format;
>  	unsigned int rotation = pending->rotation;
>  	unsigned int offset = (pending->y << 16) | pending->x;
> -	unsigned int src_size = (pending->height << 16) | pending->width;
>  	unsigned int blend_mode = state->base.pixel_blend_mode;
>  	unsigned int ignore_pixel_alpha = 0;
> -	unsigned int con;
> +	unsigned int src_size, con, src_width, src_height;
> +	unsigned int clip = 0;
>  
>  	if (!pending->enable) {
>  		mtk_ovl_layer_off(dev, idx, cmdq_pkt);
> @@ -550,9 +556,26 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
>  		addr += pending->pitch - 1;
>  	}
>  
> -	if (ovl->data->supports_afbc)
> -		mtk_ovl_set_afbc(ovl, cmdq_pkt, idx,
> -				 pending->modifier != DRM_FORMAT_MOD_LINEAR);
> +	if (ovl->data->supports_afbc && (modifier != DRM_FORMAT_MOD_LINEAR)) {
> +		/*
> +		 * In AFBC mode, OVL_SRC_SIZE must be block aligned. Due to this
> +		 * limitation of the AFBC format, OVL_CLIP is used to adjust the
> +		 * output size of the layer.
> +		 */
> +		clip = FIELD_PREP(OVL_CLIP_BOTTOM, pending->clip_bottom) |
> +		       FIELD_PREP(OVL_CLIP_TOP, pending->clip_top) |
> +		       FIELD_PREP(OVL_CLIP_RIGHT, pending->clip_right) |
> +		       FIELD_PREP(OVL_CLIP_LEFT, pending->clip_left);
> +		src_height = pending->height + pending->clip_top + pending->clip_bottom;
> +		src_width = pending->width + pending->clip_left + pending->clip_right;
> +		mtk_ovl_set_afbc(ovl, cmdq_pkt, idx, true);
> +	} else {
> +		src_height = pending->height;
> +		src_width = pending->width;
> +		mtk_ovl_set_afbc(ovl, cmdq_pkt, idx, false);
> +	}
> +
> +	src_size = (src_height << 16) | src_width;
>  
>  	mtk_ddp_write_relaxed(cmdq_pkt, con, &ovl->cmdq_reg, ovl->regs,
>  			      DISP_REG_OVL_CON(idx));
> @@ -560,6 +583,8 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
>  			      &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH(idx));
>  	mtk_ddp_write_relaxed(cmdq_pkt, src_size, &ovl->cmdq_reg, ovl->regs,
>  			      DISP_REG_OVL_SRC_SIZE(idx));
> +	mtk_ddp_write_relaxed(cmdq_pkt, clip, &ovl->cmdq_reg, ovl->regs,
> +			      DISP_REG_OVL_CLIP(idx));
>  	mtk_ddp_write_relaxed(cmdq_pkt, offset, &ovl->cmdq_reg, ovl->regs,
>  			      DISP_REG_OVL_OFFSET(idx));
>  	mtk_ddp_write_relaxed(cmdq_pkt, addr, &ovl->cmdq_reg, ovl->regs,
> diff --git a/drivers/gpu/drm/mediatek/mtk_plane.c b/drivers/gpu/drm/mediatek/mtk_plane.c
> index 1214f623859e..8fb08768e8ce 100644
> --- a/drivers/gpu/drm/mediatek/mtk_plane.c
> +++ b/drivers/gpu/drm/mediatek/mtk_plane.c
> @@ -114,6 +114,7 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
>  				       struct mtk_plane_state *mtk_plane_state)
>  {
>  	struct drm_framebuffer *fb = new_state->fb;
> +	unsigned int clip_left = 0, clip_top = 0, clip_right = 0, clip_bottom = 0;
>  	struct drm_gem_object *gem;
>  	struct mtk_gem_obj *mtk_gem;
>  	unsigned int pitch, format;
> @@ -148,6 +149,22 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
>  		int x_offset_in_blocks = (new_state->src.x1 >> 16) / AFBC_DATA_BLOCK_WIDTH;
>  		int y_offset_in_blocks = (new_state->src.y1 >> 16) / AFBC_DATA_BLOCK_HEIGHT;
>  		int hdr_size, hdr_offset;
> +		int src_width = drm_rect_width(&new_state->src) >> 16;
> +		int src_height = drm_rect_height(&new_state->src) >> 16;
> +		unsigned int remainder_right, remainder_bottom;
> +
> +		/*
> +		 * In AFBC mode, the source size configured needs to be a
> +		 * multiple of the AFBC data block size. Compute and save the
> +		 * necessary clips so the indeded x, y, width and height are
> +		 * obtained in the output despite this constraint.
> +		 */
> +		clip_left = (new_state->src.x1 >> 16) % AFBC_DATA_BLOCK_WIDTH;
> +		clip_top = (new_state->src.y1 >> 16) % AFBC_DATA_BLOCK_HEIGHT;
> +		remainder_right = (src_width + clip_left) % AFBC_DATA_BLOCK_WIDTH;
> +		clip_right = remainder_right ? AFBC_DATA_BLOCK_WIDTH - remainder_right : 0;
> +		remainder_bottom = (src_height + clip_top) % AFBC_DATA_BLOCK_HEIGHT;
> +		clip_bottom = remainder_bottom ? AFBC_DATA_BLOCK_HEIGHT - remainder_bottom : 0;
>  
>  		hdr_pitch = width_in_blocks * AFBC_HEADER_BLOCK_SIZE;
>  		pitch = width_in_blocks * AFBC_DATA_BLOCK_WIDTH *
> @@ -187,6 +204,10 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
>  	mtk_plane_state->pending.y = new_state->dst.y1;
>  	mtk_plane_state->pending.width = drm_rect_width(&new_state->dst);
>  	mtk_plane_state->pending.height = drm_rect_height(&new_state->dst);
> +	mtk_plane_state->pending.clip_left = clip_left;
> +	mtk_plane_state->pending.clip_top = clip_top;
> +	mtk_plane_state->pending.clip_right = clip_right;
> +	mtk_plane_state->pending.clip_bottom = clip_bottom;
>  	mtk_plane_state->pending.rotation = new_state->rotation;
>  	mtk_plane_state->pending.color_encoding = new_state->color_encoding;
>  }
> diff --git a/drivers/gpu/drm/mediatek/mtk_plane.h b/drivers/gpu/drm/mediatek/mtk_plane.h
> index 46be4454bc92..a9cfb2ee5859 100644
> --- a/drivers/gpu/drm/mediatek/mtk_plane.h
> +++ b/drivers/gpu/drm/mediatek/mtk_plane.h
> @@ -28,6 +28,10 @@ struct mtk_plane_pending_state {
>  	unsigned int			y;
>  	unsigned int			width;
>  	unsigned int			height;
> +	unsigned int			clip_left;
> +	unsigned int			clip_top;
> +	unsigned int			clip_right;
> +	unsigned int			clip_bottom;
>  	unsigned int			rotation;
>  	bool				dirty;
>  	bool				async_dirty;
> 

Re: [PATCH RFC 3/6] drm/mediatek: ovl: Fix misaligned layer source size on AFBC mode
Posted by Nícolas F. R. A. Prado 3 days, 20 hours ago
On Mon, 2026-02-02 at 08:46 +0000, CK Hu (胡俊光) wrote:
> On Tue, 2025-12-30 at 11:03 -0300, Nícolas F. R. A. Prado wrote:
> > From: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
> > 
> > In AFBC mode, OVL_SRC_SIZE must be block aligned. Due to this
> > limitation
> > of the AFBC format, OVL_CLIP needs to be used to achieve the
> > desired
> > output size of the layer while still meeting the alignment
> > constraints.
> > Failure to do this will result in vblank timeouts and no rendered
> > output
> > when the AFBC data source isn't aligned to the AFBC block (32x8).
> > 
> > Configure OVL_CLIP so unaligned AFBC layers can be displayed.
> > 
> > The following illustrates how the alignment is achieved through the
> > clip
> > settings for the horizontal coordinates, the vertical coordinates
> > are
> > analogous:
> > 
> > /------------------------------------------------\
> > >                                                |
> > >            ........................            |
> > >            ........................            |
> > >            ........................            |
> > >            ........................            |
> > >                                                |
> > \------------------------------------------------/
> >      |       |                      |       |
> >      |       src.x1                 src.x2  |
> >      |       |                      |       |
> >      |       |<-------------------->|       |
> >      |              src_width               |
> 
> This patch looks to me.
> But "In AFBC mode, OVL_SRC_SIZE must be block aligned", so this graph
> should show as:
> 
>      |       src.x1                 src.x2  |
>      |       |                      |       |
>      |       |                      |       |
>      N * AFBC_DATA_BLOCK_WIDTH      |       M * AFBC_DATA_BLOCK_WIDTH
>      |       |                      |       |
>      |<----->|                      |<----->|
>      |clip_left                      clip_right
>      |                                      |
>      |<------------------------------------>|
>      |              src_width               |

Ah yes indeed, thanks for spotting that!

-- 
Thanks,

Nícolas