MLK-15932-8 drm/imx: dpu: kms: Add scalers support
authorLiu Ying <victor.liu@nxp.com>
Mon, 10 Jul 2017 04:59:58 +0000 (12:59 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:33:31 +0000 (15:33 -0500)
This patch adds scalers support.  According to the DPU spec, we only
support up-scaling for display controller.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
drivers/gpu/drm/imx/dpu/dpu-crtc.c
drivers/gpu/drm/imx/dpu/dpu-kms.c
drivers/gpu/drm/imx/dpu/dpu-plane.c

index 6f60bb6..7277ef1 100644 (file)
@@ -247,6 +247,8 @@ static void dpu_crtc_atomic_begin(struct drm_crtc *crtc,
                struct dpu_plane *dplane;
                struct dpu_plane_res *res;
                struct dpu_fetchdecode *fd;
+               struct dpu_hscaler *hs;
+               struct dpu_vscaler *vs;
                struct dpu_layerblend *lb;
                struct dpu_extdst *ed;
                int fd_id, lb_id;
@@ -270,8 +272,15 @@ static void dpu_crtc_atomic_begin(struct drm_crtc *crtc,
                fd = res->fd[fd_id];
                lb = res->lb[lb_id];
 
+               hs = fetchdecode_get_hscaler(fd);
+               vs = fetchdecode_get_vscaler(fd);
+
                layerblend_pixengcfg_clken(lb, CLKEN__DISABLE);
                fetchdecode_layerproperty(fd, false);
+               hscaler_pixengcfg_clken(hs, CLKEN__DISABLE);
+               vscaler_pixengcfg_clken(vs, CLKEN__DISABLE);
+               hscaler_mode(hs, SCALER_NEUTRAL);
+               vscaler_mode(vs, SCALER_NEUTRAL);
                if (old_dpstate->is_top) {
                        ed = res->ed[dplane->stream_id];
                        extdst_pixengcfg_src_sel(ed, ED_SRC_DISABLE);
@@ -297,6 +306,18 @@ static void dpu_crtc_atomic_flush(struct drm_crtc *crtc,
                        fetchdecode_set_stream_id(res->fd[i],
                                                        DPU_PLANE_SRC_DISABLED);
        }
+
+       for (i = 0; i < ARRAY_SIZE(res->hs); i++) {
+               if (res->hs[i] && !hscaler_is_enabled(res->hs[i]))
+                       hscaler_set_stream_id(res->hs[i],
+                                                       DPU_PLANE_SRC_DISABLED);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(res->vs); i++) {
+               if (res->vs[i] && !vscaler_is_enabled(res->vs[i]))
+                       vscaler_set_stream_id(res->vs[i],
+                                                       DPU_PLANE_SRC_DISABLED);
+       }
 }
 
 static void dpu_crtc_mode_set_nofb(struct drm_crtc *crtc)
index 3c124ef..8f75b5b 100644 (file)
@@ -132,9 +132,12 @@ dpu_atomic_assign_plane_source_per_crtc(struct drm_plane_state **states, int n)
        struct dpu_plane *dplane;
        struct dpu_plane_grp *grp;
        struct dpu_fetchdecode *fd;
+       struct dpu_hscaler *hs;
+       struct dpu_vscaler *vs;
        unsigned int sid, src_sid;
        int i, j, k;
        int fd_id;
+       u32 cap_mask, hs_mask, vs_mask;
 
        /* for active planes only */
        for (i = 0; i < n; i++) {
@@ -158,6 +161,50 @@ dpu_atomic_assign_plane_source_per_crtc(struct drm_plane_state **states, int n)
                        if (src_sid && src_sid != BIT(sid))
                                continue;
 
+                       cap_mask = fetchdecode_get_vproc_mask(fd);
+
+                       if (states[i]->src_w >> 16 != states[i]->crtc_w) {
+                               hs = fetchdecode_get_hscaler(fd);
+
+                               /* avoid on-the-fly/hot migration */
+                               src_sid = hscaler_get_stream_id(hs);
+                               if (src_sid && src_sid != BIT(sid))
+                                       continue;
+
+                               /* fetch unit has the hscale capability? */
+                               if (!dpu_vproc_has_hscale_cap(cap_mask))
+                                       continue;
+
+                               hs_mask = dpu_vproc_get_hscale_cap(cap_mask);
+
+                               /* hscaler available? */
+                               if (grp->src_use_vproc_mask & hs_mask)
+                                       continue;
+
+                               grp->src_use_vproc_mask |= hs_mask;
+                       }
+
+                       if (states[i]->src_h >> 16 != states[i]->crtc_h) {
+                               vs = fetchdecode_get_vscaler(fd);
+
+                               /* avoid on-the-fly/hot migration */
+                               src_sid = vscaler_get_stream_id(vs);
+                               if (src_sid && src_sid != BIT(sid))
+                                       continue;
+
+                               /* fetch unit has the vscale capability? */
+                               if (!dpu_vproc_has_vscale_cap(cap_mask))
+                                       continue;
+
+                               vs_mask = dpu_vproc_get_vscale_cap(cap_mask);
+
+                               /* vscaler available? */
+                               if (grp->src_use_vproc_mask & vs_mask)
+                                       continue;
+
+                               grp->src_use_vproc_mask |= vs_mask;
+                       }
+
                        grp->src_mask |= BIT(k);
                        break;
                }
@@ -188,11 +235,17 @@ static int dpu_drm_atomic_check(struct drm_device *dev,
        struct drm_crtc_state *crtc_state;
        struct drm_plane *plane;
        struct dpu_plane *dpu_plane;
+       const struct drm_plane_state *plane_state;
        struct dpu_plane_grp *grp[MAX_DPU_PLANE_GRP];
-       int ret, i, grp_id, active_plane[MAX_DPU_PLANE_GRP];
+       int ret, i, grp_id;
+       int active_plane[MAX_DPU_PLANE_GRP];
+       int active_plane_hscale[MAX_DPU_PLANE_GRP];
+       int active_plane_vscale[MAX_DPU_PLANE_GRP];
 
        for (i = 0; i < MAX_DPU_PLANE_GRP; i++) {
                active_plane[i] = 0;
+               active_plane_hscale[i] = 0;
+               active_plane_vscale[i] = 0;
                grp[i] = NULL;
        }
 
@@ -205,24 +258,45 @@ static int dpu_drm_atomic_check(struct drm_device *dev,
                if (ret)
                        return ret;
 
-               drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
+               drm_atomic_crtc_state_for_each_plane_state(plane, plane_state,
+                                                          crtc_state) {
                        dpu_plane = to_dpu_plane(plane);
                        grp_id = dpu_plane->grp->id;
                        active_plane[grp_id]++;
 
+                       if (plane_state->src_w >> 16 != plane_state->crtc_w)
+                               active_plane_hscale[grp_id]++;
+
+                       if (plane_state->src_h >> 16 != plane_state->crtc_h)
+                               active_plane_vscale[grp_id]++;
+
                        if (grp[grp_id] == NULL)
                                grp[grp_id] = dpu_plane->grp;
                }
        }
 
-       for (i = 0; i < MAX_DPU_PLANE_GRP; i++)
-               if (grp[i] && active_plane[i] > grp[i]->hw_plane_num)
-                       return -EINVAL;
+       /* enough resources? */
+       for (i = 0; i < MAX_DPU_PLANE_GRP; i++) {
+               if (grp[i]) {
+                       if (active_plane[i] > grp[i]->hw_plane_num)
+                               return -EINVAL;
 
-       /* clear source mask */
+                       if (active_plane_hscale[i] >
+                           grp[i]->hw_plane_hscaler_num)
+                               return -EINVAL;
+
+                       if (active_plane_vscale[i] >
+                           grp[i]->hw_plane_vscaler_num)
+                               return -EINVAL;
+               }
+       }
+
+       /* clear resource mask */
        for (i = 0; i < MAX_DPU_PLANE_GRP; i++) {
-               if (grp[i])
+               if (grp[i]) {
                        grp[i]->src_mask = 0;
+                       grp[i]->src_use_vproc_mask = 0;
+               }
        }
 
        ret = drm_atomic_helper_check_modeset(dev, state);
index 5182ef9..a50b3bd 100644 (file)
@@ -128,6 +128,7 @@ drm_plane_state_to_baseaddr(struct drm_plane_state *state)
 static int dpu_plane_atomic_check(struct drm_plane *plane,
                                  struct drm_plane_state *state)
 {
+       struct dpu_plane *dplane = to_dpu_plane(plane);
        struct dpu_plane_state *dpstate = to_dpu_plane_state(state);
        struct drm_crtc_state *crtc_state;
        struct drm_framebuffer *fb = state->fb;
@@ -156,10 +157,17 @@ static int dpu_plane_atomic_check(struct drm_plane *plane,
                return 0;
        }
 
-       /* no scaling */
-       if (state->src_w >> 16 != state->crtc_w ||
-           state->src_h >> 16 != state->crtc_h)
-               return -EINVAL;
+       if (dplane->grp->has_vproc) {
+               /* no down scaling */
+               if (state->src_w >> 16 > state->crtc_w ||
+                   state->src_h >> 16 > state->crtc_h)
+                       return -EINVAL;
+       } else {
+               /* no scaling */
+               if (state->src_w >> 16 != state->crtc_w ||
+                   state->src_h >> 16 != state->crtc_h)
+                       return -EINVAL;
+       }
 
        /* no off screen */
        if (state->crtc_x < 0 || state->crtc_y < 0)
@@ -208,12 +216,17 @@ static void dpu_plane_atomic_update(struct drm_plane *plane,
        struct drm_framebuffer *fb = state->fb;
        struct dpu_plane_res *res = &dplane->grp->res;
        struct dpu_fetchdecode *fd;
+       struct dpu_hscaler *hs;
+       struct dpu_vscaler *vs;
        struct dpu_layerblend *lb;
        struct dpu_constframe *cf;
        struct dpu_extdst *ed;
        struct device *dev = plane->dev->dev;
+       dpu_block_id_t vs_id = ID_NONE, hs_id;
+       lb_sec_sel_t lb_src = dpstate->source;
        unsigned int depth, src_w, src_h;
        int bpp, fd_id, lb_id;
+       bool need_hscaler = false, need_vscaler = false;
 
        /*
         * Do nothing since the plane is disabled by
@@ -236,6 +249,20 @@ static void dpu_plane_atomic_update(struct drm_plane *plane,
        src_w = state->src_w >> 16;
        src_h = state->src_h >> 16;
 
+       if (src_w != state->crtc_w) {
+               need_hscaler = true;
+               hs = fetchdecode_get_hscaler(fd);
+               if (IS_ERR(hs))
+                       return;
+       }
+
+       if (src_h != state->crtc_h) {
+               need_vscaler = true;
+               vs = fetchdecode_get_vscaler(fd);
+               if (IS_ERR(vs))
+                       return;
+       }
+
        drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
 
        fetchdecode_source_bpp(fd, bpp);
@@ -249,8 +276,58 @@ static void dpu_plane_atomic_update(struct drm_plane *plane,
                                        DPU_PLANE_SRC_TO_DISP_STREAM1 :
                                        DPU_PLANE_SRC_TO_DISP_STREAM0);
 
+       /* vscaler comes first */
+       if (need_vscaler) {
+               vs_id = vscaler_get_block_id(vs);
+               if (vs_id == ID_NONE)
+                       return;
+
+               vscaler_pixengcfg_dynamic_src_sel(vs,
+                                       (vs_src_sel_t)(dpstate->source));
+               vscaler_pixengcfg_clken(vs, CLKEN__AUTOMATIC);
+               vscaler_setup1(vs, src_h, state->crtc_h);
+               vscaler_output_size(vs, state->crtc_h);
+               vscaler_field_mode(vs, SCALER_INPUT);
+               vscaler_filter_mode(vs, SCALER_LINEAR);
+               vscaler_scale_mode(vs, SCLAER_UPSCALE);
+               vscaler_mode(vs, SCALER_ACTIVE);
+               vscaler_set_stream_id(vs, dplane->stream_id ?
+                                       DPU_PLANE_SRC_TO_DISP_STREAM1 :
+                                       DPU_PLANE_SRC_TO_DISP_STREAM0);
+
+               lb_src = (lb_sec_sel_t)vs_id;
+
+               dev_dbg(dev, "[PLANE:%d:%s] vscaler-0x%02x\n",
+                                       plane->base.id, plane->name, vs_id);
+       }
+
+       /* and then, hscaler */
+       if (need_hscaler) {
+               hs_id = hscaler_get_block_id(hs);
+               if (hs_id == ID_NONE)
+                       return;
+
+               hscaler_pixengcfg_dynamic_src_sel(hs, need_vscaler ?
+                                       (hs_src_sel_t)(vs_id) :
+                                       (hs_src_sel_t)(dpstate->source));
+               hscaler_pixengcfg_clken(hs, CLKEN__AUTOMATIC);
+               hscaler_setup1(hs, src_w, state->crtc_w);
+               hscaler_output_size(hs, state->crtc_w);
+               hscaler_filter_mode(hs, SCALER_LINEAR);
+               hscaler_scale_mode(hs, SCLAER_UPSCALE);
+               hscaler_mode(hs, SCALER_ACTIVE);
+               hscaler_set_stream_id(hs, dplane->stream_id ?
+                                       DPU_PLANE_SRC_TO_DISP_STREAM1 :
+                                       DPU_PLANE_SRC_TO_DISP_STREAM0);
+
+               lb_src = (lb_sec_sel_t)hs_id;
+
+               dev_dbg(dev, "[PLANE:%d:%s] hscaler-0x%02x\n",
+                                       plane->base.id, plane->name, hs_id);
+       }
+
        layerblend_pixengcfg_dynamic_prim_sel(lb, dpstate->stage);
-       layerblend_pixengcfg_dynamic_sec_sel(lb, dpstate->source);
+       layerblend_pixengcfg_dynamic_sec_sel(lb, lb_src);
        layerblend_control(lb, LB_BLEND);
        layerblend_pixengcfg_clken(lb, CLKEN__AUTOMATIC);
        layerblend_position(lb, dpstate->layer_x, dpstate->layer_y);