Merge branch 'for-upstream/mali-dp' of git://linux-arm.org/linux-ld into drm-next
[sfrench/cifs-2.6.git] / drivers / gpu / drm / rockchip / rockchip_drm_vop.c
index fb70fb486fbf4a57da6755a4c513d2dd426d1f33..619b6db05d58c361aa35a41e404485f24fe0ffeb 100644 (file)
 #include <drm/drm.h>
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
+#include <drm/drm_atomic_uapi.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_flip_work.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
 #ifdef CONFIG_DRM_ANALOGIX_DP
 #include <drm/bridge/analogix_dp.h>
 #include "rockchip_drm_vop.h"
 #include "rockchip_rgb.h"
 
-#define VOP_WIN_SET(x, win, name, v) \
+#define VOP_WIN_SET(vop, win, name, v) \
                vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name)
-#define VOP_SCL_SET(x, win, name, v) \
+#define VOP_SCL_SET(vop, win, name, v) \
                vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name)
-#define VOP_SCL_SET_EXT(x, win, name, v) \
+#define VOP_SCL_SET_EXT(vop, win, name, v) \
                vop_reg_set(vop, &win->phy->scl->ext->name, \
                            win->base, ~0, v, #name)
 
+#define VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, name, v) \
+       do { \
+               if (win_yuv2yuv && win_yuv2yuv->name.mask) \
+                       vop_reg_set(vop, &win_yuv2yuv->name, 0, ~0, v, #name); \
+       } while (0)
+
+#define VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, win_yuv2yuv, name, v) \
+       do { \
+               if (win_yuv2yuv && win_yuv2yuv->phy->name.mask) \
+                       vop_reg_set(vop, &win_yuv2yuv->phy->name, win_yuv2yuv->base, ~0, v, #name); \
+       } while (0)
+
 #define VOP_INTR_SET_MASK(vop, name, mask, v) \
                vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name)
 
 #define VOP_INTR_GET_TYPE(vop, name, type) \
                vop_get_intr_type(vop, &vop->data->intr->name, type)
 
-#define VOP_WIN_GET(x, win, name) \
-               vop_read_reg(x, win->offset, win->phy->name)
+#define VOP_WIN_GET(vop, win, name) \
+               vop_read_reg(vop, win->offset, win->phy->name)
+
+#define VOP_WIN_HAS_REG(win, name) \
+       (!!(win->phy->name.mask))
 
 #define VOP_WIN_GET_YRGBADDR(vop, win) \
                vop_readl(vop, win->base + win->phy->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)
 
+/*
+ * 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.
+ * They are all represented in two's complement.
+ */
+static const uint32_t bt601_yuv2rgb[] = {
+       0x4A8, 0x0,    0x662,
+       0x4A8, 0x1E6F, 0x1CBF,
+       0x4A8, 0x812,  0x0,
+       0x321168, 0x0877CF, 0x2EB127
+};
+
 enum vop_pending {
        VOP_PENDING_FB_UNREF,
 };
@@ -91,6 +120,7 @@ enum vop_pending {
 struct vop_win {
        struct drm_plane base;
        const struct vop_win_data *data;
+       const struct vop_win_yuv2yuv_data *yuv2yuv_data;
        struct vop *vop;
 };
 
@@ -685,6 +715,11 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
                return -EINVAL;
        }
 
+       if (fb->format->is_yuv && state->rotation & DRM_MODE_REFLECT_Y) {
+               DRM_ERROR("Invalid Source: Yuv format does not support this rotation\n");
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -712,6 +747,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        struct drm_crtc *crtc = state->crtc;
        struct vop_win *vop_win = to_vop_win(plane);
        const struct vop_win_data *win = vop_win->data;
+       const struct vop_win_yuv2yuv_data *win_yuv2yuv = vop_win->yuv2yuv_data;
        struct vop *vop = to_vop(state->crtc);
        struct drm_framebuffer *fb = state->fb;
        unsigned int actual_w, actual_h;
@@ -727,6 +763,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        bool rb_swap;
        int win_index = VOP_WIN_TO_INDEX(vop_win);
        int format;
+       int is_yuv = fb->format->is_yuv;
+       int i;
 
        /*
         * can't update plane when vop is disabled.
@@ -760,6 +798,13 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        offset += (src->y1 >> 16) * fb->pitches[0];
        dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];
 
+       /*
+        * For y-mirroring we need to move address
+        * to the beginning of the last line.
+        */
+       if (state->rotation & DRM_MODE_REFLECT_Y)
+               dma_addr += (actual_h - 1) * fb->pitches[0];
+
        format = vop_convert_format(fb->format->format);
 
        spin_lock(&vop->reg_lock);
@@ -767,7 +812,13 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        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);
-       if (fb->format->is_yuv) {
+       VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv);
+       VOP_WIN_SET(vop, win, y_mir_en,
+                   (state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0);
+       VOP_WIN_SET(vop, win, x_mir_en,
+                   (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0);
+
+       if (is_yuv) {
                int hsub = drm_format_horz_chroma_subsampling(fb->format->format);
                int vsub = drm_format_vert_chroma_subsampling(fb->format->format);
                int bpp = fb->format->cpp[1];
@@ -781,6 +832,13 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
                dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
                VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4));
                VOP_WIN_SET(vop, win, uv_mst, dma_addr);
+
+               for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) {
+                       VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop,
+                                                       win_yuv2yuv,
+                                                       y2r_coefficients[i],
+                                                       bt601_yuv2rgb[i]);
+               }
        }
 
        if (win->phy->scl)
@@ -819,10 +877,84 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        spin_unlock(&vop->reg_lock);
 }
 
+static int vop_plane_atomic_async_check(struct drm_plane *plane,
+                                       struct drm_plane_state *state)
+{
+       struct vop_win *vop_win = to_vop_win(plane);
+       const struct vop_win_data *win = vop_win->data;
+       int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
+                                       DRM_PLANE_HELPER_NO_SCALING;
+       int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
+                                       DRM_PLANE_HELPER_NO_SCALING;
+       struct drm_crtc_state *crtc_state;
+
+       if (plane != state->crtc->cursor)
+               return -EINVAL;
+
+       if (!plane->state)
+               return -EINVAL;
+
+       if (!plane->state->fb)
+               return -EINVAL;
+
+       if (state->state)
+               crtc_state = drm_atomic_get_existing_crtc_state(state->state,
+                                                               state->crtc);
+       else /* Special case for asynchronous cursor updates. */
+               crtc_state = plane->crtc->state;
+
+       return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
+                                                  min_scale, max_scale,
+                                                  true, true);
+}
+
+static void vop_plane_atomic_async_update(struct drm_plane *plane,
+                                         struct drm_plane_state *new_state)
+{
+       struct vop *vop = to_vop(plane->state->crtc);
+       struct drm_plane_state *plane_state;
+
+       plane_state = plane->funcs->atomic_duplicate_state(plane);
+       plane_state->crtc_x = new_state->crtc_x;
+       plane_state->crtc_y = new_state->crtc_y;
+       plane_state->crtc_h = new_state->crtc_h;
+       plane_state->crtc_w = new_state->crtc_w;
+       plane_state->src_x = new_state->src_x;
+       plane_state->src_y = new_state->src_y;
+       plane_state->src_h = new_state->src_h;
+       plane_state->src_w = new_state->src_w;
+
+       if (plane_state->fb != new_state->fb)
+               drm_atomic_set_fb_for_plane(plane_state, new_state->fb);
+
+       swap(plane_state, plane->state);
+
+       if (plane->state->fb && plane->state->fb != new_state->fb) {
+               drm_framebuffer_get(plane->state->fb);
+               WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0);
+               drm_flip_work_queue(&vop->fb_unref_work, plane->state->fb);
+               set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
+       }
+
+       if (vop->is_enabled) {
+               rockchip_drm_psr_inhibit_get_state(new_state->state);
+               vop_plane_atomic_update(plane, plane->state);
+               spin_lock(&vop->reg_lock);
+               vop_cfg_done(vop);
+               spin_unlock(&vop->reg_lock);
+               rockchip_drm_psr_inhibit_put_state(new_state->state);
+       }
+
+       plane->funcs->atomic_destroy_state(plane, plane_state);
+}
+
 static const struct drm_plane_helper_funcs plane_helper_funcs = {
        .atomic_check = vop_plane_atomic_check,
        .atomic_update = vop_plane_atomic_update,
        .atomic_disable = vop_plane_atomic_disable,
+       .atomic_async_check = vop_plane_atomic_async_check,
+       .atomic_async_update = vop_plane_atomic_async_update,
+       .prepare_fb = drm_gem_fb_prepare_fb,
 };
 
 static const struct drm_plane_funcs vop_plane_funcs = {
@@ -1272,6 +1404,18 @@ out:
        return ret;
 }
 
+static void vop_plane_add_properties(struct drm_plane *plane,
+                                    const struct vop_win_data *win_data)
+{
+       unsigned int flags = 0;
+
+       flags |= VOP_WIN_HAS_REG(win_data, x_mir_en) ? DRM_MODE_REFLECT_X : 0;
+       flags |= VOP_WIN_HAS_REG(win_data, y_mir_en) ? DRM_MODE_REFLECT_Y : 0;
+       if (flags)
+               drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
+                                                  DRM_MODE_ROTATE_0 | flags);
+}
+
 static int vop_create_crtc(struct vop *vop)
 {
        const struct vop_data *vop_data = vop->data;
@@ -1309,6 +1453,7 @@ static int vop_create_crtc(struct vop *vop)
 
                plane = &vop_win->base;
                drm_plane_helper_add(plane, &plane_helper_funcs);
+               vop_plane_add_properties(plane, win_data);
                if (plane->type == DRM_PLANE_TYPE_PRIMARY)
                        primary = plane;
                else if (plane->type == DRM_PLANE_TYPE_CURSOR)
@@ -1346,6 +1491,7 @@ static int vop_create_crtc(struct vop *vop)
                        goto err_cleanup_crtc;
                }
                drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
+               vop_plane_add_properties(&vop_win->base, win_data);
        }
 
        port = of_get_child_by_name(dev->of_node, "port");
@@ -1529,6 +1675,7 @@ static void vop_win_init(struct vop *vop)
 
                vop_win->data = win_data;
                vop_win->vop = vop;
+               vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i];
        }
 }