Commit 9d9486e4 authored by Thomas Hellstrom's avatar Thomas Hellstrom
Browse files

drm/vmwgfx: Fix up the implicit display unit handling



Make the connector is_implicit property immutable.
As far as we know, no user-space application is writing to it.

Also move the verification that all implicit display units scan out
from the same framebuffer to atomic_check().

Signed-off-by: default avatarThomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: default avatarSinclair Yeh <syeh@vmware.com>
Reviewed-by: default avatarDeepak Rawat <drawat@vmware.com>
parent 66502d49
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -484,8 +484,6 @@ struct vmw_private {
	struct vmw_overlay *overlay_priv;
	struct drm_property *hotplug_mode_update_property;
	struct drm_property *implicit_placement_property;
	unsigned num_implicit;
	struct vmw_framebuffer *implicit_fb;
	struct mutex global_kms_state_mutex;
	spinlock_t cursor_lock;
	struct drm_atomic_state *suspend_state;
+88 −189
Original line number Diff line number Diff line
@@ -457,21 +457,8 @@ int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
		struct drm_crtc *crtc = state->crtc;
		struct vmw_connector_state *vcs;
		struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
		struct vmw_private *dev_priv = vmw_priv(crtc->dev);
		struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb);

		vcs = vmw_connector_state_to_vcs(du->connector.state);

		/* Only one active implicit framebuffer at a time. */
		mutex_lock(&dev_priv->global_kms_state_mutex);
		if (vcs->is_implicit && dev_priv->implicit_fb &&
		    !(dev_priv->num_implicit == 1 && du->active_implicit)
		    && dev_priv->implicit_fb != vfb) {
			DRM_ERROR("Multiple implicit framebuffers "
				  "not supported.\n");
			ret = -EINVAL;
		}
		mutex_unlock(&dev_priv->global_kms_state_mutex);
	}


@@ -1519,6 +1506,88 @@ static int vmw_kms_check_display_memory(struct drm_device *dev,
	return 0;
}

/**
 * vmw_crtc_state_and_lock - Return new or current crtc state with locked
 * crtc mutex
 * @state: The atomic state pointer containing the new atomic state
 * @crtc: The crtc
 *
 * This function returns the new crtc state if it's part of the state update.
 * Otherwise returns the current crtc state. It also makes sure that the
 * crtc mutex is locked.
 *
 * Returns: A valid crtc state pointer or NULL. It may also return a
 * pointer error, in particular -EDEADLK if locking needs to be rerun.
 */
static struct drm_crtc_state *
vmw_crtc_state_and_lock(struct drm_atomic_state *state, struct drm_crtc *crtc)
{
	struct drm_crtc_state *crtc_state;

	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
	if (crtc_state) {
		lockdep_assert_held(&crtc->mutex.mutex.base);
	} else {
		int ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx);

		if (ret != 0 && ret != -EALREADY)
			return ERR_PTR(ret);

		crtc_state = crtc->state;
	}

	return crtc_state;
}

/**
 * vmw_kms_check_implicit - Verify that all implicit display units scan out
 * from the same fb after the new state is committed.
 * @dev: The drm_device.
 * @state: The new state to be checked.
 *
 * Returns:
 *   Zero on success,
 *   -EINVAL on invalid state,
 *   -EDEADLK if modeset locking needs to be rerun.
 */
static int vmw_kms_check_implicit(struct drm_device *dev,
				  struct drm_atomic_state *state)
{
	struct drm_framebuffer *implicit_fb = NULL;
	struct drm_crtc *crtc;
	struct drm_crtc_state *crtc_state;
	struct drm_plane_state *plane_state;

	drm_for_each_crtc(crtc, dev) {
		struct vmw_display_unit *du = vmw_crtc_to_du(crtc);

		if (!du->is_implicit)
			continue;

		crtc_state = vmw_crtc_state_and_lock(state, crtc);
		if (IS_ERR(crtc_state))
			return PTR_ERR(crtc_state);

		if (!crtc_state || !crtc_state->enable)
			continue;

		/*
		 * Can't move primary planes across crtcs, so this is OK.
		 * It also means we don't need to take the plane mutex.
		 */
		plane_state = du->primary.state;
		if (plane_state->crtc != crtc)
			continue;

		if (!implicit_fb)
			implicit_fb = plane_state->fb;
		else if (implicit_fb != plane_state->fb)
			return -EINVAL;
	}

	return 0;
}

/**
 * vmw_kms_check_topology - Validates topology in drm_atomic_state
 * @dev: DRM device
@@ -1636,6 +1705,10 @@ vmw_kms_atomic_check_modeset(struct drm_device *dev,
	if (ret)
		return ret;

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

	if (!state->allow_modeset)
		return ret;

@@ -2230,84 +2303,6 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
	return 1;
}

int vmw_du_connector_set_property(struct drm_connector *connector,
				  struct drm_property *property,
				  uint64_t val)
{
	struct vmw_display_unit *du = vmw_connector_to_du(connector);
	struct vmw_private *dev_priv = vmw_priv(connector->dev);

	if (property == dev_priv->implicit_placement_property)
		du->is_implicit = val;

	return 0;
}



/**
 * vmw_du_connector_atomic_set_property - Atomic version of get property
 *
 * @crtc - crtc the property is associated with
 *
 * Returns:
 * Zero on success, negative errno on failure.
 */
int
vmw_du_connector_atomic_set_property(struct drm_connector *connector,
				     struct drm_connector_state *state,
				     struct drm_property *property,
				     uint64_t val)
{
	struct vmw_private *dev_priv = vmw_priv(connector->dev);
	struct vmw_connector_state *vcs = vmw_connector_state_to_vcs(state);
	struct vmw_display_unit *du = vmw_connector_to_du(connector);


	if (property == dev_priv->implicit_placement_property) {
		vcs->is_implicit = val;

		/*
		 * We should really be doing a drm_atomic_commit() to
		 * commit the new state, but since this doesn't cause
		 * an immedate state change, this is probably ok
		 */
		du->is_implicit = vcs->is_implicit;
	} else {
		return -EINVAL;
	}

	return 0;
}


/**
 * vmw_du_connector_atomic_get_property - Atomic version of get property
 *
 * @connector - connector the property is associated with
 *
 * Returns:
 * Zero on success, negative errno on failure.
 */
int
vmw_du_connector_atomic_get_property(struct drm_connector *connector,
				     const struct drm_connector_state *state,
				     struct drm_property *property,
				     uint64_t *val)
{
	struct vmw_private *dev_priv = vmw_priv(connector->dev);
	struct vmw_connector_state *vcs = vmw_connector_state_to_vcs(state);

	if (property == dev_priv->implicit_placement_property)
		*val = vcs->is_implicit;
	else {
		DRM_ERROR("Invalid Property %s\n", property->name);
		return -EINVAL;
	}

	return 0;
}

/**
 * vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl
 * @dev: drm device for the ioctl
@@ -2696,120 +2691,24 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv,
	return ret;
}

/**
 * vmw_kms_del_active - unregister a crtc binding to the implicit framebuffer
 *
 * @dev_priv: Pointer to a device private struct.
 * @du: The display unit of the crtc.
 */
void vmw_kms_del_active(struct vmw_private *dev_priv,
			struct vmw_display_unit *du)
{
	mutex_lock(&dev_priv->global_kms_state_mutex);
	if (du->active_implicit) {
		if (--(dev_priv->num_implicit) == 0)
			dev_priv->implicit_fb = NULL;
		du->active_implicit = false;
	}
	mutex_unlock(&dev_priv->global_kms_state_mutex);
}

/**
 * vmw_kms_add_active - register a crtc binding to an implicit framebuffer
 *
 * @vmw_priv: Pointer to a device private struct.
 * @du: The display unit of the crtc.
 * @vfb: The implicit framebuffer
 *
 * Registers a binding to an implicit framebuffer.
 */
void vmw_kms_add_active(struct vmw_private *dev_priv,
			struct vmw_display_unit *du,
			struct vmw_framebuffer *vfb)
{
	mutex_lock(&dev_priv->global_kms_state_mutex);
	WARN_ON_ONCE(!dev_priv->num_implicit && dev_priv->implicit_fb);

	if (!du->active_implicit && du->is_implicit) {
		dev_priv->implicit_fb = vfb;
		du->active_implicit = true;
		dev_priv->num_implicit++;
	}
	mutex_unlock(&dev_priv->global_kms_state_mutex);
}

/**
 * vmw_kms_screen_object_flippable - Check whether we can page-flip a crtc.
 *
 * @dev_priv: Pointer to device-private struct.
 * @crtc: The crtc we want to flip.
 *
 * Returns true or false depending whether it's OK to flip this crtc
 * based on the criterion that we must not have more than one implicit
 * frame-buffer at any one time.
 */
bool vmw_kms_crtc_flippable(struct vmw_private *dev_priv,
			    struct drm_crtc *crtc)
{
	struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
	bool ret;

	mutex_lock(&dev_priv->global_kms_state_mutex);
	ret = !du->is_implicit || dev_priv->num_implicit == 1;
	mutex_unlock(&dev_priv->global_kms_state_mutex);

	return ret;
}

/**
 * vmw_kms_update_implicit_fb - Update the implicit fb.
 *
 * @dev_priv: Pointer to device-private struct.
 * @crtc: The crtc the new implicit frame-buffer is bound to.
 */
void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv,
				struct drm_crtc *crtc)
{
	struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
	struct drm_plane *plane = crtc->primary;
	struct vmw_framebuffer *vfb;

	mutex_lock(&dev_priv->global_kms_state_mutex);

	if (!du->is_implicit)
		goto out_unlock;

	vfb = vmw_framebuffer_to_vfb(plane->state->fb);
	WARN_ON_ONCE(dev_priv->num_implicit != 1 &&
		     dev_priv->implicit_fb != vfb);

	dev_priv->implicit_fb = vfb;
out_unlock:
	mutex_unlock(&dev_priv->global_kms_state_mutex);
}

/**
 * vmw_kms_create_implicit_placement_proparty - Set up the implicit placement
 * property.
 *
 * @dev_priv: Pointer to a device private struct.
 * @immutable: Whether the property is immutable.
 *
 * Sets up the implicit placement property unless it's already set up.
 */
void
vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv,
					   bool immutable)
vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv)
{
	if (dev_priv->implicit_placement_property)
		return;

	dev_priv->implicit_placement_property =
		drm_property_create_range(dev_priv->dev,
					  immutable ?
					  DRM_MODE_PROP_IMMUTABLE : 0,
					  DRM_MODE_PROP_IMMUTABLE,
					  "implicit_placement", 0, 1);

}

/**
+2 −14
Original line number Diff line number Diff line
@@ -307,8 +307,6 @@ struct vmw_plane_state {
struct vmw_connector_state {
	struct drm_connector_state base;

	bool is_implicit;

	/**
	 * @gui_x:
	 *
@@ -370,7 +368,6 @@ struct vmw_display_unit {
	int gui_x;
	int gui_y;
	bool is_implicit;
	bool active_implicit;
	int set_gui_x;
	int set_gui_y;
};
@@ -450,17 +447,8 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv,
			    struct drm_crtc **p_crtc,
			    struct drm_display_mode **p_mode);
void vmw_guess_mode_timing(struct drm_display_mode *mode);
void vmw_kms_del_active(struct vmw_private *dev_priv,
			struct vmw_display_unit *du);
void vmw_kms_add_active(struct vmw_private *dev_priv,
			struct vmw_display_unit *du,
			struct vmw_framebuffer *vfb);
bool vmw_kms_crtc_flippable(struct vmw_private *dev_priv,
			    struct drm_crtc *crtc);
void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv,
				struct drm_crtc *crtc);
void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv,
						bool immutable);
void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv);
void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv);

/* Universal Plane Helpers */
void vmw_du_primary_plane_destroy(struct drm_plane *plane);
+1 −8
Original line number Diff line number Diff line
@@ -263,13 +263,10 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = {
	.dpms = vmw_du_connector_dpms,
	.detect = vmw_du_connector_detect,
	.fill_modes = vmw_du_connector_fill_modes,
	.set_property = vmw_du_connector_set_property,
	.destroy = vmw_ldu_connector_destroy,
	.reset = vmw_du_connector_reset,
	.atomic_duplicate_state = vmw_du_connector_duplicate_state,
	.atomic_destroy_state = vmw_du_connector_destroy_state,
	.atomic_set_property = vmw_du_connector_atomic_set_property,
	.atomic_get_property = vmw_du_connector_atomic_get_property,
};

static const struct
@@ -416,7 +413,6 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)

	drm_plane_helper_add(cursor, &vmw_ldu_cursor_plane_helper_funcs);


	vmw_du_connector_reset(connector);
	ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
				 DRM_MODE_CONNECTOR_VIRTUAL);
@@ -427,8 +423,6 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)

	drm_connector_helper_add(connector, &vmw_ldu_connector_helper_funcs);
	connector->status = vmw_du_connector_detect(connector, true);
	vmw_connector_state_to_vcs(connector->state)->is_implicit = true;


	ret = drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs,
			       DRM_MODE_ENCODER_VIRTUAL, NULL);
@@ -447,7 +441,6 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
		goto err_free_encoder;
	}


	vmw_du_crtc_reset(crtc);
	ret = drm_crtc_init_with_planes(dev, crtc, &ldu->base.primary,
					&ldu->base.cursor,
@@ -513,7 +506,7 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv)
	if (ret != 0)
		goto err_free;

	vmw_kms_create_implicit_placement_property(dev_priv, true);
	vmw_kms_create_implicit_placement_property(dev_priv);

	if (dev_priv->capabilities & SVGA_CAP_MULTIMON)
		for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i)
+5 −36
Original line number Diff line number Diff line
@@ -247,28 +247,20 @@ static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc)
		sou->buffer = vps->bo;
		sou->buffer_size = vps->bo_size;

		if (sou->base.is_implicit) {
			x = crtc->x;
			y = crtc->y;
		} else {
		conn_state = sou->base.connector.state;
		vmw_conn_state = vmw_connector_state_to_vcs(conn_state);

		x = vmw_conn_state->gui_x;
		y = vmw_conn_state->gui_y;
		}

		ret = vmw_sou_fifo_create(dev_priv, sou, x, y, &crtc->mode);
		if (ret)
			DRM_ERROR("Failed to define Screen Object %dx%d\n",
				  crtc->x, crtc->y);

		vmw_kms_add_active(dev_priv, &sou->base, vfb);
	} else {
		sou->buffer = NULL;
		sou->buffer_size = 0;

		vmw_kms_del_active(dev_priv, &sou->base);
	}
}

@@ -329,21 +321,14 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc,
				  uint32_t flags,
				  struct drm_modeset_acquire_ctx *ctx)
{
	struct vmw_private *dev_priv = vmw_priv(crtc->dev);
	int ret;

	if (!vmw_kms_crtc_flippable(dev_priv, crtc))
		return -EINVAL;

	ret = drm_atomic_helper_page_flip(crtc, new_fb, event, flags, ctx);
	if (ret) {
		DRM_ERROR("Page flip error %d.\n", ret);
		return ret;
	}

	if (vmw_crtc_to_du(crtc)->is_implicit)
		vmw_kms_update_implicit_fb(dev_priv, crtc);

	return ret;
}

@@ -383,13 +368,10 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = {
	.dpms = vmw_du_connector_dpms,
	.detect = vmw_du_connector_detect,
	.fill_modes = vmw_du_connector_fill_modes,
	.set_property = vmw_du_connector_set_property,
	.destroy = vmw_sou_connector_destroy,
	.reset = vmw_du_connector_reset,
	.atomic_duplicate_state = vmw_du_connector_duplicate_state,
	.atomic_destroy_state = vmw_du_connector_destroy_state,
	.atomic_set_property = vmw_du_connector_atomic_set_property,
	.atomic_get_property = vmw_du_connector_atomic_get_property,
};


@@ -883,7 +865,6 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
	primary = &sou->base.primary;
	cursor = &sou->base.cursor;

	sou->base.active_implicit = false;
	sou->base.pref_active = (unit == 0);
	sou->base.pref_width = dev_priv->initial_width;
	sou->base.pref_height = dev_priv->initial_height;
@@ -937,8 +918,6 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)

	drm_connector_helper_add(connector, &vmw_sou_connector_helper_funcs);
	connector->status = vmw_du_connector_detect(connector, true);
	vmw_connector_state_to_vcs(connector->state)->is_implicit = false;


	ret = drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs,
			       DRM_MODE_ENCODER_VIRTUAL, NULL);
@@ -977,12 +956,6 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
				   dev->mode_config.suggested_x_property, 0);
	drm_object_attach_property(&connector->base,
				   dev->mode_config.suggested_y_property, 0);
	if (dev_priv->implicit_placement_property)
		drm_object_attach_property
			(&connector->base,
			 dev_priv->implicit_placement_property,
			 sou->base.is_implicit);

	return 0;

err_free_unregister:
@@ -1008,15 +981,11 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv)
	}

	ret = -ENOMEM;
	dev_priv->num_implicit = 0;
	dev_priv->implicit_fb = NULL;

	ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS);
	if (unlikely(ret != 0))
		return ret;

	vmw_kms_create_implicit_placement_property(dev_priv, false);

	for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i)
		vmw_sou_init(dev_priv, i);

Loading