diff mbox series

[v4] drm/msm/disp/dpu1: turn off vblank irqs aggressively in dpu driver

Message ID 1613651746-12783-1-git-send-email-kalyan_t@codeaurora.org (mailing list archive)
State New
Headers show
Series [v4] drm/msm/disp/dpu1: turn off vblank irqs aggressively in dpu driver | expand

Commit Message

Kalyan Thota Feb. 18, 2021, 12:35 p.m. UTC
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).

 Signed-off-by: Kalyan Thota <kalyan_t@codeaurora.org>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c           | 80 ++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        | 30 ++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        | 11 +++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  1 +
 .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   | 26 +++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c        |  1 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h        |  1 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |  5 ++
 8 files changed, 155 insertions(+)

Comments

Rob Clark Feb. 22, 2021, 4:08 p.m. UTC | #1
On Thu, Feb 18, 2021 at 4:36 AM Kalyan Thota <kalyan_t@codeaurora.org> wrote:
>
> 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).
>

could you let me know what the delta was in v4?  (No need to resend
yet, if needed I can amend the commit msg when applying)

BR,
-R

>  Signed-off-by: Kalyan Thota <kalyan_t@codeaurora.org>
> ---
>  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c           | 80 ++++++++++++++++++++++
>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        | 30 ++++++++
>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        | 11 +++
>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  1 +
>  .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   | 26 +++++++
>  drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c        |  1 +
>  drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h        |  1 +
>  drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |  5 ++
>  8 files changed, 155 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> index d4662e8..9a80981 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> @@ -65,6 +65,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)
>  {
> @@ -1243,6 +1320,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 = {
> @@ -1251,6 +1330,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 */
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index f7f5c25..5cd3f31 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -425,6 +425,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)
>  {
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> index b491346..99a5d73 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> @@ -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__ */
> 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 f8f2515..ecbc4be 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -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);
>  };
>
>  /**
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> index 9a69fad..0e06b7e 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> @@ -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(
> 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 6f0f545..717178b 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
> @@ -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);
> 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 0ead64d..3568be8 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
> @@ -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 */
>  };
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index 374b0e8..ed636f1 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -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"
> @@ -1020,6 +1021,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
> --
> 2.7.4
>
Kalyan Thota Feb. 23, 2021, 3:11 p.m. UTC | #2
On 2021-02-22 21:38, Rob Clark wrote:
> On Thu, Feb 18, 2021 at 4:36 AM Kalyan Thota <kalyan_t@codeaurora.org> 
> wrote:
>> 
>> 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).
>> 
> 
> could you let me know what the delta was in v4?  (No need to resend
> yet, if needed I can amend the commit msg when applying)
> 
     - 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.
> BR,
> -R
> 
>>  Signed-off-by: Kalyan Thota <kalyan_t@codeaurora.org>
>> ---
>>  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c           | 80 
>> ++++++++++++++++++++++
>>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        | 30 ++++++++
>>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        | 11 +++
>>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  1 +
>>  .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   | 26 +++++++
>>  drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c        |  1 +
>>  drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h        |  1 +
>>  drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |  5 ++
>>  8 files changed, 155 insertions(+)
>> 
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
>> index d4662e8..9a80981 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
>> @@ -65,6 +65,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)
>>  {
>> @@ -1243,6 +1320,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 = {
>> @@ -1251,6 +1330,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 */
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> index f7f5c25..5cd3f31 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> @@ -425,6 +425,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)
>>  {
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
>> index b491346..99a5d73 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
>> @@ -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__ */
>> 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 f8f2515..ecbc4be 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> @@ -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);
>>  };
>> 
>>  /**
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
>> index 9a69fad..0e06b7e 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
>> @@ -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(
>> 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 6f0f545..717178b 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
>> @@ -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);
>> 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 0ead64d..3568be8 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
>> @@ -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 */
>>  };
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> index 374b0e8..ed636f1 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> @@ -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"
>> @@ -1020,6 +1021,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
>> --
>> 2.7.4
>> 
> _______________________________________________
> Freedreno mailing list
> Freedreno@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/freedreno
diff mbox series

Patch

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index d4662e8..9a80981 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -65,6 +65,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)
 {
@@ -1243,6 +1320,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 = {
@@ -1251,6 +1330,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 */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index f7f5c25..5cd3f31 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -425,6 +425,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)
 {
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
index b491346..99a5d73 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
@@ -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__ */
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 f8f2515..ecbc4be 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -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);
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
index 9a69fad..0e06b7e 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
@@ -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(
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 6f0f545..717178b 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
@@ -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);
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 0ead64d..3568be8 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
@@ -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 */
 };
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 374b0e8..ed636f1 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -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"
@@ -1020,6 +1021,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