Merge remote-tracking branch 'drm/drm-next' into drm-misc-next
[sfrench/cifs-2.6.git] / drivers / gpu / drm / rockchip / rockchip_drm_vop.c
index 20a9c296d0272d7ef8ad89cf6f66a81deace12c4..09a790c2f3a1ead32cd7360b645eca62a1d2f1a0 100644 (file)
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
  * Author:Mark Yao <mark.yao@rock-chips.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 
 #include <drm/drm.h>
@@ -315,24 +307,19 @@ static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
 
 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)
+                            uint32_t dst_h, const struct drm_format_info *info)
 {
        uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
        uint16_t cbcr_hor_scl_mode = SCALE_NONE;
        uint16_t cbcr_ver_scl_mode = SCALE_NONE;
-       int hsub = drm_format_horz_chroma_subsampling(pixel_format);
-       int vsub = drm_format_vert_chroma_subsampling(pixel_format);
-       const struct drm_format_info *info;
        bool is_yuv = false;
-       uint16_t cbcr_src_w = src_w / hsub;
-       uint16_t cbcr_src_h = src_h / vsub;
+       uint16_t cbcr_src_w = src_w / info->hsub;
+       uint16_t cbcr_src_h = src_h / info->vsub;
        uint16_t vsu_mode;
        uint16_t lb_mode;
        uint32_t val;
        int vskiplines;
 
-       info = drm_format_info(pixel_format);
-
        if (info->is_yuv)
                is_yuv = true;
 
@@ -831,8 +818,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
                    (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 hsub = fb->format->hsub;
+               int vsub = fb->format->vsub;
                int bpp = fb->format->cpp[1];
 
                uv_obj = fb->obj[1];
@@ -856,7 +843,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        if (win->phy->scl)
                scl_vop_cal_scl_fac(vop, win, actual_w, actual_h,
                                    drm_rect_width(dest), drm_rect_height(dest),
-                                   fb->format->format);
+                                   fb->format);
 
        VOP_WIN_SET(vop, win, act_info, act_info);
        VOP_WIN_SET(vop, win, dsp_info, dsp_info);
@@ -924,29 +911,17 @@ 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);
-       }
+       struct drm_framebuffer *old_fb = plane->state->fb;
+
+       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;
+       swap(plane->state->fb, new_state->fb);
 
        if (vop->is_enabled) {
                rockchip_drm_psr_inhibit_get_state(new_state->state);
@@ -955,9 +930,22 @@ static void vop_plane_atomic_async_update(struct drm_plane *plane,
                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);
+               /*
+                * A scanout can still be occurring, so we can't drop the
+                * reference to the old framebuffer. To solve this we get a
+                * reference to old_fb and set a worker to release it later.
+                * FIXME: if we perform 500 async_update calls before the
+                * vblank, then we can have 500 different framebuffers waiting
+                * to be released.
+                */
+               if (old_fb && plane->state->fb != old_fb) {
+                       drm_framebuffer_get(old_fb);
+                       WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0);
+                       drm_flip_work_queue(&vop->fb_unref_work, old_fb);
+                       set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
+               }
+       }
 }
 
 static const struct drm_plane_helper_funcs plane_helper_funcs = {
@@ -1018,7 +1006,8 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
        struct vop *vop = to_vop(crtc);
 
        adjusted_mode->clock =
-               clk_round_rate(vop->dclk, mode->clock * 1000) / 1000;
+               DIV_ROUND_UP(clk_round_rate(vop->dclk,
+                                           adjusted_mode->clock * 1000), 1000);
 
        return true;
 }
@@ -1222,17 +1211,6 @@ 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->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;
@@ -1254,6 +1232,17 @@ static void vop_crtc_destroy_state(struct drm_crtc *crtc,
        kfree(s);
 }
 
+static void vop_crtc_reset(struct drm_crtc *crtc)
+{
+       struct rockchip_crtc_state *crtc_state =
+               kzalloc(sizeof(*crtc_state), GFP_KERNEL);
+
+       if (crtc->state)
+               vop_crtc_destroy_state(crtc, crtc->state);
+
+       __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base);
+}
+
 #ifdef CONFIG_DRM_ANALOGIX_DP
 static struct drm_connector *vop_get_edp_connector(struct vop *vop)
 {