OSDN Git Service

drm/msm/disp/dpu1: turn off vblank irqs aggressively in dpu driver
authorKalyan Thota <kalyan_t@codeaurora.org>
Thu, 18 Feb 2021 12:35:46 +0000 (04:35 -0800)
committerRob Clark <robdclark@chromium.org>
Wed, 7 Apr 2021 18:05:43 +0000 (11:05 -0700)
Set the flag vblank_disable_immediate = true to turn off vblank irqs
immediately as soon as drm_vblank_put is requested so that there are
no irqs triggered during idle state. This will reduce cpu wakeups
and help in power saving.

To enable vblank_disable_immediate flag the underlying KMS driver
needs to support high precision vblank timestamping and also a
reliable way of providing vblank counter which is incrementing
at the leading edge of vblank.

This patch also brings in changes to support vblank_disable_immediate
requirement in dpu driver.

Changes in v1:
 - Specify reason to add vblank timestamp support. (Rob).
 - Add changes to provide vblank counter from dpu driver.

Changes in v2:
 - Fix warn stack reported by Rob Clark with v2 patch.

Changes in v3:
 - Move back to HW frame counter (Rob).

Changes in v4:
 - Frame count mismatch was causing a DRM WARN stack spew.
   DPU HW will increment the frame count at the end of
   the sync, where as vblank will be triggered at the
   fetch_start counter which is calculated as v_total - vfp.
   This is to start fetching early for panels with low
   vbp w.r.t hw latency lines.

   Add logic to detect the line count if it falls between
   vactive and v_total then return incremented frame count value.

Signed-off-by: Kalyan Thota <kalyan_t@codeaurora.org>
Link: https://lore.kernel.org/r/1613651746-12783-1-git-send-email-kalyan_t@codeaurora.org
Signed-off-by: Rob Clark <robdclark@chromium.org>
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c

index 56eb225..2a70f02 100644 (file)
@@ -66,6 +66,83 @@ static void dpu_crtc_destroy(struct drm_crtc *crtc)
        kfree(dpu_crtc);
 }
 
+static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_encoder *encoder;
+
+       drm_for_each_encoder(encoder, dev)
+               if (encoder->crtc == crtc)
+                       return encoder;
+
+       return NULL;
+}
+
+static u32 dpu_crtc_get_vblank_counter(struct drm_crtc *crtc)
+{
+       struct drm_encoder *encoder;
+
+       encoder = get_encoder_from_crtc(crtc);
+       if (!encoder) {
+               DRM_ERROR("no encoder found for crtc %d\n", crtc->index);
+               return false;
+       }
+
+       return dpu_encoder_get_frame_count(encoder);
+}
+
+static bool dpu_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)
+{
+       unsigned int pipe = crtc->index;
+       struct drm_encoder *encoder;
+       int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
+
+       encoder = get_encoder_from_crtc(crtc);
+       if (!encoder) {
+               DRM_ERROR("no encoder found for crtc %d\n", pipe);
+               return false;
+       }
+
+       vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
+       vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
+
+       /*
+        * the line counter is 1 at the start of the VSYNC pulse and VTOTAL at
+        * the end of VFP. Translate the porch values relative to the line
+        * counter positions.
+        */
+
+       vactive_start = vsw + vbp + 1;
+       vactive_end = vactive_start + mode->crtc_vdisplay;
+
+       /* last scan line before VSYNC */
+       vfp_end = mode->crtc_vtotal;
+
+       if (stime)
+               *stime = ktime_get();
+
+       line = dpu_encoder_get_linecount(encoder);
+
+       if (line < vactive_start)
+               line -= vactive_start;
+       else if (line > vactive_end)
+               line = line - vfp_end - vactive_start;
+       else
+               line -= vactive_start;
+
+       *vpos = line;
+       *hpos = 0;
+
+       if (etime)
+               *etime = ktime_get();
+
+       return true;
+}
+
 static void _dpu_crtc_setup_blend_cfg(struct dpu_crtc_mixer *mixer,
                struct dpu_plane_state *pstate, struct dpu_format *format)
 {
@@ -1249,6 +1326,8 @@ static const struct drm_crtc_funcs dpu_crtc_funcs = {
        .early_unregister = dpu_crtc_early_unregister,
        .enable_vblank  = msm_crtc_enable_vblank,
        .disable_vblank = msm_crtc_disable_vblank,
+       .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
+       .get_vblank_counter = dpu_crtc_get_vblank_counter,
 };
 
 static const struct drm_crtc_helper_funcs dpu_crtc_helper_funcs = {
@@ -1257,6 +1336,7 @@ static const struct drm_crtc_helper_funcs dpu_crtc_helper_funcs = {
        .atomic_check = dpu_crtc_atomic_check,
        .atomic_begin = dpu_crtc_atomic_begin,
        .atomic_flush = dpu_crtc_atomic_flush,
+       .get_scanout_position = dpu_crtc_get_scanout_position,
 };
 
 /* initialize crtc */
index 288e95e..8d94205 100644 (file)
@@ -426,6 +426,36 @@ int dpu_encoder_helper_unregister_irq(struct dpu_encoder_phys *phys_enc,
        return 0;
 }
 
+int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc)
+{
+       struct dpu_encoder_virt *dpu_enc;
+       struct dpu_encoder_phys *phys;
+       int framecount = 0;
+
+       dpu_enc = to_dpu_encoder_virt(drm_enc);
+       phys = dpu_enc ? dpu_enc->cur_master : NULL;
+
+       if (phys && phys->ops.get_frame_count)
+               framecount = phys->ops.get_frame_count(phys);
+
+       return framecount;
+}
+
+int dpu_encoder_get_linecount(struct drm_encoder *drm_enc)
+{
+       struct dpu_encoder_virt *dpu_enc;
+       struct dpu_encoder_phys *phys;
+       int linecount = 0;
+
+       dpu_enc = to_dpu_encoder_virt(drm_enc);
+       phys = dpu_enc ? dpu_enc->cur_master : NULL;
+
+       if (phys && phys->ops.get_line_count)
+               linecount = phys->ops.get_line_count(phys);
+
+       return linecount;
+}
+
 void dpu_encoder_get_hw_resources(struct drm_encoder *drm_enc,
                                  struct dpu_encoder_hw_resources *hw_res)
 {
index b491346..99a5d73 100644 (file)
@@ -156,5 +156,16 @@ void dpu_encoder_prepare_commit(struct drm_encoder *drm_enc);
  */
 void dpu_encoder_set_idle_timeout(struct drm_encoder *drm_enc,
                                                        u32 idle_timeout);
+/**
+ * dpu_encoder_get_linecount - get interface line count for the encoder.
+ * @drm_enc:    Pointer to previously created drm encoder structure
+ */
+int dpu_encoder_get_linecount(struct drm_encoder *drm_enc);
+
+/**
+ * dpu_encoder_get_frame_count - get interface frame count for the encoder.
+ * @drm_enc:    Pointer to previously created drm encoder structure
+ */
+int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc);
 
 #endif /* __DPU_ENCODER_H__ */
index f8f2515..ecbc4be 100644 (file)
@@ -143,6 +143,7 @@ struct dpu_encoder_phys_ops {
        void (*prepare_idle_pc)(struct dpu_encoder_phys *phys_enc);
        void (*restore)(struct dpu_encoder_phys *phys);
        int (*get_line_count)(struct dpu_encoder_phys *phys);
+       int (*get_frame_count)(struct dpu_encoder_phys *phys);
 };
 
 /**
index 9a69fad..0e06b7e 100644 (file)
@@ -658,6 +658,31 @@ static int dpu_encoder_phys_vid_get_line_count(
        return phys_enc->hw_intf->ops.get_line_count(phys_enc->hw_intf);
 }
 
+static int dpu_encoder_phys_vid_get_frame_count(
+               struct dpu_encoder_phys *phys_enc)
+{
+       struct intf_status s = {0};
+       u32 fetch_start = 0;
+       struct drm_display_mode mode = phys_enc->cached_mode;
+
+       if (!dpu_encoder_phys_vid_is_master(phys_enc))
+               return -EINVAL;
+
+       if (!phys_enc->hw_intf || !phys_enc->hw_intf->ops.get_status)
+               return -EINVAL;
+
+       phys_enc->hw_intf->ops.get_status(phys_enc->hw_intf, &s);
+
+       if (s.is_prog_fetch_en && s.is_en) {
+               fetch_start = mode.vtotal - (mode.vsync_start - mode.vdisplay);
+               if ((s.line_count > fetch_start) &&
+                       (s.line_count <= mode.vtotal))
+                       return s.frame_count + 1;
+       }
+
+       return s.frame_count;
+}
+
 static void dpu_encoder_phys_vid_init_ops(struct dpu_encoder_phys_ops *ops)
 {
        ops->is_master = dpu_encoder_phys_vid_is_master;
@@ -676,6 +701,7 @@ static void dpu_encoder_phys_vid_init_ops(struct dpu_encoder_phys_ops *ops)
        ops->handle_post_kickoff = dpu_encoder_phys_vid_handle_post_kickoff;
        ops->needs_single_flush = dpu_encoder_phys_vid_needs_single_flush;
        ops->get_line_count = dpu_encoder_phys_vid_get_line_count;
+       ops->get_frame_count = dpu_encoder_phys_vid_get_frame_count;
 }
 
 struct dpu_encoder_phys *dpu_encoder_phys_vid_init(
index 6f0f545..717178b 100644 (file)
@@ -256,6 +256,7 @@ static void dpu_hw_intf_get_status(
        struct dpu_hw_blk_reg_map *c = &intf->hw;
 
        s->is_en = DPU_REG_READ(c, INTF_TIMING_ENGINE_EN);
+       s->is_prog_fetch_en = !!(DPU_REG_READ(c, INTF_CONFIG) & BIT(31));
        if (s->is_en) {
                s->frame_count = DPU_REG_READ(c, INTF_FRAME_COUNT);
                s->line_count = DPU_REG_READ(c, INTF_LINE_COUNT);
index 0ead64d..3568be8 100644 (file)
@@ -40,6 +40,7 @@ struct intf_prog_fetch {
 
 struct intf_status {
        u8 is_en;               /* interface timing engine is enabled or not */
+       u8 is_prog_fetch_en;    /* interface prog fetch counter is enabled or not */
        u32 frame_count;        /* frame count since timing engine enabled */
        u32 line_count;         /* current line count including blanking */
 };
index 17f7102..4d83500 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_file.h>
+#include <drm/drm_vblank.h>
 
 #include "msm_drv.h"
 #include "msm_mmu.h"
@@ -1025,6 +1026,10 @@ static int dpu_kms_hw_init(struct msm_kms *kms)
         */
        dev->mode_config.allow_fb_modifiers = true;
 
+       dev->max_vblank_count = 0xffffffff;
+       /* Disable vblank irqs aggressively for power-saving */
+       dev->vblank_disable_immediate = true;
+
        /*
         * _dpu_kms_drm_obj_init should create the DRM related objects
         * i.e. CRTCs, planes, encoders, connectors and so forth