drm/rockchip: vop: remove unused device
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_vop.c
index 58fdb6d98629804312d4990b2376da622f64dad0..63a66b96fff6d3f98f789a472da9fd00988ed782 100644 (file)
 #include <linux/reset.h>
 #include <linux/delay.h>
 #include <linux/sort.h>
+#include <uapi/drm/rockchip_drm.h>
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
 #include "rockchip_drm_vop.h"
 
-#define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
-               vop_mask_write(x, off, mask, shift, v, write_mask, true)
+#define VOP_REG_SUPPORT(vop, reg) \
+               (!reg.major || (reg.major == VOP_MAJOR(vop->data->version) && \
+               reg.begin_minor <= VOP_MINOR(vop->data->version) && \
+               reg.end_minor >= VOP_MINOR(vop->data->version) && \
+               reg.mask))
 
-#define __REG_SET_NORMAL(x, off, mask, shift, v, write_mask) \
-               vop_mask_write(x, off, mask, shift, v, write_mask, false)
+#define VOP_WIN_SUPPORT(vop, win, name) \
+               VOP_REG_SUPPORT(vop, win->phy->name)
 
-#define REG_SET(x, off, reg, v, mode) \
-               __REG_SET_##mode(x, off + reg.offset, \
-                                reg.mask, reg.shift, v, reg.write_mask)
-#define REG_SET_MASK(x, off, reg, mask, v, mode) \
-               __REG_SET_##mode(x, off + reg.offset, \
-                                mask, reg.shift, v, reg.write_mask)
+#define VOP_CTRL_SUPPORT(vop, win, name) \
+               VOP_REG_SUPPORT(vop, vop->data->ctrl->name)
+
+#define VOP_INTR_SUPPORT(vop, win, name) \
+               VOP_REG_SUPPORT(vop, vop->data->intr->name)
+
+#define __REG_SET(x, off, mask, shift, v, write_mask, relaxed) \
+               vop_mask_write(x, off, mask, shift, v, write_mask, relaxed)
+
+#define _REG_SET(vop, name, off, reg, mask, v, relaxed) \
+       do { \
+               if (VOP_REG_SUPPORT(vop, reg)) \
+                       __REG_SET(vop, off + reg.offset, mask, reg.shift, \
+                                 v, reg.write_mask, relaxed); \
+               else \
+                       dev_dbg(vop->dev, "Warning: not support "#name"\n"); \
+       } while(0)
+
+#define REG_SET(x, name, off, reg, v, relaxed) \
+               _REG_SET(x, name, off, reg, reg.mask, v, relaxed)
+#define REG_SET_MASK(x, name, off, reg, mask, v, relaxed) \
+               _REG_SET(x, name, off, reg, reg.mask & mask, v, relaxed)
 
 #define VOP_WIN_SET(x, win, name, v) \
-               REG_SET(x, win->offset, VOP_WIN_NAME(win, name), v, RELAXED)
+               REG_SET(x, name, win->offset, VOP_WIN_NAME(win, name), v, true)
 #define VOP_SCL_SET(x, win, name, v) \
-               REG_SET(x, win->offset, win->phy->scl->name, v, RELAXED)
+               REG_SET(x, name, win->offset, win->phy->scl->name, v, true)
 #define VOP_SCL_SET_EXT(x, win, name, v) \
-               REG_SET(x, win->offset, win->phy->scl->ext->name, v, RELAXED)
+               REG_SET(x, name, win->offset, win->phy->scl->ext->name, v, true)
 
 #define VOP_CTRL_SET(x, name, v) \
-               REG_SET(x, 0, (x)->data->ctrl->name, v, NORMAL)
+               REG_SET(x, name, 0, (x)->data->ctrl->name, v, false)
 
 #define VOP_INTR_GET(vop, name) \
                vop_read_reg(vop, 0, &vop->data->ctrl->name)
 
 #define VOP_INTR_SET(vop, name, mask, v) \
-               REG_SET_MASK(vop, 0, vop->data->intr->name, mask, v, NORMAL)
+               REG_SET_MASK(vop, name, 0, vop->data->intr->name, \
+                            mask, v, false)
+
 #define VOP_INTR_SET_TYPE(vop, name, type, v) \
        do { \
                int i, reg = 0, mask = 0; \
 #define VOP_INTR_GET_TYPE(vop, name, type) \
                vop_get_intr_type(vop, &vop->data->intr->name, type)
 
+#define VOP_CTRL_GET(x, name) \
+               vop_read_reg(x, 0, vop->data->ctrl->name)
+
 #define VOP_WIN_GET(x, win, name) \
                vop_read_reg(x, win->offset, &VOP_WIN_NAME(win, name))
 
@@ -120,6 +145,7 @@ struct vop_win {
        uint32_t nformats;
        struct vop *vop;
 
+       struct drm_property *rotation_prop;
        struct vop_plane_state state;
 };
 
@@ -128,7 +154,9 @@ struct vop {
        struct device *dev;
        struct drm_device *drm_dev;
        struct drm_property *plane_zpos_prop;
-       bool is_enabled;
+       struct drm_property *plane_feature_prop;
+       bool is_iommu_enabled;
+       bool is_iommu_needed;
 
        /* mutex vsync_ work */
        struct mutex vsync_mutex;
@@ -233,6 +261,43 @@ static inline void vop_cfg_done(struct vop *vop)
        VOP_CTRL_SET(vop, cfg_done, 1);
 }
 
+static bool vop_is_allwin_disabled(struct vop *vop)
+{
+       int i;
+
+       for (i = 0; i < vop->num_wins; i++) {
+               struct vop_win *win = &vop->win[i];
+
+               if (VOP_WIN_GET(vop, win, enable) != 0)
+                       return false;
+       }
+
+       return true;
+}
+
+static bool vop_win_pending_is_complete(struct vop *vop)
+{
+       dma_addr_t yrgb_mst;
+       int i;
+
+       for (i = 0; i < vop->num_wins; i++) {
+               struct vop_win *win = &vop->win[i];
+               struct drm_plane *plane = &win->base;
+               struct vop_plane_state *state =
+                               to_vop_plane_state(plane->state);
+               if (!state->enable) {
+                       if (VOP_WIN_GET(vop, win, enable) != 0)
+                               return false;
+                       continue;
+               }
+               yrgb_mst = VOP_WIN_GET_YRGBADDR(vop, win);
+               if (yrgb_mst != state->yrgb_mst)
+                       return false;
+       }
+
+       return true;
+}
+
 static bool has_rb_swapped(uint32_t format)
 {
        switch (format) {
@@ -436,9 +501,6 @@ static void vop_dsp_hold_valid_irq_enable(struct vop *vop)
 {
        unsigned long flags;
 
-       if (WARN_ON(!vop->is_enabled))
-               return;
-
        spin_lock_irqsave(&vop->irq_lock, flags);
 
        VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1);
@@ -450,9 +512,6 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 {
        unsigned long flags;
 
-       if (WARN_ON(!vop->is_enabled))
-               return;
-
        spin_lock_irqsave(&vop->irq_lock, flags);
 
        VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 0);
@@ -463,24 +522,21 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 static void vop_enable(struct drm_crtc *crtc)
 {
        struct vop *vop = to_vop(crtc);
-       int ret;
-
-       if (vop->is_enabled)
-               return;
+       int ret, i;
 
-       ret = clk_enable(vop->hclk);
+       ret = clk_prepare_enable(vop->hclk);
        if (ret < 0) {
                dev_err(vop->dev, "failed to enable hclk - %d\n", ret);
                return;
        }
 
-       ret = clk_enable(vop->dclk);
+       ret = clk_prepare_enable(vop->dclk);
        if (ret < 0) {
                dev_err(vop->dev, "failed to enable dclk - %d\n", ret);
                goto err_disable_hclk;
        }
 
-       ret = clk_enable(vop->aclk);
+       ret = clk_prepare_enable(vop->aclk);
        if (ret < 0) {
                dev_err(vop->dev, "failed to enable aclk - %d\n", ret);
                goto err_disable_dclk;
@@ -492,23 +548,15 @@ static void vop_enable(struct drm_crtc *crtc)
                return;
        }
 
-       /*
-        * Slave iommu shares power, irq and clock with vop.  It was associated
-        * automatically with this master device via common driver code.
-        * Now that we have enabled the clock we attach it to the shared drm
-        * mapping.
-        */
-       ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev);
-       if (ret) {
-               dev_err(vop->dev, "failed to attach dma mapping, %d\n", ret);
-               goto err_disable_aclk;
-       }
+       memcpy(vop->regsbak, vop->regs, vop->len);
 
-       memcpy(vop->regs, vop->regsbak, vop->len);
-       /*
-        * At here, vop clock & iommu is enable, R/W vop regs would be safe.
-        */
-       vop->is_enabled = true;
+       VOP_CTRL_SET(vop, global_regdone_en, 1);
+
+       for (i = 0; i < vop->num_wins; i++) {
+               struct vop_win *win = &vop->win[i];
+
+               VOP_WIN_SET(vop, win, gate, 1);
+       }
 
        spin_lock(&vop->reg_lock);
 
@@ -522,12 +570,10 @@ static void vop_enable(struct drm_crtc *crtc)
 
        return;
 
-err_disable_aclk:
-       clk_disable(vop->aclk);
 err_disable_dclk:
-       clk_disable(vop->dclk);
+       clk_disable_unprepare(vop->dclk);
 err_disable_hclk:
-       clk_disable(vop->hclk);
+       clk_disable_unprepare(vop->hclk);
 }
 
 static void vop_crtc_disable(struct drm_crtc *crtc)
@@ -535,9 +581,6 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
        struct vop *vop = to_vop(crtc);
        int i;
 
-       if (!vop->is_enabled)
-               return;
-
        /*
         * We need to make sure that all windows are disabled before we
         * disable that crtc. Otherwise we might try to scan from a destroyed
@@ -550,6 +593,7 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
                VOP_WIN_SET(vop, win, enable, 0);
                spin_unlock(&vop->reg_lock);
        }
+       vop_cfg_done(vop);
 
        drm_crtc_vblank_off(crtc);
 
@@ -575,17 +619,18 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 
        disable_irq(vop->irq);
 
-       vop->is_enabled = false;
-
-       /*
-        * vop standby complete, so iommu detach is safe.
-        */
-       rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
+       if (vop->is_iommu_enabled) {
+               /*
+                * vop standby complete, so iommu detach is safe.
+                */
+               rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
+               vop->is_iommu_enabled = false;
+       }
 
        pm_runtime_put(vop->dev);
-       clk_disable(vop->dclk);
-       clk_disable(vop->aclk);
-       clk_disable(vop->hclk);
+       clk_disable_unprepare(vop->dclk);
+       clk_disable_unprepare(vop->aclk);
+       clk_disable_unprepare(vop->hclk);
 }
 
 static void vop_plane_destroy(struct drm_plane *plane)
@@ -717,10 +762,9 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        uint32_t act_info, dsp_info, dsp_st;
        struct drm_rect *src = &vop_plane_state->src;
        struct drm_rect *dest = &vop_plane_state->dest;
-       struct drm_gem_object *obj, *uv_obj;
-       struct rockchip_gem_object *rk_obj, *rk_uv_obj;
        unsigned long offset;
        dma_addr_t dma_addr;
+       int ymirror, xmirror;
        uint32_t val;
        bool rb_swap;
 
@@ -730,17 +774,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        if (!crtc)
                return;
 
-       if (WARN_ON(!vop->is_enabled))
-               return;
-
        if (!vop_plane_state->enable) {
                vop_plane_atomic_disable(plane, old_state);
                return;
        }
 
-       obj = rockchip_fb_get_gem_obj(fb, 0);
-       rk_obj = to_rockchip_obj(obj);
-
        actual_w = drm_rect_width(src) >> 16;
        actual_h = drm_rect_height(src) >> 16;
        act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
@@ -753,11 +791,21 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
 
        offset = (src->x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0);
-       offset += (src->y1 >> 16) * fb->pitches[0];
-       vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
+       if (state->rotation & BIT(DRM_REFLECT_Y))
+               offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
+       else
+               offset += (src->y1 >> 16) * fb->pitches[0];
+
+       dma_addr = rockchip_fb_get_dma_addr(fb, 0);
+       vop_plane_state->yrgb_mst = dma_addr + offset + fb->offsets[0];
+
+       ymirror = !!(state->rotation & BIT(DRM_REFLECT_Y));
+       xmirror = !!(state->rotation & BIT(DRM_REFLECT_X));
 
        spin_lock(&vop->reg_lock);
 
+       VOP_WIN_SET(vop, win, xmirror, xmirror);
+       VOP_WIN_SET(vop, win, ymirror, ymirror);
        VOP_WIN_SET(vop, win, format, vop_plane_state->format);
        VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2);
        VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst);
@@ -766,13 +814,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
                int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
                int bpp = drm_format_plane_cpp(fb->pixel_format, 1);
 
-               uv_obj = rockchip_fb_get_gem_obj(fb, 1);
-               rk_uv_obj = to_rockchip_obj(uv_obj);
-
                offset = (src->x1 >> 16) * bpp / hsub;
                offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
 
-               dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
+               dma_addr = rockchip_fb_get_dma_addr(fb, 1);
+               dma_addr += offset + fb->offsets[1];
                VOP_WIN_SET(vop, win, uv_vir, fb->pitches[1] >> 2);
                VOP_WIN_SET(vop, win, uv_mst, dma_addr);
        }
@@ -798,12 +844,16 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
                        SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
                        SRC_FACTOR_M0(ALPHA_ONE);
                VOP_WIN_SET(vop, win, src_alpha_ctl, val);
+               VOP_WIN_SET(vop, win, alpha_mode, 1);
+               VOP_WIN_SET(vop, win, alpha_en, 1);
        } else {
                VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0));
+               VOP_WIN_SET(vop, win, alpha_en, 0);
        }
 
        VOP_WIN_SET(vop, win, enable, 1);
        spin_unlock(&vop->reg_lock);
+       vop->is_iommu_needed = true;
 }
 
 static const struct drm_plane_helper_funcs plane_helper_funcs = {
@@ -877,6 +927,11 @@ static int vop_atomic_plane_set_property(struct drm_plane *plane,
                return 0;
        }
 
+       if (property == win->rotation_prop) {
+               state->rotation = val;
+               return 0;
+       }
+
        DRM_ERROR("failed to set vop plane property\n");
        return -EINVAL;
 }
@@ -894,6 +949,11 @@ static int vop_atomic_plane_get_property(struct drm_plane *plane,
                return 0;
        }
 
+       if (property == win->rotation_prop) {
+               *val = state->rotation;
+               return 0;
+       }
+
        DRM_ERROR("failed to get vop plane property\n");
        return -EINVAL;
 }
@@ -914,9 +974,6 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
        struct vop *vop = to_vop(crtc);
        unsigned long flags;
 
-       if (WARN_ON(!vop->is_enabled))
-               return -EPERM;
-
        spin_lock_irqsave(&vop->irq_lock, flags);
 
        VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1);
@@ -931,9 +988,6 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
        struct vop *vop = to_vop(crtc);
        unsigned long flags;
 
-       if (WARN_ON(!vop->is_enabled))
-               return;
-
        spin_lock_irqsave(&vop->irq_lock, flags);
 
        VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
@@ -1095,39 +1149,47 @@ static int vop_zpos_cmp(const void *a, const void *b)
        struct vop_zpos *pa = (struct vop_zpos *)a;
        struct vop_zpos *pb = (struct vop_zpos *)b;
 
-       return pb->zpos - pa->zpos;
+       return pa->zpos - pb->zpos;
 }
 
 static int vop_crtc_atomic_check(struct drm_crtc *crtc,
-                                struct drm_crtc_state *state)
+                                struct drm_crtc_state *crtc_state)
 {
-       struct drm_device *dev = crtc->dev;
-       struct rockchip_crtc_state *s = to_rockchip_crtc_state(state);
+       struct drm_atomic_state *state = crtc_state->state;
+       struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
        struct vop *vop = to_vop(crtc);
        const struct vop_data *vop_data = vop->data;
        struct drm_plane *plane;
+       struct drm_plane_state *pstate;
+       struct vop_plane_state *plane_state;
        struct vop_zpos *pzpos;
        int dsp_layer_sel = 0;
-       int i, cnt = 0, ret = 0;
+       int i, j, cnt = 0, ret = 0;
 
-       pzpos = kmalloc_array(vop->num_wins, sizeof(*pzpos), GFP_KERNEL);
+       pzpos = kmalloc_array(vop_data->win_size, sizeof(*pzpos), GFP_KERNEL);
        if (!pzpos)
                return -ENOMEM;
 
-       drm_atomic_crtc_state_for_each_plane(plane, state) {
-               struct drm_plane_state *pstate;
-               struct vop_plane_state *plane_state;
-               struct vop_win *win = to_vop_win(plane);
+       for (i = 0; i < vop_data->win_size; i++) {
+               const struct vop_win_data *win_data = &vop_data->win[i];
+               struct vop_win *win;
 
-               if (plane->parent)
+               if (!win_data->phy)
                        continue;
-               if (cnt >= vop->num_wins) {
-                       dev_err(dev->dev, "too many planes!\n");
+
+               for (j = 0; j < vop->num_wins; j++) {
+                       win = &vop->win[j];
+
+                       if (win->win_id == i && !win->area_id)
+                               break;
+               }
+               if (WARN_ON(j >= vop->num_wins)) {
                        ret = -EINVAL;
                        goto err_free_pzpos;
                }
-               pstate = state->state->plane_states[drm_plane_index(plane)];
 
+               plane = &win->base;
+               pstate = state->plane_states[drm_plane_index(plane)];
                /*
                 * plane might not have changed, in which case take
                 * current state:
@@ -1136,27 +1198,22 @@ static int vop_crtc_atomic_check(struct drm_crtc *crtc,
                        pstate = plane->state;
                plane_state = to_vop_plane_state(pstate);
                pzpos[cnt].zpos = plane_state->zpos;
-               pzpos[cnt].win_id = win->win_id;
-
-               cnt++;
+               pzpos[cnt++].win_id = win->win_id;
        }
 
        sort(pzpos, cnt, sizeof(pzpos[0]), vop_zpos_cmp, NULL);
 
-       WARN_ON(vop_data->win_size < cnt);
-       for (i = 0; i < (vop_data->win_size - cnt); i++) {
-               dsp_layer_sel <<= 2;
-               /*
-                * after sort, pzpos[0] is the top zpos layer.
-                */
-               dsp_layer_sel |= pzpos[0].win_id;
-       }
+       for (i = 0, cnt = 0; i < vop_data->win_size; i++) {
+               const struct vop_win_data *win_data = &vop_data->win[i];
+               int shift = i * 2;
 
-       for (i = 0; i < cnt; i++) {
-               struct vop_zpos *zpos = &pzpos[i];
+               if (win_data->phy) {
+                       struct vop_zpos *zpos = &pzpos[cnt++];
 
-               dsp_layer_sel <<= 2;
-               dsp_layer_sel |= zpos->win_id;
+                       dsp_layer_sel |= zpos->win_id << shift;
+               } else {
+                       dsp_layer_sel |= i << shift;
+               }
        }
 
        s->dsp_layer_sel = dsp_layer_sel;
@@ -1166,16 +1223,13 @@ err_free_pzpos:
        return ret;
 }
 
-static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
-                                 struct drm_crtc_state *old_crtc_state)
+static void vop_cfg_update(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))
-               return;
-
        spin_lock(&vop->reg_lock);
 
        VOP_CTRL_SET(vop, dsp_layer_sel, s->dsp_layer_sel);
@@ -1184,6 +1238,27 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
        spin_unlock(&vop->reg_lock);
 }
 
+static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
+                                 struct drm_crtc_state *old_crtc_state)
+{
+       struct vop *vop = to_vop(crtc);
+
+       if (!vop->is_iommu_enabled && vop->is_iommu_needed) {
+               int ret;
+               if (!vop_is_allwin_disabled(vop)) {
+                       vop_cfg_update(crtc, old_crtc_state);
+                       while(!vop_win_pending_is_complete(vop));
+               }
+               ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev);
+               if (ret) {
+                       dev_err(vop->dev, "failed to attach dma mapping, %d\n", ret);
+               }
+               vop->is_iommu_enabled = true;
+       }
+
+       vop_cfg_update(crtc, old_crtc_state);
+}
+
 static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_crtc_state)
 {
@@ -1211,6 +1286,17 @@ static void vop_crtc_destroy(struct drm_crtc *crtc)
        drm_crtc_cleanup(crtc);
 }
 
+static void vop_crtc_reset(struct drm_crtc *crtc)
+{
+       if (crtc->state)
+               __drm_atomic_helper_crtc_destroy_state(crtc, crtc->state);
+       kfree(crtc->state);
+
+       crtc->state = kzalloc(sizeof(struct rockchip_crtc_state), GFP_KERNEL);
+       if (crtc->state)
+               crtc->state->crtc = crtc;
+}
+
 static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
 {
        struct rockchip_crtc_state *rockchip_state;
@@ -1236,36 +1322,19 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
        .set_config = drm_atomic_helper_set_config,
        .page_flip = drm_atomic_helper_page_flip,
        .destroy = vop_crtc_destroy,
-       .reset = drm_atomic_helper_crtc_reset,
+       .reset = vop_crtc_reset,
        .atomic_duplicate_state = vop_crtc_duplicate_state,
        .atomic_destroy_state = vop_crtc_destroy_state,
 };
 
-static bool vop_win_pending_is_complete(struct vop_win *vop_win)
-{
-       struct drm_plane *plane = &vop_win->base;
-       struct vop_plane_state *state = to_vop_plane_state(plane->state);
-       dma_addr_t yrgb_mst;
-
-       if (!state->enable)
-               return VOP_WIN_GET(vop_win->vop, vop_win, enable) == 0;
-
-       yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win);
-
-       return yrgb_mst == state->yrgb_mst;
-}
-
 static void vop_handle_vblank(struct vop *vop)
 {
        struct drm_device *drm = vop->drm_dev;
        struct drm_crtc *crtc = &vop->crtc;
        unsigned long flags;
-       int i;
 
-       for (i = 0; i < vop->num_wins; i++) {
-               if (!vop_win_pending_is_complete(&vop->win[i]))
-                       return;
-       }
+       if (!vop_win_pending_is_complete(vop))
+               return;
 
        if (vop->event) {
                spin_lock_irqsave(&drm->event_lock, flags);
@@ -1329,6 +1398,9 @@ static int vop_plane_init(struct vop *vop, struct vop_win *win,
                          unsigned long possible_crtcs)
 {
        struct drm_plane *share = NULL;
+       unsigned int rotations = 0;
+       struct drm_property *prop;
+       uint64_t feature = 0;
        int ret;
 
        if (win->parent)
@@ -1344,6 +1416,34 @@ static int vop_plane_init(struct vop *vop, struct vop_win *win,
        drm_plane_helper_add(&win->base, &plane_helper_funcs);
        drm_object_attach_property(&win->base.base,
                                   vop->plane_zpos_prop, win->win_id);
+
+       if (VOP_WIN_SUPPORT(vop, win, xmirror))
+               rotations |= BIT(DRM_REFLECT_X);
+
+       if (VOP_WIN_SUPPORT(vop, win, ymirror))
+               rotations |= BIT(DRM_REFLECT_Y);
+
+       if (rotations) {
+               rotations |= BIT(DRM_ROTATE_0);
+               prop = drm_mode_create_rotation_property(vop->drm_dev,
+                                                        rotations);
+               if (!prop) {
+                       DRM_ERROR("failed to create zpos property\n");
+                       return -EINVAL;
+               }
+               drm_object_attach_property(&win->base.base, prop,
+                                          BIT(DRM_ROTATE_0));
+               win->rotation_prop = prop;
+       }
+       if (win->phy->scl)
+               feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_SCALE);
+       if (VOP_WIN_SUPPORT(vop, win, src_alpha_ctl) ||
+           VOP_WIN_SUPPORT(vop, win, alpha_en))
+               feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_ALPHA);
+
+       drm_object_attach_property(&win->base.base, vop->plane_feature_prop,
+                                  feature);
+
        return 0;
 }
 
@@ -1456,103 +1556,6 @@ static void vop_destroy_crtc(struct vop *vop)
        drm_crtc_cleanup(crtc);
 }
 
-static int vop_initial(struct vop *vop)
-{
-       const struct vop_data *vop_data = vop->data;
-       const struct vop_reg_data *init_table = vop_data->init_table;
-       struct reset_control *ahb_rst;
-       int i, ret;
-
-       vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
-       if (IS_ERR(vop->hclk)) {
-               dev_err(vop->dev, "failed to get hclk source\n");
-               return PTR_ERR(vop->hclk);
-       }
-       vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
-       if (IS_ERR(vop->aclk)) {
-               dev_err(vop->dev, "failed to get aclk source\n");
-               return PTR_ERR(vop->aclk);
-       }
-       vop->dclk = devm_clk_get(vop->dev, "dclk_vop");
-       if (IS_ERR(vop->dclk)) {
-               dev_err(vop->dev, "failed to get dclk source\n");
-               return PTR_ERR(vop->dclk);
-       }
-
-       ret = clk_prepare(vop->dclk);
-       if (ret < 0) {
-               dev_err(vop->dev, "failed to prepare dclk\n");
-               return ret;
-       }
-
-       /* Enable both the hclk and aclk to setup the vop */
-       ret = clk_prepare_enable(vop->hclk);
-       if (ret < 0) {
-               dev_err(vop->dev, "failed to prepare/enable hclk\n");
-               goto err_unprepare_dclk;
-       }
-
-       ret = clk_prepare_enable(vop->aclk);
-       if (ret < 0) {
-               dev_err(vop->dev, "failed to prepare/enable aclk\n");
-               goto err_disable_hclk;
-       }
-
-       /*
-        * do hclk_reset, reset all vop registers.
-        */
-       ahb_rst = devm_reset_control_get(vop->dev, "ahb");
-       if (IS_ERR(ahb_rst)) {
-               dev_err(vop->dev, "failed to get ahb reset\n");
-               ret = PTR_ERR(ahb_rst);
-               goto err_disable_aclk;
-       }
-       reset_control_assert(ahb_rst);
-       usleep_range(10, 20);
-       reset_control_deassert(ahb_rst);
-
-       memcpy(vop->regsbak, vop->regs, vop->len);
-
-       for (i = 0; i < vop_data->table_size; i++)
-               vop_writel(vop, init_table[i].offset, init_table[i].value);
-
-       for (i = 0; i < vop->num_wins; i++) {
-               struct vop_win *win = &vop->win[i];
-
-               VOP_WIN_SET(vop, win, enable, 0);
-       }
-
-       vop_cfg_done(vop);
-
-       /*
-        * do dclk_reset, let all config take affect.
-        */
-       vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk");
-       if (IS_ERR(vop->dclk_rst)) {
-               dev_err(vop->dev, "failed to get dclk reset\n");
-               ret = PTR_ERR(vop->dclk_rst);
-               goto err_disable_aclk;
-       }
-       reset_control_assert(vop->dclk_rst);
-       usleep_range(10, 20);
-       reset_control_deassert(vop->dclk_rst);
-
-       clk_disable(vop->hclk);
-       clk_disable(vop->aclk);
-
-       vop->is_enabled = false;
-
-       return 0;
-
-err_disable_aclk:
-       clk_disable_unprepare(vop->aclk);
-err_disable_hclk:
-       clk_disable_unprepare(vop->hclk);
-err_unprepare_dclk:
-       clk_unprepare(vop->dclk);
-       return ret;
-}
-
 /*
  * Initialize the vop->win array elements.
  */
@@ -1562,11 +1565,18 @@ static int vop_win_init(struct vop *vop)
        unsigned int i, j;
        unsigned int num_wins = 0;
        struct drm_property *prop;
+       static const struct drm_prop_enum_list props[] = {
+               { ROCKCHIP_DRM_PLANE_FEATURE_SCALE, "scale" },
+               { ROCKCHIP_DRM_PLANE_FEATURE_ALPHA, "alpha" },
+       };
 
        for (i = 0; i < vop_data->win_size; i++) {
                struct vop_win *vop_win = &vop->win[num_wins];
                const struct vop_win_data *win_data = &vop_data->win[i];
 
+               if (!win_data->phy)
+                       continue;
+
                vop_win->phy = win_data->phy;
                vop_win->offset = win_data->base;
                vop_win->type = win_data->type;
@@ -1593,6 +1603,9 @@ static int vop_win_init(struct vop *vop)
                        num_wins++;
                }
        }
+
+       vop->num_wins = num_wins;
+
        prop = drm_property_create_range(vop->drm_dev, DRM_MODE_PROP_ATOMIC,
                                         "ZPOS", 0, vop->data->win_size);
        if (!prop) {
@@ -1601,6 +1614,16 @@ static int vop_win_init(struct vop *vop)
        }
        vop->plane_zpos_prop = prop;
 
+       vop->plane_feature_prop = drm_property_create_bitmask(vop->drm_dev,
+                               DRM_MODE_PROP_IMMUTABLE, "FEATURE",
+                               props, ARRAY_SIZE(props),
+                               BIT(ROCKCHIP_DRM_PLANE_FEATURE_SCALE) |
+                               BIT(ROCKCHIP_DRM_PLANE_FEATURE_ALPHA));
+       if (!vop->plane_feature_prop) {
+               DRM_ERROR("failed to create feature property\n");
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -1651,10 +1674,20 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
        if (!vop->regsbak)
                return -ENOMEM;
 
-       ret = vop_initial(vop);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
-               return ret;
+       vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
+       if (IS_ERR(vop->hclk)) {
+               dev_err(vop->dev, "failed to get hclk source\n");
+               return PTR_ERR(vop->hclk);
+       }
+       vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
+       if (IS_ERR(vop->aclk)) {
+               dev_err(vop->dev, "failed to get aclk source\n");
+               return PTR_ERR(vop->aclk);
+       }
+       vop->dclk = devm_clk_get(vop->dev, "dclk_vop");
+       if (IS_ERR(vop->dclk)) {
+               dev_err(vop->dev, "failed to get dclk source\n");
+               return PTR_ERR(vop->dclk);
        }
 
        irq = platform_get_irq(pdev, 0);