Re: [RFC PATCH 6/7] drm/msm/dpu: Implement tearcheck support on INTF block

From: Dmitry Baryshkov
Date: Sun Jan 01 2023 - 08:32:24 EST


On 31/12/2022 23:50, Marijn Suijten wrote:
Since DPU 5.0.0 the TEARCHECK registers and interrupts moved out of the
PINGPONG block and into the INTF. Implement the necessary callbacks in
the INTF block, and use these callbacks together with the INTF_TEAR
interrupts

Signed-off-by: Marijn Suijten <marijn.suijten@xxxxxxxxxxxxxx>

Generally I have the same question as for the patch 2. Can we have some higher level functions in the hw_pp and hw_intf files? Moreover, as I review your patch I have the feeling that it would make sense to have to two sets of encoder callbacks, one for the hw_pp tearing handling and another set for hw_intf-based one.

---
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 11 +
.../gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 10 +-
.../drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 113 +++++++---
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 206 ++++++++++++++++++
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 29 +++
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h | 2 +
6 files changed, 340 insertions(+), 31 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 9c6817b5a194..8b9070220ab2 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -673,6 +673,7 @@ static void _dpu_encoder_update_vsync_source(struct dpu_encoder_virt *dpu_enc,
struct dpu_kms *dpu_kms;
struct dpu_hw_mdp *hw_mdptop;
struct drm_encoder *drm_enc;
+ struct dpu_encoder_phys *phys_enc;
int i;
if (!dpu_enc || !disp_info) {
@@ -703,12 +704,22 @@ static void _dpu_encoder_update_vsync_source(struct dpu_encoder_virt *dpu_enc,
vsync_cfg.ppnumber[i] = dpu_enc->hw_pp[i]->idx;
vsync_cfg.pp_count = dpu_enc->num_phys_encs;
+ vsync_cfg.frame_rate = drm_mode_vrefresh(&dpu_enc->base.crtc->state->adjusted_mode);
+
if (disp_info->is_te_using_watchdog_timer)
vsync_cfg.vsync_source = DPU_VSYNC_SOURCE_WD_TIMER_0;
else
vsync_cfg.vsync_source = DPU_VSYNC0_SOURCE_GPIO;
hw_mdptop->ops.setup_vsync_source(hw_mdptop, &vsync_cfg);
+
+ for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+ phys_enc = dpu_enc->phys_encs[i];
+
+ if (phys_enc->has_intf_te && phys_enc->hw_intf->ops.vsync_sel)
+ phys_enc->hw_intf->ops.vsync_sel(phys_enc->hw_intf,
+ vsync_cfg.vsync_source);
+ }
}
}
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index f2af07d87f56..47e79401032c 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -148,10 +148,10 @@ struct dpu_encoder_phys_ops {
/**
* enum dpu_intr_idx - dpu encoder interrupt index
* @INTR_IDX_VSYNC: Vsync interrupt for video mode panel
- * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
- * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel
- * @INTR_IDX_RDPTR: Readpointer done unterrupt for cmd mode panel
- * @INTR_IDX_WB_DONE: Writeback fone interrupt for virtual connector
+ * @INTR_IDX_PINGPONG: Pingpong done interrupt for cmd mode panel
+ * @INTR_IDX_UNDERRUN: Underrun interrupt for video and cmd mode panel
+ * @INTR_IDX_RDPTR: Readpointer done interrupt for cmd mode panel
+ * @INTR_IDX_WB_DONE: Writeback done interrupt for virtual connector
*/
enum dpu_intr_idx {
INTR_IDX_VSYNC,
@@ -195,6 +195,7 @@ enum dpu_intr_idx {
* pending.
* @pending_kickoff_wq: Wait queue for blocking until kickoff completes
* @irq: IRQ indices
+ * @has_intf_te: Interface TE configuration support
*/
struct dpu_encoder_phys {
struct drm_encoder *parent;
@@ -220,6 +221,7 @@ struct dpu_encoder_phys {
atomic_t pending_kickoff_cnt;
wait_queue_head_t pending_kickoff_wq;
int irq[INTR_IDX_MAX];
+ bool has_intf_te;
};
static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
index 7e5ba52197cd..ca44a8087f01 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
@@ -100,12 +100,12 @@ static void dpu_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx)
DPU_ATRACE_END("pp_done_irq");
}
-static void dpu_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
+static void dpu_encoder_phys_cmd_te_rd_ptr_irq(void *arg, int irq_idx)
{
struct dpu_encoder_phys *phys_enc = arg;
struct dpu_encoder_phys_cmd *cmd_enc;
- if (!phys_enc->hw_pp)
+ if (!phys_enc->hw_pp || !phys_enc->hw_intf)
return;
DPU_ATRACE_BEGIN("rd_ptr_irq");
@@ -147,11 +147,19 @@ static void dpu_encoder_phys_cmd_atomic_mode_set(
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
+ if (phys_enc->has_intf_te && !phys_enc->hw_intf) {
+ DPU_ERROR("invalid intf configuration\n");
+ return;
+ }
+
phys_enc->irq[INTR_IDX_CTL_START] = phys_enc->hw_ctl->caps->intr_start;
phys_enc->irq[INTR_IDX_PINGPONG] = phys_enc->hw_pp->caps->intr_done;
- phys_enc->irq[INTR_IDX_RDPTR] = phys_enc->hw_pp->caps->intr_rdptr;
+ if (phys_enc->has_intf_te)
+ phys_enc->irq[INTR_IDX_RDPTR] = phys_enc->hw_intf->cap->intr_tear_rd_ptr;
+ else
+ phys_enc->irq[INTR_IDX_RDPTR] = phys_enc->hw_pp->caps->intr_rdptr;
phys_enc->irq[INTR_IDX_UNDERRUN] = phys_enc->hw_intf->cap->intr_underrun;
}
@@ -264,7 +272,7 @@ static int dpu_encoder_phys_cmd_control_vblank_irq(
if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
ret = dpu_core_irq_register_callback(phys_enc->dpu_kms,
phys_enc->irq[INTR_IDX_RDPTR],
- dpu_encoder_phys_cmd_pp_rd_ptr_irq,
+ dpu_encoder_phys_cmd_te_rd_ptr_irq,
phys_enc);
else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
ret = dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
@@ -336,10 +344,18 @@ static void dpu_encoder_phys_cmd_tearcheck_config(
DPU_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0);
- if (!phys_enc->hw_pp->ops.setup_tearcheck ||
- !phys_enc->hw_pp->ops.enable_tearcheck) {
- DPU_DEBUG_CMDENC(cmd_enc, "tearcheck not supported\n");
- return;
+ if (phys_enc->has_intf_te) {
+ if (!phys_enc->hw_intf->ops.setup_tearcheck ||
+ !phys_enc->hw_intf->ops.enable_tearcheck) {
+ DPU_DEBUG_CMDENC(cmd_enc, "tearcheck not supported\n");
+ return;
+ }
+ } else {
+ if (!phys_enc->hw_pp->ops.setup_tearcheck ||
+ !phys_enc->hw_pp->ops.enable_tearcheck) {
+ DPU_DEBUG_CMDENC(cmd_enc, "tearcheck not supported\n");
+ return;
+ }
}
dpu_kms = phys_enc->dpu_kms;
@@ -392,8 +408,13 @@ static void dpu_encoder_phys_cmd_tearcheck_config(
phys_enc->hw_pp->idx - PINGPONG_0, tc_cfg.sync_cfg_height,
tc_cfg.sync_threshold_start, tc_cfg.sync_threshold_continue);
- phys_enc->hw_pp->ops.setup_tearcheck(phys_enc->hw_pp, &tc_cfg);
- phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp, tc_enable);

A simple random example: setup_tearcheck is always followed with the enable_tearcheck. If we merge them, the code would be simpler.

+ if (phys_enc->has_intf_te) {
+ phys_enc->hw_intf->ops.setup_tearcheck(phys_enc->hw_intf, &tc_cfg);
+ phys_enc->hw_intf->ops.enable_tearcheck(phys_enc->hw_intf, tc_enable);
+ } else {
+ phys_enc->hw_pp->ops.setup_tearcheck(phys_enc->hw_pp, &tc_cfg);
+ phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp, tc_enable);
+ }
}
static void _dpu_encoder_phys_cmd_pingpong_config(
@@ -470,11 +491,19 @@ static void dpu_encoder_phys_cmd_enable(struct dpu_encoder_phys *phys_enc)
static void _dpu_encoder_phys_cmd_connect_te(
struct dpu_encoder_phys *phys_enc, bool enable)
{
- if (!phys_enc->hw_pp || !phys_enc->hw_pp->ops.connect_external_te)
- return;
+ if (phys_enc->has_intf_te) {
+ if (!phys_enc->hw_intf || !phys_enc->hw_intf->ops.connect_external_te)
+ return;
- trace_dpu_enc_phys_cmd_connect_te(DRMID(phys_enc->parent), enable);
- phys_enc->hw_pp->ops.connect_external_te(phys_enc->hw_pp, enable);
+ trace_dpu_enc_phys_cmd_connect_te(DRMID(phys_enc->parent), enable);
+ phys_enc->hw_intf->ops.connect_external_te(phys_enc->hw_intf, enable);
+ } else {
+ if (!phys_enc->hw_pp || !phys_enc->hw_pp->ops.connect_external_te)
+ return;
+
+ trace_dpu_enc_phys_cmd_connect_te(DRMID(phys_enc->parent), enable);
+ phys_enc->hw_pp->ops.connect_external_te(phys_enc->hw_pp, enable);
+ }
}
static void dpu_encoder_phys_cmd_prepare_idle_pc(
@@ -487,6 +516,7 @@ static int dpu_encoder_phys_cmd_get_line_count(
struct dpu_encoder_phys *phys_enc)
{
struct dpu_hw_pingpong *hw_pp;
+ struct dpu_hw_intf *hw_intf;
if (!phys_enc->hw_pp)
return -EINVAL;
@@ -494,10 +524,16 @@ static int dpu_encoder_phys_cmd_get_line_count(
if (!dpu_encoder_phys_cmd_is_master(phys_enc))
return -EINVAL;
+ if (phys_enc->has_intf_te) {
+ hw_intf = phys_enc->hw_intf;
+ if (!hw_intf->ops.get_line_count)
+ return -EINVAL;
+ return hw_intf->ops.get_line_count(hw_intf);
+ }
+
hw_pp = phys_enc->hw_pp;
if (!hw_pp->ops.get_line_count)
return -EINVAL;
-
return hw_pp->ops.get_line_count(hw_pp);
}
@@ -520,7 +556,9 @@ static void dpu_encoder_phys_cmd_disable(struct dpu_encoder_phys *phys_enc)
return;
}
- if (phys_enc->hw_pp->ops.enable_tearcheck)
+ if (phys_enc->has_intf_te && phys_enc->hw_intf->ops.enable_tearcheck)
+ phys_enc->hw_intf->ops.enable_tearcheck(phys_enc->hw_intf, false);
+ else if (phys_enc->hw_pp->ops.enable_tearcheck)
phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp, false);
if (phys_enc->hw_intf->ops.bind_pingpong_blk) {
@@ -582,10 +620,19 @@ static bool dpu_encoder_phys_cmd_is_ongoing_pptx(
{
struct dpu_hw_pp_vsync_info info;
- if (!phys_enc || !phys_enc->hw_pp->ops.get_vsync_info)
+ if (!phys_enc)
return false;
- phys_enc->hw_pp->ops.get_vsync_info(phys_enc->hw_pp, &info);
+ if (phys_enc->has_intf_te) {
+ if (!phys_enc->hw_intf->ops.get_vsync_info)
+ return false;
+ phys_enc->hw_intf->ops.get_vsync_info(phys_enc->hw_intf, &info);
+ } else {
+ if (!phys_enc->hw_pp->ops.get_vsync_info)
+ return false;
+ phys_enc->hw_pp->ops.get_vsync_info(phys_enc->hw_pp, &info);
+ }
+
if (info.wr_ptr_line_count > 0 &&
info.wr_ptr_line_count < phys_enc->cached_mode.vdisplay)
return true;
@@ -602,17 +649,23 @@ static void dpu_encoder_phys_cmd_prepare_commit(
if (!phys_enc)
return;
- if (!phys_enc->hw_pp)
- return;
if (!dpu_encoder_phys_cmd_is_master(phys_enc))
return;
- if (!phys_enc->hw_pp->ops.get_autorefresh || !phys_enc->hw_pp->ops.setup_autorefresh)
- return;
-
/* If autorefresh is already disabled, we have nothing to do */
- if (!phys_enc->hw_pp->ops.get_autorefresh(phys_enc->hw_pp, NULL))
- return;
+ if (phys_enc->has_intf_te) {
+ if (!phys_enc->hw_intf || !phys_enc->hw_intf->ops.get_autorefresh ||
+ !phys_enc->hw_intf->ops.setup_autorefresh)
+ return;
+ if (!phys_enc->hw_intf->ops.get_autorefresh(phys_enc->hw_intf, NULL))
+ return;
+ } else {
+ if (!phys_enc->hw_pp || !phys_enc->hw_pp->ops.get_autorefresh ||
+ !phys_enc->hw_pp->ops.setup_autorefresh)
+ return;
+ if (!phys_enc->hw_pp->ops.get_autorefresh(phys_enc->hw_pp, NULL))
+ return;
+ }
/*
* If autorefresh is enabled, disable it and make sure it is safe to
@@ -623,7 +676,10 @@ static void dpu_encoder_phys_cmd_prepare_commit(
* 5. Enable TE back
*/
_dpu_encoder_phys_cmd_connect_te(phys_enc, false);
- phys_enc->hw_pp->ops.setup_autorefresh(phys_enc->hw_pp, 0, false);
+ if (phys_enc->has_intf_te)
+ phys_enc->hw_intf->ops.setup_autorefresh(phys_enc->hw_intf, 0, false);
+ else
+ phys_enc->hw_pp->ops.setup_autorefresh(phys_enc->hw_pp, 0, false);
do {
udelay(DPU_ENC_MAX_POLL_TIMEOUT_US);
@@ -717,7 +773,7 @@ static int dpu_encoder_phys_cmd_wait_for_vblank(
rc = dpu_encoder_helper_wait_for_irq(phys_enc,
phys_enc->irq[INTR_IDX_RDPTR],
- dpu_encoder_phys_cmd_pp_rd_ptr_irq,
+ dpu_encoder_phys_cmd_te_rd_ptr_irq,
&wait_info);
return rc;
@@ -793,6 +849,9 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
phys_enc->irq[i] = -EINVAL;
+ phys_enc->has_intf_te = test_bit(DPU_INTF_TE,
+ &phys_enc->dpu_kms->catalog->intf[p->intf_idx - INTF_0].features);
+
atomic_set(&phys_enc->vblank_refcount, 0);
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
atomic_set(&phys_enc->pending_ctlstart_cnt, 0);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
index 7ce66bf3f4c8..42929278df8e 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
@@ -9,6 +9,8 @@
#include "dpu_hw_intf.h"
#include "dpu_kms.h"
+#include <linux/iopoll.h>
+
#define INTF_TIMING_ENGINE_EN 0x000
#define INTF_CONFIG 0x004
#define INTF_HSYNC_CTL 0x008
@@ -62,6 +64,27 @@
#define INTF_LINE_COUNT 0x0B0
#define INTF_MUX 0x25C
+#define INTF_MISR_SIGNATURE 0x184
+
+#define INTF_STATUS 0x26C
+#define INTF_AVR_CONTROL 0x270
+#define INTF_AVR_MODE 0x274
+#define INTF_AVR_TRIGGER 0x278
+#define INTF_AVR_VTOTAL 0x27C
+#define INTF_TEAR_MDP_VSYNC_SEL 0x280
+#define INTF_TEAR_TEAR_CHECK_EN 0x284
+#define INTF_TEAR_SYNC_CONFIG_VSYNC 0x288
+#define INTF_TEAR_SYNC_CONFIG_HEIGHT 0x28C
+#define INTF_TEAR_SYNC_WRCOUNT 0x290
+#define INTF_TEAR_VSYNC_INIT_VAL 0x294
+#define INTF_TEAR_INT_COUNT_VAL 0x298
+#define INTF_TEAR_SYNC_THRESH 0x29C
+#define INTF_TEAR_START_POS 0x2A0
+#define INTF_TEAR_RD_PTR_IRQ 0x2A4
+#define INTF_TEAR_WR_PTR_IRQ 0x2A8
+#define INTF_TEAR_OUT_LINE_COUNT 0x2AC
+#define INTF_TEAR_LINE_COUNT 0x2B0
+#define INTF_TEAR_AUTOREFRESH_CONFIG 0x2B4
#define INTF_CFG_ACTIVE_H_EN BIT(29)
#define INTF_CFG_ACTIVE_V_EN BIT(30)
@@ -309,6 +332,22 @@ static void dpu_hw_intf_get_status(
}
}
+static void dpu_hw_intf_v1_get_status(
+ struct dpu_hw_intf *intf,
+ struct intf_status *s)
+{
+ struct dpu_hw_blk_reg_map *c = &intf->hw;
+
+ s->is_en = DPU_REG_READ(c, INTF_STATUS) & BIT(0);
+ if (s->is_en) {
+ s->frame_count = DPU_REG_READ(c, INTF_FRAME_COUNT);
+ s->line_count = DPU_REG_READ(c, INTF_LINE_COUNT);
+ } else {
+ s->line_count = 0;
+ s->frame_count = 0;
+ }
+}
+
static u32 dpu_hw_intf_get_line_count(struct dpu_hw_intf *intf)
{
struct dpu_hw_blk_reg_map *c;
@@ -331,6 +370,161 @@ static int dpu_hw_intf_collect_misr(struct dpu_hw_intf *intf, u32 *misr_value)
return dpu_hw_collect_misr(&intf->hw, INTF_MISR_CTRL, INTF_MISR_SIGNATURE, misr_value);
}
+static int dpu_hw_intf_setup_te_config(struct dpu_hw_intf *intf,
+ struct dpu_hw_tear_check *te)
+{
+ struct dpu_hw_blk_reg_map *c;
+ int cfg;
+
+ if (!intf)
+ return -EINVAL;
+
+ c = &intf->hw;
+
+ cfg = BIT(19); /* VSYNC_COUNTER_EN */
+ if (te->hw_vsync_mode)
+ cfg |= BIT(20);
+
+ cfg |= te->vsync_count;
+
+ DPU_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_VSYNC, cfg);
+ DPU_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_HEIGHT, te->sync_cfg_height);
+ DPU_REG_WRITE(c, INTF_TEAR_VSYNC_INIT_VAL, te->vsync_init_val);
+ DPU_REG_WRITE(c, INTF_TEAR_RD_PTR_IRQ, te->rd_ptr_irq);
+ DPU_REG_WRITE(c, INTF_TEAR_START_POS, te->start_pos);
+ DPU_REG_WRITE(c, INTF_TEAR_SYNC_THRESH,
+ ((te->sync_threshold_continue << 16) |
+ te->sync_threshold_start));
+ DPU_REG_WRITE(c, INTF_TEAR_SYNC_WRCOUNT,
+ (te->start_pos + te->sync_threshold_start + 1));
+
+ return 0;
+}
+
+static void dpu_hw_intf_setup_autorefresh_config(struct dpu_hw_intf *intf,
+ u32 frame_count, bool enable)
+{
+ struct dpu_hw_blk_reg_map *c;
+ u32 refresh_cfg;
+
+ c = &intf->hw;
+ refresh_cfg = DPU_REG_READ(c, INTF_TEAR_AUTOREFRESH_CONFIG);
+ if (enable)
+ refresh_cfg = BIT(31) | frame_count;
+ else
+ refresh_cfg &= ~BIT(31);
+
+ DPU_REG_WRITE(c, INTF_TEAR_AUTOREFRESH_CONFIG, refresh_cfg);
+}
+
+/*
+ * dpu_hw_intf_get_autorefresh_config - Get autorefresh config from HW
+ * @intf: DPU intf structure
+ * @frame_count: Used to return the current frame count from hw
+ *
+ * Returns: True if autorefresh enabled, false if disabled.
+ */
+static bool dpu_hw_intf_get_autorefresh_config(struct dpu_hw_intf *intf,
+ u32 *frame_count)
+{
+ u32 val = DPU_REG_READ(&intf->hw, INTF_TEAR_AUTOREFRESH_CONFIG);
+
+ if (frame_count != NULL)
+ *frame_count = val & 0xffff;
+ return !!((val & BIT(31)) >> 31);
+}
+
+static int dpu_hw_intf_poll_timeout_wr_ptr(struct dpu_hw_intf *intf,
+ u32 timeout_us)
+{
+ struct dpu_hw_blk_reg_map *c;
+ u32 val;
+ int rc;
+
+ if (!intf)
+ return -EINVAL;
+
+ c = &intf->hw;
+ rc = readl_poll_timeout(c->blk_addr + INTF_TEAR_LINE_COUNT,
+ val, (val & 0xffff) >= 1, 10, timeout_us);
+
+ return rc;
+}
+
+static int dpu_hw_intf_enable_te(struct dpu_hw_intf *intf, bool enable)
+{
+ struct dpu_hw_blk_reg_map *c;
+
+ if (!intf)
+ return -EINVAL;
+
+ c = &intf->hw;
+ DPU_REG_WRITE(c, INTF_TEAR_TEAR_CHECK_EN, enable);
+ return 0;
+}
+
+static int dpu_hw_intf_connect_external_te(struct dpu_hw_intf *intf,
+ bool enable_external_te)
+{
+ struct dpu_hw_blk_reg_map *c = &intf->hw;
+ u32 cfg;
+ int orig;
+
+ if (!intf)
+ return -EINVAL;
+
+ c = &intf->hw;
+ cfg = DPU_REG_READ(c, INTF_TEAR_SYNC_CONFIG_VSYNC);
+ orig = (bool)(cfg & BIT(20));
+ if (enable_external_te)
+ cfg |= BIT(20);
+ else
+ cfg &= ~BIT(20);
+ DPU_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_VSYNC, cfg);
+
+ return orig;
+}
+
+static int dpu_hw_intf_get_vsync_info(struct dpu_hw_intf *intf,
+ struct dpu_hw_pp_vsync_info *info)
+{
+ struct dpu_hw_blk_reg_map *c = &intf->hw;
+ u32 val;
+
+ if (!intf || !info)
+ return -EINVAL;
+
+ c = &intf->hw;
+
+ val = DPU_REG_READ(c, INTF_TEAR_VSYNC_INIT_VAL);
+ info->rd_ptr_init_val = val & 0xffff;
+
+ val = DPU_REG_READ(c, INTF_TEAR_INT_COUNT_VAL);
+ info->rd_ptr_frame_count = (val & 0xffff0000) >> 16;
+ info->rd_ptr_line_count = val & 0xffff;
+
+ val = DPU_REG_READ(c, INTF_TEAR_LINE_COUNT);
+ info->wr_ptr_line_count = val & 0xffff;
+
+ val = DPU_REG_READ(c, INTF_FRAME_COUNT);
+ info->intf_frame_count = val;
+
+ return 0;
+}
+
+static void dpu_hw_intf_vsync_sel(struct dpu_hw_intf *intf,
+ u32 vsync_source)
+{
+ struct dpu_hw_blk_reg_map *c;
+
+ if (!intf)
+ return;
+
+ c = &intf->hw;
+
+ DPU_REG_WRITE(c, INTF_TEAR_MDP_VSYNC_SEL, (vsync_source & 0xf));
+}
+
static void _setup_intf_ops(struct dpu_hw_intf_ops *ops,
unsigned long cap)
{
@@ -343,6 +537,18 @@ static void _setup_intf_ops(struct dpu_hw_intf_ops *ops,
ops->bind_pingpong_blk = dpu_hw_intf_bind_pingpong_blk;
ops->setup_misr = dpu_hw_intf_setup_misr;
ops->collect_misr = dpu_hw_intf_collect_misr;
+
+ if (cap & BIT(DPU_INTF_TE)) {
+ ops->setup_tearcheck = dpu_hw_intf_setup_te_config;
+ ops->enable_tearcheck = dpu_hw_intf_enable_te;
+ ops->connect_external_te = dpu_hw_intf_connect_external_te;
+ ops->get_vsync_info = dpu_hw_intf_get_vsync_info;
+ ops->setup_autorefresh = dpu_hw_intf_setup_autorefresh_config;
+ ops->get_autorefresh = dpu_hw_intf_get_autorefresh_config;
+ ops->poll_timeout_wr_ptr = dpu_hw_intf_poll_timeout_wr_ptr;
+ ops->vsync_sel = dpu_hw_intf_vsync_sel;
+ ops->get_status = dpu_hw_intf_v1_get_status;
+ }
}
struct dpu_hw_intf *dpu_hw_intf_init(enum dpu_intf idx,
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
index 643dd10bc030..1521c9475fad 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
@@ -60,6 +60,17 @@ struct intf_status {
* feed pixels to this interface
* @setup_misr: enable/disable MISR
* @collect_misr: read MISR signature
+ * @setup_tearcheck: Enables vsync generation and sets up init value of read
+ * pointer and programs the tear check configuration
+ * @enable_tearcheck: Enables tearcheck block
+ * @connect_external_te: Read, modify, write to either set or clear listening to external TE
+ * Return: 1 if TE was originally connected, 0 if not, or -ERROR
+ * @get_vsync_info: Provides the programmed and current line_count
+ * @setup_autorefresh: Configure and enable the autorefresh config
+ * @get_autorefresh: Retrieve autorefresh config from hardware
+ * @poll_timeout_wr_ptr: Poll until write pointer transmission starts
+ * Return: 0 on success, -ETIMEDOUT on timeout
+ * @vsync_sel: Select vsync signal for tear-effect configuration
*/
struct dpu_hw_intf_ops {
void (*setup_timing_gen)(struct dpu_hw_intf *intf,
@@ -82,6 +93,24 @@ struct dpu_hw_intf_ops {
const enum dpu_pingpong pp);
void (*setup_misr)(struct dpu_hw_intf *intf, bool enable, u32 frame_count);
int (*collect_misr)(struct dpu_hw_intf *intf, u32 *misr_value);
+
+ // Tearcheck on INTF since DPU 5.0.0
+
+ int (*setup_tearcheck)(struct dpu_hw_intf *intf, struct dpu_hw_tear_check *cfg);
+
+ int (*enable_tearcheck)(struct dpu_hw_intf *intf, bool enable);
+
+ int (*connect_external_te)(struct dpu_hw_intf *intf, bool enable_external_te);
+
+ int (*get_vsync_info)(struct dpu_hw_intf *intf, struct dpu_hw_pp_vsync_info *info);
+
+ void (*setup_autorefresh)(struct dpu_hw_intf *intf, u32 frame_count, bool enable);
+
+ bool (*get_autorefresh)(struct dpu_hw_intf *intf, u32 *frame_count);
+
+ int (*poll_timeout_wr_ptr)(struct dpu_hw_intf *intf, u32 timeout_us);
+
+ void (*vsync_sel)(struct dpu_hw_intf *intf, u32 vsync_source);
};
struct dpu_hw_intf {
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
index 64b2bf219a34..773579906961 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
@@ -494,12 +494,14 @@ struct dpu_hw_tear_check {
* @rd_ptr_frame_count: Num frames sent since enabling interface
* @rd_ptr_line_count: Current line on panel (rd ptr)
* @wr_ptr_line_count: Current line within pp fifo (wr ptr)
+ * @intf_frame_count: Frames read from intf
*/
struct dpu_hw_pp_vsync_info {
u32 rd_ptr_init_val;
u32 rd_ptr_frame_count;
u32 rd_ptr_line_count;
u32 wr_ptr_line_count;
+ u32 intf_frame_count;
};
#endif /* _DPU_HW_MDSS_H */

--
With best wishes
Dmitry