From: Mark Yacoub <[email protected]>

[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 <[email protected]>
Signed-off-byL Mark Yacoub <[email protected]>
---
 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

Reply via email to