diff mbox series

drm/mediatek: Implement get_scanout_position for DSI

Message ID 20211130153308.2560979-1-markyacoub@chromium.org (mailing list archive)
State New, archived
Headers show
Series drm/mediatek: Implement get_scanout_position for DSI | expand

Commit Message

Mark Yacoub Nov. 30, 2021, 3:33 p.m. UTC
From: Mark Yacoub <markyacoub@google.com>

[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@mediatek.com>
Signed-off-byL Mark Yacoub <markyacoub@chromium.org>
---
 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 mbox series

Patch

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);