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

Commit 65f02ce9 authored by Sandeep Panda's avatar Sandeep Panda Committed by Jin Li
Browse files

drm/msm: create separate commit thread for each display



This change creates separate threads for commit on each
display, so that one display commit is not blocked on
other display's commit. Hence improving performance in
multi display use cases.

Change-Id: Ibd0dae1da53ec3a72de8b96c3c03ce51830cb4f9
Signed-off-by: default avatarSandeep Panda <spanda@codeaurora.org>
parent d0c87b8b
Loading
Loading
Loading
Loading
+64 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ struct msm_commit {
	uint32_t fence;
	struct msm_fence_cb fence_cb;
	uint32_t crtc_mask;
	struct kthread_work commit_work;
};

/* block until specified crtcs are no longer pending update, and
@@ -435,6 +436,20 @@ static void fence_cb(struct msm_fence_cb *cb)
	complete_commit(commit);
}

static void _msm_drm_commit_work_cb(struct kthread_work *work)
{
	struct msm_commit *commit =  NULL;

	if (!work) {
		DRM_ERROR("%s: Invalid commit work data!\n", __func__);
		return;
	}

	commit = container_of(work, struct msm_commit, commit_work);

	complete_commit(commit);
}

static struct msm_commit *commit_init(struct drm_atomic_state *state)
{
	struct msm_commit *commit = kzalloc(sizeof(*commit), GFP_KERNEL);
@@ -452,6 +467,7 @@ static struct msm_commit *commit_init(struct drm_atomic_state *state)
	 * bo's..
	 */
	INIT_FENCE_CB(&commit->fence_cb, fence_cb);
	init_kthread_work(&commit->commit_work, _msm_drm_commit_work_cb);

	return commit;
}
@@ -464,6 +480,47 @@ static void commit_set_fence(struct msm_commit *commit,
			msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ));
}

/* Start display thread function */
static int msm_atomic_commit_dispatch(struct drm_device *dev,
		struct drm_atomic_state *state, struct msm_commit *commit)
{
	struct msm_drm_private *priv = dev->dev_private;
	struct drm_crtc *crtc = NULL;
	struct drm_crtc_state *crtc_state = NULL;
	int ret = -EINVAL, i = 0, j = 0;

	for_each_crtc_in_state(state, crtc, crtc_state, i) {
		for (j = 0; j < priv->num_crtcs; j++) {
			if (priv->disp_thread[j].crtc_id ==
						crtc->base.id) {
				if (priv->disp_thread[j].thread) {
					queue_kthread_work(
						&priv->disp_thread[j].worker,
							&commit->commit_work);
					/* only return zero if work is
					 * queued successfully.
					 */
					ret = 0;
				} else {
					DRM_ERROR(" Error for crtc_id: %d\n",
						priv->disp_thread[j].crtc_id);
				}
				break;
			}
		}
		/*
		 * TODO: handle cases where there will be more than
		 * one crtc per commit cycle. Remove this check then.
		 * Current assumption is there will be only one crtc
		 * per commit cycle.
		 */
		if (j < priv->num_crtcs)
			break;
	}

	return ret;
}

/**
 * drm_atomic_helper_commit - commit validated state object
 * @dev: DRM device
@@ -567,7 +624,13 @@ int msm_atomic_commit(struct drm_device *dev,
	 */

	if (async) {
		msm_queue_fence_cb(dev, &commit->fence_cb, commit->fence);
		ret = msm_atomic_commit_dispatch(dev, state, commit);
		if (ret) {
			DRM_ERROR("%s: atomic commit failed\n", __func__);
			drm_atomic_state_free(state);
			commit_destroy(commit);
			goto error;
		}
		return 0;
	}

+38 −5
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ struct vblank_event {
	bool enable;
};

static void vblank_ctrl_worker(struct work_struct *work)
static void vblank_ctrl_worker(struct kthread_work *work)
{
	struct msm_vblank_ctrl *vbl_ctrl = container_of(work,
						struct msm_vblank_ctrl, work);
@@ -171,7 +171,7 @@ static int vblank_ctrl_queue_work(struct msm_drm_private *priv,
	list_add_tail(&vbl_ev->node, &vbl_ctrl->event_list);
	spin_unlock_irqrestore(&vbl_ctrl->lock, flags);

	queue_work(priv->wq, &vbl_ctrl->work);
	queue_kthread_work(&priv->disp_thread[crtc_id].worker, &vbl_ctrl->work);

	return 0;
}
@@ -187,17 +187,27 @@ static int msm_unload(struct drm_device *dev)
	struct msm_gpu *gpu = priv->gpu;
	struct msm_vblank_ctrl *vbl_ctrl = &priv->vblank_ctrl;
	struct vblank_event *vbl_ev, *tmp;
	int i;

	/* 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);
	flush_kthread_work(&vbl_ctrl->work);
	list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) {
		list_del(&vbl_ev->node);
		kfree(vbl_ev);
	}

	/* clean up display commit worker threads */
	for (i = 0; i < priv->num_crtcs; i++) {
		if (priv->disp_thread[i].thread) {
			flush_kthread_worker(&priv->disp_thread[i].worker);
			kthread_stop(priv->disp_thread[i].thread);
			priv->disp_thread[i].thread = NULL;
		}
	}

	drm_kms_helper_poll_fini(dev);
	drm_mode_config_cleanup(dev);
	drm_vblank_cleanup(dev);
@@ -347,7 +357,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
	struct platform_device *pdev = dev->platformdev;
	struct msm_drm_private *priv;
	struct msm_kms *kms;
	int ret;
	int ret, i;

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
@@ -364,7 +374,7 @@ 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);
	init_kthread_work(&priv->vblank_ctrl.work, vblank_ctrl_worker);
	spin_lock_init(&priv->vblank_ctrl.lock);

	msm_force_submit(priv);
@@ -410,6 +420,29 @@ static int msm_load(struct drm_device *dev, unsigned long flags)

	priv->kms = kms;

	/* initialize commit thread structure */
	for (i = 0; i < priv->num_crtcs; i++) {
		priv->disp_thread[i].crtc_id = priv->crtcs[i]->base.id;
		init_kthread_worker(&priv->disp_thread[i].worker);
		priv->disp_thread[i].dev = dev;
		priv->disp_thread[i].thread =
			kthread_run(kthread_worker_fn,
				&priv->disp_thread[i].worker,
				"crtc_commit:%d",
				priv->disp_thread[i].crtc_id);

		if (IS_ERR(priv->disp_thread[i].thread)) {
			dev_err(dev->dev, "failed to create kthread\n");
			priv->disp_thread[i].thread = NULL;
			/* clean up previously created threads if any */
			for (i -= 1; i >= 0; i--) {
				kthread_stop(priv->disp_thread[i].thread);
				priv->disp_thread[i].thread = NULL;
			}
			goto fail;
		}
	}

	if (kms) {
		pm_runtime_enable(dev->dev);
		ret = kms->funcs->hw_init(kms);
+12 −1
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <linux/types.h>
#include <linux/of_graph.h>
#include <asm/sizes.h>
#include <linux/kthread.h>

#ifndef CONFIG_OF
#include <mach/board.h>
@@ -141,7 +142,7 @@ enum msm_mdp_display_id {
};

struct msm_vblank_ctrl {
	struct work_struct work;
	struct kthread_work work;
	struct list_head event_list;
	spinlock_t lock;
};
@@ -213,6 +214,14 @@ struct msm_display_info {

struct display_manager;

/* Commit thread specific structure */
struct msm_drm_commit {
	struct drm_device *dev;
	struct task_struct *thread;
	unsigned int crtc_id;
	struct kthread_worker worker;
};

struct msm_drm_private {

	struct msm_kms *kms;
@@ -271,6 +280,8 @@ struct msm_drm_private {
	unsigned int num_crtcs;
	struct drm_crtc *crtcs[MAX_CRTCS];

	struct msm_drm_commit disp_thread[MAX_CRTCS];

	unsigned int num_encoders;
	struct drm_encoder *encoders[MAX_ENCODERS];