Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78b1d470 authored by Hai Li's avatar Hai Li Committed by Rob Clark
Browse files

drm/msm: Enable clocks during enable/disable_vblank() callbacks



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: default avatarHai Li <hali@codeaurora.org>
[add comment about cancel_work_sync() before drm_irq_uninstall()]
Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent 8a94b0aa
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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);
}
+9 −0
Original line number Diff line number Diff line
@@ -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);
}

/*
+76 −2
Original line number Diff line number Diff line
@@ -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,18 @@ 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;

	/* We must cancel and cleanup any pending vblank enable/disable
	 * work before drm_irq_uninstall() to avoid work re-enabling an
	 * irq after uninstall has disabled it.
	 */
	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 +353,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);

@@ -464,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)
@@ -474,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);
}

/*
+8 −0
Original line number Diff line number Diff line
@@ -71,6 +71,12 @@ enum msm_mdp_plane_property {
	PLANE_PROP_MAX_NUM
};

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

struct msm_drm_private {

	struct msm_kms *kms;
@@ -147,6 +153,8 @@ struct msm_drm_private {
		 */
		struct drm_mm mm;
	} vram;

	struct msm_vblank_ctrl vblank_ctrl;
};

struct msm_format {