summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiu Ying <victor.liu@nxp.com>2020-07-07 15:38:52 +0800
committerLiu Ying <victor.liu@nxp.com>2020-07-09 09:42:39 +0800
commitaa6fda9c4e3c3875d3c219ad79ffe688f44402bc (patch)
tree99979d8b12600a91c55b26026d5ae7ced64ff636
parentba2b101d1f2b074edaeab489ad46d78877b29b79 (diff)
MLK-21509-4 drm/imx: dpu: crc: Add user-configurable CRC region(ROI) support
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> Reviewed-by: Robby Cai <robby.cai@nxp.com>
-rw-r--r--drivers/gpu/drm/imx/dpu/dpu-crc.c154
-rw-r--r--drivers/gpu/drm/imx/dpu/dpu-crc.h21
-rw-r--r--drivers/gpu/drm/imx/dpu/dpu-crtc.c30
-rw-r--r--drivers/gpu/drm/imx/dpu/dpu-crtc.h2
-rw-r--r--include/video/dpu.h1
5 files changed, 185 insertions, 23 deletions
diff --git a/drivers/gpu/drm/imx/dpu/dpu-crc.c b/drivers/gpu/drm/imx/dpu/dpu-crc.c
index ed8484861737..7ed7422c1ebb 100644
--- a/drivers/gpu/drm/imx/dpu/dpu-crc.c
+++ b/drivers/gpu/drm/imx/dpu/dpu-crc.c
@@ -19,8 +19,25 @@
#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,12 +54,64 @@ static void dpu_disable_signature(struct dpu_signature *sig)
signature_eval_win(sig, 0, false);
}
-static int dpu_crc_parse_source(const char *source_name, enum dpu_crc_source *s)
+/*
+ * 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)
{
+ static 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;
}
@@ -56,10 +125,11 @@ int dpu_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
struct imx_crtc_state *imx_crtc_state;
struct dpu_crtc_state *dcstate;
+ struct drm_rect roi;
enum dpu_crc_source source;
int ret;
- if (dpu_crc_parse_source(source_name, &source) < 0) {
+ if (dpu_crc_parse_source(source_name, &source, &roi) < 0) {
dev_dbg(dpu_crtc->dev, "unknown source %s\n", source_name);
return -EINVAL;
}
@@ -83,10 +153,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 (dpu_crc_parse_source(source_name, &source) < 0) {
+ if (dpu_crc_parse_source(source_name, &source, &roi) < 0) {
dev_dbg(dpu_crtc->dev, "unknown source %s\n", source_name);
return -EINVAL;
}
@@ -118,6 +189,7 @@ retry:
}
dcstate->crc.source = source;
+ dpu_copy_roi(&roi, &dcstate->crc.roi);
dpu_crtc->use_dual_crc = dcstate->use_pc;
ret = drm_atomic_commit(state);
@@ -161,9 +233,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,
@@ -172,9 +247,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);
@@ -183,7 +260,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);
@@ -191,8 +269,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)
@@ -204,23 +285,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;
@@ -242,7 +357,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)
diff --git a/drivers/gpu/drm/imx/dpu/dpu-crc.h b/drivers/gpu/drm/imx/dpu/dpu-crc.h
index 7cf53459cbad..25c03937470e 100644
--- a/drivers/gpu/drm/imx/dpu/dpu-crc.h
+++ b/drivers/gpu/drm/imx/dpu/dpu-crc.h
@@ -17,6 +17,13 @@
#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,13 +38,22 @@ 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_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
size_t *values_cnt);
int dpu_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name);
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_verify_crc_source NULL
@@ -47,7 +63,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)
diff --git a/drivers/gpu/drm/imx/dpu/dpu-crtc.c b/drivers/gpu/drm/imx/dpu/dpu-crtc.c
index 1401a10204c9..05ec22498f01 100644
--- a/drivers/gpu/drm/imx/dpu/dpu-crtc.c
+++ b/drivers/gpu/drm/imx/dpu/dpu-crtc.c
@@ -282,7 +282,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,
@@ -438,6 +439,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;
@@ -474,6 +479,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;
}
@@ -626,6 +632,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.
@@ -958,7 +983,8 @@ again2:
}
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)
diff --git a/drivers/gpu/drm/imx/dpu/dpu-crtc.h b/drivers/gpu/drm/imx/dpu/dpu-crtc.h
index 1c693c5b0cd6..423fe1339cf2 100644
--- a/drivers/gpu/drm/imx/dpu/dpu-crtc.h
+++ b/drivers/gpu/drm/imx/dpu/dpu-crtc.h
@@ -82,10 +82,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 {
diff --git a/include/video/dpu.h b/include/video/dpu.h
index 07824f64298e..4115da97aab8 100644
--- a/include/video/dpu.h
+++ b/include/video/dpu.h
@@ -335,6 +335,7 @@ enum {
enum dpu_crc_source {
DPU_CRC_SRC_NONE,
DPU_CRC_SRC_FRAMEGEN,
+ DPU_CRC_SRC_FRAMEGEN_ROI,
};
struct dpu_fetchunit;