AHB clock should be enabled before accessing registers during
enable/disable_vblank(). Since these 2 callbacks are called in
atomic context while clk_prepare may cause thread sleep, a work
is scheduled to control vblanks.

v2: fixup spinlock initialization

Signed-off-by: Hai Li <hali at codeaurora.org>
---
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c |  9 ++++
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c |  9 ++++
 drivers/gpu/drm/msm/msm_drv.c           | 74 ++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/msm/msm_drv.h           |  8 ++++
 4 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c 
b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c
index 7369ee7f..64d24fc 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c
@@ -86,13 +86,22 @@ irqreturn_t mdp4_irq(struct msm_kms *kms)

 int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
 {
+       struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
+
+       mdp4_enable(mdp4_kms);
        mdp_update_vblank_mask(to_mdp_kms(kms),
                        mdp4_crtc_vblank(crtc), true);
+       mdp4_disable(mdp4_kms);
+
        return 0;
 }

 void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
 {
+       struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
+
+       mdp4_enable(mdp4_kms);
        mdp_update_vblank_mask(to_mdp_kms(kms),
                        mdp4_crtc_vblank(crtc), false);
+       mdp4_disable(mdp4_kms);
 }
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c 
b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c
index 33bd4c6..2a578f2 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c
@@ -112,15 +112,24 @@ irqreturn_t mdp5_irq(struct msm_kms *kms)

 int mdp5_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
 {
+       struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+
+       mdp5_enable(mdp5_kms);
        mdp_update_vblank_mask(to_mdp_kms(kms),
                        mdp5_crtc_vblank(crtc), true);
+       mdp5_disable(mdp5_kms);
+
        return 0;
 }

 void mdp5_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
 {
+       struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+
+       mdp5_enable(mdp5_kms);
        mdp_update_vblank_mask(to_mdp_kms(kms),
                        mdp5_crtc_vblank(crtc), false);
+       mdp5_disable(mdp5_kms);
 }

 /*
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index b7ef56e..0c47c2d 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -116,6 +116,65 @@ u32 msm_readl(const void __iomem *addr)
        return val;
 }

+struct vblank_event {
+       struct list_head node;
+       int crtc_id;
+       bool enable;
+};
+
+static void vblank_ctrl_worker(struct work_struct *work)
+{
+       struct msm_vblank_ctrl *vbl_ctrl = container_of(work,
+                                               struct msm_vblank_ctrl, work);
+       struct msm_drm_private *priv = container_of(vbl_ctrl,
+                                       struct msm_drm_private, vblank_ctrl);
+       struct msm_kms *kms = priv->kms;
+       struct vblank_event *vbl_ev, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vbl_ctrl->lock, flags);
+       list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) {
+               list_del(&vbl_ev->node);
+               spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+
+               if (vbl_ev->enable)
+                       kms->funcs->enable_vblank(kms,
+                                               priv->crtcs[vbl_ev->crtc_id]);
+               else
+                       kms->funcs->disable_vblank(kms,
+                                               priv->crtcs[vbl_ev->crtc_id]);
+
+               kfree(vbl_ev);
+
+               spin_lock_irqsave(&vbl_ctrl->lock, flags);
+       }
+
+       spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+}
+
+static int vblank_ctrl_queue_work(struct msm_drm_private *priv,
+                                       int crtc_id, bool enable)
+{
+       struct msm_vblank_ctrl *vbl_ctrl = &priv->vblank_ctrl;
+       struct vblank_event *vbl_ev;
+       unsigned long flags;
+
+       vbl_ev = kzalloc(sizeof(*vbl_ev), GFP_ATOMIC);
+       if (!vbl_ev)
+               return -ENOMEM;
+
+       vbl_ev->crtc_id = crtc_id;
+       vbl_ev->enable = enable;
+
+       spin_lock_irqsave(&vbl_ctrl->lock, flags);
+       list_add_tail(&vbl_ev->node, &vbl_ctrl->event_list);
+       spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+
+       queue_work(priv->wq, &vbl_ctrl->work);
+
+       return 0;
+}
+
 /*
  * DRM operations:
  */
@@ -125,6 +184,14 @@ static int msm_unload(struct drm_device *dev)
        struct msm_drm_private *priv = dev->dev_private;
        struct msm_kms *kms = priv->kms;
        struct msm_gpu *gpu = priv->gpu;
+       struct msm_vblank_ctrl *vbl_ctrl = &priv->vblank_ctrl;
+       struct vblank_event *vbl_ev, *tmp;
+
+       cancel_work_sync(&vbl_ctrl->work);
+       list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) {
+               list_del(&vbl_ev->node);
+               kfree(vbl_ev);
+       }

        drm_kms_helper_poll_fini(dev);
        drm_mode_config_cleanup(dev);
@@ -282,6 +349,9 @@ static int msm_load(struct drm_device *dev, unsigned long 
flags)

        INIT_LIST_HEAD(&priv->inactive_list);
        INIT_LIST_HEAD(&priv->fence_cbs);
+       INIT_LIST_HEAD(&priv->vblank_ctrl.event_list);
+       INIT_WORK(&priv->vblank_ctrl.work, vblank_ctrl_worker);
+       spin_lock_init(&priv->vblank_ctrl.lock);

        drm_mode_config_init(dev);

@@ -468,7 +538,7 @@ static int msm_enable_vblank(struct drm_device *dev, int 
crtc_id)
        if (!kms)
                return -ENXIO;
        DBG("dev=%p, crtc=%d", dev, crtc_id);
-       return kms->funcs->enable_vblank(kms, priv->crtcs[crtc_id]);
+       return vblank_ctrl_queue_work(priv, crtc_id, true);
 }

 static void msm_disable_vblank(struct drm_device *dev, int crtc_id)
@@ -478,7 +548,7 @@ static void msm_disable_vblank(struct drm_device *dev, int 
crtc_id)
        if (!kms)
                return;
        DBG("dev=%p, crtc=%d", dev, crtc_id);
-       kms->funcs->disable_vblank(kms, priv->crtcs[crtc_id]);
+       vblank_ctrl_queue_work(priv, crtc_id, false);
 }

 /*
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index e7c5ea1..5816991 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -64,6 +64,12 @@ struct msm_file_private {
        int dummy;
 };

+struct msm_vblank_ctrl {
+       struct work_struct work;
+       struct list_head event_list;
+       spinlock_t lock;
+};
+
 struct msm_drm_private {

        struct msm_kms *kms;
@@ -137,6 +143,8 @@ struct msm_drm_private {
                 */
                struct drm_mm mm;
        } vram;
+
+       struct msm_vblank_ctrl vblank_ctrl;
 };

 struct msm_format {
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

Reply via email to