msm_atomic.c 7.9 KB
Newer Older
R
Rob Clark 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright (C) 2014 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "msm_drv.h"
#include "msm_kms.h"
#include "msm_gem.h"
21
#include "msm_fence.h"
R
Rob Clark 已提交
22 23

struct msm_commit {
24
	struct drm_device *dev;
R
Rob Clark 已提交
25
	struct drm_atomic_state *state;
R
Rob Clark 已提交
26
	struct work_struct work;
27
	uint32_t crtc_mask;
R
Rob Clark 已提交
28 29
};

R
Rob Clark 已提交
30
static void commit_worker(struct work_struct *work);
R
Rob Clark 已提交
31

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
/* block until specified crtcs are no longer pending update, and
 * atomically mark them as pending update
 */
static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
{
	int ret;

	spin_lock(&priv->pending_crtcs_event.lock);
	ret = wait_event_interruptible_locked(priv->pending_crtcs_event,
			!(priv->pending_crtcs & crtc_mask));
	if (ret == 0) {
		DBG("start: %08x", crtc_mask);
		priv->pending_crtcs |= crtc_mask;
	}
	spin_unlock(&priv->pending_crtcs_event.lock);

	return ret;
}

/* clear specified crtcs (no longer pending update)
 */
static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
{
	spin_lock(&priv->pending_crtcs_event.lock);
	DBG("end: %08x", crtc_mask);
	priv->pending_crtcs &= ~crtc_mask;
	wake_up_all_locked(&priv->pending_crtcs_event);
	spin_unlock(&priv->pending_crtcs_event.lock);
}

62
static struct msm_commit *commit_init(struct drm_atomic_state *state)
R
Rob Clark 已提交
63 64 65 66 67 68
{
	struct msm_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);

	if (!c)
		return NULL;

69
	c->dev = state->dev;
R
Rob Clark 已提交
70
	c->state = state;
71

R
Rob Clark 已提交
72
	INIT_WORK(&c->work, commit_worker);
R
Rob Clark 已提交
73 74 75 76

	return c;
}

77 78 79 80 81 82
static void commit_destroy(struct msm_commit *c)
{
	end_atomic(c->dev->dev_private, c->crtc_mask);
	kfree(c);
}

83 84 85 86
static void msm_atomic_wait_for_commit_done(struct drm_device *dev,
		struct drm_atomic_state *old_state)
{
	struct drm_crtc *crtc;
87
	struct drm_crtc_state *new_crtc_state;
88 89 90 91
	struct msm_drm_private *priv = old_state->dev->dev_private;
	struct msm_kms *kms = priv->kms;
	int i;

92 93
	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
		if (!new_crtc_state->active)
94 95 96 97 98 99
			continue;

		kms->funcs->wait_for_crtc_commit_done(kms, crtc);
	}
}

R
Rob Clark 已提交
100 101 102
/* The (potentially) asynchronous part of the commit.  At this point
 * nothing can fail short of armageddon.
 */
R
Rob Clark 已提交
103
static void complete_commit(struct msm_commit *c, bool async)
R
Rob Clark 已提交
104 105 106
{
	struct drm_atomic_state *state = c->state;
	struct drm_device *dev = state->dev;
107 108 109
	struct msm_drm_private *priv = dev->dev_private;
	struct msm_kms *kms = priv->kms;

110
	drm_atomic_helper_wait_for_fences(dev, state, false);
R
Rob Clark 已提交
111

112
	kms->funcs->prepare_commit(kms, state);
R
Rob Clark 已提交
113

114
	drm_atomic_helper_commit_modeset_disables(dev, state);
R
Rob Clark 已提交
115

116
	drm_atomic_helper_commit_planes(dev, state, 0);
R
Rob Clark 已提交
117

118
	drm_atomic_helper_commit_modeset_enables(dev, state);
R
Rob Clark 已提交
119

120 121 122 123 124 125 126 127 128 129 130 131 132
	/* NOTE: _wait_for_vblanks() only waits for vblank on
	 * enabled CRTCs.  So we end up faulting when disabling
	 * due to (potentially) unref'ing the outgoing fb's
	 * before the vblank when the disable has latched.
	 *
	 * But if it did wait on disabled (or newly disabled)
	 * CRTCs, that would be racy (ie. we could have missed
	 * the irq.  We need some way to poll for pipe shut
	 * down.  Or just live with occasionally hitting the
	 * timeout in the CRTC disable path (which really should
	 * not be critical path)
	 */

133
	msm_atomic_wait_for_commit_done(dev, state);
R
Rob Clark 已提交
134 135 136

	drm_atomic_helper_cleanup_planes(dev, state);

137
	kms->funcs->complete_commit(kms, state);
R
Rob Clark 已提交
138

139
	drm_atomic_state_put(state);
140

141
	commit_destroy(c);
R
Rob Clark 已提交
142 143
}

R
Rob Clark 已提交
144
static void commit_worker(struct work_struct *work)
R
Rob Clark 已提交
145
{
R
Rob Clark 已提交
146
	complete_commit(container_of(work, struct msm_commit, work), true);
R
Rob Clark 已提交
147 148 149 150 151 152
}

/**
 * drm_atomic_helper_commit - commit validated state object
 * @dev: DRM device
 * @state: the driver state object
153
 * @nonblock: nonblocking commit
R
Rob Clark 已提交
154 155
 *
 * This function commits a with drm_atomic_helper_check() pre-validated state
156
 * object. This can still fail when e.g. the framebuffer reservation fails.
R
Rob Clark 已提交
157 158 159 160 161
 *
 * RETURNS
 * Zero for success or -errno.
 */
int msm_atomic_commit(struct drm_device *dev,
162
		struct drm_atomic_state *state, bool nonblock)
R
Rob Clark 已提交
163
{
R
Rob Clark 已提交
164
	struct msm_drm_private *priv = dev->dev_private;
165
	struct msm_commit *c;
D
Daniel Vetter 已提交
166 167 168
	struct drm_crtc *crtc;
	struct drm_crtc_state *crtc_state;
	struct drm_plane *plane;
169
	struct drm_plane_state *old_plane_state, *new_plane_state;
R
Rob Clark 已提交
170 171 172 173 174 175
	int i, ret;

	ret = drm_atomic_helper_prepare_planes(dev, state);
	if (ret)
		return ret;

176 177 178 179 180 181 182 183 184 185 186 187
	/*
	 * Note that plane->atomic_async_check() should fail if we need
	 * to re-assign hwpipe or anything that touches global atomic
	 * state, so we'll never go down the async update path in those
	 * cases.
	 */
	if (state->async_update) {
		drm_atomic_helper_async_commit(dev, state);
		drm_atomic_helper_cleanup_planes(dev, state);
		return 0;
	}

188
	c = commit_init(state);
189 190 191 192
	if (!c) {
		ret = -ENOMEM;
		goto error;
	}
193 194 195 196

	/*
	 * Figure out what crtcs we have:
	 */
197
	for_each_new_crtc_in_state(state, crtc, crtc_state, i)
D
Daniel Vetter 已提交
198
		c->crtc_mask |= drm_crtc_mask(crtc);
R
Rob Clark 已提交
199

R
Rob Clark 已提交
200 201 202
	/*
	 * Figure out what fence to wait for:
	 */
203 204 205
	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
		if ((new_plane_state->fb != old_plane_state->fb) && new_plane_state->fb) {
			struct drm_gem_object *obj = msm_framebuffer_bo(new_plane_state->fb, 0);
R
Rob Clark 已提交
206
			struct msm_gem_object *msm_obj = to_msm_bo(obj);
207
			struct dma_fence *fence = reservation_object_get_excl_rcu(msm_obj->resv);
R
Rob Clark 已提交
208

209
			drm_atomic_set_fence_for_plane(new_plane_state, fence);
R
Rob Clark 已提交
210 211 212
		}
	}

213 214 215 216 217
	/*
	 * Wait for pending updates on any of the same crtc's and then
	 * mark our set of crtc's as busy:
	 */
	ret = start_atomic(dev->dev_private, c->crtc_mask);
218 219 220 221
	if (ret)
		goto err_free;

	BUG_ON(drm_atomic_helper_swap_state(state, false) < 0);
222

R
Rob Clark 已提交
223 224 225 226
	/*
	 * This is the point of no return - everything below never fails except
	 * when the hw goes bonghits. Which means we can commit the new state on
	 * the software side now.
227 228
	 *
	 * swap driver private state while still holding state_lock
R
Rob Clark 已提交
229
	 */
R
Rob Clark 已提交
230 231 232
	if (to_kms_state(state)->state)
		priv->kms->funcs->swap_state(priv->kms, state);

R
Rob Clark 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
	/*
	 * Everything below can be run asynchronously without the need to grab
	 * any modeset locks at all under one conditions: It must be guaranteed
	 * that the asynchronous work has either been cancelled (if the driver
	 * supports it, which at least requires that the framebuffers get
	 * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
	 * before the new state gets committed on the software side with
	 * drm_atomic_helper_swap_state().
	 *
	 * This scheme allows new atomic state updates to be prepared and
	 * checked in parallel to the asynchronous completion of the previous
	 * update. Which is important since compositors need to figure out the
	 * composition of the next frame right after having submitted the
	 * current layout.
	 */

249
	drm_atomic_state_get(state);
R
Rob Clark 已提交
250 251
	if (nonblock) {
		queue_work(priv->atomic_wq, &c->work);
R
Rob Clark 已提交
252 253 254
		return 0;
	}

R
Rob Clark 已提交
255
	complete_commit(c, false);
R
Rob Clark 已提交
256 257

	return 0;
258

259 260
err_free:
	kfree(c);
261 262 263
error:
	drm_atomic_helper_cleanup_planes(dev, state);
	return ret;
R
Rob Clark 已提交
264
}
R
Rob Clark 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev)
{
	struct msm_kms_state *state = kzalloc(sizeof(*state), GFP_KERNEL);

	if (!state || drm_atomic_state_init(dev, &state->base) < 0) {
		kfree(state);
		return NULL;
	}

	return &state->base;
}

void msm_atomic_state_clear(struct drm_atomic_state *s)
{
	struct msm_kms_state *state = to_kms_state(s);
	drm_atomic_state_default_clear(&state->base);
	kfree(state->state);
	state->state = NULL;
}

void msm_atomic_state_free(struct drm_atomic_state *state)
{
	kfree(to_kms_state(state)->state);
	drm_atomic_state_default_release(state);
	kfree(state);
}