drm/rockchip: vop: support multi area plane
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_vop.c
index d3c80337b083cb48a869facfeb0ca047fe8d9f6b..2785bcf8a8003eafa1214245dbce3b67c20a924e 100644 (file)
 #define __REG_SET_NORMAL(x, off, mask, shift, v, write_mask) \
                vop_mask_write(x, off, mask, shift, v, write_mask, false)
 
-#define REG_SET(x, base, reg, v, mode) \
-               __REG_SET_##mode(x, base + reg.offset, \
+#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, base, reg, mask, v, mode) \
-               __REG_SET_##mode(x, base + reg.offset, \
+#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_WIN_SET(x, win, name, v) \
-               REG_SET(x, win->base, win->phy->name, v, RELAXED)
+               REG_SET(x, win->offset, VOP_WIN_NAME(win, name), v, RELAXED)
 #define VOP_SCL_SET(x, win, name, v) \
-               REG_SET(x, win->base, win->phy->scl->name, v, RELAXED)
+               REG_SET(x, win->offset, win->phy->scl->name, v, RELAXED)
 #define VOP_SCL_SET_EXT(x, win, name, v) \
-               REG_SET(x, win->base, win->phy->scl->ext->name, v, RELAXED)
+               REG_SET(x, win->offset, win->phy->scl->ext->name, v, RELAXED)
+
 #define VOP_CTRL_SET(x, name, v) \
                REG_SET(x, 0, (x)->data->ctrl->name, v, NORMAL)
 
                vop_get_intr_type(vop, &vop->data->intr->name, type)
 
 #define VOP_WIN_GET(x, win, name) \
-               vop_read_reg(x, win->base, &win->phy->name)
+               vop_read_reg(x, win->offset, &VOP_WIN_NAME(win, name))
+
+#define VOP_WIN_NAME(win, name) \
+               (vop_get_win_phy(win, &win->phy->name)->name)
 
 #define VOP_WIN_GET_YRGBADDR(vop, win) \
-               vop_readl(vop, win->base + win->phy->yrgb_mst.offset)
+               vop_readl(vop, win->offset + VOP_WIN_NAME(win, yrgb_mst).offset)
 
 #define to_vop(x) container_of(x, struct vop, crtc)
 #define to_vop_win(x) container_of(x, struct vop_win, base)
@@ -97,8 +101,14 @@ struct vop_plane_state {
 };
 
 struct vop_win {
+       struct vop_win *parent;
        struct drm_plane base;
-       const struct vop_win_data *data;
+
+       uint32_t offset;
+       enum drm_plane_type type;
+       const struct vop_win_phy *phy;
+       const uint32_t *data_formats;
+       uint32_t nformats;
        struct vop *vop;
 
        struct vop_plane_state state;
@@ -118,6 +128,7 @@ struct vop {
        struct drm_pending_vblank_event *event;
 
        const struct vop_data *data;
+       int num_wins;
 
        uint32_t *regsbak;
        void __iomem *regs;
@@ -184,6 +195,15 @@ static inline void vop_mask_write(struct vop *vop, uint32_t offset,
                writel(v, vop->regs + offset);
 }
 
+static inline const struct vop_win_phy *
+vop_get_win_phy(struct vop_win *win, const struct vop_reg *reg)
+{
+       if (!reg->mask && win->parent)
+               return win->parent->phy;
+
+       return win->phy;
+}
+
 static inline uint32_t vop_get_intr_type(struct vop *vop,
                                         const struct vop_reg *reg, int type)
 {
@@ -296,9 +316,9 @@ static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
        return val;
 }
 
-static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
-                            uint32_t src_w, uint32_t src_h, uint32_t dst_w,
-                            uint32_t dst_h, uint32_t pixel_format)
+static void scl_vop_cal_scl_fac(struct vop *vop, struct vop_win *win,
+                               uint32_t src_w, uint32_t src_h, uint32_t dst_w,
+                               uint32_t dst_h, uint32_t pixel_format)
 {
        uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
        uint16_t cbcr_hor_scl_mode = SCALE_NONE;
@@ -313,6 +333,9 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
        uint32_t val;
        int vskiplines;
 
+       if (!win->phy->scl)
+               return;
+
        if (dst_w > 3840) {
                DRM_ERROR("Maximum destination width (3840) exceeded\n");
                return;
@@ -510,9 +533,8 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
         * disable that crtc. Otherwise we might try to scan from a destroyed
         * buffer later.
         */
-       for (i = 0; i < vop->data->win_size; i++) {
-               struct vop_win *vop_win = &vop->win[i];
-               const struct vop_win_data *win = vop_win->data;
+       for (i = 0; i < vop->num_wins; i++) {
+               struct vop_win *win = &vop->win[i];
 
                spin_lock(&vop->reg_lock);
                VOP_WIN_SET(vop, win, enable, 0);
@@ -566,9 +588,8 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 {
        struct drm_crtc *crtc = state->crtc;
        struct drm_framebuffer *fb = state->fb;
-       struct vop_win *vop_win = to_vop_win(plane);
+       struct vop_win *win = to_vop_win(plane);
        struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
-       const struct vop_win_data *win = vop_win->data;
        bool visible;
        int ret;
        struct drm_rect *dest = &vop_plane_state->dest;
@@ -634,8 +655,7 @@ static void vop_plane_atomic_disable(struct drm_plane *plane,
                                     struct drm_plane_state *old_state)
 {
        struct vop_plane_state *vop_plane_state = to_vop_plane_state(old_state);
-       struct vop_win *vop_win = to_vop_win(plane);
-       const struct vop_win_data *win = vop_win->data;
+       struct vop_win *win = to_vop_win(plane);
        struct vop *vop = to_vop(old_state->crtc);
 
        if (!old_state->crtc)
@@ -655,9 +675,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 {
        struct drm_plane_state *state = plane->state;
        struct drm_crtc *crtc = state->crtc;
-       struct vop_win *vop_win = to_vop_win(plane);
+       struct vop_win *win = to_vop_win(plane);
        struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
-       const struct vop_win_data *win = vop_win->data;
        struct vop *vop = to_vop(state->crtc);
        struct drm_framebuffer *fb = state->fb;
        unsigned int actual_w, actual_h;
@@ -725,10 +744,9 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
                VOP_WIN_SET(vop, win, uv_mst, dma_addr);
        }
 
-       if (win->phy->scl)
-               scl_vop_cal_scl_fac(vop, win, actual_w, actual_h,
-                                   drm_rect_width(dest), drm_rect_height(dest),
-                                   fb->pixel_format);
+       scl_vop_cal_scl_fac(vop, win, actual_w, actual_h,
+                           drm_rect_width(dest), drm_rect_height(dest),
+                           fb->pixel_format);
 
        VOP_WIN_SET(vop, win, act_info, act_info);
        VOP_WIN_SET(vop, win, dsp_info, dsp_info);
@@ -1066,9 +1084,9 @@ static bool vop_win_pending_is_complete(struct vop_win *vop_win)
        dma_addr_t yrgb_mst;
 
        if (!state->enable)
-               return VOP_WIN_GET(vop_win->vop, vop_win->data, enable) == 0;
+               return VOP_WIN_GET(vop_win->vop, vop_win, enable) == 0;
 
-       yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win->data);
+       yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win);
 
        return yrgb_mst == state->yrgb_mst;
 }
@@ -1080,7 +1098,7 @@ static void vop_handle_vblank(struct vop *vop)
        unsigned long flags;
        int i;
 
-       for (i = 0; i < vop->data->win_size; i++) {
+       for (i = 0; i < vop->num_wins; i++) {
                if (!vop_win_pending_is_complete(&vop->win[i]))
                        return;
        }
@@ -1143,9 +1161,29 @@ static irqreturn_t vop_isr(int irq, void *data)
        return ret;
 }
 
+static int vop_plane_init(struct vop *vop, struct vop_win *win,
+                         unsigned long possible_crtcs)
+{
+       struct drm_plane *share = NULL;
+       int ret;
+
+       if (win->parent)
+               share = &win->parent->base;
+
+       ret = drm_share_plane_init(vop->drm_dev, &win->base, share,
+                                  possible_crtcs, &vop_plane_funcs,
+                                  win->data_formats, win->nformats, win->type);
+       if (ret) {
+               DRM_ERROR("failed to initialize plane\n");
+               return ret;
+       }
+       drm_plane_helper_add(&win->base, &plane_helper_funcs);
+
+       return 0;
+}
+
 static int vop_create_crtc(struct vop *vop)
 {
-       const struct vop_data *vop_data = vop->data;
        struct device *dev = vop->dev;
        struct drm_device *drm_dev = vop->drm_dev;
        struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp;
@@ -1159,30 +1197,22 @@ static int vop_create_crtc(struct vop *vop)
         * to pass them to drm_crtc_init_with_planes, which sets the
         * "possible_crtcs" to the newly initialized crtc.
         */
-       for (i = 0; i < vop_data->win_size; i++) {
-               struct vop_win *vop_win = &vop->win[i];
-               const struct vop_win_data *win_data = vop_win->data;
+       for (i = 0; i < vop->num_wins; i++) {
+               struct vop_win *win = &vop->win[i];
 
-               if (win_data->type != DRM_PLANE_TYPE_PRIMARY &&
-                   win_data->type != DRM_PLANE_TYPE_CURSOR)
+               if (win->type != DRM_PLANE_TYPE_PRIMARY &&
+                   win->type != DRM_PLANE_TYPE_CURSOR)
                        continue;
 
-               ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
-                                              0, &vop_plane_funcs,
-                                              win_data->phy->data_formats,
-                                              win_data->phy->nformats,
-                                              win_data->type, NULL);
-               if (ret) {
-                       DRM_ERROR("failed to initialize plane\n");
+               if (vop_plane_init(vop, win, 0))
                        goto err_cleanup_planes;
-               }
 
-               plane = &vop_win->base;
-               drm_plane_helper_add(plane, &plane_helper_funcs);
+               plane = &win->base;
                if (plane->type == DRM_PLANE_TYPE_PRIMARY)
                        primary = plane;
                else if (plane->type == DRM_PLANE_TYPE_CURSOR)
                        cursor = plane;
+
        }
 
        ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
@@ -1196,25 +1226,15 @@ static int vop_create_crtc(struct vop *vop)
         * Create drm_planes for overlay windows with possible_crtcs restricted
         * to the newly created crtc.
         */
-       for (i = 0; i < vop_data->win_size; i++) {
-               struct vop_win *vop_win = &vop->win[i];
-               const struct vop_win_data *win_data = vop_win->data;
+       for (i = 0; i < vop->num_wins; i++) {
+               struct vop_win *win = &vop->win[i];
                unsigned long possible_crtcs = 1 << drm_crtc_index(crtc);
 
-               if (win_data->type != DRM_PLANE_TYPE_OVERLAY)
+               if (win->type != DRM_PLANE_TYPE_OVERLAY)
                        continue;
 
-               ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
-                                              possible_crtcs,
-                                              &vop_plane_funcs,
-                                              win_data->phy->data_formats,
-                                              win_data->phy->nformats,
-                                              win_data->type, NULL);
-               if (ret) {
-                       DRM_ERROR("failed to initialize overlay plane\n");
+               if (vop_plane_init(vop, win, possible_crtcs))
                        goto err_cleanup_crtc;
-               }
-               drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
        }
 
        port = of_get_child_by_name(dev->of_node, "port");
@@ -1329,8 +1349,8 @@ static int vop_initial(struct vop *vop)
        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_data->win_size; i++) {
-               const struct vop_win_data *win = &vop_data->win[i];
+       for (i = 0; i < vop->num_wins; i++) {
+               struct vop_win *win = &vop->win[i];
 
                VOP_WIN_SET(vop, win, enable, 0);
        }
@@ -1372,14 +1392,34 @@ err_unprepare_dclk:
 static void vop_win_init(struct vop *vop)
 {
        const struct vop_data *vop_data = vop->data;
-       unsigned int i;
+       unsigned int i, j;
+       unsigned int num_wins = 0;
 
        for (i = 0; i < vop_data->win_size; i++) {
-               struct vop_win *vop_win = &vop->win[i];
+               struct vop_win *vop_win = &vop->win[num_wins];
                const struct vop_win_data *win_data = &vop_data->win[i];
 
-               vop_win->data = win_data;
+               vop_win->phy = win_data->phy;
+               vop_win->offset = win_data->base;
+               vop_win->type = win_data->type;
+               vop_win->data_formats = win_data->phy->data_formats;
+               vop_win->nformats = win_data->phy->nformats;
                vop_win->vop = vop;
+               num_wins++;
+
+               for (j = 0; j < win_data->area_size; j++) {
+                       struct vop_win *vop_area = &vop->win[num_wins];
+                       const struct vop_win_phy *area = win_data->area[j];
+
+                       vop_area->parent = vop_win;
+                       vop_area->offset = vop_win->offset;
+                       vop_area->phy = area;
+                       vop_area->type = DRM_PLANE_TYPE_OVERLAY;
+                       vop_area->data_formats = vop_win->data_formats;
+                       vop_area->nformats = vop_win->nformats;
+                       vop_area->vop = vop;
+                       num_wins++;
+               }
        }
 }
 
@@ -1391,14 +1431,21 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
        struct vop *vop;
        struct resource *res;
        size_t alloc_size;
-       int ret, irq;
+       int ret, irq, i;
+       int num_wins = 0;
 
        vop_data = of_device_get_match_data(dev);
        if (!vop_data)
                return -ENODEV;
 
+       for (i = 0; i < vop_data->win_size; i++) {
+               const struct vop_win_data *win_data = &vop_data->win[i];
+
+               num_wins += win_data->area_size + 1;
+       }
+
        /* Allocate vop struct and its vop_win array */
-       alloc_size = sizeof(*vop) + sizeof(*vop->win) * vop_data->win_size;
+       alloc_size = sizeof(*vop) + sizeof(*vop->win) * num_wins;
        vop = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
        if (!vop)
                return -ENOMEM;
@@ -1406,6 +1453,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
        vop->dev = dev;
        vop->data = vop_data;
        vop->drm_dev = drm_dev;
+       vop->num_wins = num_wins;
        dev_set_drvdata(dev, vop);
 
        vop_win_init(vop);