1
0
Fork 0

MLK-21509-3 drm/imx: dpu: kms: Add auto control mode debugfs CRC support

This patch adds auto control mode debugfs CRC support based on
DPU Signature units.  The evaluation window is the same to the
display region as indicated by drm_crtc_state->adjusted_mode.
The CRC data count is three when single DPU display stream is used,
while six when the pixel combiner(dual DPU display streams) is used.
Each data shows the CRC value for the 8 most significant bits of
red/green/blue channel of the entire evaluation window, which reflects
how DPU Signature unit reports CRC values.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
Reviewed-by: Robby Cai <robby.cai@nxp.com>
5.4-rM2-2.2.x-imx-squashed
Liu Ying 2019-04-11 19:45:11 +08:00
parent f9d0e0c46f
commit ba2b101d1f
5 changed files with 423 additions and 1 deletions

View File

@ -1,6 +1,7 @@
ccflags-y += -I $(srctree)/$(src)/../
imx-dpu-crtc-objs := dpu-crtc.o dpu-kms.o dpu-plane.o
imx-dpu-crtc-$(CONFIG_DEBUG_FS) := dpu-crc.o
obj-$(CONFIG_DRM_IMX_DPU) += imx-dpu-crtc.o
imx-dpu-render-objs := dpu-blit.o

View File

@ -0,0 +1,269 @@
/*
* Copyright 2019,2020 NXP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* 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_atomic.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_plane.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <video/dpu.h>
#include "dpu-crtc.h"
static void
dpu_enable_signature_roi(struct dpu_signature *sig, struct drm_rect *roi)
{
signature_continuous_mode(sig, true);
signature_win(sig, 0, roi->x1, roi->y1, roi->x2, roi->y2);
signature_eval_win(sig, 0, true);
signature_shdldreq(sig, 0x1);
}
static void dpu_disable_signature(struct dpu_signature *sig)
{
signature_continuous_mode(sig, false);
signature_wait_for_idle(sig);
signature_eval_win(sig, 0, false);
}
static int dpu_crc_parse_source(const char *source_name, enum dpu_crc_source *s)
{
if (!source_name) {
*s = DPU_CRC_SRC_NONE;
} else if (!strcmp(source_name, "auto")) {
*s = DPU_CRC_SRC_FRAMEGEN;
} else {
return -EINVAL;
}
return 0;
}
int dpu_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
size_t *values_cnt)
{
struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
struct imx_crtc_state *imx_crtc_state;
struct dpu_crtc_state *dcstate;
enum dpu_crc_source source;
int ret;
if (dpu_crc_parse_source(source_name, &source) < 0) {
dev_dbg(dpu_crtc->dev, "unknown source %s\n", source_name);
return -EINVAL;
}
ret = drm_modeset_lock_single_interruptible(&crtc->mutex);
if (ret)
return ret;
imx_crtc_state = to_imx_crtc_state(crtc->state);
dcstate = to_dpu_crtc_state(imx_crtc_state);
*values_cnt = dcstate->use_pc ? 6 : 3;
drm_modeset_unlock(&crtc->mutex);
return ret;
}
int dpu_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name)
{
struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
struct drm_modeset_acquire_ctx ctx;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
enum dpu_crc_source source;
int ret;
if (dpu_crc_parse_source(source_name, &source) < 0) {
dev_dbg(dpu_crtc->dev, "unknown source %s\n", source_name);
return -EINVAL;
}
/* Perform an atomic commit to set the CRC source. */
drm_modeset_acquire_init(&ctx, 0);
state = drm_atomic_state_alloc(crtc->dev);
if (!state) {
ret = -ENOMEM;
goto unlock;
}
state->acquire_ctx = &ctx;
retry:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (!IS_ERR(crtc_state)) {
struct imx_crtc_state *imx_crtc_state;
struct dpu_crtc_state *dcstate;
imx_crtc_state = to_imx_crtc_state(crtc_state);
dcstate = to_dpu_crtc_state(imx_crtc_state);
if ((dcstate->use_pc && crtc->crc.values_cnt != 6) ||
(!dcstate->use_pc && crtc->crc.values_cnt != 3)) {
ret = -EINVAL;
goto put;
}
dcstate->crc.source = source;
dpu_crtc->use_dual_crc = dcstate->use_pc;
ret = drm_atomic_commit(state);
} else {
ret = PTR_ERR(crtc_state);
}
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
drm_modeset_backoff(&ctx);
goto retry;
}
put:
drm_atomic_state_put(state);
unlock:
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
return ret;
}
irqreturn_t dpu_crc_valid_irq_threaded_handler(int irq, void *dev_id)
{
struct dpu_crtc *dpu_crtc = dev_id;
struct dpu_signature *sig = dpu_crtc->sig;
struct dpu_crtc *aux_dpu_crtc = dpu_crtc_get_aux_dpu_crtc(dpu_crtc);
bool dual_crc = dpu_crtc->use_dual_crc;
unsigned long ret;
uint32_t crcs[6] = {0, 0, 0, 0, 0, 0};
dev_dbg(dpu_crtc->dev, "CRC valid irq threaded handler\n");
signature_crc_value(sig, 0, &dpu_crtc->crc_red,
&dpu_crtc->crc_green,
&dpu_crtc->crc_blue);
if (dual_crc && dpu_crtc->stream_id == 1) {
complete(&aux_dpu_crtc->aux_crc_done);
return IRQ_HANDLED;
}
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,
HZ / 20);
if (ret == 0)
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;
}
drm_crtc_add_crc_entry(&dpu_crtc->base, false, 0, crcs);
return IRQ_HANDLED;
}
void dpu_crtc_enable_crc_source(struct drm_crtc *crtc,
enum dpu_crc_source source)
{
struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
struct dpu_crtc *aux_dpu_crtc = dpu_crtc_get_aux_dpu_crtc(dpu_crtc);
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc->state);
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;
bool dual_crc = dpu_crtc->use_dual_crc;
unsigned long ret;
if (source == DPU_CRC_SRC_NONE)
return;
if (dual_crc != dcstate->use_pc)
return;
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;
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);
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);
}
shdld_done = &dpu_crtc->crc_shdld_done;
ret = wait_for_completion_timeout(shdld_done, HZ);
if (ret == 0)
dev_warn(dpu_crtc->dev, "wait for CRC shdld done timeout\n");
if (dual_crc) {
shdld_done = &aux_dpu_crtc->crc_shdld_done;
ret = wait_for_completion_timeout(shdld_done, HZ);
if (ret == 0)
dev_warn(dpu_crtc->dev,
"wait for auxiliary CRC shdld done timeout\n");
}
disable_irq(dpu_crtc->crc_shdld_irq);
if (dual_crc)
disable_irq(aux_dpu_crtc->crc_shdld_irq);
dpu_crtc->crc_is_enabled = true;
dev_dbg(dpu_crtc->dev, "enable CRC source\n");
}
void dpu_crtc_disable_crc_source(struct drm_crtc *crtc, bool dual_crc)
{
struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
struct dpu_crtc *aux_dpu_crtc = dpu_crtc_get_aux_dpu_crtc(dpu_crtc);
if (!dpu_crtc->crc_is_enabled)
return;
dpu_disable_signature(dpu_crtc->sig);
if (dual_crc)
dpu_disable_signature(dpu_crtc->aux_sig);
disable_irq(dpu_crtc->crc_valid_irq);
if (dual_crc) {
disable_irq(aux_dpu_crtc->crc_valid_irq);
reinit_completion(&dpu_crtc->aux_crc_done);
}
dpu_crtc->crc_is_enabled = false;
dev_dbg(dpu_crtc->dev, "disable CRC source\n");
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019,2020 NXP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* 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.
*/
#ifndef _DPU_CRC_H_
#define _DPU_CRC_H_
#include "dpu-crtc.h"
static inline bool to_enable_dpu_crc(struct dpu_crtc_state *new_dcstate,
struct dpu_crtc_state *old_dcstate)
{
return old_dcstate->crc.source == DPU_CRC_SRC_NONE &&
new_dcstate->crc.source != DPU_CRC_SRC_NONE;
}
static inline bool to_disable_dpu_crc(struct dpu_crtc_state *new_dcstate,
struct dpu_crtc_state *old_dcstate)
{
return old_dcstate->crc.source != DPU_CRC_SRC_NONE &&
new_dcstate->crc.source == DPU_CRC_SRC_NONE;
}
#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);
void dpu_crtc_disable_crc_source(struct drm_crtc *crtc, bool dual_crc);
#else
#define dpu_crtc_verify_crc_source NULL
#define dpu_crtc_set_crc_source NULL
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)
{
}
void dpu_crtc_disable_crc_source(struct drm_crtc *crtc, bool dual_crc)
{
}
#endif
#endif

View File

@ -28,6 +28,7 @@
#include <video/dpu.h>
#include <video/imx8-pc.h>
#include <video/imx8-prefetch.h>
#include "dpu-crc.h"
#include "dpu-crtc.h"
#include "dpu-kms.h"
#include "dpu-plane.h"
@ -279,6 +280,9 @@ static void dpu_crtc_atomic_enable(struct drm_crtc *crtc,
crtc->base.id, crtc->name, __func__);
}
}
if (dcstate->crc.source != DPU_CRC_SRC_NONE)
dpu_crtc_enable_crc_source(crtc, dcstate->crc.source);
}
static void dpu_crtc_atomic_disable(struct drm_crtc *crtc,
@ -296,6 +300,9 @@ static void dpu_crtc_atomic_disable(struct drm_crtc *crtc,
unsigned long flags;
int i;
if (dcstate->crc.source != DPU_CRC_SRC_NONE)
dpu_crtc_disable_crc_source(crtc, dcstate->use_pc);
if (dcstate->use_pc) {
tcon_disable_pc(dpu_crtc->tcon);
@ -430,6 +437,8 @@ 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;
crtc->state = &state->imx_crtc_state.base;
crtc->state->crtc = crtc;
@ -464,6 +473,7 @@ dpu_drm_crtc_duplicate_state(struct drm_crtc *crtc)
imx_crtc_state = to_imx_crtc_state(crtc->state);
state = to_dpu_crtc_state(imx_crtc_state);
copy->use_pc = state->use_pc;
copy->crc.source = state->crc.source;
return &copy->imx_crtc_state.base;
}
@ -507,6 +517,8 @@ static const struct drm_crtc_funcs dpu_crtc_funcs = {
.atomic_destroy_state = dpu_drm_crtc_destroy_state,
.enable_vblank = dpu_enable_vblank,
.disable_vblank = dpu_disable_vblank,
.set_crc_source = dpu_crtc_set_crc_source,
.verify_crc_source = dpu_crtc_verify_crc_source,
};
static irqreturn_t dpu_vbl_irq_handler(int irq, void *dev_id)
@ -555,6 +567,15 @@ static irqreturn_t dpu_dec_shdld_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static irqreturn_t dpu_crc_shdld_irq_handler(int irq, void *dev_id)
{
struct dpu_crtc *dpu_crtc = dev_id;
complete(&dpu_crtc->crc_shdld_done);
return IRQ_HANDLED;
}
static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *crtc_state)
{
@ -565,6 +586,10 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
struct dpu_plane_state *dpstate;
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
struct dpu_crtc_state *dcstate = to_dpu_crtc_state(imx_crtc_state);
struct imx_crtc_state *old_imx_crtc_state =
to_imx_crtc_state(crtc->state);
struct dpu_crtc_state *old_dcstate =
to_dpu_crtc_state(old_imx_crtc_state);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
struct videomode vm;
unsigned long encoder_type = DRM_MODE_ENCODER_NONE;
@ -596,6 +621,11 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
}
}
/* disallow to enable CRC when CRTC keeps at inactive status */
if (!crtc->state->active && !crtc_state->enable &&
to_enable_dpu_crc(dcstate, old_dcstate))
return -EINVAL;
/*
* cache the plane states so that the planes can be disabled in
* ->atomic_begin.
@ -743,6 +773,9 @@ static void dpu_crtc_atomic_flush(struct drm_crtc *crtc,
if (!crtc->state->active && !old_crtc_state->active)
return;
if (!need_modeset && to_disable_dpu_crc(dcstate, old_dcstate))
dpu_crtc_disable_crc_source(crtc, old_dcstate->use_pc);
/*
* Scan over old plane fetchunits to determine if we
* need to wait for FrameGen frame counter moving in
@ -923,6 +956,9 @@ again2:
extdst_pixengcfg_sync_trigger(ed);
}
}
if (!need_modeset && to_enable_dpu_crc(dcstate, old_dcstate))
dpu_crtc_enable_crc_source(crtc, dcstate->crc.source);
}
static void dpu_crtc_mode_set_nofb(struct drm_crtc *crtc)
@ -1050,6 +1086,8 @@ static void dpu_crtc_put_resources(struct dpu_crtc *dpu_crtc)
dpu_ed_put(dpu_crtc->ed);
if (!IS_ERR_OR_NULL(dpu_crtc->fg))
dpu_fg_put(dpu_crtc->fg);
if (!IS_ERR_OR_NULL(dpu_crtc->sig))
dpu_sig_put(dpu_crtc->sig);
if (!IS_ERR_OR_NULL(dpu_crtc->tcon))
dpu_tcon_put(dpu_crtc->tcon);
}
@ -1095,6 +1133,13 @@ static int dpu_crtc_get_resources(struct dpu_crtc *dpu_crtc)
}
dpu_crtc->aux_fg = dpu_aux_fg_peek(dpu_crtc->fg);
dpu_crtc->sig = dpu_sig_get(dpu, stream_id);
if (IS_ERR(dpu_crtc->sig)) {
ret = PTR_ERR(dpu_crtc->sig);
goto err_out;
}
dpu_crtc->aux_sig = dpu_aux_sig_peek(dpu_crtc->sig);
dpu_crtc->tcon = dpu_tcon_get(dpu, stream_id);
if (IS_ERR(dpu_crtc->tcon)) {
ret = PTR_ERR(dpu_crtc->tcon);
@ -1152,6 +1197,8 @@ static int dpu_crtc_init(struct dpu_crtc *dpu_crtc,
init_completion(&dpu_crtc->safety_shdld_done);
init_completion(&dpu_crtc->content_shdld_done);
init_completion(&dpu_crtc->dec_shdld_done);
init_completion(&dpu_crtc->crc_shdld_done);
init_completion(&dpu_crtc->aux_crc_done);
dpu_crtc->stream_id = stream_id;
dpu_crtc->crtc_grp_id = pdata->di_grp_id;
@ -1260,6 +1307,34 @@ static int dpu_crtc_init(struct dpu_crtc *dpu_crtc,
}
disable_irq(dpu_crtc->dec_shdld_irq);
dpu_crtc->crc_valid_irq = dpu_map_irq(dpu, stream_id ?
IRQ_SIG1_VALID : IRQ_SIG0_VALID);
irq_set_status_flags(dpu_crtc->crc_valid_irq, IRQ_DISABLE_UNLAZY);
ret = devm_request_threaded_irq(dev, dpu_crtc->crc_valid_irq, NULL,
dpu_crc_valid_irq_threaded_handler,
IRQF_ONESHOT, "imx_drm", dpu_crtc);
if (ret < 0) {
dev_err(dev,
"CRC valid irq request failed with %d.\n",
ret);
goto err_put_resources;
}
disable_irq(dpu_crtc->crc_valid_irq);
dpu_crtc->crc_shdld_irq = dpu_map_irq(dpu, stream_id ?
IRQ_SIG1_SHDLOAD : IRQ_SIG0_SHDLOAD);
irq_set_status_flags(dpu_crtc->crc_shdld_irq, IRQ_DISABLE_UNLAZY);
ret = devm_request_irq(dev, dpu_crtc->crc_shdld_irq,
dpu_crc_shdld_irq_handler, 0, "imx_drm",
dpu_crtc);
if (ret < 0) {
dev_err(dev,
"CRC shadow load irq request failed with %d.\n",
ret);
goto err_put_resources;
}
disable_irq(dpu_crtc->crc_shdld_irq);
return 0;
err_put_resources:

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 NXP
* Copyright 2017-2020 NXP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -29,6 +29,7 @@ struct dpu_crtc {
struct dpu_disengcfg *dec;
struct dpu_extdst *ed;
struct dpu_framegen *fg;
struct dpu_signature *sig;
struct dpu_tcon *tcon;
struct dpu_store *st;
struct dpu_constframe *aux_pa_cf;
@ -36,6 +37,7 @@ struct dpu_crtc {
struct dpu_disengcfg *aux_dec;
struct dpu_extdst *aux_ed;
struct dpu_framegen *aux_fg;
struct dpu_signature *aux_sig;
struct dpu_tcon *aux_tcon;
/* master */
struct dpu_constframe *m_pa_cf;
@ -62,19 +64,34 @@ struct dpu_crtc {
int safety_shdld_irq;
int content_shdld_irq;
int dec_shdld_irq;
int crc_valid_irq;
int crc_shdld_irq;
bool aux_is_master;
bool use_dual_crc;
bool crc_is_enabled;
struct completion safety_shdld_done;
struct completion content_shdld_done;
struct completion dec_shdld_done;
struct completion crc_shdld_done;
struct completion aux_crc_done;
struct drm_pending_vblank_event *event;
u32 crc_red;
u32 crc_green;
u32 crc_blue;
};
struct dpu_crc {
enum dpu_crc_source source;
};
struct dpu_crtc_state {
struct imx_crtc_state imx_crtc_state;
struct dpu_plane_state **dpu_plane_states;
struct dpu_crc crc;
bool use_pc;
};
@ -91,4 +108,6 @@ static inline struct dpu_crtc *to_dpu_crtc(struct drm_crtc *crtc)
struct dpu_plane_state **
crtc_state_get_dpu_plane_states(struct drm_crtc_state *state);
struct dpu_crtc *dpu_crtc_get_aux_dpu_crtc(struct dpu_crtc *dpu_crtc);
#endif