[PATCHv7,6/6] drm/rockchip: Add support for afbc
diff mbox series

Message ID 20200311145541.29186-7-andrzej.p@collabora.com
State New
Headers show
Series
  • Add AFBC support for Rockchip
Related show

Commit Message

Andrzej Pietrasiewicz March 11, 2020, 2:55 p.m. UTC
This patch adds support for afbc handling. afbc is a compressed format
which reduces the necessary memory bandwidth.

Co-developed-by: Mark Yao <mark.yao@rock-chips.com>
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   1 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  43 +++++-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 137 +++++++++++++++++++-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |  17 +++
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |  83 +++++++++++-
 5 files changed, 276 insertions(+), 5 deletions(-)

Comments

Emil Velikov March 16, 2020, 2:10 p.m. UTC | #1
On Wed, 11 Mar 2020 at 14:56, Andrzej Pietrasiewicz
<andrzej.p@collabora.com> wrote:
>
> This patch adds support for afbc handling. afbc is a compressed format
> which reduces the necessary memory bandwidth.
>
> Co-developed-by: Mark Yao <mark.yao@rock-chips.com>
> Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
> Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
> ---
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   1 +
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  43 +++++-
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 137 +++++++++++++++++++-
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |  17 +++
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |  83 +++++++++++-
>  5 files changed, 276 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index c5b06048124e..e33c2dcd0d4b 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -30,6 +30,7 @@ struct rockchip_crtc_state {
>         int output_mode;
>         int output_bpc;
>         int output_flags;
> +       bool enable_afbc;
>  };
>  #define to_rockchip_crtc_state(s) \
>                 container_of(s, struct rockchip_crtc_state, base)
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 221e72e71432..9b13c784b347 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -57,8 +57,49 @@ static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers =
>         .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
>  };
>
> +static struct drm_framebuffer *
> +rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
> +                  const struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> +       struct drm_afbc_framebuffer *afbc_fb;
> +       const struct drm_format_info *info;
> +       int ret;
> +
> +       info = drm_get_format_info(dev, mode_cmd);
> +       if (!info)
> +               return ERR_PTR(-ENOMEM);
> +
> +       afbc_fb = kzalloc(sizeof(*afbc_fb), GFP_KERNEL);
> +       if (!afbc_fb)
> +               return ERR_PTR(-ENOMEM);
> +
> +       ret = drm_gem_fb_init_with_funcs(dev, &afbc_fb->base, file, mode_cmd,
> +                                        &rockchip_drm_fb_funcs);
> +       if (ret) {
> +               kfree(afbc_fb);
> +               return ERR_PTR(ret);
> +       }
> +
> +       if (drm_is_afbc(mode_cmd->modifier[0])) {
> +               int ret, i;
> +
> +               ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
> +               if (ret) {
> +                       struct drm_gem_object **obj = afbc_fb->base.obj;
> +
> +                       for (i = 0; i < info->num_planes; ++i)
> +                               drm_gem_object_put_unlocked(obj[i]);
> +
> +                       kfree(afbc_fb);
> +                       return ERR_PTR(ret);
> +               }
> +       }
> +
> +       return &afbc_fb->base;
> +}
> +
>  static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
> -       .fb_create = drm_gem_fb_create_with_dirty,
> +       .fb_create = rockchip_fb_create,
>         .output_poll_changed = drm_fb_helper_output_poll_changed,
>         .atomic_check = drm_atomic_helper_check,
>         .atomic_commit = drm_atomic_helper_commit,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index cecb2cc781f5..b87d22eb6ae1 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -91,9 +91,22 @@
>  #define VOP_WIN_TO_INDEX(vop_win) \
>         ((vop_win) - (vop_win)->vop->win)
>
> +#define VOP_AFBC_SET(vop, name, v) \
> +       do { \
> +               if ((vop)->data->afbc) \
> +                       vop_reg_set((vop), &(vop)->data->afbc->name, \
> +                                   0, ~0, v, #name); \
> +       } while (0)
> +
>  #define to_vop(x) container_of(x, struct vop, crtc)
>  #define to_vop_win(x) container_of(x, struct vop_win, base)
>
> +#define AFBC_FMT_RGB565                0x0
> +#define AFBC_FMT_U8U8U8U8      0x5
> +#define AFBC_FMT_U8U8U8                0x4
> +
> +#define AFBC_TILE_16x16                BIT(4)
> +
>  /*
>   * The coefficients of the following matrix are all fixed points.
>   * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets.
> @@ -274,6 +287,29 @@ static enum vop_data_format vop_convert_format(uint32_t format)
>         }
>  }
>
> +static int vop_convert_afbc_format(uint32_t format)
> +{
> +       switch (format) {
> +       case DRM_FORMAT_XRGB8888:
> +       case DRM_FORMAT_ARGB8888:
> +       case DRM_FORMAT_XBGR8888:
> +       case DRM_FORMAT_ABGR8888:
> +               return AFBC_FMT_U8U8U8U8;
> +       case DRM_FORMAT_RGB888:
> +       case DRM_FORMAT_BGR888:
> +               return AFBC_FMT_U8U8U8;
> +       case DRM_FORMAT_RGB565:
> +       case DRM_FORMAT_BGR565:
> +               return AFBC_FMT_RGB565;
> +       /* either of the below should not be reachable */
> +       default:
> +               DRM_WARN_ONCE("unsupported AFBC format[%08x]\n", format);
> +               return -EINVAL;
> +       }
> +
> +       return -EINVAL;
> +}
> +
>  static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
>                                   uint32_t dst, bool is_horizontal,
>                                   int vsu_mode, int *vskiplines)
> @@ -598,6 +634,17 @@ static int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
>                         vop_win_disable(vop, vop_win);
>                 }
>         }
> +
> +       if (vop->data->afbc) {
> +               struct rockchip_crtc_state *s;
> +               /*
> +                * Disable AFBC and forget there was a vop window with AFBC
> +                */
> +               VOP_AFBC_SET(vop, enable, 0);
> +               s = to_rockchip_crtc_state(crtc->state);
> +               s->enable_afbc = false;
> +       }
> +
>         spin_unlock(&vop->reg_lock);
>
>         vop_cfg_done(vop);
> @@ -710,6 +757,26 @@ static void vop_plane_destroy(struct drm_plane *plane)
>         drm_plane_cleanup(plane);
>  }
>
> +static inline bool rockchip_afbc(u64 modifier)
> +{
> +       return modifier == ROCKCHIP_AFBC_MOD;
> +}
> +
> +static bool rockchip_mod_supported(struct drm_plane *plane,
> +                                  u32 format, u64 modifier)
> +{
> +       if (modifier == DRM_FORMAT_MOD_LINEAR)
> +               return true;
> +
> +       if (!rockchip_afbc(modifier)) {
> +               DRM_DEBUG_KMS("Unsupported format modifer 0x%llx\n", modifier);
> +
> +               return false;
> +       }
> +
> +       return vop_convert_afbc_format(format) >= 0;
> +}
> +
>  static int vop_plane_atomic_check(struct drm_plane *plane,
>                            struct drm_plane_state *state)
>  {
> @@ -758,6 +825,30 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
>                 return -EINVAL;
>         }
>
> +       if (rockchip_afbc(fb->modifier)) {
> +               struct vop *vop = to_vop(crtc);
> +
> +               if (!vop->data->afbc) {
> +                       DRM_ERROR("vop does not support AFBC\n");
> +                       return -EINVAL;
> +               }
> +
> +               ret = vop_convert_afbc_format(fb->format->format);
> +               if (ret < 0)
> +                       return ret;
> +
> +               if (state->src.x1 || state->src.y1) {
> +                       DRM_ERROR("AFBC does not support offset display, xpos=%d, ypos=%d, offset=%d\n", state->src.x1, state->src.y1, fb->offsets[0]);
> +                       return -EINVAL;
> +               }
> +
> +               if (state->rotation && state->rotation != DRM_MODE_ROTATE_0) {
> +                       DRM_ERROR("No rotation support in AFBC, rotation=%d\n",
> +                                 state->rotation);
> +                       return -EINVAL;
> +               }
> +       }
> +
>         return 0;
>  }
>
> @@ -846,6 +937,16 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
>
>         spin_lock(&vop->reg_lock);
>
> +       if (rockchip_afbc(fb->modifier)) {
> +               int afbc_format = vop_convert_afbc_format(fb->format->format);
> +
> +               VOP_AFBC_SET(vop, format, afbc_format | AFBC_TILE_16x16);
> +               VOP_AFBC_SET(vop, hreg_block_split, 0);
> +               VOP_AFBC_SET(vop, win_sel, VOP_WIN_TO_INDEX(vop_win));
> +               VOP_AFBC_SET(vop, hdr_ptr, dma_addr);
> +               VOP_AFBC_SET(vop, pic_size, act_info);
> +       }
> +
>         VOP_WIN_SET(vop, win, format, format);
>         VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
>         VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
> @@ -1001,6 +1102,7 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>         .reset = drm_atomic_helper_plane_reset,
>         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>         .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +       .format_mod_supported = rockchip_mod_supported,
>  };
>
>  static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
> @@ -1310,6 +1412,10 @@ static int vop_crtc_atomic_check(struct drm_crtc *crtc,
>                                  struct drm_crtc_state *crtc_state)
>  {
>         struct vop *vop = to_vop(crtc);
> +       struct drm_plane *plane;
> +       struct drm_plane_state *plane_state;
> +       struct rockchip_crtc_state *s;
> +       int afbc_planes = 0;
>
>         if (vop->lut_regs && crtc_state->color_mgmt_changed &&
>             crtc_state->gamma_lut) {
> @@ -1323,6 +1429,27 @@ static int vop_crtc_atomic_check(struct drm_crtc *crtc,
>                 }
>         }
>
> +       drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
> +               plane_state =
> +                       drm_atomic_get_plane_state(crtc_state->state, plane);
> +               if (IS_ERR(plane_state)) {
> +                       DRM_DEBUG_KMS("Cannot get plane state for plane %s\n",
> +                                     plane->name);
> +                       return PTR_ERR(plane_state);
> +               }
> +
> +               if (drm_is_afbc(plane_state->fb->modifier))
> +                       ++afbc_planes;
> +       }
> +
> +       if (afbc_planes > 1) {
> +               DRM_DEBUG_KMS("Invalid number of AFBC planes; got %d, expected at most 1\n", afbc_planes);
> +               return -EINVAL;
> +       }
> +
> +       s = to_rockchip_crtc_state(crtc_state);
> +       s->enable_afbc = afbc_planes > 0;
> +
>         return 0;
>  }
>
> @@ -1333,6 +1460,7 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
>         struct drm_plane_state *old_plane_state, *new_plane_state;
>         struct vop *vop = to_vop(crtc);
>         struct drm_plane *plane;
> +       struct rockchip_crtc_state *s;
>         int i;
>
>         if (WARN_ON(!vop->is_enabled))
> @@ -1340,6 +1468,9 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
>
>         spin_lock(&vop->reg_lock);
>
> +       /* Enable AFBC if there is some AFBC window, disable otherwise. */
> +       s = to_rockchip_crtc_state(crtc->state);
> +       VOP_AFBC_SET(vop, enable, s->enable_afbc);
>         vop_cfg_done(vop);
>
>         spin_unlock(&vop->reg_lock);
> @@ -1634,7 +1765,8 @@ static int vop_create_crtc(struct vop *vop)
>                                                0, &vop_plane_funcs,
>                                                win_data->phy->data_formats,
>                                                win_data->phy->nformats,
> -                                              NULL, win_data->type, NULL);
> +                                              win_data->phy->format_modifiers,
> +                                              win_data->type, NULL);
>                 if (ret) {
>                         DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n",
>                                       ret);
> @@ -1678,7 +1810,8 @@ static int vop_create_crtc(struct vop *vop)
>                                                &vop_plane_funcs,
>                                                win_data->phy->data_formats,
>                                                win_data->phy->nformats,
> -                                              NULL, win_data->type, NULL);
> +                                              win_data->phy->format_modifiers,
> +                                              win_data->type, NULL);
>                 if (ret) {
>                         DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",
>                                       ret);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index cc672620d6e0..d03bdb531ef2 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -17,6 +17,11 @@
>
>  #define NUM_YUV2YUV_COEFFICIENTS 12
>
> +#define ROCKCHIP_AFBC_MOD \
> +       DRM_FORMAT_MOD_ARM_AFBC( \
> +               AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE \
> +       )
> +
>  enum vop_data_format {
>         VOP_FMT_ARGB8888 = 0,
>         VOP_FMT_RGB888,
> @@ -34,6 +39,16 @@ struct vop_reg {
>         bool relaxed;
>  };
>
> +struct vop_afbc {
> +       struct vop_reg enable;
> +       struct vop_reg win_sel;
> +       struct vop_reg format;
> +       struct vop_reg hreg_block_split;
> +       struct vop_reg pic_size;
> +       struct vop_reg hdr_ptr;
> +       struct vop_reg rstn;
> +};
> +
>  struct vop_modeset {
>         struct vop_reg htotal_pw;
>         struct vop_reg hact_st_end;
> @@ -134,6 +149,7 @@ struct vop_win_phy {
>         const struct vop_scl_regs *scl;
>         const uint32_t *data_formats;
>         uint32_t nformats;
> +       const uint64_t *format_modifiers;
>
>         struct vop_reg enable;
>         struct vop_reg gate;
> @@ -173,6 +189,7 @@ struct vop_data {
>         const struct vop_misc *misc;
>         const struct vop_modeset *modeset;
>         const struct vop_output *output;
> +       const struct vop_afbc *afbc;
>         const struct vop_win_yuv2yuv_data *win_yuv2yuv;
>         const struct vop_win_data *win;
>         unsigned int win_size;
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 7a9d979c8d5d..2413deded22c 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -50,6 +50,17 @@ static const uint32_t formats_win_full[] = {
>         DRM_FORMAT_NV24,
>  };
>
> +static const uint64_t format_modifiers_win_full[] = {
> +       DRM_FORMAT_MOD_LINEAR,
> +       DRM_FORMAT_MOD_INVALID,
> +};
> +
> +static const uint64_t format_modifiers_win_full_afbc[] = {
> +       ROCKCHIP_AFBC_MOD,
> +       DRM_FORMAT_MOD_LINEAR,
> +       DRM_FORMAT_MOD_INVALID,
> +};
> +
>  static const uint32_t formats_win_lite[] = {
>         DRM_FORMAT_XRGB8888,
>         DRM_FORMAT_ARGB8888,
> @@ -61,6 +72,11 @@ static const uint32_t formats_win_lite[] = {
>         DRM_FORMAT_BGR565,
>  };
>
> +static const uint64_t format_modifiers_win_lite[] = {
> +       DRM_FORMAT_MOD_LINEAR,
> +       DRM_FORMAT_MOD_INVALID,
> +};
> +
>  static const struct vop_scl_regs rk3036_win_scl = {
>         .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
>         .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
> @@ -72,6 +88,7 @@ static const struct vop_win_phy rk3036_win0_data = {
>         .scl = &rk3036_win_scl,
>         .data_formats = formats_win_full,
>         .nformats = ARRAY_SIZE(formats_win_full),
> +       .format_modifiers = format_modifiers_win_full,
>         .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
>         .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
>         .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
> @@ -87,6 +104,7 @@ static const struct vop_win_phy rk3036_win0_data = {
>  static const struct vop_win_phy rk3036_win1_data = {
>         .data_formats = formats_win_lite,
>         .nformats = ARRAY_SIZE(formats_win_lite),
> +       .format_modifiers = format_modifiers_win_lite,
>         .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
>         .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
>         .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
> @@ -153,6 +171,7 @@ static const struct vop_data rk3036_vop = {
>  static const struct vop_win_phy rk3126_win1_data = {
>         .data_formats = formats_win_lite,
>         .nformats = ARRAY_SIZE(formats_win_lite),
> +       .format_modifiers = format_modifiers_win_lite,
>         .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
>         .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
>         .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
> @@ -234,6 +253,7 @@ static const struct vop_win_phy px30_win0_data = {
>         .scl = &px30_win_scl,
>         .data_formats = formats_win_full,
>         .nformats = ARRAY_SIZE(formats_win_full),
> +       .format_modifiers = format_modifiers_win_full,
>         .enable = VOP_REG(PX30_WIN0_CTRL0, 0x1, 0),
>         .format = VOP_REG(PX30_WIN0_CTRL0, 0x7, 1),
>         .rb_swap = VOP_REG(PX30_WIN0_CTRL0, 0x1, 12),
> @@ -249,6 +269,7 @@ static const struct vop_win_phy px30_win0_data = {
>  static const struct vop_win_phy px30_win1_data = {
>         .data_formats = formats_win_lite,
>         .nformats = ARRAY_SIZE(formats_win_lite),
> +       .format_modifiers = format_modifiers_win_lite,
>         .enable = VOP_REG(PX30_WIN1_CTRL0, 0x1, 0),
>         .format = VOP_REG(PX30_WIN1_CTRL0, 0x7, 4),
>         .rb_swap = VOP_REG(PX30_WIN1_CTRL0, 0x1, 12),
> @@ -261,6 +282,7 @@ static const struct vop_win_phy px30_win1_data = {
>  static const struct vop_win_phy px30_win2_data = {
>         .data_formats = formats_win_lite,
>         .nformats = ARRAY_SIZE(formats_win_lite),
> +       .format_modifiers = format_modifiers_win_lite,
>         .gate = VOP_REG(PX30_WIN2_CTRL0, 0x1, 4),
>         .enable = VOP_REG(PX30_WIN2_CTRL0, 0x1, 0),
>         .format = VOP_REG(PX30_WIN2_CTRL0, 0x3, 5),
> @@ -316,6 +338,7 @@ static const struct vop_win_phy rk3066_win0_data = {
>         .scl = &rk3066_win_scl,
>         .data_formats = formats_win_full,
>         .nformats = ARRAY_SIZE(formats_win_full),
> +       .format_modifiers = format_modifiers_win_full,
>         .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 0),
>         .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 4),
>         .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 19),
> @@ -332,6 +355,7 @@ static const struct vop_win_phy rk3066_win1_data = {
>         .scl = &rk3066_win_scl,
>         .data_formats = formats_win_full,
>         .nformats = ARRAY_SIZE(formats_win_full),
> +       .format_modifiers = format_modifiers_win_full,
>         .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 1),
>         .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 7),
>         .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 23),
> @@ -347,6 +371,7 @@ static const struct vop_win_phy rk3066_win1_data = {
>  static const struct vop_win_phy rk3066_win2_data = {
>         .data_formats = formats_win_lite,
>         .nformats = ARRAY_SIZE(formats_win_lite),
> +       .format_modifiers = format_modifiers_win_lite,
>         .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 2),
>         .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 10),
>         .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 27),
> @@ -426,6 +451,7 @@ static const struct vop_win_phy rk3188_win0_data = {
>         .scl = &rk3188_win_scl,
>         .data_formats = formats_win_full,
>         .nformats = ARRAY_SIZE(formats_win_full),
> +       .format_modifiers = format_modifiers_win_full,
>         .enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 0),
>         .format = VOP_REG(RK3188_SYS_CTRL, 0x7, 3),
>         .rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 15),
> @@ -440,6 +466,7 @@ static const struct vop_win_phy rk3188_win0_data = {
>  static const struct vop_win_phy rk3188_win1_data = {
>         .data_formats = formats_win_lite,
>         .nformats = ARRAY_SIZE(formats_win_lite),
> +       .format_modifiers = format_modifiers_win_lite,
>         .enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 1),
>         .format = VOP_REG(RK3188_SYS_CTRL, 0x7, 6),
>         .rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 19),
> @@ -545,6 +572,7 @@ static const struct vop_win_phy rk3288_win01_data = {
>         .scl = &rk3288_win_full_scl,
>         .data_formats = formats_win_full,
>         .nformats = ARRAY_SIZE(formats_win_full),
> +       .format_modifiers = format_modifiers_win_full,
>         .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
>         .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
>         .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
> @@ -563,6 +591,7 @@ static const struct vop_win_phy rk3288_win01_data = {
>  static const struct vop_win_phy rk3288_win23_data = {
>         .data_formats = formats_win_lite,
>         .nformats = ARRAY_SIZE(formats_win_lite),
> +       .format_modifiers = format_modifiers_win_lite,
>         .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4),
>         .gate = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0),
>         .format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1),
> @@ -677,6 +706,7 @@ static const struct vop_win_phy rk3368_win01_data = {
>         .scl = &rk3288_win_full_scl,
>         .data_formats = formats_win_full,
>         .nformats = ARRAY_SIZE(formats_win_full),
> +       .format_modifiers = format_modifiers_win_full,
>         .enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0),
>         .format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1),
>         .rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12),
> @@ -697,6 +727,7 @@ static const struct vop_win_phy rk3368_win01_data = {
>  static const struct vop_win_phy rk3368_win23_data = {
>         .data_formats = formats_win_lite,
>         .nformats = ARRAY_SIZE(formats_win_lite),
> +       .format_modifiers = format_modifiers_win_lite,
>         .gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0),
>         .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4),
>         .format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5),
> @@ -817,6 +848,53 @@ static const struct vop_win_yuv2yuv_data rk3399_vop_big_win_yuv2yuv_data[] = {
>           .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 9) },
>         { .base = 0xC0, .phy = &rk3399_yuv2yuv_win23_data },
>         { .base = 0x120, .phy = &rk3399_yuv2yuv_win23_data },
> +
> +};
> +
> +static const struct vop_win_phy rk3399_win01_data = {
> +       .scl = &rk3288_win_full_scl,
> +       .data_formats = formats_win_full,
> +       .nformats = ARRAY_SIZE(formats_win_full),
> +       .format_modifiers = format_modifiers_win_full_afbc,
> +       .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
> +       .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
> +       .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
> +       .y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22),
> +       .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
> +       .dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
> +       .dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0),
> +       .yrgb_mst = VOP_REG(RK3288_WIN0_YRGB_MST, 0xffffffff, 0),
> +       .uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0),
> +       .yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0),
> +       .uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16),
> +       .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
> +       .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0),
> +};
> +
> +/*
> + * rk3399 vop big windows register layout is same as rk3288, but we
> + * have a separate rk3399 win data array here so that we can advertise
> + * AFBC on the primary plane.
> + */
> +static const struct vop_win_data rk3399_vop_win_data[] = {
> +       { .base = 0x00, .phy = &rk3399_win01_data,
> +         .type = DRM_PLANE_TYPE_PRIMARY },
> +       { .base = 0x40, .phy = &rk3288_win01_data,
> +         .type = DRM_PLANE_TYPE_OVERLAY },
> +       { .base = 0x00, .phy = &rk3288_win23_data,
> +         .type = DRM_PLANE_TYPE_OVERLAY },
> +       { .base = 0x50, .phy = &rk3288_win23_data,
> +         .type = DRM_PLANE_TYPE_CURSOR },
> +};
> +
> +static const struct vop_afbc rk3399_vop_afbc = {
> +       .rstn = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 3),
> +       .enable = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 0),
> +       .win_sel = VOP_REG(RK3399_AFBCD0_CTRL, 0x3, 1),
> +       .format = VOP_REG(RK3399_AFBCD0_CTRL, 0x1f, 16),
> +       .hreg_block_split = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 21),
> +       .hdr_ptr = VOP_REG(RK3399_AFBCD0_HDR_PTR, 0xffffffff, 0),
> +       .pic_size = VOP_REG(RK3399_AFBCD0_PIC_SIZE, 0xffffffff, 0),
>  };
>
>  static const struct vop_data rk3399_vop_big = {
> @@ -826,9 +904,10 @@ static const struct vop_data rk3399_vop_big = {
>         .common = &rk3288_common,
>         .modeset = &rk3288_modeset,
>         .output = &rk3399_output,
> +       .afbc = &rk3399_vop_afbc,
>         .misc = &rk3368_misc,
> -       .win = rk3368_vop_win_data,
> -       .win_size = ARRAY_SIZE(rk3368_vop_win_data),
> +       .win = rk3399_vop_win_data,
> +       .win_size = ARRAY_SIZE(rk3399_vop_win_data),
>         .win_yuv2yuv = rk3399_vop_big_win_yuv2yuv_data,
>  };
>
> --
> 2.17.1

Heiko, Sandy, being the maintainers of the Rockchip driver, can you
review/ack this patch?

I believe the intention is to merge the series via drm-misc. Andrzej
already has commit access.

-Emil
Huang Jiachai March 19, 2020, 2:57 a.m. UTC | #2
Hi Emil,

在 2020/3/16 下午10:10, Emil Velikov 写道:
> On Wed, 11 Mar 2020 at 14:56, Andrzej Pietrasiewicz
> <andrzej.p@collabora.com> wrote:
>> This patch adds support for afbc handling. afbc is a compressed format
>> which reduces the necessary memory bandwidth.
>>
>> Co-developed-by: Mark Yao <mark.yao@rock-chips.com>
>> Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
>> Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
>> ---
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   1 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  43 +++++-
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 137 +++++++++++++++++++-
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |  17 +++
>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |  83 +++++++++++-
>>   5 files changed, 276 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index c5b06048124e..e33c2dcd0d4b 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -30,6 +30,7 @@ struct rockchip_crtc_state {
>>          int output_mode;
>>          int output_bpc;
>>          int output_flags;
>> +       bool enable_afbc;
>>   };
>>   #define to_rockchip_crtc_state(s) \
>>                  container_of(s, struct rockchip_crtc_state, base)
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> index 221e72e71432..9b13c784b347 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> @@ -57,8 +57,49 @@ static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers =
>>          .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
>>   };
>>
>> +static struct drm_framebuffer *
>> +rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
>> +                  const struct drm_mode_fb_cmd2 *mode_cmd)
>> +{
>> +       struct drm_afbc_framebuffer *afbc_fb;
>> +       const struct drm_format_info *info;
>> +       int ret;
>> +
>> +       info = drm_get_format_info(dev, mode_cmd);
>> +       if (!info)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       afbc_fb = kzalloc(sizeof(*afbc_fb), GFP_KERNEL);
>> +       if (!afbc_fb)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       ret = drm_gem_fb_init_with_funcs(dev, &afbc_fb->base, file, mode_cmd,
>> +                                        &rockchip_drm_fb_funcs);
>> +       if (ret) {
>> +               kfree(afbc_fb);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       if (drm_is_afbc(mode_cmd->modifier[0])) {
>> +               int ret, i;
>> +
>> +               ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
>> +               if (ret) {
>> +                       struct drm_gem_object **obj = afbc_fb->base.obj;
>> +
>> +                       for (i = 0; i < info->num_planes; ++i)
>> +                               drm_gem_object_put_unlocked(obj[i]);
>> +
>> +                       kfree(afbc_fb);
>> +                       return ERR_PTR(ret);
>> +               }
>> +       }
>> +
>> +       return &afbc_fb->base;
>> +}
>> +
>>   static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
>> -       .fb_create = drm_gem_fb_create_with_dirty,
>> +       .fb_create = rockchip_fb_create,
>>          .output_poll_changed = drm_fb_helper_output_poll_changed,
>>          .atomic_check = drm_atomic_helper_check,
>>          .atomic_commit = drm_atomic_helper_commit,
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index cecb2cc781f5..b87d22eb6ae1 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -91,9 +91,22 @@
>>   #define VOP_WIN_TO_INDEX(vop_win) \
>>          ((vop_win) - (vop_win)->vop->win)
>>
>> +#define VOP_AFBC_SET(vop, name, v) \
>> +       do { \
>> +               if ((vop)->data->afbc) \
>> +                       vop_reg_set((vop), &(vop)->data->afbc->name, \
>> +                                   0, ~0, v, #name); \
>> +       } while (0)
>> +
>>   #define to_vop(x) container_of(x, struct vop, crtc)
>>   #define to_vop_win(x) container_of(x, struct vop_win, base)
>>
>> +#define AFBC_FMT_RGB565                0x0
>> +#define AFBC_FMT_U8U8U8U8      0x5
>> +#define AFBC_FMT_U8U8U8                0x4
>> +
>> +#define AFBC_TILE_16x16                BIT(4)
>> +
>>   /*
>>    * The coefficients of the following matrix are all fixed points.
>>    * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets.
>> @@ -274,6 +287,29 @@ static enum vop_data_format vop_convert_format(uint32_t format)
>>          }
>>   }
>>
>> +static int vop_convert_afbc_format(uint32_t format)
>> +{
>> +       switch (format) {
>> +       case DRM_FORMAT_XRGB8888:
>> +       case DRM_FORMAT_ARGB8888:
>> +       case DRM_FORMAT_XBGR8888:
>> +       case DRM_FORMAT_ABGR8888:
>> +               return AFBC_FMT_U8U8U8U8;
>> +       case DRM_FORMAT_RGB888:
>> +       case DRM_FORMAT_BGR888:
>> +               return AFBC_FMT_U8U8U8;
>> +       case DRM_FORMAT_RGB565:
>> +       case DRM_FORMAT_BGR565:
>> +               return AFBC_FMT_RGB565;
>> +       /* either of the below should not be reachable */
>> +       default:
>> +               DRM_WARN_ONCE("unsupported AFBC format[%08x]\n", format);
>> +               return -EINVAL;
>> +       }
>> +
>> +       return -EINVAL;
>> +}
>> +
>>   static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
>>                                    uint32_t dst, bool is_horizontal,
>>                                    int vsu_mode, int *vskiplines)
>> @@ -598,6 +634,17 @@ static int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
>>                          vop_win_disable(vop, vop_win);
>>                  }
>>          }
>> +
>> +       if (vop->data->afbc) {
>> +               struct rockchip_crtc_state *s;
>> +               /*
>> +                * Disable AFBC and forget there was a vop window with AFBC
>> +                */
>> +               VOP_AFBC_SET(vop, enable, 0);
>> +               s = to_rockchip_crtc_state(crtc->state);
>> +               s->enable_afbc = false;
>> +       }
>> +
>>          spin_unlock(&vop->reg_lock);
>>
>>          vop_cfg_done(vop);
>> @@ -710,6 +757,26 @@ static void vop_plane_destroy(struct drm_plane *plane)
>>          drm_plane_cleanup(plane);
>>   }
>>
>> +static inline bool rockchip_afbc(u64 modifier)
>> +{
>> +       return modifier == ROCKCHIP_AFBC_MOD;
>> +}
>> +
>> +static bool rockchip_mod_supported(struct drm_plane *plane,
>> +                                  u32 format, u64 modifier)
>> +{
>> +       if (modifier == DRM_FORMAT_MOD_LINEAR)
>> +               return true;
>> +
>> +       if (!rockchip_afbc(modifier)) {
>> +               DRM_DEBUG_KMS("Unsupported format modifer 0x%llx\n", modifier);
>> +
>> +               return false;
>> +       }
>> +
>> +       return vop_convert_afbc_format(format) >= 0;
>> +}
>> +
>>   static int vop_plane_atomic_check(struct drm_plane *plane,
>>                             struct drm_plane_state *state)
>>   {
>> @@ -758,6 +825,30 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
>>                  return -EINVAL;
>>          }
>>
>> +       if (rockchip_afbc(fb->modifier)) {
>> +               struct vop *vop = to_vop(crtc);
>> +
>> +               if (!vop->data->afbc) {
>> +                       DRM_ERROR("vop does not support AFBC\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               ret = vop_convert_afbc_format(fb->format->format);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               if (state->src.x1 || state->src.y1) {
>> +                       DRM_ERROR("AFBC does not support offset display, xpos=%d, ypos=%d, offset=%d\n", state->src.x1, state->src.y1, fb->offsets[0]);
>> +                       return -EINVAL;
>> +               }
>> +
>> +               if (state->rotation && state->rotation != DRM_MODE_ROTATE_0) {
>> +                       DRM_ERROR("No rotation support in AFBC, rotation=%d\n",
>> +                                 state->rotation);
>> +                       return -EINVAL;
>> +               }
>> +       }
>> +
>>          return 0;
>>   }
>>
>> @@ -846,6 +937,16 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
>>
>>          spin_lock(&vop->reg_lock);
>>
>> +       if (rockchip_afbc(fb->modifier)) {
>> +               int afbc_format = vop_convert_afbc_format(fb->format->format);
>> +
>> +               VOP_AFBC_SET(vop, format, afbc_format | AFBC_TILE_16x16);
>> +               VOP_AFBC_SET(vop, hreg_block_split, 0);
>> +               VOP_AFBC_SET(vop, win_sel, VOP_WIN_TO_INDEX(vop_win));
>> +               VOP_AFBC_SET(vop, hdr_ptr, dma_addr);
>> +               VOP_AFBC_SET(vop, pic_size, act_info);
>> +       }
>> +
>>          VOP_WIN_SET(vop, win, format, format);
>>          VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
>>          VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
>> @@ -1001,6 +1102,7 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>          .reset = drm_atomic_helper_plane_reset,
>>          .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
>>          .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
>> +       .format_mod_supported = rockchip_mod_supported,
>>   };
>>
>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>> @@ -1310,6 +1412,10 @@ static int vop_crtc_atomic_check(struct drm_crtc *crtc,
>>                                   struct drm_crtc_state *crtc_state)
>>   {
>>          struct vop *vop = to_vop(crtc);
>> +       struct drm_plane *plane;
>> +       struct drm_plane_state *plane_state;
>> +       struct rockchip_crtc_state *s;
>> +       int afbc_planes = 0;
>>
>>          if (vop->lut_regs && crtc_state->color_mgmt_changed &&
>>              crtc_state->gamma_lut) {
>> @@ -1323,6 +1429,27 @@ static int vop_crtc_atomic_check(struct drm_crtc *crtc,
>>                  }
>>          }
>>
>> +       drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
>> +               plane_state =
>> +                       drm_atomic_get_plane_state(crtc_state->state, plane);
>> +               if (IS_ERR(plane_state)) {
>> +                       DRM_DEBUG_KMS("Cannot get plane state for plane %s\n",
>> +                                     plane->name);
>> +                       return PTR_ERR(plane_state);
>> +               }
>> +
>> +               if (drm_is_afbc(plane_state->fb->modifier))
>> +                       ++afbc_planes;
>> +       }
>> +
>> +       if (afbc_planes > 1) {
>> +               DRM_DEBUG_KMS("Invalid number of AFBC planes; got %d, expected at most 1\n", afbc_planes);
>> +               return -EINVAL;
>> +       }
>> +
>> +       s = to_rockchip_crtc_state(crtc_state);
>> +       s->enable_afbc = afbc_planes > 0;
>> +
>>          return 0;
>>   }
>>
>> @@ -1333,6 +1460,7 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
>>          struct drm_plane_state *old_plane_state, *new_plane_state;
>>          struct vop *vop = to_vop(crtc);
>>          struct drm_plane *plane;
>> +       struct rockchip_crtc_state *s;
>>          int i;
>>
>>          if (WARN_ON(!vop->is_enabled))
>> @@ -1340,6 +1468,9 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
>>
>>          spin_lock(&vop->reg_lock);
>>
>> +       /* Enable AFBC if there is some AFBC window, disable otherwise. */
>> +       s = to_rockchip_crtc_state(crtc->state);
>> +       VOP_AFBC_SET(vop, enable, s->enable_afbc);
>>          vop_cfg_done(vop);
>>
>>          spin_unlock(&vop->reg_lock);
>> @@ -1634,7 +1765,8 @@ static int vop_create_crtc(struct vop *vop)
>>                                                 0, &vop_plane_funcs,
>>                                                 win_data->phy->data_formats,
>>                                                 win_data->phy->nformats,
>> -                                              NULL, win_data->type, NULL);
>> +                                              win_data->phy->format_modifiers,
>> +                                              win_data->type, NULL);
>>                  if (ret) {
>>                          DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n",
>>                                        ret);
>> @@ -1678,7 +1810,8 @@ static int vop_create_crtc(struct vop *vop)
>>                                                 &vop_plane_funcs,
>>                                                 win_data->phy->data_formats,
>>                                                 win_data->phy->nformats,
>> -                                              NULL, win_data->type, NULL);
>> +                                              win_data->phy->format_modifiers,
>> +                                              win_data->type, NULL);
>>                  if (ret) {
>>                          DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",
>>                                        ret);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> index cc672620d6e0..d03bdb531ef2 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -17,6 +17,11 @@
>>
>>   #define NUM_YUV2YUV_COEFFICIENTS 12
>>
>> +#define ROCKCHIP_AFBC_MOD \
>> +       DRM_FORMAT_MOD_ARM_AFBC( \
>> +               AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE \
>> +       )
>> +
>>   enum vop_data_format {
>>          VOP_FMT_ARGB8888 = 0,
>>          VOP_FMT_RGB888,
>> @@ -34,6 +39,16 @@ struct vop_reg {
>>          bool relaxed;
>>   };
>>
>> +struct vop_afbc {
>> +       struct vop_reg enable;
>> +       struct vop_reg win_sel;
>> +       struct vop_reg format;
>> +       struct vop_reg hreg_block_split;
>> +       struct vop_reg pic_size;
>> +       struct vop_reg hdr_ptr;
>> +       struct vop_reg rstn;
>> +};
>> +
>>   struct vop_modeset {
>>          struct vop_reg htotal_pw;
>>          struct vop_reg hact_st_end;
>> @@ -134,6 +149,7 @@ struct vop_win_phy {
>>          const struct vop_scl_regs *scl;
>>          const uint32_t *data_formats;
>>          uint32_t nformats;
>> +       const uint64_t *format_modifiers;
>>
>>          struct vop_reg enable;
>>          struct vop_reg gate;
>> @@ -173,6 +189,7 @@ struct vop_data {
>>          const struct vop_misc *misc;
>>          const struct vop_modeset *modeset;
>>          const struct vop_output *output;
>> +       const struct vop_afbc *afbc;
>>          const struct vop_win_yuv2yuv_data *win_yuv2yuv;
>>          const struct vop_win_data *win;
>>          unsigned int win_size;
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> index 7a9d979c8d5d..2413deded22c 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -50,6 +50,17 @@ static const uint32_t formats_win_full[] = {
>>          DRM_FORMAT_NV24,
>>   };
>>
>> +static const uint64_t format_modifiers_win_full[] = {
>> +       DRM_FORMAT_MOD_LINEAR,
>> +       DRM_FORMAT_MOD_INVALID,
>> +};
>> +
>> +static const uint64_t format_modifiers_win_full_afbc[] = {
>> +       ROCKCHIP_AFBC_MOD,
>> +       DRM_FORMAT_MOD_LINEAR,
>> +       DRM_FORMAT_MOD_INVALID,
>> +};
>> +
>>   static const uint32_t formats_win_lite[] = {
>>          DRM_FORMAT_XRGB8888,
>>          DRM_FORMAT_ARGB8888,
>> @@ -61,6 +72,11 @@ static const uint32_t formats_win_lite[] = {
>>          DRM_FORMAT_BGR565,
>>   };
>>
>> +static const uint64_t format_modifiers_win_lite[] = {
>> +       DRM_FORMAT_MOD_LINEAR,
>> +       DRM_FORMAT_MOD_INVALID,
>> +};
>> +
>>   static const struct vop_scl_regs rk3036_win_scl = {
>>          .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
>>          .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
>> @@ -72,6 +88,7 @@ static const struct vop_win_phy rk3036_win0_data = {
>>          .scl = &rk3036_win_scl,
>>          .data_formats = formats_win_full,
>>          .nformats = ARRAY_SIZE(formats_win_full),
>> +       .format_modifiers = format_modifiers_win_full,
>>          .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
>>          .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
>>          .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
>> @@ -87,6 +104,7 @@ static const struct vop_win_phy rk3036_win0_data = {
>>   static const struct vop_win_phy rk3036_win1_data = {
>>          .data_formats = formats_win_lite,
>>          .nformats = ARRAY_SIZE(formats_win_lite),
>> +       .format_modifiers = format_modifiers_win_lite,
>>          .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
>>          .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
>>          .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
>> @@ -153,6 +171,7 @@ static const struct vop_data rk3036_vop = {
>>   static const struct vop_win_phy rk3126_win1_data = {
>>          .data_formats = formats_win_lite,
>>          .nformats = ARRAY_SIZE(formats_win_lite),
>> +       .format_modifiers = format_modifiers_win_lite,
>>          .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
>>          .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
>>          .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
>> @@ -234,6 +253,7 @@ static const struct vop_win_phy px30_win0_data = {
>>          .scl = &px30_win_scl,
>>          .data_formats = formats_win_full,
>>          .nformats = ARRAY_SIZE(formats_win_full),
>> +       .format_modifiers = format_modifiers_win_full,
>>          .enable = VOP_REG(PX30_WIN0_CTRL0, 0x1, 0),
>>          .format = VOP_REG(PX30_WIN0_CTRL0, 0x7, 1),
>>          .rb_swap = VOP_REG(PX30_WIN0_CTRL0, 0x1, 12),
>> @@ -249,6 +269,7 @@ static const struct vop_win_phy px30_win0_data = {
>>   static const struct vop_win_phy px30_win1_data = {
>>          .data_formats = formats_win_lite,
>>          .nformats = ARRAY_SIZE(formats_win_lite),
>> +       .format_modifiers = format_modifiers_win_lite,
>>          .enable = VOP_REG(PX30_WIN1_CTRL0, 0x1, 0),
>>          .format = VOP_REG(PX30_WIN1_CTRL0, 0x7, 4),
>>          .rb_swap = VOP_REG(PX30_WIN1_CTRL0, 0x1, 12),
>> @@ -261,6 +282,7 @@ static const struct vop_win_phy px30_win1_data = {
>>   static const struct vop_win_phy px30_win2_data = {
>>          .data_formats = formats_win_lite,
>>          .nformats = ARRAY_SIZE(formats_win_lite),
>> +       .format_modifiers = format_modifiers_win_lite,
>>          .gate = VOP_REG(PX30_WIN2_CTRL0, 0x1, 4),
>>          .enable = VOP_REG(PX30_WIN2_CTRL0, 0x1, 0),
>>          .format = VOP_REG(PX30_WIN2_CTRL0, 0x3, 5),
>> @@ -316,6 +338,7 @@ static const struct vop_win_phy rk3066_win0_data = {
>>          .scl = &rk3066_win_scl,
>>          .data_formats = formats_win_full,
>>          .nformats = ARRAY_SIZE(formats_win_full),
>> +       .format_modifiers = format_modifiers_win_full,
>>          .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 0),
>>          .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 4),
>>          .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 19),
>> @@ -332,6 +355,7 @@ static const struct vop_win_phy rk3066_win1_data = {
>>          .scl = &rk3066_win_scl,
>>          .data_formats = formats_win_full,
>>          .nformats = ARRAY_SIZE(formats_win_full),
>> +       .format_modifiers = format_modifiers_win_full,
>>          .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 1),
>>          .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 7),
>>          .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 23),
>> @@ -347,6 +371,7 @@ static const struct vop_win_phy rk3066_win1_data = {
>>   static const struct vop_win_phy rk3066_win2_data = {
>>          .data_formats = formats_win_lite,
>>          .nformats = ARRAY_SIZE(formats_win_lite),
>> +       .format_modifiers = format_modifiers_win_lite,
>>          .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 2),
>>          .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 10),
>>          .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 27),
>> @@ -426,6 +451,7 @@ static const struct vop_win_phy rk3188_win0_data = {
>>          .scl = &rk3188_win_scl,
>>          .data_formats = formats_win_full,
>>          .nformats = ARRAY_SIZE(formats_win_full),
>> +       .format_modifiers = format_modifiers_win_full,
>>          .enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 0),
>>          .format = VOP_REG(RK3188_SYS_CTRL, 0x7, 3),
>>          .rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 15),
>> @@ -440,6 +466,7 @@ static const struct vop_win_phy rk3188_win0_data = {
>>   static const struct vop_win_phy rk3188_win1_data = {
>>          .data_formats = formats_win_lite,
>>          .nformats = ARRAY_SIZE(formats_win_lite),
>> +       .format_modifiers = format_modifiers_win_lite,
>>          .enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 1),
>>          .format = VOP_REG(RK3188_SYS_CTRL, 0x7, 6),
>>          .rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 19),
>> @@ -545,6 +572,7 @@ static const struct vop_win_phy rk3288_win01_data = {
>>          .scl = &rk3288_win_full_scl,
>>          .data_formats = formats_win_full,
>>          .nformats = ARRAY_SIZE(formats_win_full),
>> +       .format_modifiers = format_modifiers_win_full,
>>          .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
>>          .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
>>          .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
>> @@ -563,6 +591,7 @@ static const struct vop_win_phy rk3288_win01_data = {
>>   static const struct vop_win_phy rk3288_win23_data = {
>>          .data_formats = formats_win_lite,
>>          .nformats = ARRAY_SIZE(formats_win_lite),
>> +       .format_modifiers = format_modifiers_win_lite,
>>          .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4),
>>          .gate = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0),
>>          .format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1),
>> @@ -677,6 +706,7 @@ static const struct vop_win_phy rk3368_win01_data = {
>>          .scl = &rk3288_win_full_scl,
>>          .data_formats = formats_win_full,
>>          .nformats = ARRAY_SIZE(formats_win_full),
>> +       .format_modifiers = format_modifiers_win_full,
>>          .enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0),
>>          .format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1),
>>          .rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12),
>> @@ -697,6 +727,7 @@ static const struct vop_win_phy rk3368_win01_data = {
>>   static const struct vop_win_phy rk3368_win23_data = {
>>          .data_formats = formats_win_lite,
>>          .nformats = ARRAY_SIZE(formats_win_lite),
>> +       .format_modifiers = format_modifiers_win_lite,
>>          .gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0),
>>          .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4),
>>          .format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5),
>> @@ -817,6 +848,53 @@ static const struct vop_win_yuv2yuv_data rk3399_vop_big_win_yuv2yuv_data[] = {
>>            .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 9) },
>>          { .base = 0xC0, .phy = &rk3399_yuv2yuv_win23_data },
>>          { .base = 0x120, .phy = &rk3399_yuv2yuv_win23_data },
>> +
>> +};
>> +
>> +static const struct vop_win_phy rk3399_win01_data = {
>> +       .scl = &rk3288_win_full_scl,
>> +       .data_formats = formats_win_full,
>> +       .nformats = ARRAY_SIZE(formats_win_full),
>> +       .format_modifiers = format_modifiers_win_full_afbc,
>> +       .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
>> +       .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
>> +       .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
>> +       .y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22),
>> +       .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
>> +       .dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
>> +       .dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0),
>> +       .yrgb_mst = VOP_REG(RK3288_WIN0_YRGB_MST, 0xffffffff, 0),
>> +       .uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0),
>> +       .yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0),
>> +       .uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16),
>> +       .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
>> +       .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0),
>> +};
>> +
>> +/*
>> + * rk3399 vop big windows register layout is same as rk3288, but we
>> + * have a separate rk3399 win data array here so that we can advertise
>> + * AFBC on the primary plane.
>> + */
>> +static const struct vop_win_data rk3399_vop_win_data[] = {
>> +       { .base = 0x00, .phy = &rk3399_win01_data,
>> +         .type = DRM_PLANE_TYPE_PRIMARY },
>> +       { .base = 0x40, .phy = &rk3288_win01_data,
>> +         .type = DRM_PLANE_TYPE_OVERLAY },
>> +       { .base = 0x00, .phy = &rk3288_win23_data,
>> +         .type = DRM_PLANE_TYPE_OVERLAY },
>> +       { .base = 0x50, .phy = &rk3288_win23_data,
>> +         .type = DRM_PLANE_TYPE_CURSOR },
>> +};
>> +
>> +static const struct vop_afbc rk3399_vop_afbc = {
>> +       .rstn = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 3),
>> +       .enable = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 0),
>> +       .win_sel = VOP_REG(RK3399_AFBCD0_CTRL, 0x3, 1),
>> +       .format = VOP_REG(RK3399_AFBCD0_CTRL, 0x1f, 16),
>> +       .hreg_block_split = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 21),
>> +       .hdr_ptr = VOP_REG(RK3399_AFBCD0_HDR_PTR, 0xffffffff, 0),
>> +       .pic_size = VOP_REG(RK3399_AFBCD0_PIC_SIZE, 0xffffffff, 0),
>>   };
>>
>>   static const struct vop_data rk3399_vop_big = {
>> @@ -826,9 +904,10 @@ static const struct vop_data rk3399_vop_big = {
>>          .common = &rk3288_common,
>>          .modeset = &rk3288_modeset,
>>          .output = &rk3399_output,
>> +       .afbc = &rk3399_vop_afbc,
>>          .misc = &rk3368_misc,
>> -       .win = rk3368_vop_win_data,
>> -       .win_size = ARRAY_SIZE(rk3368_vop_win_data),
>> +       .win = rk3399_vop_win_data,
>> +       .win_size = ARRAY_SIZE(rk3399_vop_win_data),
>>          .win_yuv2yuv = rk3399_vop_big_win_yuv2yuv_data,
>>   };
>>
>> --
>> 2.17.1
> Heiko, Sandy, being the maintainers of the Rockchip driver, can you
> review/ack this patch?
>
> I believe the intention is to merge the series via drm-misc. Andrzej
> already has commit access.
>
> -Emil
>
>
Thanks for you patch, maybe we need to consider the compatibility with 
px30 platform afbc features,the main difference is:

1.  px30 support x offset and y offset display, and the state->src.x1 / 
state->src.y1  should be alibegned as 16pixel/line;

2.px30 only win1 can support afbdc format;
Andrzej Pietrasiewicz March 19, 2020, 9:54 a.m. UTC | #3
Hi Sandy,


W dniu 19.03.2020 o 03:57, Sandy Huang pisze:
> Hi Emil,
> 

<snip>

>>> -- 
>>> 2.17.1
>> Heiko, Sandy, being the maintainers of the Rockchip driver, can you
>> review/ack this patch?
>>
>> I believe the intention is to merge the series via drm-misc. Andrzej
>> already has commit access.
>>
>> -Emil
>>
>>
> Thanks for you patch, maybe we need to consider the compatibility with px30 
> platform afbc features,the main difference is:
> 
> 1.  px30 support x offset and y offset display, and the state->src.x1 / 
> state->src.y1  should be alibegned as 16pixel/line;
> 
> 2.px30 only win1 can support afbdc format;
> 
> 

Actually I sent the patch, Emil kindly forwarded it to you.

Unfortunately I don't have px30 hardware, so I can't test. Can we
merge the patch as is (if you are ok with the way it is now) and then
I will help reviewing the patch adding px30 support on top of it?

Regards,

Andrzej
Huang Jiachai March 20, 2020, 11:34 a.m. UTC | #4
Hi Andrzej ,


在 2020/3/19 下午5:54, Andrzej Pietrasiewicz 写道:
> Hi Sandy,
>
>
> W dniu 19.03.2020 o 03:57, Sandy Huang pisze:
>> Hi Emil,
>>
>
> <snip>
>
>>>> -- 
>>>> 2.17.1
>>> Heiko, Sandy, being the maintainers of the Rockchip driver, can you
>>> review/ack this patch?
>>>
>>> I believe the intention is to merge the series via drm-misc. Andrzej
>>> already has commit access.
>>>
>>> -Emil
>>>
>>>
>> Thanks for you patch, maybe we need to consider the compatibility 
>> with px30 platform afbc features,the main difference is:
>>
>> 1.  px30 support x offset and y offset display, and the state->src.x1 
>> / state->src.y1  should be alibegned as 16pixel/line;
>>
>> 2.px30 only win1 can support afbdc format;
>>
>>
>
> Actually I sent the patch, Emil kindly forwarded it to you.
>
> Unfortunately I don't have px30 hardware, so I can't test. Can we
> merge the patch as is (if you are ok with the way it is now) and then
> I will help reviewing the patch adding px30 support on top of it?
>
> Regards,
>
> Andrzej

OK, Let's add px30 AFBC support later.

Reviewed-by: Sandy Huang <hjc@rock-chips.com>

>
>
>

Patch
diff mbox series

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index c5b06048124e..e33c2dcd0d4b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -30,6 +30,7 @@  struct rockchip_crtc_state {
 	int output_mode;
 	int output_bpc;
 	int output_flags;
+	bool enable_afbc;
 };
 #define to_rockchip_crtc_state(s) \
 		container_of(s, struct rockchip_crtc_state, base)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 221e72e71432..9b13c784b347 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -57,8 +57,49 @@  static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers =
 	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
 };
 
+static struct drm_framebuffer *
+rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
+		   const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct drm_afbc_framebuffer *afbc_fb;
+	const struct drm_format_info *info;
+	int ret;
+
+	info = drm_get_format_info(dev, mode_cmd);
+	if (!info)
+		return ERR_PTR(-ENOMEM);
+
+	afbc_fb = kzalloc(sizeof(*afbc_fb), GFP_KERNEL);
+	if (!afbc_fb)
+		return ERR_PTR(-ENOMEM);
+
+	ret = drm_gem_fb_init_with_funcs(dev, &afbc_fb->base, file, mode_cmd,
+					 &rockchip_drm_fb_funcs);
+	if (ret) {
+		kfree(afbc_fb);
+		return ERR_PTR(ret);
+	}
+
+	if (drm_is_afbc(mode_cmd->modifier[0])) {
+		int ret, i;
+
+		ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
+		if (ret) {
+			struct drm_gem_object **obj = afbc_fb->base.obj;
+
+			for (i = 0; i < info->num_planes; ++i)
+				drm_gem_object_put_unlocked(obj[i]);
+
+			kfree(afbc_fb);
+			return ERR_PTR(ret);
+		}
+	}
+
+	return &afbc_fb->base;
+}
+
 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
-	.fb_create = drm_gem_fb_create_with_dirty,
+	.fb_create = rockchip_fb_create,
 	.output_poll_changed = drm_fb_helper_output_poll_changed,
 	.atomic_check = drm_atomic_helper_check,
 	.atomic_commit = drm_atomic_helper_commit,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index cecb2cc781f5..b87d22eb6ae1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -91,9 +91,22 @@ 
 #define VOP_WIN_TO_INDEX(vop_win) \
 	((vop_win) - (vop_win)->vop->win)
 
+#define VOP_AFBC_SET(vop, name, v) \
+	do { \
+		if ((vop)->data->afbc) \
+			vop_reg_set((vop), &(vop)->data->afbc->name, \
+				    0, ~0, v, #name); \
+	} while (0)
+
 #define to_vop(x) container_of(x, struct vop, crtc)
 #define to_vop_win(x) container_of(x, struct vop_win, base)
 
+#define AFBC_FMT_RGB565		0x0
+#define AFBC_FMT_U8U8U8U8	0x5
+#define AFBC_FMT_U8U8U8		0x4
+
+#define AFBC_TILE_16x16		BIT(4)
+
 /*
  * The coefficients of the following matrix are all fixed points.
  * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets.
@@ -274,6 +287,29 @@  static enum vop_data_format vop_convert_format(uint32_t format)
 	}
 }
 
+static int vop_convert_afbc_format(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		return AFBC_FMT_U8U8U8U8;
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+		return AFBC_FMT_U8U8U8;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		return AFBC_FMT_RGB565;
+	/* either of the below should not be reachable */
+	default:
+		DRM_WARN_ONCE("unsupported AFBC format[%08x]\n", format);
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
 static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
 				  uint32_t dst, bool is_horizontal,
 				  int vsu_mode, int *vskiplines)
@@ -598,6 +634,17 @@  static int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
 			vop_win_disable(vop, vop_win);
 		}
 	}
+
+	if (vop->data->afbc) {
+		struct rockchip_crtc_state *s;
+		/*
+		 * Disable AFBC and forget there was a vop window with AFBC
+		 */
+		VOP_AFBC_SET(vop, enable, 0);
+		s = to_rockchip_crtc_state(crtc->state);
+		s->enable_afbc = false;
+	}
+
 	spin_unlock(&vop->reg_lock);
 
 	vop_cfg_done(vop);
@@ -710,6 +757,26 @@  static void vop_plane_destroy(struct drm_plane *plane)
 	drm_plane_cleanup(plane);
 }
 
+static inline bool rockchip_afbc(u64 modifier)
+{
+	return modifier == ROCKCHIP_AFBC_MOD;
+}
+
+static bool rockchip_mod_supported(struct drm_plane *plane,
+				   u32 format, u64 modifier)
+{
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return true;
+
+	if (!rockchip_afbc(modifier)) {
+		DRM_DEBUG_KMS("Unsupported format modifer 0x%llx\n", modifier);
+
+		return false;
+	}
+
+	return vop_convert_afbc_format(format) >= 0;
+}
+
 static int vop_plane_atomic_check(struct drm_plane *plane,
 			   struct drm_plane_state *state)
 {
@@ -758,6 +825,30 @@  static int vop_plane_atomic_check(struct drm_plane *plane,
 		return -EINVAL;
 	}
 
+	if (rockchip_afbc(fb->modifier)) {
+		struct vop *vop = to_vop(crtc);
+
+		if (!vop->data->afbc) {
+			DRM_ERROR("vop does not support AFBC\n");
+			return -EINVAL;
+		}
+
+		ret = vop_convert_afbc_format(fb->format->format);
+		if (ret < 0)
+			return ret;
+
+		if (state->src.x1 || state->src.y1) {
+			DRM_ERROR("AFBC does not support offset display, xpos=%d, ypos=%d, offset=%d\n", state->src.x1, state->src.y1, fb->offsets[0]);
+			return -EINVAL;
+		}
+
+		if (state->rotation && state->rotation != DRM_MODE_ROTATE_0) {
+			DRM_ERROR("No rotation support in AFBC, rotation=%d\n",
+				  state->rotation);
+			return -EINVAL;
+		}
+	}
+
 	return 0;
 }
 
@@ -846,6 +937,16 @@  static void vop_plane_atomic_update(struct drm_plane *plane,
 
 	spin_lock(&vop->reg_lock);
 
+	if (rockchip_afbc(fb->modifier)) {
+		int afbc_format = vop_convert_afbc_format(fb->format->format);
+
+		VOP_AFBC_SET(vop, format, afbc_format | AFBC_TILE_16x16);
+		VOP_AFBC_SET(vop, hreg_block_split, 0);
+		VOP_AFBC_SET(vop, win_sel, VOP_WIN_TO_INDEX(vop_win));
+		VOP_AFBC_SET(vop, hdr_ptr, dma_addr);
+		VOP_AFBC_SET(vop, pic_size, act_info);
+	}
+
 	VOP_WIN_SET(vop, win, format, format);
 	VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
 	VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
@@ -1001,6 +1102,7 @@  static const struct drm_plane_funcs vop_plane_funcs = {
 	.reset = drm_atomic_helper_plane_reset,
 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.format_mod_supported = rockchip_mod_supported,
 };
 
 static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
@@ -1310,6 +1412,10 @@  static int vop_crtc_atomic_check(struct drm_crtc *crtc,
 				 struct drm_crtc_state *crtc_state)
 {
 	struct vop *vop = to_vop(crtc);
+	struct drm_plane *plane;
+	struct drm_plane_state *plane_state;
+	struct rockchip_crtc_state *s;
+	int afbc_planes = 0;
 
 	if (vop->lut_regs && crtc_state->color_mgmt_changed &&
 	    crtc_state->gamma_lut) {
@@ -1323,6 +1429,27 @@  static int vop_crtc_atomic_check(struct drm_crtc *crtc,
 		}
 	}
 
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
+		plane_state =
+			drm_atomic_get_plane_state(crtc_state->state, plane);
+		if (IS_ERR(plane_state)) {
+			DRM_DEBUG_KMS("Cannot get plane state for plane %s\n",
+				      plane->name);
+			return PTR_ERR(plane_state);
+		}
+
+		if (drm_is_afbc(plane_state->fb->modifier))
+			++afbc_planes;
+	}
+
+	if (afbc_planes > 1) {
+		DRM_DEBUG_KMS("Invalid number of AFBC planes; got %d, expected at most 1\n", afbc_planes);
+		return -EINVAL;
+	}
+
+	s = to_rockchip_crtc_state(crtc_state);
+	s->enable_afbc = afbc_planes > 0;
+
 	return 0;
 }
 
@@ -1333,6 +1460,7 @@  static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
 	struct drm_plane_state *old_plane_state, *new_plane_state;
 	struct vop *vop = to_vop(crtc);
 	struct drm_plane *plane;
+	struct rockchip_crtc_state *s;
 	int i;
 
 	if (WARN_ON(!vop->is_enabled))
@@ -1340,6 +1468,9 @@  static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
 
 	spin_lock(&vop->reg_lock);
 
+	/* Enable AFBC if there is some AFBC window, disable otherwise. */
+	s = to_rockchip_crtc_state(crtc->state);
+	VOP_AFBC_SET(vop, enable, s->enable_afbc);
 	vop_cfg_done(vop);
 
 	spin_unlock(&vop->reg_lock);
@@ -1634,7 +1765,8 @@  static int vop_create_crtc(struct vop *vop)
 					       0, &vop_plane_funcs,
 					       win_data->phy->data_formats,
 					       win_data->phy->nformats,
-					       NULL, win_data->type, NULL);
+					       win_data->phy->format_modifiers,
+					       win_data->type, NULL);
 		if (ret) {
 			DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n",
 				      ret);
@@ -1678,7 +1810,8 @@  static int vop_create_crtc(struct vop *vop)
 					       &vop_plane_funcs,
 					       win_data->phy->data_formats,
 					       win_data->phy->nformats,
-					       NULL, win_data->type, NULL);
+					       win_data->phy->format_modifiers,
+					       win_data->type, NULL);
 		if (ret) {
 			DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",
 				      ret);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index cc672620d6e0..d03bdb531ef2 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -17,6 +17,11 @@ 
 
 #define NUM_YUV2YUV_COEFFICIENTS 12
 
+#define ROCKCHIP_AFBC_MOD \
+	DRM_FORMAT_MOD_ARM_AFBC( \
+		AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE \
+	)
+
 enum vop_data_format {
 	VOP_FMT_ARGB8888 = 0,
 	VOP_FMT_RGB888,
@@ -34,6 +39,16 @@  struct vop_reg {
 	bool relaxed;
 };
 
+struct vop_afbc {
+	struct vop_reg enable;
+	struct vop_reg win_sel;
+	struct vop_reg format;
+	struct vop_reg hreg_block_split;
+	struct vop_reg pic_size;
+	struct vop_reg hdr_ptr;
+	struct vop_reg rstn;
+};
+
 struct vop_modeset {
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
@@ -134,6 +149,7 @@  struct vop_win_phy {
 	const struct vop_scl_regs *scl;
 	const uint32_t *data_formats;
 	uint32_t nformats;
+	const uint64_t *format_modifiers;
 
 	struct vop_reg enable;
 	struct vop_reg gate;
@@ -173,6 +189,7 @@  struct vop_data {
 	const struct vop_misc *misc;
 	const struct vop_modeset *modeset;
 	const struct vop_output *output;
+	const struct vop_afbc *afbc;
 	const struct vop_win_yuv2yuv_data *win_yuv2yuv;
 	const struct vop_win_data *win;
 	unsigned int win_size;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 7a9d979c8d5d..2413deded22c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -50,6 +50,17 @@  static const uint32_t formats_win_full[] = {
 	DRM_FORMAT_NV24,
 };
 
+static const uint64_t format_modifiers_win_full[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID,
+};
+
+static const uint64_t format_modifiers_win_full_afbc[] = {
+	ROCKCHIP_AFBC_MOD,
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID,
+};
+
 static const uint32_t formats_win_lite[] = {
 	DRM_FORMAT_XRGB8888,
 	DRM_FORMAT_ARGB8888,
@@ -61,6 +72,11 @@  static const uint32_t formats_win_lite[] = {
 	DRM_FORMAT_BGR565,
 };
 
+static const uint64_t format_modifiers_win_lite[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID,
+};
+
 static const struct vop_scl_regs rk3036_win_scl = {
 	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
 	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
@@ -72,6 +88,7 @@  static const struct vop_win_phy rk3036_win0_data = {
 	.scl = &rk3036_win_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
+	.format_modifiers = format_modifiers_win_full,
 	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
 	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
 	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
@@ -87,6 +104,7 @@  static const struct vop_win_phy rk3036_win0_data = {
 static const struct vop_win_phy rk3036_win1_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
+	.format_modifiers = format_modifiers_win_lite,
 	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
 	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
 	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
@@ -153,6 +171,7 @@  static const struct vop_data rk3036_vop = {
 static const struct vop_win_phy rk3126_win1_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
+	.format_modifiers = format_modifiers_win_lite,
 	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
 	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
 	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
@@ -234,6 +253,7 @@  static const struct vop_win_phy px30_win0_data = {
 	.scl = &px30_win_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
+	.format_modifiers = format_modifiers_win_full,
 	.enable = VOP_REG(PX30_WIN0_CTRL0, 0x1, 0),
 	.format = VOP_REG(PX30_WIN0_CTRL0, 0x7, 1),
 	.rb_swap = VOP_REG(PX30_WIN0_CTRL0, 0x1, 12),
@@ -249,6 +269,7 @@  static const struct vop_win_phy px30_win0_data = {
 static const struct vop_win_phy px30_win1_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
+	.format_modifiers = format_modifiers_win_lite,
 	.enable = VOP_REG(PX30_WIN1_CTRL0, 0x1, 0),
 	.format = VOP_REG(PX30_WIN1_CTRL0, 0x7, 4),
 	.rb_swap = VOP_REG(PX30_WIN1_CTRL0, 0x1, 12),
@@ -261,6 +282,7 @@  static const struct vop_win_phy px30_win1_data = {
 static const struct vop_win_phy px30_win2_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
+	.format_modifiers = format_modifiers_win_lite,
 	.gate = VOP_REG(PX30_WIN2_CTRL0, 0x1, 4),
 	.enable = VOP_REG(PX30_WIN2_CTRL0, 0x1, 0),
 	.format = VOP_REG(PX30_WIN2_CTRL0, 0x3, 5),
@@ -316,6 +338,7 @@  static const struct vop_win_phy rk3066_win0_data = {
 	.scl = &rk3066_win_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
+	.format_modifiers = format_modifiers_win_full,
 	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 0),
 	.format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 4),
 	.rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 19),
@@ -332,6 +355,7 @@  static const struct vop_win_phy rk3066_win1_data = {
 	.scl = &rk3066_win_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
+	.format_modifiers = format_modifiers_win_full,
 	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 1),
 	.format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 7),
 	.rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 23),
@@ -347,6 +371,7 @@  static const struct vop_win_phy rk3066_win1_data = {
 static const struct vop_win_phy rk3066_win2_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
+	.format_modifiers = format_modifiers_win_lite,
 	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 2),
 	.format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 10),
 	.rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 27),
@@ -426,6 +451,7 @@  static const struct vop_win_phy rk3188_win0_data = {
 	.scl = &rk3188_win_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
+	.format_modifiers = format_modifiers_win_full,
 	.enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 0),
 	.format = VOP_REG(RK3188_SYS_CTRL, 0x7, 3),
 	.rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 15),
@@ -440,6 +466,7 @@  static const struct vop_win_phy rk3188_win0_data = {
 static const struct vop_win_phy rk3188_win1_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
+	.format_modifiers = format_modifiers_win_lite,
 	.enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 1),
 	.format = VOP_REG(RK3188_SYS_CTRL, 0x7, 6),
 	.rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 19),
@@ -545,6 +572,7 @@  static const struct vop_win_phy rk3288_win01_data = {
 	.scl = &rk3288_win_full_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
+	.format_modifiers = format_modifiers_win_full,
 	.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
 	.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
 	.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
@@ -563,6 +591,7 @@  static const struct vop_win_phy rk3288_win01_data = {
 static const struct vop_win_phy rk3288_win23_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
+	.format_modifiers = format_modifiers_win_lite,
 	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4),
 	.gate = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0),
 	.format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1),
@@ -677,6 +706,7 @@  static const struct vop_win_phy rk3368_win01_data = {
 	.scl = &rk3288_win_full_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
+	.format_modifiers = format_modifiers_win_full,
 	.enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0),
 	.format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1),
 	.rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12),
@@ -697,6 +727,7 @@  static const struct vop_win_phy rk3368_win01_data = {
 static const struct vop_win_phy rk3368_win23_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
+	.format_modifiers = format_modifiers_win_lite,
 	.gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0),
 	.enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4),
 	.format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5),
@@ -817,6 +848,53 @@  static const struct vop_win_yuv2yuv_data rk3399_vop_big_win_yuv2yuv_data[] = {
 	  .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 9) },
 	{ .base = 0xC0, .phy = &rk3399_yuv2yuv_win23_data },
 	{ .base = 0x120, .phy = &rk3399_yuv2yuv_win23_data },
+
+};
+
+static const struct vop_win_phy rk3399_win01_data = {
+	.scl = &rk3288_win_full_scl,
+	.data_formats = formats_win_full,
+	.nformats = ARRAY_SIZE(formats_win_full),
+	.format_modifiers = format_modifiers_win_full_afbc,
+	.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
+	.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
+	.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
+	.y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22),
+	.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN0_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0),
+	.uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16),
+	.src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+	.dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+/*
+ * rk3399 vop big windows register layout is same as rk3288, but we
+ * have a separate rk3399 win data array here so that we can advertise
+ * AFBC on the primary plane.
+ */
+static const struct vop_win_data rk3399_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3399_win01_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY },
+	{ .base = 0x40, .phy = &rk3288_win01_data,
+	  .type = DRM_PLANE_TYPE_OVERLAY },
+	{ .base = 0x00, .phy = &rk3288_win23_data,
+	  .type = DRM_PLANE_TYPE_OVERLAY },
+	{ .base = 0x50, .phy = &rk3288_win23_data,
+	  .type = DRM_PLANE_TYPE_CURSOR },
+};
+
+static const struct vop_afbc rk3399_vop_afbc = {
+	.rstn = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 3),
+	.enable = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 0),
+	.win_sel = VOP_REG(RK3399_AFBCD0_CTRL, 0x3, 1),
+	.format = VOP_REG(RK3399_AFBCD0_CTRL, 0x1f, 16),
+	.hreg_block_split = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 21),
+	.hdr_ptr = VOP_REG(RK3399_AFBCD0_HDR_PTR, 0xffffffff, 0),
+	.pic_size = VOP_REG(RK3399_AFBCD0_PIC_SIZE, 0xffffffff, 0),
 };
 
 static const struct vop_data rk3399_vop_big = {
@@ -826,9 +904,10 @@  static const struct vop_data rk3399_vop_big = {
 	.common = &rk3288_common,
 	.modeset = &rk3288_modeset,
 	.output = &rk3399_output,
+	.afbc = &rk3399_vop_afbc,
 	.misc = &rk3368_misc,
-	.win = rk3368_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3368_vop_win_data),
+	.win = rk3399_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3399_vop_win_data),
 	.win_yuv2yuv = rk3399_vop_big_win_yuv2yuv_data,
 };