[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