drm/msm/mdp5: high precision vblank timestamp support
authorArchit Taneja <architt@codeaurora.org>
Mon, 26 Oct 2015 09:13:57 +0000 (14:43 +0530)
committerRob Clark <robdclark@gmail.com>
Mon, 14 Dec 2015 15:37:01 +0000 (10:37 -0500)
MDP5 has line count and frame count registers for each interface. Enable
these counters and use them to implement the get_vblank_timestamp drm
driver op.

The line counter starts with the value 1 at the beginning of the VSYNC
pulse and ends with value VTOTAL at the end of VFP. This value is used
to determine whether we're in blanking period or not, and an adjusted
value of this counter is used to get vpos as expected by
get_scanout_position. Since there is no way to calculate hpos, we always
set it to 0.

Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h

index c9e32b08a7a0b3c8807ce76b78018630d315632c..a0196568bf5d46ba85f578a82eaa7ce70fb2b033 100644 (file)
@@ -293,6 +293,24 @@ static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = {
        .enable = mdp5_encoder_enable,
 };
 
+int mdp5_encoder_get_linecount(struct drm_encoder *encoder)
+{
+       struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
+       struct mdp5_kms *mdp5_kms = get_kms(encoder);
+       int intf = mdp5_encoder->intf.num;
+
+       return mdp5_read(mdp5_kms, REG_MDP5_INTF_LINE_COUNT(intf));
+}
+
+u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder)
+{
+       struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
+       struct mdp5_kms *mdp5_kms = get_kms(encoder);
+       int intf = mdp5_encoder->intf.num;
+
+       return mdp5_read(mdp5_kms, REG_MDP5_INTF_FRAME_COUNT(intf));
+}
+
 int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
                                        struct drm_encoder *slave_encoder)
 {
index b532faa8026d95079dc2cf19ad0aef0f97782849..e115318402bd877a3f84c0cba4f77ba82657b7a2 100644 (file)
@@ -468,6 +468,127 @@ static int get_clk(struct platform_device *pdev, struct clk **clkp,
        return 0;
 }
 
+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 int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
+                              unsigned int flags, int *vpos, int *hpos,
+                              ktime_t *stime, ktime_t *etime,
+                              const struct drm_display_mode *mode)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct drm_crtc *crtc;
+       struct drm_encoder *encoder;
+       int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
+       int ret = 0;
+
+       crtc = priv->crtcs[pipe];
+       if (!crtc) {
+               DRM_ERROR("Invalid crtc %d\n", pipe);
+               return 0;
+       }
+
+       encoder = get_encoder_from_crtc(crtc);
+       if (!encoder) {
+               DRM_ERROR("no encoder found for crtc %d\n", pipe);
+               return 0;
+       }
+
+       ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
+
+       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 = mdp5_encoder_get_linecount(encoder);
+
+       if (line < vactive_start) {
+               line -= vactive_start;
+               ret |= DRM_SCANOUTPOS_IN_VBLANK;
+       } else if (line > vactive_end) {
+               line = line - vfp_end - vactive_start;
+               ret |= DRM_SCANOUTPOS_IN_VBLANK;
+       } else {
+               line -= vactive_start;
+       }
+
+       *vpos = line;
+       *hpos = 0;
+
+       if (etime)
+               *etime = ktime_get();
+
+       return ret;
+}
+
+static int mdp5_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
+                                    int *max_error,
+                                    struct timeval *vblank_time,
+                                    unsigned flags)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct drm_crtc *crtc;
+
+       if (pipe < 0 || pipe >= priv->num_crtcs) {
+               DRM_ERROR("Invalid crtc %d\n", pipe);
+               return -EINVAL;
+       }
+
+       crtc = priv->crtcs[pipe];
+       if (!crtc) {
+               DRM_ERROR("Invalid crtc %d\n", pipe);
+               return -EINVAL;
+       }
+
+       return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
+                                                    vblank_time, flags,
+                                                    &crtc->mode);
+}
+
+static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct drm_crtc *crtc;
+       struct drm_encoder *encoder;
+
+       if (pipe < 0 || pipe >= priv->num_crtcs)
+               return 0;
+
+       crtc = priv->crtcs[pipe];
+       if (!crtc)
+               return 0;
+
+       encoder = get_encoder_from_crtc(crtc);
+       if (!encoder)
+               return 0;
+
+       return mdp5_encoder_get_framecount(encoder);
+}
+
 struct msm_kms *mdp5_kms_init(struct drm_device *dev)
 {
        struct platform_device *pdev = dev->platformdev;
@@ -590,6 +711,8 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
                                !config->hw->intf.base[i])
                        continue;
                mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0);
+
+               mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(i), 0x3);
        }
        mdp5_disable(mdp5_kms);
        mdelay(16);
@@ -635,6 +758,12 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
        dev->mode_config.max_width = config->hw->lm.max_width;
        dev->mode_config.max_height = config->hw->lm.max_height;
 
+       dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp;
+       dev->driver->get_scanout_position = mdp5_get_scanoutpos;
+       dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
+       dev->max_vblank_count = 0xffffffff;
+       dev->vblank_disable_immediate = true;
+
        return kms;
 
 fail:
index 84f65d415598ea4b6c7d85bc8e17d83be3fa1e6e..00730ba08a60ac5754176357880ca4f3c0be0c54 100644 (file)
@@ -222,6 +222,8 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
                struct mdp5_interface *intf, struct mdp5_ctl *ctl);
 int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
                                        struct drm_encoder *slave_encoder);
+int mdp5_encoder_get_linecount(struct drm_encoder *encoder);
+u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder);
 
 #ifdef CONFIG_DRM_MSM_DSI
 struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,