[PATCH] drm/rockchip: vop: support afbc format for rk3399 vop

Mark Yao
2016-09-10T03:07:32Z

Rk3399 vop big can support one afbc decoder, the afbc decoder
can select which overlay window use it.

on vop hardware, we call afbc decoder as afbdc.

afbdc window has some limit:
1, not support offset on source buffer.
2, if feed non-afbc buffer to afbc decoder, afbc decoder hardware
would die, we need take care of using it.

AFBC is a compressed format, means lower bandwidth consume,
it's useful to improve performance.

Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 6 ++
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 89 +++++++++++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 13 +++++
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 7 +++
4 files changed, 115 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index ea39329..1e07fd6 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -44,6 +44,12 @@ struct rockchip_crtc_funcs {

struct rockchip_crtc_state {
struct drm_crtc_state base;
+ int afbdc_win_format;
+ int afbdc_win_width;
+ int afbdc_win_height;
+ int afbdc_win_ptr;
+ int afbdc_win_id;
+ int afbdc_en;
int output_type;
int output_mode;
};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 834456f..6918223 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -99,6 +99,7 @@ struct vop_win {
struct drm_plane base;
const struct vop_win_data *data;
struct vop *vop;
+ int id;

/* protected by dev->event_lock */
bool enable;
@@ -514,6 +515,7 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
VOP_WIN_SET(vop, win, enable, 0);
spin_unlock(&vop->reg_lock);
}
+ VOP_CTRL_SET(vop, afbdc_en, 0);

drm_crtc_vblank_off(crtc);

@@ -1007,9 +1009,81 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
VOP_CTRL_SET(vop, standby, 0);
}

+static int vop_afbdc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+ struct drm_atomic_state *state = crtc_state->state;
+ struct drm_plane *plane;
+ struct drm_plane_state *pstate;
+ struct vop_plane_state *plane_state;
+ struct vop_win *win;
+ int afbdc_format;
+ int i;
+
+ s->afbdc_en = 0;
+
+ for_each_plane_in_state(state, plane, pstate, i) {
+ struct drm_framebuffer *fb = pstate->fb;
+ struct drm_rect *src;
+
+ win = to_vop_win(plane);
+ plane_state = to_vop_plane_state(pstate);
+
+ if (pstate->crtc != crtc || !fb)
+ continue;
+
+ if (fb->modifier[0] != DRM_FORMAT_MOD_ARM_AFBC)
+ continue;
+
+ switch (plane_state->format) {
+ case VOP_FMT_ARGB8888:
+ afbdc_format = AFBDC_FMT_U8U8U8U8;
+ break;
+ case VOP_FMT_RGB888:
+ afbdc_format = AFBDC_FMT_U8U8U8;
+ break;
+ case VOP_FMT_RGB565:
+ afbdc_format = AFBDC_FMT_RGB565;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (s->afbdc_en) {
+ DRM_ERROR("vop only support one afbc layer\n");
+ return -EINVAL;
+ }
+
+ src = &pstate->src;
+ if (src->x1 || src->y1 || fb->offsets[0]) {
+ DRM_ERROR("win[%d] afbdc not support offset display\n",
+ win->id);
+ DRM_ERROR("xpos=%d, ypos=%d, offset=%d\n",
+ src->x1, src->y1, fb->offsets[0]);
+ return -EINVAL;
+ }
+ s->afbdc_win_format = afbdc_format;
+ s->afbdc_win_width = pstate->fb->width - 1;
+ s->afbdc_win_height = (drm_rect_height(src) >> 16) - 1;
+ s->afbdc_win_id = win->id;
+ s->afbdc_win_ptr = plane_state->yrgb_mst;
+ s->afbdc_en = 1;
+ }
+
+ return 0;
+}
+
+static int vop_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state)
+{
+ return vop_afbdc_atomic_check(crtc, crtc_state);
+}
+
static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
struct vop *vop = to_vop(crtc);

if (WARN_ON(!vop->is_enabled))
@@ -1017,6 +1091,19 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,

spin_lock(&vop->reg_lock);

+ if (s->afbdc_en) {
+ uint32_t pic_size;
+
+ VOP_CTRL_SET(vop, afbdc_format, s->afbdc_win_format | 1 << 4);
+ VOP_CTRL_SET(vop, afbdc_hreg_block_split, 0);
+ VOP_CTRL_SET(vop, afbdc_sel, s->afbdc_win_id);
+ VOP_CTRL_SET(vop, afbdc_hdr_ptr, s->afbdc_win_ptr);
+ pic_size = (s->afbdc_win_width & 0xffff);
+ pic_size |= s->afbdc_win_height << 16;
+ VOP_CTRL_SET(vop, afbdc_pic_size, pic_size);
+ }
+
+ VOP_CTRL_SET(vop, afbdc_en, s->afbdc_en);
vop_cfg_done(vop);

spin_unlock(&vop->reg_lock);
@@ -1042,6 +1129,7 @@ static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
.enable = vop_crtc_enable,
.disable = vop_crtc_disable,
.mode_fixup = vop_crtc_mode_fixup,
+ .atomic_check = vop_crtc_atomic_check,
.atomic_flush = vop_crtc_atomic_flush,
.atomic_begin = vop_crtc_atomic_begin,
};
@@ -1413,6 +1501,7 @@ static void vop_win_init(struct vop *vop)
struct vop_win *vop_win = &vop->win[i];
const struct vop_win_data *win_data = &vop_data->win[i];

+ vop_win->id = i;
vop_win->data = win_data;
vop_win->vop = vop;
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..b9b120e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -15,6 +15,10 @@
#ifndef _ROCKCHIP_DRM_VOP_H
#define _ROCKCHIP_DRM_VOP_H

+#define AFBDC_FMT_RGB565 0x0
+#define AFBDC_FMT_U8U8U8U8 0x5
+#define AFBDC_FMT_U8U8U8 0x4
+
enum vop_data_format {
VOP_FMT_ARGB8888 = 0,
VOP_FMT_RGB888,
@@ -61,6 +65,15 @@ struct vop_ctrl {
struct vop_reg hpost_st_end;
struct vop_reg vpost_st_end;

+ /* AFBDC */
+ struct vop_reg afbdc_en;
+ struct vop_reg afbdc_sel;
+ struct vop_reg afbdc_format;
+ struct vop_reg afbdc_hreg_block_split;
+ struct vop_reg afbdc_pic_size;
+ struct vop_reg afbdc_hdr_ptr;
+ struct vop_reg afbdc_rstn;
+
struct vop_reg cfg_done;
};

diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index f1a1688..a09437c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -300,6 +300,13 @@ static const struct vop_ctrl rk3399_ctrl_data = {
.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+ .afbdc_rstn = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 3),
+ .afbdc_en = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 0),
+ .afbdc_sel = VOP_REG(RK3399_AFBCD0_CTRL, 0x3, 1),
+ .afbdc_format = VOP_REG(RK3399_AFBCD0_CTRL, 0x1f, 16),
+ .afbdc_hreg_block_split = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 21),
+ .afbdc_hdr_ptr = VOP_REG(RK3399_AFBCD0_HDR_PTR, 0xffffffff, 0),
+ .afbdc_pic_size = VOP_REG(RK3399_AFBCD0_PIC_SIZE, 0xffffffff, 0),
.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),