MLK-21509-4 drm/imx: dpu: crc: Add user-configurable CRC region(ROI) support
authorLiu Ying <victor.liu@nxp.com>
Sat, 13 Apr 2019 07:59:39 +0000 (15:59 +0800)
committerLiu Ying <victor.liu@nxp.com>
Mon, 29 Apr 2019 07:35:54 +0000 (15:35 +0800)
This patch adds user-configurable CRC region support.
The users may choose a region of interest(ROI) as the CRC source
(i.e., the CRC evaluation window) via the debugfs control node.
The ROI cannot exceed the display region as indicated by
drm_crtc_state->adjusted_mode.  The users may write a string in
the fashion of "roi:x1,y1,x2,y2" to the node to specify the ROI
within the display region.  The inclusive position at (x1, y1)
indicates the upper left of the region, while the exclusive
position at (x2, y2) indicates the lower right of the region.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
drivers/gpu/drm/imx/dpu/dpu-crc.c
drivers/gpu/drm/imx/dpu/dpu-crc.h
drivers/gpu/drm/imx/dpu/dpu-crtc.c
drivers/gpu/drm/imx/dpu/dpu-crtc.h
include/video/dpu.h

index 4fe0af5..042c4f5 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/types.h>
 #include <video/dpu.h>
+#include "dpu-crc.h"
 #include "dpu-crtc.h"
 
+static inline void get_left(struct drm_rect *r, struct drm_display_mode *m)
+{
+       r->x1 = 0;
+       r->y1 = 0;
+       r->x2 = m->hdisplay >> 1;
+       r->y2 = m->vdisplay;
+}
+
+static inline void get_right(struct drm_rect *r, struct drm_display_mode *m)
+{
+       r->x1 = m->hdisplay >> 1;
+       r->y1 = 0;
+       r->x2 = m->hdisplay;
+       r->y2 = m->vdisplay;
+}
+
 static void
 dpu_enable_signature_roi(struct dpu_signature *sig, struct drm_rect *roi)
 {
@@ -37,6 +54,71 @@ static void dpu_disable_signature(struct dpu_signature *sig)
        signature_eval_win(sig, 0, false);
 }
 
+/*
+ * Supported modes and source names:
+ * 1) auto mode:
+ *    "auto" should be selected as the source name.
+ *    The evaluation window is the same to the display region as
+ *    indicated by drm_crtc_state->adjusted_mode.
+ *
+ * 2) region of interest(ROI) mode:
+ *    "roi:x1,y1,x2,y2" should be selected as the source name.
+ *    The region of interest is defined by the inclusive upper left
+ *    position at (x1, y1) and the exclusive lower right position
+ *    at (x2, y2), see struct drm_rect for the same idea.
+ *    The evaluation window is the region of interest.
+ */
+static int
+dpu_crc_parse_source(const char *source_name, enum dpu_crc_source *s,
+                    struct drm_rect *roi)
+{
+       const char roi_prefix[] = "roi:";
+
+       if (!source_name) {
+               *s = DPU_CRC_SRC_NONE;
+       } else if (!strcmp(source_name, "auto")) {
+               *s = DPU_CRC_SRC_FRAMEGEN;
+       } else if (strstarts(source_name, roi_prefix)) {
+               char *options, *opt;
+               int len = strlen(roi_prefix);
+               int params[4];
+               int i = 0, ret;
+
+               options = kstrdup(source_name + len, GFP_KERNEL);
+
+               while ((opt = strsep(&options, ",")) != NULL) {
+                       if (i > 4)
+                               return -EINVAL;
+
+                       ret = kstrtouint(opt, 10, &params[i]);
+                       if (ret < 0)
+                               return ret;
+
+                       if (params[i] < 0)
+                               return -EINVAL;
+
+                       i++;
+               }
+
+               if (i != 4)
+                       return -EINVAL;
+
+               roi->x1 = params[0];
+               roi->y1 = params[1];
+               roi->x2 = params[2];
+               roi->y2 = params[3];
+
+               if (!drm_rect_visible(roi))
+                       return -EINVAL;
+
+               *s = DPU_CRC_SRC_FRAMEGEN_ROI;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 int dpu_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name,
                            size_t *values_cnt)
 {
@@ -44,14 +126,11 @@ int dpu_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name,
        struct drm_modeset_acquire_ctx ctx;
        struct drm_crtc_state *crtc_state;
        struct drm_atomic_state *state;
+       struct drm_rect roi = {0, 0, 0, 0};
        enum dpu_crc_source source;
        int ret;
 
-       if (!source_name) {
-               source = DPU_CRC_SRC_NONE;
-       } else if (!strcmp(source_name, "auto")) {
-               source = DPU_CRC_SRC_FRAMEGEN;
-       } else {
+       if (dpu_crc_parse_source(source_name, &source, &roi) < 0) {
                dev_dbg(dpu_crtc->dev, "unknown source %s\n", source_name);
                return -EINVAL;
        }
@@ -76,6 +155,7 @@ retry:
                imx_crtc_state = to_imx_crtc_state(crtc_state);
                dcstate = to_dpu_crtc_state(imx_crtc_state);
                dcstate->crc.source = source;
+               dpu_copy_roi(&roi, &dcstate->crc.roi);
 
                dpu_crtc->use_dual_crc = dcstate->use_pc;
                *values_cnt = dpu_crtc->use_dual_crc ? 6 : 3;
@@ -120,9 +200,12 @@ irqreturn_t dpu_crc_valid_irq_threaded_handler(int irq, void *dev_id)
                return IRQ_HANDLED;
        }
 
-       crcs[2] = dpu_crtc->crc_red;
-       crcs[1] = dpu_crtc->crc_green;
-       crcs[0] = dpu_crtc->crc_blue;
+       if (!dual_crc ||
+           (dual_crc && dpu_crtc->dual_crc_flag != DPU_DUAL_CRC_FLAG_RIGHT)) {
+               crcs[2] = dpu_crtc->crc_red;
+               crcs[1] = dpu_crtc->crc_green;
+               crcs[0] = dpu_crtc->crc_blue;
+       }
 
        if (dual_crc && dpu_crtc->stream_id == 0) {
                ret = wait_for_completion_timeout(&dpu_crtc->aux_crc_done,
@@ -131,9 +214,11 @@ irqreturn_t dpu_crc_valid_irq_threaded_handler(int irq, void *dev_id)
                        dev_warn(dpu_crtc->dev,
                                "wait for auxiliary CRC done timeout\n");
 
-               crcs[5] = aux_dpu_crtc->crc_red;
-               crcs[4] = aux_dpu_crtc->crc_green;
-               crcs[3] = aux_dpu_crtc->crc_blue;
+               if (dpu_crtc->dual_crc_flag != DPU_DUAL_CRC_FLAG_LEFT) {
+                       crcs[5] = aux_dpu_crtc->crc_red;
+                       crcs[4] = aux_dpu_crtc->crc_green;
+                       crcs[3] = aux_dpu_crtc->crc_blue;
+               }
        }
 
        drm_crtc_add_crc_entry(&dpu_crtc->base, false, 0, crcs);
@@ -142,7 +227,8 @@ irqreturn_t dpu_crc_valid_irq_threaded_handler(int irq, void *dev_id)
 }
 
 void dpu_crtc_enable_crc_source(struct drm_crtc *crtc,
-                               enum dpu_crc_source source)
+                               enum dpu_crc_source source,
+                               struct drm_rect *roi)
 {
        struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
        struct dpu_crtc *aux_dpu_crtc = dpu_crtc_get_aux_dpu_crtc(dpu_crtc);
@@ -150,8 +236,11 @@ void dpu_crtc_enable_crc_source(struct drm_crtc *crtc,
        struct dpu_crtc_state *dcstate = to_dpu_crtc_state(imx_crtc_state);
        struct drm_display_mode *mode = &crtc->state->adjusted_mode;
        struct completion *shdld_done;
-       struct drm_rect roi;
+       struct drm_rect left, right;
+       struct drm_rect r, aux_r, clip;
        bool dual_crc = dpu_crtc->use_dual_crc;
+       bool use_left, use_right;
+       int half_hdisplay;
        unsigned long ret;
 
        if (source == DPU_CRC_SRC_NONE)
@@ -163,23 +252,57 @@ void dpu_crtc_enable_crc_source(struct drm_crtc *crtc,
        if (dpu_crtc->crc_is_enabled)
                return;
 
-       /* region of interest */
-       roi.x1 = 0;
-       roi.y1 = 0;
-       roi.x2 = dual_crc ? mode->hdisplay >> 1 : mode->hdisplay;
-       roi.y2 = mode->vdisplay;
+       if (dual_crc) {
+               half_hdisplay = mode->hdisplay >> 1;
+
+               get_left(&left, mode);
+               get_right(&right, mode);
+
+               dpu_copy_roi(&left, &clip);
+               if (drm_rect_intersect(&clip, roi)) {
+                       dpu_copy_roi(&clip, &r);
+                       use_left = true;
+               } else {
+                       dpu_copy_roi(&left, &r);
+                       use_left = false;
+               }
+
+               if (drm_rect_intersect(&right, roi)) {
+                       right.x1 -= half_hdisplay;
+                       right.x2 -= half_hdisplay;
+                       dpu_copy_roi(&right, &aux_r);
+                       use_right = true;
+               } else {
+                       dpu_copy_roi(&left, &aux_r);
+                       use_right = false;
+               }
+
+               if (use_left && !use_right) {
+                       dpu_crtc->dual_crc_flag = DPU_DUAL_CRC_FLAG_LEFT;
+               } else if (!use_left && use_right) {
+                       dpu_crtc->dual_crc_flag = DPU_DUAL_CRC_FLAG_RIGHT;
+               } else if (use_left && use_right) {
+                       dpu_crtc->dual_crc_flag = DPU_DUAL_CRC_FLAG_DUAL;
+               } else {
+                       dpu_crtc->dual_crc_flag = DPU_DUAL_CRC_FLAG_ERR_NONE;
+                       dev_err(dpu_crtc->dev, "error flag for dual CRC\n");
+                       return;
+               }
+       } else {
+               dpu_copy_roi(roi, &r);
+       }
 
        enable_irq(dpu_crtc->crc_valid_irq);
        enable_irq(dpu_crtc->crc_shdld_irq);
        disengcfg_sig_select(dpu_crtc->dec, DEC_SIG_SEL_FRAMEGEN);
-       dpu_enable_signature_roi(dpu_crtc->sig, &roi);
+       dpu_enable_signature_roi(dpu_crtc->sig, &r);
 
        if (dual_crc) {
                aux_dpu_crtc->use_dual_crc = dual_crc;
                enable_irq(aux_dpu_crtc->crc_valid_irq);
                enable_irq(aux_dpu_crtc->crc_shdld_irq);
                disengcfg_sig_select(dpu_crtc->aux_dec, DEC_SIG_SEL_FRAMEGEN);
-               dpu_enable_signature_roi(dpu_crtc->aux_sig, &roi);
+               dpu_enable_signature_roi(dpu_crtc->aux_sig, &aux_r);
        }
 
        shdld_done = &dpu_crtc->crc_shdld_done;
@@ -201,7 +324,8 @@ void dpu_crtc_enable_crc_source(struct drm_crtc *crtc,
 
        dpu_crtc->crc_is_enabled = true;
 
-       dev_dbg(dpu_crtc->dev, "enable CRC source\n");
+       dev_dbg(dpu_crtc->dev, "enable CRC source %d, ROI:" DRM_RECT_FMT "\n",
+               source, DRM_RECT_ARG(roi));
 }
 
 void dpu_crtc_disable_crc_source(struct drm_crtc *crtc, bool dual_crc)
index 6eda099..55253ed 100644 (file)
 
 #include "dpu-crtc.h"
 
+enum {
+       DPU_DUAL_CRC_FLAG_DUAL,
+       DPU_DUAL_CRC_FLAG_LEFT,
+       DPU_DUAL_CRC_FLAG_RIGHT,
+       DPU_DUAL_CRC_FLAG_ERR_NONE,
+};
+
 static inline bool to_enable_dpu_crc(struct dpu_crtc_state *new_dcstate,
                                     struct dpu_crtc_state *old_dcstate)
 {
@@ -31,12 +38,21 @@ static inline bool to_disable_dpu_crc(struct dpu_crtc_state *new_dcstate,
               new_dcstate->crc.source == DPU_CRC_SRC_NONE;
 }
 
+static inline void dpu_copy_roi(struct drm_rect *from, struct drm_rect *to)
+{
+       to->x1 = from->x1;
+       to->y1 = from->y1;
+       to->x2 = from->x2;
+       to->y2 = from->y2;
+}
+
 #ifdef CONFIG_DEBUG_FS
 int dpu_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name,
                            size_t *values_cnt);
 irqreturn_t dpu_crc_valid_irq_threaded_handler(int irq, void *dev_id);
 void dpu_crtc_enable_crc_source(struct drm_crtc *crtc,
-                               enum dpu_crc_source source);
+                               enum dpu_crc_source source,
+                               struct drm_rect *roi);
 void dpu_crtc_disable_crc_source(struct drm_crtc *crtc, bool dual_crc);
 #else
 #define dpu_crtc_set_crc_source        NULL
@@ -45,7 +61,8 @@ irqreturn_t dpu_crc_valid_irq_threaded_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 void dpu_crtc_enable_crc_source(struct drm_crtc *crtc,
-                               enum dpu_crc_source source)
+                               enum dpu_crc_source source,
+                               struct drm_rect *roi)
 {
 }
 void dpu_crtc_disable_crc_source(struct drm_crtc *crtc, bool dual_crc)
index 4cea669..83e8eb9 100644 (file)
@@ -257,7 +257,8 @@ static void dpu_crtc_atomic_enable(struct drm_crtc *crtc,
        }
 
        if (dcstate->crc.source != DPU_CRC_SRC_NONE)
-               dpu_crtc_enable_crc_source(crtc, dcstate->crc.source);
+               dpu_crtc_enable_crc_source(crtc,
+                               dcstate->crc.source, &dcstate->crc.roi);
 }
 
 static void dpu_crtc_atomic_disable(struct drm_crtc *crtc,
@@ -322,6 +323,10 @@ static void dpu_drm_crtc_reset(struct drm_crtc *crtc)
        state = kzalloc(sizeof(*state), GFP_KERNEL);
        if (state) {
                state->crc.source = DPU_CRC_SRC_NONE;
+               state->crc.roi.x1 = 0;
+               state->crc.roi.y1 = 0;
+               state->crc.roi.x2 = 0;
+               state->crc.roi.y2 = 0;
 
                crtc->state = &state->imx_crtc_state.base;
                crtc->state->crtc = crtc;
@@ -358,6 +363,7 @@ dpu_drm_crtc_duplicate_state(struct drm_crtc *crtc)
        state = to_dpu_crtc_state(imx_crtc_state);
        copy->use_pc = state->use_pc;
        copy->crc.source = state->crc.source;
+       dpu_copy_roi(&state->crc.roi, &copy->crc.roi);
 
        return &copy->imx_crtc_state.base;
 }
@@ -496,6 +502,25 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
            to_enable_dpu_crc(dcstate, old_dcstate))
                return -EINVAL;
 
+       if (crtc_state->enable && dcstate->crc.source == DPU_CRC_SRC_FRAMEGEN) {
+               dcstate->crc.roi.x1 = 0;
+               dcstate->crc.roi.y1 = 0;
+               dcstate->crc.roi.x2 = mode->hdisplay;
+               dcstate->crc.roi.y2 = mode->vdisplay;
+       }
+
+       if (crtc_state->enable && dcstate->crc.source != DPU_CRC_SRC_NONE) {
+               if (dcstate->crc.roi.x1 < 0 || dcstate->crc.roi.y1 < 0)
+                       return -EINVAL;
+
+               if (dcstate->crc.roi.x2 > mode->hdisplay ||
+                   dcstate->crc.roi.y2 > mode->vdisplay)
+                       return -EINVAL;
+
+               if (!drm_rect_visible(&dcstate->crc.roi))
+                       return -EINVAL;
+       }
+
        /*
         * cache the plane states so that the planes can be disabled in
         * ->atomic_begin.
@@ -797,7 +822,8 @@ again:
        }
 
        if (!need_modeset && to_enable_dpu_crc(dcstate, old_dcstate))
-               dpu_crtc_enable_crc_source(crtc, dcstate->crc.source);
+               dpu_crtc_enable_crc_source(crtc,
+                               dcstate->crc.source, &dcstate->crc.roi);
 }
 
 static void dpu_crtc_mode_set_nofb(struct drm_crtc *crtc)
index a818631..f82590f 100644 (file)
@@ -76,10 +76,12 @@ struct dpu_crtc {
        u32                     crc_red;
        u32                     crc_green;
        u32                     crc_blue;
+       u32                     dual_crc_flag;
 };
 
 struct dpu_crc {
        enum dpu_crc_source     source;
+       struct drm_rect         roi;
 };
 
 struct dpu_crtc_state {
index d81bf2b..569b553 100644 (file)
@@ -449,6 +449,7 @@ enum {
 enum dpu_crc_source {
        DPU_CRC_SRC_NONE,
        DPU_CRC_SRC_FRAMEGEN,
+       DPU_CRC_SRC_FRAMEGEN_ROI,
 };
 
 struct dpu_fetchunit;