[PATCH] drm/mediatek: Implement get_scanout_position for DSI

From: Mark Yacoub
Date: Tue Nov 30 2021 - 10:33:16 EST


From: Mark Yacoub <markyacoub@xxxxxxxxxx>

[Why]
vblank timestamp needs GPU high precision timestamp query to avoid using
the current monotonic/gettimeofday timestamp as best estimate.

[How]
Implement driver get_vblank_timestamp call which calls
get_scanout_position which loops over the CRTC comps to find a
component with get_scanout_position implemented.
Implement get_scanout_position for DSI component.

Tested on: Jacuzzi (MT8183)
Fixes: igt@kms_flip expected frametime standard deviation to be lower
than 0.05%

Suggested-by: jason-jh.lin <jason-jh.lin@xxxxxxxxxxxx>
Signed-off-byL Mark Yacoub <markyacoub@xxxxxxxxxxxx>
---
drivers/gpu/drm/mediatek/mtk_disp_drv.h | 4 ++
drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 48 ++++++++++++------
drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 1 +
drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | 18 +++++++
drivers/gpu/drm/mediatek/mtk_dsi.c | 55 +++++++++++++++++++++
5 files changed, 112 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
index 86c3068894b11..6c0f1acb2cc22 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
@@ -44,6 +44,10 @@ void mtk_dpi_stop(struct device *dev);

void mtk_dsi_ddp_start(struct device *dev);
void mtk_dsi_ddp_stop(struct device *dev);
+bool mtk_dsi_get_scanout_position(struct device *dev, bool in_vblank_irq,
+ int *vpos, int *hpos, ktime_t *stime,
+ ktime_t *etime,
+ const struct drm_display_mode *mode);

int mtk_gamma_clk_enable(struct device *dev);
void mtk_gamma_clk_disable(struct device *dev);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index 798cce90351b6..5e4e8aa005d7c 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -674,25 +674,45 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
}
mtk_drm_crtc_update_config(mtk_crtc, !!mtk_crtc->event);
}
+static bool mtk_drm_crtc_get_scanout_position(
+ struct drm_crtc *crtc, bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime, const struct drm_display_mode *mode)
+{
+ struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+ int i;
+
+ if (!mtk_crtc->enabled)
+ return false;
+
+ for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+ struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
+ if (mtk_ddp_comp_get_scanout_position(comp, in_vblank_irq, vpos,
+ hpos, stime, etime, mode))
+ return true;
+ }
+ return false;
+}

static const struct drm_crtc_funcs mtk_crtc_funcs = {
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .destroy = mtk_drm_crtc_destroy,
- .reset = mtk_drm_crtc_reset,
- .atomic_duplicate_state = mtk_drm_crtc_duplicate_state,
- .atomic_destroy_state = mtk_drm_crtc_destroy_state,
- .enable_vblank = mtk_drm_crtc_enable_vblank,
- .disable_vblank = mtk_drm_crtc_disable_vblank,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .destroy = mtk_drm_crtc_destroy,
+ .reset = mtk_drm_crtc_reset,
+ .atomic_duplicate_state = mtk_drm_crtc_duplicate_state,
+ .atomic_destroy_state = mtk_drm_crtc_destroy_state,
+ .enable_vblank = mtk_drm_crtc_enable_vblank,
+ .disable_vblank = mtk_drm_crtc_disable_vblank,
+ .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
};

static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
- .mode_fixup = mtk_drm_crtc_mode_fixup,
- .mode_set_nofb = mtk_drm_crtc_mode_set_nofb,
- .atomic_begin = mtk_drm_crtc_atomic_begin,
- .atomic_flush = mtk_drm_crtc_atomic_flush,
- .atomic_enable = mtk_drm_crtc_atomic_enable,
- .atomic_disable = mtk_drm_crtc_atomic_disable,
+ .mode_fixup = mtk_drm_crtc_mode_fixup,
+ .mode_set_nofb = mtk_drm_crtc_mode_set_nofb,
+ .atomic_begin = mtk_drm_crtc_atomic_begin,
+ .atomic_flush = mtk_drm_crtc_atomic_flush,
+ .atomic_enable = mtk_drm_crtc_atomic_enable,
+ .atomic_disable = mtk_drm_crtc_atomic_disable,
+ .get_scanout_position = mtk_drm_crtc_get_scanout_position,
};

static int mtk_drm_crtc_init(struct drm_device *drm,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 5860533ee9532..1c5ac4ccdcd30 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -252,6 +252,7 @@ static const struct mtk_ddp_comp_funcs ddp_dpi = {
static const struct mtk_ddp_comp_funcs ddp_dsi = {
.start = mtk_dsi_ddp_start,
.stop = mtk_dsi_ddp_stop,
+ .get_scanout_position = mtk_dsi_get_scanout_position,
};

static const struct mtk_ddp_comp_funcs ddp_gamma = {
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 1b582262b682b..f1e6cee8175f7 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -16,6 +16,7 @@ struct drm_crtc;
struct drm_device;
struct mtk_plane_state;
struct drm_crtc_state;
+struct drm_display_mode;

enum mtk_ddp_comp_type {
MTK_DISP_OVL,
@@ -65,6 +66,10 @@ struct mtk_ddp_comp_funcs {
void (*bgclr_in_off)(struct device *dev);
void (*ctm_set)(struct device *dev,
struct drm_crtc_state *state);
+ bool (*get_scanout_position)(struct device *dev, bool in_vblank_irq,
+ int *vpos, int *hpos, ktime_t *stime,
+ ktime_t *etime,
+ const struct drm_display_mode *mode);
};

struct mtk_ddp_comp {
@@ -184,6 +189,19 @@ static inline void mtk_ddp_ctm_set(struct mtk_ddp_comp *comp,
comp->funcs->ctm_set(comp->dev, state);
}

+static inline bool mtk_ddp_comp_get_scanout_position(
+ struct mtk_ddp_comp *comp, bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime, const struct drm_display_mode *mode)
+{
+ if (comp->funcs && comp->funcs->get_scanout_position) {
+ return comp->funcs->get_scanout_position(comp->dev,
+ in_vblank_irq, vpos,
+ hpos, stime, etime,
+ mode);
+ }
+ return false;
+}
+
int mtk_ddp_comp_get_id(struct device_node *node,
enum mtk_ddp_comp_type comp_type);
unsigned int mtk_drm_find_possible_crtc_by_comp(struct drm_device *drm,
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index 0ad7157660afa..1c2b95bcc3e9c 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -3,6 +3,7 @@
* Copyright (c) 2015 MediaTek Inc.
*/

+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/iopoll.h>
@@ -147,6 +148,9 @@

#define MMSYS_SW_RST_DSI_B BIT(25)

+#define DSI_INPUT_DEBUG 0x1D4
+#define INP_LINE_CNT GENMASK(29, 16)
+
#define NS_TO_CYCLE(n, c) ((n) / (c) + (((n) % (c)) ? 1 : 0))

#define MTK_DSI_HOST_IS_READ(type) \
@@ -1208,6 +1212,57 @@ static int mtk_dsi_probe(struct platform_device *pdev)
return ret;
}

+bool mtk_dsi_get_scanout_position(struct device *dev, bool in_vblank_irq,
+ int *vpos, int *hpos, ktime_t *stime,
+ ktime_t *etime,
+ const struct drm_display_mode *mode)
+{
+ struct mtk_dsi *dsi = dev_get_drvdata(dev);
+ int line_count = 0;
+
+ int vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
+ int vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
+
+ int vactive_start = vsw + vbp + 1;
+ int vactive_end = vactive_start + mode->crtc_vdisplay;
+ int vfp_end = mode->crtc_vtotal;
+
+ /*
+ * Target location for timestamp taken immediately before
+ * scanout position query.
+ */
+ if (stime)
+ *stime = ktime_get();
+
+ line_count =
+ FIELD_GET(INP_LINE_CNT, readl(dsi->regs + DSI_INPUT_DEBUG));
+
+ /*
+ * Target location for timestamp taken immediately after
+ * scanout position query.
+ */
+ if (etime)
+ *etime = ktime_get();
+
+ /*
+ * Returns vpos as a positive number while in active scanout area.
+ * Returns vpos as a negative number inside vblank, counting the number
+ * of scanlines to go until end of vblank, e.g., -1 means "one scanline
+ * until start of active scanout / end of vblank."
+ */
+ if (line_count < vactive_start)
+ line_count -= vactive_start;
+ else if (line_count > vactive_end)
+ line_count = line_count - vfp_end - vactive_start;
+ else
+ line_count -= vactive_start;
+
+ *vpos = line_count;
+ *hpos = 0; /* keep 0, this is informational */
+
+ return true;
+}
+
static int mtk_dsi_remove(struct platform_device *pdev)
{
struct mtk_dsi *dsi = platform_get_drvdata(pdev);
--
2.34.0.rc2.393.gf8c9666880-goog