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

Commit 21773f16 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'topic/atomic-core-2015-01-27' of git://anongit.freedesktop.org/drm-intel into drm-next

* tag 'topic/atomic-core-2015-01-27' of git://anongit.freedesktop.org/drm-intel:
  drm/atomic: Fix potential use of state after free
  drm/atomic-helper: debug output for modesets
  drm/atomic-helpers: Saner encoder/crtc callbacks
  drm/atomic-helpers: Recover full cursor plane behaviour
  drm/atomic-helper: add connector->dpms() implementation
  drm/atomic: Add drm_crtc_state->active
  drm: Add standardized boolean props
  drm/plane-helper: Fix transitional helper kerneldocs
  drm/plane-helper: Skip prepare_fb/cleanup_fb when newfb==oldfb

Conflicts:
	include/drm/drm_crtc_helper.h
parents 2f5b4ef1 9469244d
Loading
Loading
Loading
Loading
+19 −2
Original line number Diff line number Diff line
@@ -134,6 +134,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)

		connector->funcs->atomic_destroy_state(connector,
						       state->connector_states[i]);
		state->connector_states[i] = NULL;
	}

	for (i = 0; i < config->num_crtc; i++) {
@@ -144,6 +145,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)

		crtc->funcs->atomic_destroy_state(crtc,
						  state->crtc_states[i]);
		state->crtc_states[i] = NULL;
	}

	for (i = 0; i < config->num_total_plane; i++) {
@@ -154,6 +156,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)

		plane->funcs->atomic_destroy_state(plane,
						   state->plane_states[i]);
		state->plane_states[i] = NULL;
	}
}
EXPORT_SYMBOL(drm_atomic_state_clear);
@@ -241,7 +244,13 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
		struct drm_crtc_state *state, struct drm_property *property,
		uint64_t val)
{
	if (crtc->funcs->atomic_set_property)
	struct drm_device *dev = crtc->dev;
	struct drm_mode_config *config = &dev->mode_config;

	/* FIXME: Mode prop is missing, which also controls ->enable. */
	if (property == config->prop_active) {
		state->active = val;
	} else if (crtc->funcs->atomic_set_property)
		return crtc->funcs->atomic_set_property(crtc, state, property, val);
	return -EINVAL;
}
@@ -282,6 +291,13 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
	 *
	 * TODO: Add generic modeset state checks once we support those.
	 */

	if (state->active && !state->enable) {
		DRM_DEBUG_KMS("[CRTC:%d] active without enabled\n",
			      crtc->base.id);
		return -EINVAL;
	}

	return 0;
}

@@ -978,7 +994,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
			if (!crtc)
				continue;

			if (crtc_state->mode_changed) {
			if (crtc_state->mode_changed ||
			    crtc_state->active_changed) {
				DRM_DEBUG_KMS("[CRTC:%d] requires full modeset\n",
					      crtc->base.id);
				return -EINVAL;
+170 −11
Original line number Diff line number Diff line
@@ -339,6 +339,12 @@ mode_fixup(struct drm_atomic_state *state)
	return 0;
}

static bool
needs_modeset(struct drm_crtc_state *state)
{
	return state->mode_changed || state->active_changed;
}

/**
 * drm_atomic_helper_check - validate state object for modeset changes
 * @dev: DRM device
@@ -413,12 +419,27 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
		crtc = state->crtcs[i];
		crtc_state = state->crtc_states[i];

		if (!crtc || !crtc_state->mode_changed)
		if (!crtc)
			continue;

		DRM_DEBUG_KMS("[CRTC:%d] needs full modeset, enable: %c\n",
		/*
		 * We must set ->active_changed after walking connectors for
		 * otherwise an update that only changes active would result in
		 * a full modeset because update_connector_routing force that.
		 */
		if (crtc->state->active != crtc_state->active) {
			DRM_DEBUG_KMS("[CRTC:%d] active changed\n",
				      crtc->base.id);
			crtc_state->active_changed = true;
		}

		if (!needs_modeset(crtc_state))
			continue;

		DRM_DEBUG_KMS("[CRTC:%d] needs all connectors, enable: %c, active: %c\n",
			      crtc->base.id,
			      crtc_state->enable ? 'y' : 'n');
			      crtc_state->enable ? 'y' : 'n',
			      crtc_state->active ? 'y' : 'n');

		ret = drm_atomic_add_affected_connectors(state, crtc);
		if (ret != 0)
@@ -554,6 +575,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
		struct drm_connector *connector;
		struct drm_encoder_helper_funcs *funcs;
		struct drm_encoder *encoder;
		struct drm_crtc_state *old_crtc_state;

		old_conn_state = old_state->connector_states[i];
		connector = old_state->connectors[i];
@@ -563,6 +585,11 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
		if (!old_conn_state || !old_conn_state->crtc)
			continue;

		old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)];

		if (!old_crtc_state->active)
			continue;

		encoder = old_conn_state->best_encoder;

		/* We shouldn't get this far if we didn't previously have
@@ -573,6 +600,9 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)

		funcs = encoder->helper_private;

		DRM_DEBUG_KMS("disabling [ENCODER:%d:%s]\n",
			      encoder->base.id, encoder->name);

		/*
		 * Each encoder has at most one connector (since we always steal
		 * it away), so we won't call call disable hooks twice.
@@ -581,7 +611,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
			encoder->bridge->funcs->disable(encoder->bridge);

		/* Right function depends upon target state. */
		if (connector->state->crtc)
		if (connector->state->crtc && funcs->prepare)
			funcs->prepare(encoder);
		else if (funcs->disable)
			funcs->disable(encoder);
@@ -595,17 +625,26 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
	for (i = 0; i < ncrtcs; i++) {
		struct drm_crtc_helper_funcs *funcs;
		struct drm_crtc *crtc;
		struct drm_crtc_state *old_crtc_state;

		crtc = old_state->crtcs[i];
		old_crtc_state = old_state->crtc_states[i];

		/* Shut down everything that needs a full modeset. */
		if (!crtc || !crtc->state->mode_changed)
		if (!crtc || !needs_modeset(crtc->state))
			continue;

		if (!old_crtc_state->active)
			continue;

		funcs = crtc->helper_private;

		DRM_DEBUG_KMS("disabling [CRTC:%d]\n",
			      crtc->base.id);


		/* Right function depends upon target state. */
		if (crtc->state->enable)
		if (crtc->state->enable && funcs->prepare)
			funcs->prepare(crtc);
		else if (funcs->disable)
			funcs->disable(crtc);
@@ -684,9 +723,13 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)

		funcs = crtc->helper_private;

		if (crtc->state->enable)
		if (crtc->state->enable) {
			DRM_DEBUG_KMS("modeset on [CRTC:%d]\n",
				      crtc->base.id);

			funcs->mode_set_nofb(crtc);
		}
	}

	for (i = 0; i < old_state->num_connector; i++) {
		struct drm_connector *connector;
@@ -706,6 +749,12 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
		mode = &new_crtc_state->mode;
		adjusted_mode = &new_crtc_state->adjusted_mode;

		if (!new_crtc_state->mode_changed)
			continue;

		DRM_DEBUG_KMS("modeset on [ENCODER:%d:%s]\n",
			      encoder->base.id, encoder->name);

		/*
		 * Each encoder has at most one connector (since we always steal
		 * it away), so we won't call call mode_set hooks twice.
@@ -758,14 +807,24 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
		crtc = old_state->crtcs[i];

		/* Need to filter out CRTCs where only planes change. */
		if (!crtc || !crtc->state->mode_changed)
		if (!crtc || !needs_modeset(crtc->state))
			continue;

		if (!crtc->state->active)
			continue;

		funcs = crtc->helper_private;

		if (crtc->state->enable)
		if (crtc->state->enable) {
			DRM_DEBUG_KMS("enabling [CRTC:%d]\n",
				      crtc->base.id);

			if (funcs->enable)
				funcs->enable(crtc);
			else
				funcs->commit(crtc);
		}
	}

	for (i = 0; i < old_state->num_connector; i++) {
		struct drm_connector *connector;
@@ -777,9 +836,15 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
		if (!connector || !connector->state->best_encoder)
			continue;

		if (!connector->state->crtc->state->active)
			continue;

		encoder = connector->state->best_encoder;
		funcs = encoder->helper_private;

		DRM_DEBUG_KMS("enabling [ENCODER:%d:%s]\n",
			      encoder->base.id, encoder->name);

		/*
		 * Each encoder has at most one connector (since we always steal
		 * it away), so we won't call call enable hooks twice.
@@ -787,6 +852,9 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
		if (encoder->bridge)
			encoder->bridge->funcs->pre_enable(encoder->bridge);

		if (funcs->enable)
			funcs->enable(encoder);
		else
			funcs->commit(encoder);

		if (encoder->bridge)
@@ -877,6 +945,11 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
		if (!crtc->state->enable)
			continue;

		/* Legacy cursor ioctls are completely unsynced, and userspace
		 * relies on that (by doing tons of cursor updates). */
		if (old_state->legacy_cursor_update)
			continue;

		if (!framebuffer_changed(dev, old_state, crtc))
			continue;

@@ -1310,6 +1383,9 @@ int drm_atomic_helper_update_plane(struct drm_plane *plane,
	if (ret != 0)
		goto fail;

	if (plane == crtc->cursor)
		state->legacy_cursor_update = true;

	/* Driver takes ownership of state on successful commit. */
	return 0;
fail:
@@ -1385,6 +1461,9 @@ int drm_atomic_helper_disable_plane(struct drm_plane *plane)
	plane_state->src_h = 0;
	plane_state->src_w = 0;

	if (plane == plane->crtc->cursor)
		state->legacy_cursor_update = true;

	ret = drm_atomic_commit(state);
	if (ret != 0)
		goto fail;
@@ -1534,6 +1613,7 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set)
		WARN_ON(set->num_connectors);

		crtc_state->enable = false;
		crtc_state->active = false;

		ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
		if (ret != 0)
@@ -1548,6 +1628,7 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set)
	WARN_ON(!set->num_connectors);

	crtc_state->enable = true;
	crtc_state->active = true;
	drm_mode_copy(&crtc_state->mode, set->mode);

	ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
@@ -1859,6 +1940,83 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
}
EXPORT_SYMBOL(drm_atomic_helper_page_flip);

/**
 * drm_atomic_helper_connector_dpms() - connector dpms helper implementation
 * @connector: affected connector
 * @mode: DPMS mode
 *
 * This is the main helper function provided by the atomic helper framework for
 * implementing the legacy DPMS connector interface. It computes the new desired
 * ->active state for the corresponding CRTC (if the connector is enabled) and
 *  updates it.
 */
void drm_atomic_helper_connector_dpms(struct drm_connector *connector,
				      int mode)
{
	struct drm_mode_config *config = &connector->dev->mode_config;
	struct drm_atomic_state *state;
	struct drm_crtc_state *crtc_state;
	struct drm_crtc *crtc;
	struct drm_connector *tmp_connector;
	int ret;
	bool active = false;

	if (mode != DRM_MODE_DPMS_ON)
		mode = DRM_MODE_DPMS_OFF;

	connector->dpms = mode;
	crtc = connector->state->crtc;

	if (!crtc)
		return;

	/* FIXME: ->dpms has no return value so can't forward the -ENOMEM. */
	state = drm_atomic_state_alloc(connector->dev);
	if (!state)
		return;

	state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc);
retry:
	crtc_state = drm_atomic_get_crtc_state(state, crtc);
	if (IS_ERR(crtc_state))
		return;

	WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));

	list_for_each_entry(tmp_connector, &config->connector_list, head) {
		if (connector->state->crtc != crtc)
			continue;

		if (connector->dpms == DRM_MODE_DPMS_ON) {
			active = true;
			break;
		}
	}
	crtc_state->active = active;

	ret = drm_atomic_commit(state);
	if (ret != 0)
		goto fail;

	/* Driver takes ownership of state on successful async commit. */
	return;
fail:
	if (ret == -EDEADLK)
		goto backoff;

	drm_atomic_state_free(state);

	WARN(1, "Driver bug: Changing ->active failed with ret=%i\n", ret);

	return;
backoff:
	drm_atomic_state_clear(state);
	drm_atomic_legacy_backoff(state);

	goto retry;
}
EXPORT_SYMBOL(drm_atomic_helper_connector_dpms);

/**
 * DOC: atomic state reset and initialization
 *
@@ -1910,6 +2068,7 @@ drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc)

	if (state) {
		state->mode_changed = false;
		state->active_changed = false;
		state->planes_changed = false;
		state->event = NULL;
	}
+73 −3
Original line number Diff line number Diff line
@@ -691,6 +691,10 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
	if (cursor)
		cursor->possible_crtcs = 1 << drm_crtc_index(crtc);

	if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
		drm_object_attach_property(&crtc->base, config->prop_active, 0);
	}

	return 0;
}
EXPORT_SYMBOL(drm_crtc_init_with_planes);
@@ -1481,6 +1485,12 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
		return -ENOMEM;
	dev->mode_config.prop_crtc_id = prop;

	prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC,
			"ACTIVE");
	if (!prop)
		return -ENOMEM;
	dev->mode_config.prop_active = prop;

	return 0;
}

@@ -3810,7 +3820,7 @@ static struct drm_property *property_create_range(struct drm_device *dev,
}

/**
 * drm_property_create_range - create a new ranged property type
 * drm_property_create_range - create a new unsigned ranged property type
 * @dev: drm device
 * @flags: flags specifying the property type
 * @name: name of the property
@@ -3821,8 +3831,8 @@ static struct drm_property *property_create_range(struct drm_device *dev,
 * object with drm_object_attach_property. The returned property object must be
 * freed with drm_property_destroy.
 *
 * Userspace is allowed to set any integer value in the (min, max) range
 * inclusive.
 * Userspace is allowed to set any unsigned integer value in the (min, max)
 * range inclusive.
 *
 * Returns:
 * A pointer to the newly created property on success, NULL on failure.
@@ -3836,6 +3846,24 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
}
EXPORT_SYMBOL(drm_property_create_range);

/**
 * drm_property_create_signed_range - create a new signed ranged property type
 * @dev: drm device
 * @flags: flags specifying the property type
 * @name: name of the property
 * @min: minimum value of the property
 * @max: maximum value of the property
 *
 * This creates a new generic drm property which can then be attached to a drm
 * object with drm_object_attach_property. The returned property object must be
 * freed with drm_property_destroy.
 *
 * Userspace is allowed to set any signed integer value in the (min, max)
 * range inclusive.
 *
 * Returns:
 * A pointer to the newly created property on success, NULL on failure.
 */
struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
					 int flags, const char *name,
					 int64_t min, int64_t max)
@@ -3845,6 +3873,23 @@ struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_property_create_signed_range);

/**
 * drm_property_create_object - create a new object property type
 * @dev: drm device
 * @flags: flags specifying the property type
 * @name: name of the property
 * @type: object type from DRM_MODE_OBJECT_* defines
 *
 * This creates a new generic drm property which can then be attached to a drm
 * object with drm_object_attach_property. The returned property object must be
 * freed with drm_property_destroy.
 *
 * Userspace is only allowed to set this to any property value of the given
 * @type. Only useful for atomic properties, which is enforced.
 *
 * Returns:
 * A pointer to the newly created property on success, NULL on failure.
 */
struct drm_property *drm_property_create_object(struct drm_device *dev,
					 int flags, const char *name, uint32_t type)
{
@@ -3852,6 +3897,9 @@ struct drm_property *drm_property_create_object(struct drm_device *dev,

	flags |= DRM_MODE_PROP_OBJECT;

	if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC)))
		return NULL;

	property = drm_property_create(dev, flags, name, 1);
	if (!property)
		return NULL;
@@ -3862,6 +3910,28 @@ struct drm_property *drm_property_create_object(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_property_create_object);

/**
 * drm_property_create_bool - create a new boolean property type
 * @dev: drm device
 * @flags: flags specifying the property type
 * @name: name of the property
 *
 * This creates a new generic drm property which can then be attached to a drm
 * object with drm_object_attach_property. The returned property object must be
 * freed with drm_property_destroy.
 *
 * This is implemented as a ranged property with only {0, 1} as valid values.
 *
 * Returns:
 * A pointer to the newly created property on success, NULL on failure.
 */
struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags,
					 const char *name)
{
	return drm_property_create_range(dev, flags, name, 0, 1);
}
EXPORT_SYMBOL(drm_property_create_bool);

/**
 * drm_property_add_enum - add a possible value to an enumeration property
 * @property: enumeration property to change
+11 −3
Original line number Diff line number Diff line
@@ -435,7 +435,8 @@ int drm_plane_helper_commit(struct drm_plane *plane,
			goto out;
	}

	if (plane_funcs->prepare_fb && plane_state->fb) {
	if (plane_funcs->prepare_fb && plane_state->fb &&
	    plane_state->fb != old_fb) {
		ret = plane_funcs->prepare_fb(plane, plane_state->fb);
		if (ret)
			goto out;
@@ -464,6 +465,13 @@ int drm_plane_helper_commit(struct drm_plane *plane,
			crtc_funcs[i]->atomic_flush(crtc[i]);
	}

	/*
	 * If we only moved the plane and didn't change fb's, there's no need to
	 * wait for vblank.
	 */
	if (plane->state->fb == old_fb)
		goto out;

	for (i = 0; i < 2; i++) {
		if (!crtc[i])
			continue;
@@ -492,7 +500,7 @@ int drm_plane_helper_commit(struct drm_plane *plane,
}

/**
 * drm_plane_helper_update() - Helper for primary plane update
 * drm_plane_helper_update() - Transitional helper for plane update
 * @plane: plane object to update
 * @crtc: owning CRTC of owning plane
 * @fb: framebuffer to flip onto plane
@@ -549,7 +557,7 @@ int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
EXPORT_SYMBOL(drm_plane_helper_update);

/**
 * drm_plane_helper_disable() - Helper for primary plane disable
 * drm_plane_helper_disable() - Transitional helper for plane disable
 * @plane: plane to disable
 *
 * Provides a default plane disable handler using the atomic plane update
+2 −0
Original line number Diff line number Diff line
@@ -82,6 +82,8 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
				struct drm_framebuffer *fb,
				struct drm_pending_vblank_event *event,
				uint32_t flags);
void drm_atomic_helper_connector_dpms(struct drm_connector *connector,
				      int mode);

/* default implementations for state handling */
void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc);
Loading