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

CK Hu (胡俊光) ck.hu at mediatek.com
Mon Feb 2 00:46:21 PST 2026


On Tue, 2025-12-30 at 11:03 -0300, Nícolas F. R. A. Prado wrote:
> From: Ariel D'Alessandro <ariel.dalessandro at 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 at collabora.com>
> Co-developed-by: Nícolas F. R. A. Prado <nfraprado at collabora.com>
> Signed-off-by: Nícolas F. R. A. Prado <nfraprado at 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;
> 



More information about the Linux-mediatek mailing list