msm_atomic.c 8.4 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;
D
Daniel Vetter 已提交
87
	struct drm_crtc_state *crtc_state;
88 89 90 91
	struct msm_drm_private *priv = old_state->dev->dev_private;
	struct msm_kms *kms = priv->kms;
	int i;

D
Daniel Vetter 已提交
92
	for_each_crtc_in_state(old_state, crtc, crtc_state, i) {
93 94 95 96 97 98 99
		if (!crtc->state->enable)
			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 153 154 155 156 157 158 159 160 161
/*
 * this func is identical to the drm_atomic_helper_check, but we keep this
 * because we might eventually need to have a more finegrained check
 * sequence without using the atomic helpers.
 *
 * In the past, we first called drm_atomic_helper_check_planes, and then
 * drm_atomic_helper_check_modeset. We needed this because the MDP5 plane's
 * ->atomic_check could update ->mode_changed for pixel format changes.
 * This, however isn't needed now because if there is a pixel format change,
 * we just assign a new hwpipe for it with a new SMP allocation. We might
 * eventually hit a condition where we would need to do a full modeset if
 * we run out of planes. There, we'd probably need to set mode_changed.
 */
162 163 164 165 166
int msm_atomic_check(struct drm_device *dev,
		     struct drm_atomic_state *state)
{
	int ret;

167
	ret = drm_atomic_helper_check_modeset(dev, state);
168 169 170
	if (ret)
		return ret;

171
	ret = drm_atomic_helper_check_planes(dev, state);
172 173 174 175 176 177
	if (ret)
		return ret;

	return ret;
}

R
Rob Clark 已提交
178 179 180 181
/**
 * drm_atomic_helper_commit - commit validated state object
 * @dev: DRM device
 * @state: the driver state object
182
 * @nonblock: nonblocking commit
R
Rob Clark 已提交
183 184
 *
 * This function commits a with drm_atomic_helper_check() pre-validated state
185
 * object. This can still fail when e.g. the framebuffer reservation fails.
R
Rob Clark 已提交
186 187 188 189 190
 *
 * RETURNS
 * Zero for success or -errno.
 */
int msm_atomic_commit(struct drm_device *dev,
191
		struct drm_atomic_state *state, bool nonblock)
R
Rob Clark 已提交
192
{
R
Rob Clark 已提交
193
	struct msm_drm_private *priv = dev->dev_private;
194
	struct msm_commit *c;
D
Daniel Vetter 已提交
195 196 197 198
	struct drm_crtc *crtc;
	struct drm_crtc_state *crtc_state;
	struct drm_plane *plane;
	struct drm_plane_state *plane_state;
R
Rob Clark 已提交
199 200 201 202 203 204
	int i, ret;

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

205
	c = commit_init(state);
206 207 208 209
	if (!c) {
		ret = -ENOMEM;
		goto error;
	}
210 211 212 213

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

R
Rob Clark 已提交
217 218 219
	/*
	 * Figure out what fence to wait for:
	 */
D
Daniel Vetter 已提交
220 221 222
	for_each_plane_in_state(state, plane, plane_state, i) {
		if ((plane->state->fb != plane_state->fb) && plane_state->fb) {
			struct drm_gem_object *obj = msm_framebuffer_bo(plane_state->fb, 0);
R
Rob Clark 已提交
223
			struct msm_gem_object *msm_obj = to_msm_bo(obj);
224
			struct dma_fence *fence = reservation_object_get_excl_rcu(msm_obj->resv);
R
Rob Clark 已提交
225

226
			drm_atomic_set_fence_for_plane(plane_state, fence);
R
Rob Clark 已提交
227 228 229
		}
	}

230 231 232 233 234
	/*
	 * 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);
235 236
	if (ret) {
		kfree(c);
237
		goto error;
238
	}
239

R
Rob Clark 已提交
240 241 242 243 244 245
	/*
	 * 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.
	 */

246
	drm_atomic_helper_swap_state(state, true);
R
Rob Clark 已提交
247

R
Rob Clark 已提交
248 249 250 251
	/* swap driver private state while still holding state_lock */
	if (to_kms_state(state)->state)
		priv->kms->funcs->swap_state(priv->kms, state);

R
Rob Clark 已提交
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	/*
	 * 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.
	 */

268
	drm_atomic_state_get(state);
R
Rob Clark 已提交
269 270
	if (nonblock) {
		queue_work(priv->atomic_wq, &c->work);
R
Rob Clark 已提交
271 272 273
		return 0;
	}

R
Rob Clark 已提交
274
	complete_commit(c, false);
R
Rob Clark 已提交
275 276

	return 0;
277 278 279 280

error:
	drm_atomic_helper_cleanup_planes(dev, state);
	return ret;
R
Rob Clark 已提交
281
}
R
Rob Clark 已提交
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308

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);
}