diff options
Diffstat (limited to 'drivers/gpu/drm')
62 files changed, 952 insertions, 651 deletions
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 2d2c2f8d6dc..df0d0a08097 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -94,9 +94,9 @@ static int ast_drm_thaw(struct drm_device *dev)  	ast_post_gpu(dev);  	drm_mode_config_reset(dev); -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	drm_helper_resume_force_mode(dev); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	console_lock();  	ast_fbdev_set_suspend(dev, 0); diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 5ccf984f063..528429252f0 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -98,6 +98,8 @@ struct ast_private {  	struct drm_gem_object *cursor_cache;  	uint64_t cursor_cache_gpu_addr; +	/* Acces to this cache is protected by the crtc->mutex of the only crtc +	 * we have. */  	struct ttm_bo_kmap_obj cache_kmap;  	int next_cursor;  }; diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index d9ec77959df..3e6584b940d 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -290,6 +290,7 @@ static void ast_fbdev_destroy(struct drm_device *dev,  	drm_fb_helper_fini(&afbdev->helper);  	vfree(afbdev->sysram); +	drm_framebuffer_unregister_private(&afb->base);  	drm_framebuffer_cleanup(&afb->base);  } diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index f668e6cc0f7..f60fd7bd118 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -246,16 +246,8 @@ static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb)  	kfree(fb);  } -static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb, -					      struct drm_file *file, -					      unsigned int *handle) -{ -	return -EINVAL; -} -  static const struct drm_framebuffer_funcs ast_fb_funcs = {  	.destroy = ast_user_framebuffer_destroy, -	.create_handle = ast_user_framebuffer_create_handle,  }; @@ -266,13 +258,13 @@ int ast_framebuffer_init(struct drm_device *dev,  {  	int ret; +	drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); +	ast_fb->obj = obj;  	ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs);  	if (ret) {  		DRM_ERROR("framebuffer init failed %d\n", ret);  		return ret;  	} -	drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); -	ast_fb->obj = obj;  	return 0;  } diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 6c6b4c87d30..3daea0f638c 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -258,6 +258,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,  	vfree(gfbdev->sysram);  	drm_fb_helper_fini(&gfbdev->helper); +	drm_framebuffer_unregister_private(&gfb->base);  	drm_framebuffer_cleanup(&gfb->base);  	return 0; diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 6a9b12e88d4..35cbae82777 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -23,16 +23,8 @@ static void cirrus_user_framebuffer_destroy(struct drm_framebuffer *fb)  	kfree(fb);  } -static int cirrus_user_framebuffer_create_handle(struct drm_framebuffer *fb, -						 struct drm_file *file_priv, -						 unsigned int *handle) -{ -	return 0; -} -  static const struct drm_framebuffer_funcs cirrus_fb_funcs = {  	.destroy = cirrus_user_framebuffer_destroy, -	.create_handle = cirrus_user_framebuffer_create_handle,  };  int cirrus_framebuffer_init(struct drm_device *dev, @@ -42,13 +34,13 @@ int cirrus_framebuffer_init(struct drm_device *dev,  {  	int ret; +	drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); +	gfb->obj = obj;  	ret = drm_framebuffer_init(dev, &gfb->base, &cirrus_fb_funcs);  	if (ret) {  		DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);  		return ret;  	} -	drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); -	gfb->obj = obj;  	return 0;  } diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f2d667b8bee..9c797f6fea7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -37,6 +37,40 @@  #include <drm/drm_edid.h>  #include <drm/drm_fourcc.h> +/** + * drm_modeset_lock_all - take all modeset locks + * @dev: drm device + * + * This function takes all modeset locks, suitable where a more fine-grained + * scheme isn't (yet) implemented. + */ +void drm_modeset_lock_all(struct drm_device *dev) +{ +	struct drm_crtc *crtc; + +	mutex_lock(&dev->mode_config.mutex); + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) +		mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_modeset_lock_all); + +/** + * drm_modeset_unlock_all - drop all modeset locks + * @dev: device + */ +void drm_modeset_unlock_all(struct drm_device *dev) +{ +	struct drm_crtc *crtc; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) +		mutex_unlock(&crtc->mutex); + +	mutex_unlock(&dev->mode_config.mutex); +} + +EXPORT_SYMBOL(drm_modeset_unlock_all); +  /* Avoid boilerplate.  I'm tired of typing. */  #define DRM_ENUM_NAME_FN(fnname, list)				\  	char *fnname(int val)					\ @@ -203,12 +237,10 @@ char *drm_get_connector_status_name(enum drm_connector_status status)  }  /** - * drm_mode_object_get - allocate a new identifier + * drm_mode_object_get - allocate a new modeset identifier   * @dev: DRM device - * @ptr: object pointer, used to generate unique ID - * @type: object type - * - * LOCKING: + * @obj: object pointer, used to generate unique ID + * @obj_type: object type   *   * Create a unique identifier based on @ptr in @dev's identifier space.  Used   * for tracking modes, CRTCs and connectors. @@ -231,24 +263,27 @@ again:  	mutex_lock(&dev->mode_config.idr_mutex);  	ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); + +	if (!ret) { +		/* +		 * Set up the object linking under the protection of the idr +		 * lock so that other users can't see inconsistent state. +		 */ +		obj->id = new_id; +		obj->type = obj_type; +	}  	mutex_unlock(&dev->mode_config.idr_mutex); +  	if (ret == -EAGAIN)  		goto again; -	else if (ret) -		return ret; -	obj->id = new_id; -	obj->type = obj_type; -	return 0; +	return ret;  }  /** - * drm_mode_object_put - free an identifer + * drm_mode_object_put - free a modeset identifer   * @dev: DRM device - * @id: ID to free - * - * LOCKING: - * Caller must hold DRM mode_config lock. + * @object: object to free   *   * Free @id from @dev's unique identifier pool.   */ @@ -260,11 +295,24 @@ static void drm_mode_object_put(struct drm_device *dev,  	mutex_unlock(&dev->mode_config.idr_mutex);  } +/** + * drm_mode_object_find - look up a drm object with static lifetime + * @dev: drm device + * @id: id of the mode object + * @type: type of the mode object + * + * Note that framebuffers cannot be looked up with this functions - since those + * are reference counted, they need special treatment. + */  struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,  		uint32_t id, uint32_t type)  {  	struct drm_mode_object *obj = NULL; +	/* Framebuffers are reference counted and need their own lookup +	 * function.*/ +	WARN_ON(type == DRM_MODE_OBJECT_FB); +  	mutex_lock(&dev->mode_config.idr_mutex);  	obj = idr_find(&dev->mode_config.crtc_idr, id);  	if (!obj || (obj->type != type) || (obj->id != id)) @@ -278,13 +326,18 @@ EXPORT_SYMBOL(drm_mode_object_find);  /**   * drm_framebuffer_init - initialize a framebuffer   * @dev: DRM device - * - * LOCKING: - * Caller must hold mode config lock. + * @fb: framebuffer to be initialized + * @funcs: ... with these functions   *   * Allocates an ID for the framebuffer's parent mode object, sets its mode   * functions & device file and adds it to the master fd list.   * + * IMPORTANT: + * This functions publishes the fb and makes it available for concurrent access + * by other users. Which means by this point the fb _must_ be fully set up - + * since all the fb attributes are invariant over its lifetime, no further + * locking but only correct reference counting is required. + *   * RETURNS:   * Zero on success, error code on failure.   */ @@ -293,16 +346,23 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,  {  	int ret; +	mutex_lock(&dev->mode_config.fb_lock);  	kref_init(&fb->refcount); +	INIT_LIST_HEAD(&fb->filp_head); +	fb->dev = dev; +	fb->funcs = funcs;  	ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);  	if (ret) -		return ret; +		goto out; + +	/* Grab the idr reference. */ +	drm_framebuffer_reference(fb); -	fb->dev = dev; -	fb->funcs = funcs;  	dev->mode_config.num_fb++;  	list_add(&fb->head, &dev->mode_config.fb_list); +out: +	mutex_unlock(&dev->mode_config.fb_lock);  	return 0;  } @@ -315,23 +375,63 @@ static void drm_framebuffer_free(struct kref *kref)  	fb->funcs->destroy(fb);  } +static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, +							uint32_t id) +{ +	struct drm_mode_object *obj = NULL; +	struct drm_framebuffer *fb; + +	mutex_lock(&dev->mode_config.idr_mutex); +	obj = idr_find(&dev->mode_config.crtc_idr, id); +	if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) +		fb = NULL; +	else +		fb = obj_to_fb(obj); +	mutex_unlock(&dev->mode_config.idr_mutex); + +	return fb; +} + +/** + * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference + * @dev: drm device + * @id: id of the fb object + * + * If successful, this grabs an additional reference to the framebuffer - + * callers need to make sure to eventually unreference the returned framebuffer + * again. + */ +struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, +					       uint32_t id) +{ +	struct drm_framebuffer *fb; + +	mutex_lock(&dev->mode_config.fb_lock); +	fb = __drm_framebuffer_lookup(dev, id); +	if (fb) +		kref_get(&fb->refcount); +	mutex_unlock(&dev->mode_config.fb_lock); + +	return fb; +} +EXPORT_SYMBOL(drm_framebuffer_lookup); +  /**   * drm_framebuffer_unreference - unref a framebuffer + * @fb: framebuffer to unref   * - * LOCKING: - * Caller must hold mode config lock. + * This functions decrements the fb's refcount and frees it if it drops to zero.   */  void drm_framebuffer_unreference(struct drm_framebuffer *fb)  { -	struct drm_device *dev = fb->dev;  	DRM_DEBUG("FB ID: %d\n", fb->base.id); -	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));  	kref_put(&fb->refcount, drm_framebuffer_free);  }  EXPORT_SYMBOL(drm_framebuffer_unreference);  /**   * drm_framebuffer_reference - incr the fb refcnt + * @fb: framebuffer   */  void drm_framebuffer_reference(struct drm_framebuffer *fb)  { @@ -340,29 +440,74 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb)  }  EXPORT_SYMBOL(drm_framebuffer_reference); +static void drm_framebuffer_free_bug(struct kref *kref) +{ +	BUG(); +} + +static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) +{ +	DRM_DEBUG("FB ID: %d\n", fb->base.id); +	kref_put(&fb->refcount, drm_framebuffer_free_bug); +} + +/* dev->mode_config.fb_lock must be held! */ +static void __drm_framebuffer_unregister(struct drm_device *dev, +					 struct drm_framebuffer *fb) +{ +	mutex_lock(&dev->mode_config.idr_mutex); +	idr_remove(&dev->mode_config.crtc_idr, fb->base.id); +	mutex_unlock(&dev->mode_config.idr_mutex); + +	fb->base.id = 0; + +	__drm_framebuffer_unreference(fb); +} + +/** + * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr + * @fb: fb to unregister + * + * Drivers need to call this when cleaning up driver-private framebuffers, e.g. + * those used for fbdev. Note that the caller must hold a reference of it's own, + * i.e. the object may not be destroyed through this call (since it'll lead to a + * locking inversion). + */ +void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) +{ +	struct drm_device *dev = fb->dev; + +	mutex_lock(&dev->mode_config.fb_lock); +	/* Mark fb as reaped and drop idr ref. */ +	__drm_framebuffer_unregister(dev, fb); +	mutex_unlock(&dev->mode_config.fb_lock); +} +EXPORT_SYMBOL(drm_framebuffer_unregister_private); +  /**   * drm_framebuffer_cleanup - remove a framebuffer object   * @fb: framebuffer to remove   * - * LOCKING: - * Caller must hold mode config lock. + * Cleanup references to a user-created framebuffer. This function is intended + * to be used from the drivers ->destroy callback. + * + * Note that this function does not remove the fb from active usuage - if it is + * still used anywhere, hilarity can ensue since userspace could call getfb on + * the id and get back -EINVAL. Obviously no concern at driver unload time.   * - * Scans all the CRTCs in @dev's mode_config.  If they're using @fb, removes - * it, setting it to NULL. + * Also, the framebuffer will not be removed from the lookup idr - for + * user-created framebuffers this will happen in in the rmfb ioctl. For + * driver-private objects (e.g. for fbdev) drivers need to explicitly call + * drm_framebuffer_unregister_private.   */  void drm_framebuffer_cleanup(struct drm_framebuffer *fb)  {  	struct drm_device *dev = fb->dev; -	/* -	 * This could be moved to drm_framebuffer_remove(), but for -	 * debugging is nice to keep around the list of fb's that are -	 * no longer associated w/ a drm_file but are not unreferenced -	 * yet.  (i915 and omapdrm have debugfs files which will show -	 * this.) -	 */ -	drm_mode_object_put(dev, &fb->base); + +	mutex_lock(&dev->mode_config.fb_lock);  	list_del(&fb->head);  	dev->mode_config.num_fb--; +	mutex_unlock(&dev->mode_config.fb_lock);  }  EXPORT_SYMBOL(drm_framebuffer_cleanup); @@ -370,11 +515,13 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);   * drm_framebuffer_remove - remove and unreference a framebuffer object   * @fb: framebuffer to remove   * - * LOCKING: - * Caller must hold mode config lock. - *   * Scans all the CRTCs and planes in @dev's mode_config.  If they're - * using @fb, removes it, setting it to NULL. + * using @fb, removes it, setting it to NULL. Then drops the reference to the + * passed-in framebuffer. Might take the modeset locks. + * + * Note that this function optimizes the cleanup away if the caller holds the + * last reference to the framebuffer. It is also guaranteed to not take the + * modeset locks in this case.   */  void drm_framebuffer_remove(struct drm_framebuffer *fb)  { @@ -384,33 +531,53 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)  	struct drm_mode_set set;  	int ret; -	/* remove from any CRTC */ -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -		if (crtc->fb == fb) { -			/* should turn off the crtc */ -			memset(&set, 0, sizeof(struct drm_mode_set)); -			set.crtc = crtc; -			set.fb = NULL; -			ret = crtc->funcs->set_config(&set); -			if (ret) -				DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); +	WARN_ON(!list_empty(&fb->filp_head)); + +	/* +	 * drm ABI mandates that we remove any deleted framebuffers from active +	 * useage. But since most sane clients only remove framebuffers they no +	 * longer need, try to optimize this away. +	 * +	 * Since we're holding a reference ourselves, observing a refcount of 1 +	 * means that we're the last holder and can skip it. Also, the refcount +	 * can never increase from 1 again, so we don't need any barriers or +	 * locks. +	 * +	 * Note that userspace could try to race with use and instate a new +	 * usage _after_ we've cleared all current ones. End result will be an +	 * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot +	 * in this manner. +	 */ +	if (atomic_read(&fb->refcount.refcount) > 1) { +		drm_modeset_lock_all(dev); +		/* remove from any CRTC */ +		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +			if (crtc->fb == fb) { +				/* should turn off the crtc */ +				memset(&set, 0, sizeof(struct drm_mode_set)); +				set.crtc = crtc; +				set.fb = NULL; +				ret = drm_mode_set_config_internal(&set); +				if (ret) +					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); +			}  		} -	} -	list_for_each_entry(plane, &dev->mode_config.plane_list, head) { -		if (plane->fb == fb) { -			/* should turn off the crtc */ -			ret = plane->funcs->disable_plane(plane); -			if (ret) -				DRM_ERROR("failed to disable plane with busy fb\n"); -			/* disconnect the plane from the fb and crtc: */ -			plane->fb = NULL; -			plane->crtc = NULL; +		list_for_each_entry(plane, &dev->mode_config.plane_list, head) { +			if (plane->fb == fb) { +				/* should turn off the crtc */ +				ret = plane->funcs->disable_plane(plane); +				if (ret) +					DRM_ERROR("failed to disable plane with busy fb\n"); +				/* disconnect the plane from the fb and crtc: */ +				__drm_framebuffer_unreference(plane->fb); +				plane->fb = NULL; +				plane->crtc = NULL; +			}  		} +		drm_modeset_unlock_all(dev);  	} -	list_del(&fb->filp_head); -  	drm_framebuffer_unreference(fb);  }  EXPORT_SYMBOL(drm_framebuffer_remove); @@ -421,9 +588,6 @@ EXPORT_SYMBOL(drm_framebuffer_remove);   * @crtc: CRTC object to init   * @funcs: callbacks for the new CRTC   * - * LOCKING: - * Takes mode_config lock. - *   * Inits a new object created as base part of an driver crtc object.   *   * RETURNS: @@ -438,7 +602,9 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,  	crtc->funcs = funcs;  	crtc->invert_dimensions = false; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev); +	mutex_init(&crtc->mutex); +	mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);  	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);  	if (ret) @@ -450,7 +616,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,  	dev->mode_config.num_crtc++;   out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -460,9 +626,6 @@ EXPORT_SYMBOL(drm_crtc_init);   * drm_crtc_cleanup - Cleans up the core crtc usage.   * @crtc: CRTC to cleanup   * - * LOCKING: - * Caller must hold mode config lock. - *   * Cleanup @crtc. Removes from drm modesetting space   * does NOT free object, caller does that.   */ @@ -484,9 +647,6 @@ EXPORT_SYMBOL(drm_crtc_cleanup);   * @connector: connector the new mode   * @mode: mode data   * - * LOCKING: - * Caller must hold mode config lock. - *   * Add @mode to @connector's mode list for later use.   */  void drm_mode_probed_add(struct drm_connector *connector, @@ -501,9 +661,6 @@ EXPORT_SYMBOL(drm_mode_probed_add);   * @connector: connector list to modify   * @mode: mode to remove   * - * LOCKING: - * Caller must hold mode config lock. - *   * Remove @mode from @connector's mode list, then free it.   */  void drm_mode_remove(struct drm_connector *connector, @@ -519,10 +676,7 @@ EXPORT_SYMBOL(drm_mode_remove);   * @dev: DRM device   * @connector: the connector to init   * @funcs: callbacks for this connector - * @name: user visible name of the connector - * - * LOCKING: - * Takes mode config lock. + * @connector_type: user visible type of the connector   *   * Initialises a preallocated connector. Connectors should be   * subclassed as part of driver connector objects. @@ -537,7 +691,7 @@ int drm_connector_init(struct drm_device *dev,  {  	int ret; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);  	if (ret) @@ -567,7 +721,7 @@ int drm_connector_init(struct drm_device *dev,  				      dev->mode_config.dpms_property, 0);   out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -577,9 +731,6 @@ EXPORT_SYMBOL(drm_connector_init);   * drm_connector_cleanup - cleans up an initialised connector   * @connector: connector to cleanup   * - * LOCKING: - * Takes mode config lock. - *   * Cleans up the connector but doesn't free the object.   */  void drm_connector_cleanup(struct drm_connector *connector) @@ -596,11 +747,9 @@ void drm_connector_cleanup(struct drm_connector *connector)  	list_for_each_entry_safe(mode, t, &connector->user_modes, head)  		drm_mode_remove(connector, mode); -	mutex_lock(&dev->mode_config.mutex);  	drm_mode_object_put(dev, &connector->base);  	list_del(&connector->head);  	dev->mode_config.num_connector--; -	mutex_unlock(&dev->mode_config.mutex);  }  EXPORT_SYMBOL(drm_connector_cleanup); @@ -622,7 +771,7 @@ int drm_encoder_init(struct drm_device *dev,  {  	int ret; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);  	if (ret) @@ -636,7 +785,7 @@ int drm_encoder_init(struct drm_device *dev,  	dev->mode_config.num_encoder++;   out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -645,11 +794,11 @@ EXPORT_SYMBOL(drm_encoder_init);  void drm_encoder_cleanup(struct drm_encoder *encoder)  {  	struct drm_device *dev = encoder->dev; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	drm_mode_object_put(dev, &encoder->base);  	list_del(&encoder->head);  	dev->mode_config.num_encoder--; -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  }  EXPORT_SYMBOL(drm_encoder_cleanup); @@ -661,7 +810,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,  {  	int ret; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);  	if (ret) @@ -695,7 +844,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,  	}   out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -705,7 +854,7 @@ void drm_plane_cleanup(struct drm_plane *plane)  {  	struct drm_device *dev = plane->dev; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	kfree(plane->format_types);  	drm_mode_object_put(dev, &plane->base);  	/* if not added to a list, it must be a private plane */ @@ -713,7 +862,7 @@ void drm_plane_cleanup(struct drm_plane *plane)  		list_del(&plane->head);  		dev->mode_config.num_plane--;  	} -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  }  EXPORT_SYMBOL(drm_plane_cleanup); @@ -721,9 +870,6 @@ EXPORT_SYMBOL(drm_plane_cleanup);   * drm_mode_create - create a new display mode   * @dev: DRM device   * - * LOCKING: - * Caller must hold DRM mode_config lock. - *   * Create a new drm_display_mode, give it an ID, and return it.   *   * RETURNS: @@ -751,9 +897,6 @@ EXPORT_SYMBOL(drm_mode_create);   * @dev: DRM device   * @mode: mode to remove   * - * LOCKING: - * Caller must hold mode config lock. - *   * Free @mode's unique identifier, then free it.   */  void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) @@ -978,16 +1121,19 @@ EXPORT_SYMBOL(drm_mode_create_dirty_info_property);   * drm_mode_config_init - initialize DRM mode_configuration structure   * @dev: DRM device   * - * LOCKING: - * None, should happen single threaded at init time. - *   * Initialize @dev's mode_config structure, used for tracking the graphics   * configuration of @dev. + * + * Since this initializes the modeset locks, no locking is possible. Which is no + * problem, since this should happen single threaded at init time. It is the + * driver's problem to ensure this guarantee. + *   */  void drm_mode_config_init(struct drm_device *dev)  {  	mutex_init(&dev->mode_config.mutex);  	mutex_init(&dev->mode_config.idr_mutex); +	mutex_init(&dev->mode_config.fb_lock);  	INIT_LIST_HEAD(&dev->mode_config.fb_list);  	INIT_LIST_HEAD(&dev->mode_config.crtc_list);  	INIT_LIST_HEAD(&dev->mode_config.connector_list); @@ -997,9 +1143,9 @@ void drm_mode_config_init(struct drm_device *dev)  	INIT_LIST_HEAD(&dev->mode_config.plane_list);  	idr_init(&dev->mode_config.crtc_idr); -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	drm_mode_create_standard_connector_properties(dev); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	/* Just to be sure */  	dev->mode_config.num_fb = 0; @@ -1057,12 +1203,13 @@ EXPORT_SYMBOL(drm_mode_group_init_legacy_group);   * drm_mode_config_cleanup - free up DRM mode_config info   * @dev: DRM device   * - * LOCKING: - * Caller must hold mode config lock. - *   * Free up all the connectors and CRTCs associated with this DRM device, then   * free up the framebuffers and associated buffer objects.   * + * Note that since this /should/ happen single-threaded at driver/device + * teardown time, no locking is required. It's the driver's job to ensure that + * this guarantee actually holds true. + *   * FIXME: cleanup any dangling user buffer objects too   */  void drm_mode_config_cleanup(struct drm_device *dev) @@ -1089,6 +1236,15 @@ void drm_mode_config_cleanup(struct drm_device *dev)  		drm_property_destroy(dev, property);  	} +	/* +	 * Single-threaded teardown context, so it's not required to grab the +	 * fb_lock to protect against concurrent fb_list access. Contrary, it +	 * would actually deadlock with the drm_framebuffer_cleanup function. +	 * +	 * Also, if there are any framebuffers left, that's a driver leak now, +	 * so politely WARN about this. +	 */ +	WARN_ON(!list_empty(&dev->mode_config.fb_list));  	list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {  		drm_framebuffer_remove(fb);  	} @@ -1112,9 +1268,6 @@ EXPORT_SYMBOL(drm_mode_config_cleanup);   * @out: drm_mode_modeinfo struct to return to the user   * @in: drm_display_mode to use   * - * LOCKING: - * None. - *   * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to   * the user.   */ @@ -1151,9 +1304,6 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,   * @out: drm_display_mode to return to the user   * @in: drm_mode_modeinfo to use   * - * LOCKING: - * None. - *   * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to   * the caller.   * @@ -1188,13 +1338,9 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out,  /**   * drm_mode_getresources - get graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Construct a set of configuration description structures and return   * them to the user, including CRTC, connector and framebuffer configuration. @@ -1228,8 +1374,8 @@ int drm_mode_getresources(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	mutex_lock(&file_priv->fbs_lock);  	/*  	 * For the non-control nodes we need to limit the list of resources  	 * by IDs in the group list for this node @@ -1237,6 +1383,23 @@ int drm_mode_getresources(struct drm_device *dev, void *data,  	list_for_each(lh, &file_priv->fbs)  		fb_count++; +	/* handle this in 4 parts */ +	/* FBs */ +	if (card_res->count_fbs >= fb_count) { +		copied = 0; +		fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; +		list_for_each_entry(fb, &file_priv->fbs, filp_head) { +			if (put_user(fb->base.id, fb_id + copied)) { +				mutex_unlock(&file_priv->fbs_lock); +				return -EFAULT; +			} +			copied++; +		} +	} +	card_res->count_fbs = fb_count; +	mutex_unlock(&file_priv->fbs_lock); + +	drm_modeset_lock_all(dev);  	mode_group = &file_priv->master->minor->mode_group;  	if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { @@ -1260,21 +1423,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data,  	card_res->max_width = dev->mode_config.max_width;  	card_res->min_width = dev->mode_config.min_width; -	/* handle this in 4 parts */ -	/* FBs */ -	if (card_res->count_fbs >= fb_count) { -		copied = 0; -		fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; -		list_for_each_entry(fb, &file_priv->fbs, filp_head) { -			if (put_user(fb->base.id, fb_id + copied)) { -				ret = -EFAULT; -				goto out; -			} -			copied++; -		} -	} -	card_res->count_fbs = fb_count; -  	/* CRTCs */  	if (card_res->count_crtcs >= crtc_count) {  		copied = 0; @@ -1370,19 +1518,15 @@ int drm_mode_getresources(struct drm_device *dev, void *data,  		  card_res->count_connectors, card_res->count_encoders);  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  }  /**   * drm_mode_getcrtc - get CRTC configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Construct a CRTC configuration structure to return to the user.   * @@ -1402,7 +1546,7 @@ int drm_mode_getcrtc(struct drm_device *dev,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, crtc_resp->crtc_id,  				   DRM_MODE_OBJECT_CRTC); @@ -1430,19 +1574,15 @@ int drm_mode_getcrtc(struct drm_device *dev,  	}  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  }  /**   * drm_mode_getconnector - get connector configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Construct a connector configuration structure to return to the user.   * @@ -1575,6 +1715,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,  out:  	mutex_unlock(&dev->mode_config.mutex); +  	return ret;  } @@ -1589,7 +1730,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, enc_resp->encoder_id,  				   DRM_MODE_OBJECT_ENCODER);  	if (!obj) { @@ -1608,7 +1749,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,  	enc_resp->possible_clones = encoder->possible_clones;  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -1618,9 +1759,6 @@ out:   * @data: ioctl data   * @file_priv: DRM file info   * - * LOCKING: - * Takes mode config lock. - *   * Return an plane count and set of IDs.   */  int drm_mode_getplane_res(struct drm_device *dev, void *data, @@ -1635,7 +1773,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	config = &dev->mode_config;  	/* @@ -1657,7 +1795,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,  	plane_resp->count_planes = config->num_plane;  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -1667,9 +1805,6 @@ out:   * @data: ioctl data   * @file_priv: DRM file info   * - * LOCKING: - * Takes mode config lock. - *   * Return plane info, including formats supported, gamma size, any   * current fb, etc.   */ @@ -1685,7 +1820,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, plane_resp->plane_id,  				   DRM_MODE_OBJECT_PLANE);  	if (!obj) { @@ -1725,7 +1860,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data,  	plane_resp->count_format_types = plane->format_count;  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -1733,10 +1868,7 @@ out:   * drm_mode_setplane - set up or tear down an plane   * @dev: DRM device   * @data: ioctl data* - * @file_prive: DRM file info - * - * LOCKING: - * Takes mode config lock. + * @file_priv: DRM file info   *   * Set plane info, including placement, fb, scaling, and other factors.   * Or pass a NULL fb to disable. @@ -1748,7 +1880,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data,  	struct drm_mode_object *obj;  	struct drm_plane *plane;  	struct drm_crtc *crtc; -	struct drm_framebuffer *fb; +	struct drm_framebuffer *fb = NULL, *old_fb = NULL;  	int ret = 0;  	unsigned int fb_width, fb_height;  	int i; @@ -1756,8 +1888,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); -  	/*  	 * First, find the plane, crtc, and fb objects.  If not available,  	 * we don't bother to call the driver. @@ -1767,16 +1897,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data,  	if (!obj) {  		DRM_DEBUG_KMS("Unknown plane ID %d\n",  			      plane_req->plane_id); -		ret = -ENOENT; -		goto out; +		return -ENOENT;  	}  	plane = obj_to_plane(obj);  	/* No fb means shut it down */  	if (!plane_req->fb_id) { +		drm_modeset_lock_all(dev); +		old_fb = plane->fb;  		plane->funcs->disable_plane(plane);  		plane->crtc = NULL;  		plane->fb = NULL; +		drm_modeset_unlock_all(dev);  		goto out;  	} @@ -1790,15 +1922,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data,  	}  	crtc = obj_to_crtc(obj); -	obj = drm_mode_object_find(dev, plane_req->fb_id, -				   DRM_MODE_OBJECT_FB); -	if (!obj) { +	fb = drm_framebuffer_lookup(dev, plane_req->fb_id); +	if (!fb) {  		DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",  			      plane_req->fb_id);  		ret = -ENOENT;  		goto out;  	} -	fb = obj_to_fb(obj);  	/* Check whether this plane supports the fb pixel format. */  	for (i = 0; i < plane->format_count; i++) @@ -1844,31 +1974,62 @@ int drm_mode_setplane(struct drm_device *dev, void *data,  		goto out;  	} +	drm_modeset_lock_all(dev);  	ret = plane->funcs->update_plane(plane, crtc, fb,  					 plane_req->crtc_x, plane_req->crtc_y,  					 plane_req->crtc_w, plane_req->crtc_h,  					 plane_req->src_x, plane_req->src_y,  					 plane_req->src_w, plane_req->src_h);  	if (!ret) { +		old_fb = plane->fb; +		fb = NULL;  		plane->crtc = crtc;  		plane->fb = fb;  	} +	drm_modeset_unlock_all(dev);  out: -	mutex_unlock(&dev->mode_config.mutex); +	if (fb) +		drm_framebuffer_unreference(fb); +	if (old_fb) +		drm_framebuffer_unreference(old_fb);  	return ret;  }  /** - * drm_mode_setcrtc - set CRTC configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * drm_mode_set_config_internal - helper to call ->set_config + * @set: modeset config to set   * - * LOCKING: - * Takes mode config lock. + * This is a little helper to wrap internal calls to the ->set_config driver + * interface. The only thing it adds is correct refcounting dance. + */ +int drm_mode_set_config_internal(struct drm_mode_set *set) +{ +	struct drm_crtc *crtc = set->crtc; +	struct drm_framebuffer *fb, *old_fb; +	int ret; + +	old_fb = crtc->fb; +	fb = set->fb; + +	ret = crtc->funcs->set_config(set); +	if (ret == 0) { +		if (old_fb) +			drm_framebuffer_unreference(old_fb); +		if (fb) +			drm_framebuffer_reference(fb); +	} + +	return ret; +} +EXPORT_SYMBOL(drm_mode_set_config_internal); + +/** + * drm_mode_setcrtc - set CRTC configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Build a new CRTC configuration based on user request.   * @@ -1899,7 +2060,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,  	if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)  		return -ERANGE; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, crtc_req->crtc_id,  				   DRM_MODE_OBJECT_CRTC);  	if (!obj) { @@ -1921,16 +2082,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,  				goto out;  			}  			fb = crtc->fb; +			/* Make refcounting symmetric with the lookup path. */ +			drm_framebuffer_reference(fb);  		} else { -			obj = drm_mode_object_find(dev, crtc_req->fb_id, -						   DRM_MODE_OBJECT_FB); -			if (!obj) { +			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); +			if (!fb) {  				DRM_DEBUG_KMS("Unknown FB ID%d\n",  						crtc_req->fb_id);  				ret = -EINVAL;  				goto out;  			} -			fb = obj_to_fb(obj);  		}  		mode = drm_mode_create(dev); @@ -2027,12 +2188,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,  	set.connectors = connector_set;  	set.num_connectors = crtc_req->count_connectors;  	set.fb = fb; -	ret = crtc->funcs->set_config(&set); +	ret = drm_mode_set_config_internal(&set);  out: +	if (fb) +		drm_framebuffer_unreference(fb); +  	kfree(connector_set);  	drm_mode_destroy(dev, mode); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -2050,15 +2214,14 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,  	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex);  	obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);  	if (!obj) {  		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); -		ret = -EINVAL; -		goto out; +		return -EINVAL;  	}  	crtc = obj_to_crtc(obj); +	mutex_lock(&crtc->mutex);  	if (req->flags & DRM_MODE_CURSOR_BO) {  		if (!crtc->funcs->cursor_set) {  			ret = -ENXIO; @@ -2078,7 +2241,8 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,  		}  	}  out: -	mutex_unlock(&dev->mode_config.mutex); +	mutex_unlock(&crtc->mutex); +  	return ret;  } @@ -2120,13 +2284,9 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format);  /**   * drm_mode_addfb - add an FB to the graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Add a new FB to the specified CRTC, given a user request.   * @@ -2161,24 +2321,19 @@ int drm_mode_addfb(struct drm_device *dev,  	if ((config->min_height > r.height) || (r.height > config->max_height))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); - -	/* TODO check buffer is sufficiently large */ -	/* TODO setup destructor callback */ -  	fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r);  	if (IS_ERR(fb)) {  		DRM_DEBUG_KMS("could not create framebuffer\n"); -		ret = PTR_ERR(fb); -		goto out; +		drm_modeset_unlock_all(dev); +		return PTR_ERR(fb);  	} +	mutex_lock(&file_priv->fbs_lock);  	or->fb_id = fb->base.id;  	list_add(&fb->filp_head, &file_priv->fbs);  	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); +	mutex_unlock(&file_priv->fbs_lock); -out: -	mutex_unlock(&dev->mode_config.mutex);  	return ret;  } @@ -2304,13 +2459,9 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)  /**   * drm_mode_addfb2 - add an FB to the graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Add a new FB to the specified CRTC, given a user request with format.   * @@ -2350,33 +2501,28 @@ int drm_mode_addfb2(struct drm_device *dev,  	if (ret)  		return ret; -	mutex_lock(&dev->mode_config.mutex); -  	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);  	if (IS_ERR(fb)) {  		DRM_DEBUG_KMS("could not create framebuffer\n"); -		ret = PTR_ERR(fb); -		goto out; +		drm_modeset_unlock_all(dev); +		return PTR_ERR(fb);  	} +	mutex_lock(&file_priv->fbs_lock);  	r->fb_id = fb->base.id;  	list_add(&fb->filp_head, &file_priv->fbs);  	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); +	mutex_unlock(&file_priv->fbs_lock); + -out: -	mutex_unlock(&dev->mode_config.mutex);  	return ret;  }  /**   * drm_mode_rmfb - remove an FB from the configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Remove the FB specified by the user.   * @@ -2388,50 +2534,49 @@ out:  int drm_mode_rmfb(struct drm_device *dev,  		   void *data, struct drm_file *file_priv)  { -	struct drm_mode_object *obj;  	struct drm_framebuffer *fb = NULL;  	struct drm_framebuffer *fbl = NULL;  	uint32_t *id = data; -	int ret = 0;  	int found = 0;  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); -	obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); -	/* TODO check that we really get a framebuffer back. */ -	if (!obj) { -		ret = -EINVAL; -		goto out; -	} -	fb = obj_to_fb(obj); +	mutex_lock(&file_priv->fbs_lock); +	mutex_lock(&dev->mode_config.fb_lock); +	fb = __drm_framebuffer_lookup(dev, *id); +	if (!fb) +		goto fail_lookup;  	list_for_each_entry(fbl, &file_priv->fbs, filp_head)  		if (fb == fbl)  			found = 1; +	if (!found) +		goto fail_lookup; -	if (!found) { -		ret = -EINVAL; -		goto out; -	} +	/* Mark fb as reaped, we still have a ref from fpriv->fbs. */ +	__drm_framebuffer_unregister(dev, fb); + +	list_del_init(&fb->filp_head); +	mutex_unlock(&dev->mode_config.fb_lock); +	mutex_unlock(&file_priv->fbs_lock);  	drm_framebuffer_remove(fb); -out: -	mutex_unlock(&dev->mode_config.mutex); -	return ret; +	return 0; + +fail_lookup: +	mutex_unlock(&dev->mode_config.fb_lock); +	mutex_unlock(&file_priv->fbs_lock); + +	return -EINVAL;  }  /**   * drm_mode_getfb - get FB info - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Lookup the FB given its ID and return info about it.   * @@ -2444,30 +2589,28 @@ int drm_mode_getfb(struct drm_device *dev,  		   void *data, struct drm_file *file_priv)  {  	struct drm_mode_fb_cmd *r = data; -	struct drm_mode_object *obj;  	struct drm_framebuffer *fb; -	int ret = 0; +	int ret;  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); -	obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); -	if (!obj) { -		ret = -EINVAL; -		goto out; -	} -	fb = obj_to_fb(obj); +	fb = drm_framebuffer_lookup(dev, r->fb_id); +	if (!fb) +		return -EINVAL;  	r->height = fb->height;  	r->width = fb->width;  	r->depth = fb->depth;  	r->bpp = fb->bits_per_pixel;  	r->pitch = fb->pitches[0]; -	fb->funcs->create_handle(fb, file_priv, &r->handle); +	if (fb->funcs->create_handle) +		ret = fb->funcs->create_handle(fb, file_priv, &r->handle); +	else +		ret = -ENODEV; + +	drm_framebuffer_unreference(fb); -out: -	mutex_unlock(&dev->mode_config.mutex);  	return ret;  } @@ -2477,7 +2620,6 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,  	struct drm_clip_rect __user *clips_ptr;  	struct drm_clip_rect *clips = NULL;  	struct drm_mode_fb_dirty_cmd *r = data; -	struct drm_mode_object *obj;  	struct drm_framebuffer *fb;  	unsigned flags;  	int num_clips; @@ -2486,13 +2628,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); -	obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); -	if (!obj) { -		ret = -EINVAL; -		goto out_err1; -	} -	fb = obj_to_fb(obj); +	fb = drm_framebuffer_lookup(dev, r->fb_id); +	if (!fb) +		return -EINVAL;  	num_clips = r->num_clips;  	clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; @@ -2530,27 +2668,26 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,  	}  	if (fb->funcs->dirty) { +		drm_modeset_lock_all(dev);  		ret = fb->funcs->dirty(fb, file_priv, flags, r->color,  				       clips, num_clips); +		drm_modeset_unlock_all(dev);  	} else {  		ret = -ENOSYS; -		goto out_err2;  	}  out_err2:  	kfree(clips);  out_err1: -	mutex_unlock(&dev->mode_config.mutex); +	drm_framebuffer_unreference(fb); +  	return ret;  }  /**   * drm_fb_release - remove and free the FBs on this file - * @filp: file * from the ioctl - * - * LOCKING: - * Takes mode config lock. + * @priv: drm file for the ioctl   *   * Destroy all the FBs associated with @filp.   * @@ -2564,11 +2701,20 @@ void drm_fb_release(struct drm_file *priv)  	struct drm_device *dev = priv->minor->dev;  	struct drm_framebuffer *fb, *tfb; -	mutex_lock(&dev->mode_config.mutex); +	mutex_lock(&priv->fbs_lock);  	list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { + +		mutex_lock(&dev->mode_config.fb_lock); +		/* Mark fb as reaped, we still have a ref from fpriv->fbs. */ +		__drm_framebuffer_unregister(dev, fb); +		mutex_unlock(&dev->mode_config.fb_lock); + +		list_del_init(&fb->filp_head); + +		/* This will also drop the fpriv->fbs reference. */  		drm_framebuffer_remove(fb);  	} -	mutex_unlock(&dev->mode_config.mutex); +	mutex_unlock(&priv->fbs_lock);  }  /** @@ -2660,10 +2806,9 @@ EXPORT_SYMBOL(drm_mode_detachmode_crtc);  /**   * drm_fb_attachmode - Attach a user mode to an connector - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * This attaches a user specified mode to an connector.   * Called by the user via ioctl. @@ -2684,7 +2829,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);  	if (!obj) { @@ -2708,17 +2853,16 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,  	drm_mode_attachmode(dev, connector, mode);  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  }  /**   * drm_fb_detachmode - Detach a user specified mode from an connector - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call   *   * Called by the user via ioctl.   * @@ -2738,7 +2882,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);  	if (!obj) { @@ -2755,7 +2899,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,  	ret = drm_mode_detachmode(dev, connector, &mode);  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -3001,7 +3145,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);  	if (!obj) {  		ret = -EINVAL; @@ -3079,7 +3223,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,  		out_resp->count_enum_blobs = blob_count;  	}  done: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -3130,7 +3274,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);  	if (!obj) {  		ret = -EINVAL; @@ -3148,7 +3292,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,  	out_resp->length = blob->length;  done: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -3290,7 +3434,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);  	if (!obj) { @@ -3327,7 +3471,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,  	}  	arg->count_props = props_count;  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -3344,7 +3488,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);  	if (!arg_obj) @@ -3382,7 +3526,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,  	}  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -3444,7 +3588,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);  	if (!obj) {  		ret = -EINVAL; @@ -3485,7 +3629,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,  	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -3503,7 +3647,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);  	if (!obj) {  		ret = -EINVAL; @@ -3536,7 +3680,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,  		goto out;  	}  out: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -3546,7 +3690,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,  	struct drm_mode_crtc_page_flip *page_flip = data;  	struct drm_mode_object *obj;  	struct drm_crtc *crtc; -	struct drm_framebuffer *fb; +	struct drm_framebuffer *fb = NULL, *old_fb = NULL;  	struct drm_pending_vblank_event *e = NULL;  	unsigned long flags;  	int hdisplay, vdisplay; @@ -3556,12 +3700,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,  	    page_flip->reserved != 0)  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex);  	obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);  	if (!obj) -		goto out; +		return -EINVAL;  	crtc = obj_to_crtc(obj); +	mutex_lock(&crtc->mutex);  	if (crtc->fb == NULL) {  		/* The framebuffer is currently unbound, presumably  		 * due to a hotplug event, that userspace has not @@ -3574,10 +3718,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,  	if (crtc->funcs->page_flip == NULL)  		goto out; -	obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); -	if (!obj) +	fb = drm_framebuffer_lookup(dev, page_flip->fb_id); +	if (!fb)  		goto out; -	fb = obj_to_fb(obj);  	hdisplay = crtc->mode.hdisplay;  	vdisplay = crtc->mode.vdisplay; @@ -3623,6 +3766,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,  			(void (*) (struct drm_pending_event *)) kfree;  	} +	old_fb = crtc->fb;  	ret = crtc->funcs->page_flip(crtc, fb, e);  	if (ret) {  		if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { @@ -3631,10 +3775,20 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,  			spin_unlock_irqrestore(&dev->event_lock, flags);  			kfree(e);  		} +		/* Keep the old fb, don't unref it. */ +		old_fb = NULL; +	} else { +		/* Unref only the old framebuffer. */ +		fb = NULL;  	}  out: -	mutex_unlock(&dev->mode_config.mutex); +	if (fb) +		drm_framebuffer_unreference(fb); +	if (old_fb) +		drm_framebuffer_unreference(old_fb); +	mutex_unlock(&crtc->mutex); +  	return ret;  } diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index fd9d0af4d53..3742bc96421 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -85,6 +85,11 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev,  	if (!fb_cma)  		return ERR_PTR(-ENOMEM); +	drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); + +	for (i = 0; i < num_planes; i++) +		fb_cma->obj[i] = obj[i]; +  	ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs);  	if (ret) {  		dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret); @@ -92,11 +97,6 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev,  		return ERR_PTR(ret);  	} -	drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); - -	for (i = 0; i < num_planes; i++) -		fb_cma->obj[i] = obj[i]; -  	return fb_cma;  } @@ -266,6 +266,7 @@ static int drm_fbdev_cma_create(struct drm_fb_helper *helper,  	return 0;  err_drm_fb_cma_destroy: +	drm_framebuffer_unregister_private(fb);  	drm_fb_cma_destroy(fb);  err_framebuffer_release:  	framebuffer_release(fbi); @@ -370,8 +371,10 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)  		framebuffer_release(info);  	} -	if (fbdev_cma->fb) +	if (fbdev_cma->fb) { +		drm_framebuffer_unregister_private(&fbdev_cma->fb->fb);  		drm_fb_cma_destroy(&fbdev_cma->fb->fb); +	}  	drm_fb_helper_fini(&fbdev_cma->fb_helper);  	kfree(fbdev_cma); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 954d175bd7f..0c6e25e979d 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -245,7 +245,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)  	int i, ret;  	for (i = 0; i < fb_helper->crtc_count; i++) {  		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; -		ret = mode_set->crtc->funcs->set_config(mode_set); +		ret = drm_mode_set_config_internal(mode_set);  		if (ret)  			error = true;  	} @@ -305,6 +305,24 @@ void drm_fb_helper_restore(void)  }  EXPORT_SYMBOL(drm_fb_helper_restore); +static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) +{ +	struct drm_device *dev = fb_helper->dev; +	struct drm_crtc *crtc; +	int bound = 0, crtcs_bound = 0; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		if (crtc->fb) +			crtcs_bound++; +		if (crtc->fb == fb_helper->fb) +			bound++; +	} + +	if (bound < crtcs_bound) +		return false; +	return true; +} +  #ifdef CONFIG_MAGIC_SYSRQ  static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)  { @@ -337,7 +355,12 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)  	/*  	 * For each CRTC in this fb, turn the connectors on/off.  	 */ -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev); +	if (!drm_fb_helper_is_bound(fb_helper)) { +		drm_modeset_unlock_all(dev); +		return; +	} +  	for (i = 0; i < fb_helper->crtc_count; i++) {  		crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -352,7 +375,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)  				dev->mode_config.dpms_property, dpms_mode);  		}  	} -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  }  int drm_fb_helper_blank(int blank, struct fb_info *info) @@ -672,16 +695,16 @@ int drm_fb_helper_set_par(struct fb_info *info)  		return -EINVAL;  	} -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	for (i = 0; i < fb_helper->crtc_count; i++) {  		crtc = fb_helper->crtc_info[i].mode_set.crtc; -		ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); +		ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set);  		if (ret) { -			mutex_unlock(&dev->mode_config.mutex); +			drm_modeset_unlock_all(dev);  			return ret;  		}  	} -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	if (fb_helper->delayed_hotplug) {  		fb_helper->delayed_hotplug = false; @@ -701,7 +724,12 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,  	int ret = 0;  	int i; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev); +	if (!drm_fb_helper_is_bound(fb_helper)) { +		drm_modeset_unlock_all(dev); +		return -EBUSY; +	} +  	for (i = 0; i < fb_helper->crtc_count; i++) {  		crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -711,14 +739,14 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,  		modeset->y = var->yoffset;  		if (modeset->num_connectors) { -			ret = crtc->funcs->set_config(modeset); +			ret = drm_mode_set_config_internal(modeset);  			if (!ret) {  				info->var.xoffset = var->xoffset;  				info->var.yoffset = var->yoffset;  			}  		}  	} -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  }  EXPORT_SYMBOL(drm_fb_helper_pan_display); @@ -1369,23 +1397,14 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)  	struct drm_device *dev = fb_helper->dev;  	int count = 0;  	u32 max_width, max_height, bpp_sel; -	int bound = 0, crtcs_bound = 0; -	struct drm_crtc *crtc;  	if (!fb_helper->fb)  		return 0; -	mutex_lock(&dev->mode_config.mutex); -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -		if (crtc->fb) -			crtcs_bound++; -		if (crtc->fb == fb_helper->fb) -			bound++; -	} - -	if (bound < crtcs_bound) { +	drm_modeset_lock_all(dev); +	if (!drm_fb_helper_is_bound(fb_helper)) {  		fb_helper->delayed_hotplug = true; -		mutex_unlock(&dev->mode_config.mutex); +		drm_modeset_unlock_all(dev);  		return 0;  	}  	DRM_DEBUG_KMS("\n"); @@ -1397,7 +1416,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)  	count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,  						    max_height);  	drm_setup_crtcs(fb_helper); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);  } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 133b4132983..13fdcd10a60 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -276,6 +276,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  	INIT_LIST_HEAD(&priv->lhead);  	INIT_LIST_HEAD(&priv->fbs); +	mutex_init(&priv->fbs_lock);  	INIT_LIST_HEAD(&priv->event_list);  	init_waitqueue_head(&priv->event_wait);  	priv->event_space = 4096; /* set aside 4k for event buffer */ diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 86272f04b82..db1e2d6f90d 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -252,11 +252,13 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,  	BUG_ON(!hole_node->hole_follows || node->allocated); -	if (mm->color_adjust) -		mm->color_adjust(hole_node, color, &adj_start, &adj_end); -  	if (adj_start < start)  		adj_start = start; +	if (adj_end > end) +		adj_end = end; + +	if (mm->color_adjust) +		mm->color_adjust(hole_node, color, &adj_start, &adj_end);  	if (alignment) {  		unsigned tmp = adj_start % alignment; @@ -536,7 +538,7 @@ void drm_mm_init_scan(struct drm_mm *mm,  	mm->scan_size = size;  	mm->scanned_blocks = 0;  	mm->scan_hit_start = 0; -	mm->scan_hit_size = 0; +	mm->scan_hit_end = 0;  	mm->scan_check_range = 0;  	mm->prev_scanned_node = NULL;  } @@ -563,7 +565,7 @@ void drm_mm_init_scan_with_range(struct drm_mm *mm,  	mm->scan_size = size;  	mm->scanned_blocks = 0;  	mm->scan_hit_start = 0; -	mm->scan_hit_size = 0; +	mm->scan_hit_end = 0;  	mm->scan_start = start;  	mm->scan_end = end;  	mm->scan_check_range = 1; @@ -582,8 +584,7 @@ int drm_mm_scan_add_block(struct drm_mm_node *node)  	struct drm_mm *mm = node->mm;  	struct drm_mm_node *prev_node;  	unsigned long hole_start, hole_end; -	unsigned long adj_start; -	unsigned long adj_end; +	unsigned long adj_start, adj_end;  	mm->scanned_blocks++; @@ -600,14 +601,8 @@ int drm_mm_scan_add_block(struct drm_mm_node *node)  	node->node_list.next = &mm->prev_scanned_node->node_list;  	mm->prev_scanned_node = node; -	hole_start = drm_mm_hole_node_start(prev_node); -	hole_end = drm_mm_hole_node_end(prev_node); - -	adj_start = hole_start; -	adj_end = hole_end; - -	if (mm->color_adjust) -		mm->color_adjust(prev_node, mm->scan_color, &adj_start, &adj_end); +	adj_start = hole_start = drm_mm_hole_node_start(prev_node); +	adj_end = hole_end = drm_mm_hole_node_end(prev_node);  	if (mm->scan_check_range) {  		if (adj_start < mm->scan_start) @@ -616,11 +611,14 @@ int drm_mm_scan_add_block(struct drm_mm_node *node)  			adj_end = mm->scan_end;  	} +	if (mm->color_adjust) +		mm->color_adjust(prev_node, mm->scan_color, +				 &adj_start, &adj_end); +  	if (check_free_hole(adj_start, adj_end,  			    mm->scan_size, mm->scan_alignment)) {  		mm->scan_hit_start = hole_start; -		mm->scan_hit_size = hole_end; - +		mm->scan_hit_end = hole_end;  		return 1;  	} @@ -656,19 +654,10 @@ int drm_mm_scan_remove_block(struct drm_mm_node *node)  			       node_list);  	prev_node->hole_follows = node->scanned_preceeds_hole; -	INIT_LIST_HEAD(&node->node_list);  	list_add(&node->node_list, &prev_node->node_list); -	/* Only need to check for containement because start&size for the -	 * complete resulting free block (not just the desired part) is -	 * stored. */ -	if (node->start >= mm->scan_hit_start && -	    node->start + node->size -	    		<= mm->scan_hit_start + mm->scan_hit_size) { -		return 1; -	} - -	return 0; +	 return (drm_mm_hole_node_end(node) > mm->scan_hit_start && +		 node->start < mm->scan_hit_end);  }  EXPORT_SYMBOL(drm_mm_scan_remove_block); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 71f867340a8..90d335cfb8c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -326,8 +326,10 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev,  	/* release drm framebuffer and real buffer */  	if (fb_helper->fb && fb_helper->fb->funcs) {  		fb = fb_helper->fb; -		if (fb) +		if (fb) { +			drm_framebuffer_unregister_private(fb);  			drm_framebuffer_remove(fb); +		}  	}  	/* release linux framebuffer */ diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index afded54dbb1..c1ef37e2efd 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -260,13 +260,13 @@ static int psb_framebuffer_init(struct drm_device *dev,  	default:  		return -EINVAL;  	} +	drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); +	fb->gtt = gt;  	ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs);  	if (ret) {  		dev_err(dev->dev, "framebuffer init failed: %d\n", ret);  		return ret;  	} -	drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); -	fb->gtt = gt;  	return 0;  } @@ -590,6 +590,7 @@ static int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev)  		framebuffer_release(info);  	}  	drm_fb_helper_fini(&fbdev->psb_fb_helper); +	drm_framebuffer_unregister_private(&psbfb->base);  	drm_framebuffer_cleanup(&psbfb->base);  	if (psbfb->gtt) @@ -668,30 +669,6 @@ static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb)  {  	struct psb_framebuffer *psbfb = to_psb_fb(fb);  	struct gtt_range *r = psbfb->gtt; -	struct drm_device *dev = fb->dev; -	struct drm_psb_private *dev_priv = dev->dev_private; -	struct psb_fbdev *fbdev = dev_priv->fbdev; -	struct drm_crtc *crtc; -	int reset = 0; - -	/* Should never get stolen memory for a user fb */ -	WARN_ON(r->stolen); - -	/* Check if we are erroneously live */ -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) -		if (crtc->fb == fb) -			reset = 1; - -	if (reset) -		/* -		 * Now force a sane response before we permit the DRM CRTC -		 * layer to do stupid things like blank the display. Instead -		 * we reset this framebuffer as if the user had forced a reset. -		 * We must do this before the cleanup so that the DRM layer -		 * doesn't get a chance to stick its oar in where it isn't -		 * wanted. -		 */ -		drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);  	/* Let DRM do its clean up */  	drm_framebuffer_cleanup(fb); diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index b58c4701c4e..f6f534b4197 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -194,7 +194,7 @@ static int psb_save_display_registers(struct drm_device *dev)  	regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);  	/* Save crtc and output state */ -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {  		if (drm_helper_crtc_in_use(crtc))  			crtc->funcs->save(crtc); @@ -204,7 +204,7 @@ static int psb_save_display_registers(struct drm_device *dev)  		if (connector->funcs->save)  			connector->funcs->save(connector); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return 0;  } @@ -234,7 +234,7 @@ static int psb_restore_display_registers(struct drm_device *dev)  	/*make sure VGA plane is off. it initializes to on after reset!*/  	PSB_WVDC32(0x80000000, VGACNTRL); -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)  		if (drm_helper_crtc_in_use(crtc))  			crtc->funcs->restore(crtc); @@ -243,7 +243,7 @@ static int psb_restore_display_registers(struct drm_device *dev)  		if (connector->funcs->restore)  			connector->funcs->restore(connector); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return 0;  } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index dd1fbfa7e46..111e3df9c5d 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -149,6 +149,16 @@ static struct drm_ioctl_desc psb_ioctls[] = {  static void psb_lastclose(struct drm_device *dev)  { +	int ret; +	struct drm_psb_private *dev_priv = dev->dev_private; +	struct psb_fbdev *fbdev = dev_priv->fbdev; + +	drm_modeset_lock_all(dev); +	ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); +	if (ret) +		DRM_DEBUG("failed to restore crtc mode\n"); +	drm_modeset_unlock_all(dev); +  	return;  } @@ -476,7 +486,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,  	case PSB_MODE_OPERATION_MODE_VALID:  		umode = &arg->mode; -		mutex_lock(&dev->mode_config.mutex); +		drm_modeset_lock_all(dev);  		obj = drm_mode_object_find(dev, obj_id,  					DRM_MODE_OBJECT_CONNECTOR); @@ -525,7 +535,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,  		if (mode)  			drm_mode_destroy(dev, mode);  mode_op_out: -		mutex_unlock(&dev->mode_config.mutex); +		drm_modeset_unlock_all(dev);  		return ret;  	default: diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index b865d0728e2..51fa3239202 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -364,7 +364,7 @@ static int ch7006_encoder_set_property(struct drm_encoder *encoder,  				.crtc = crtc,  			}; -			crtc->funcs->set_config(&modeset); +			drm_mode_set_config_internal(&modeset);  		}  	} diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 749114881c2..aa9833babad 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1445,28 +1445,31 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)  	ifbdev = dev_priv->fbdev;  	fb = to_intel_framebuffer(ifbdev->helper.fb); -	seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, obj ", +	seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, refcount %d, obj ",  		   fb->base.width,  		   fb->base.height,  		   fb->base.depth, -		   fb->base.bits_per_pixel); +		   fb->base.bits_per_pixel, +		   atomic_read(&fb->base.refcount.refcount));  	describe_obj(m, fb->obj);  	seq_printf(m, "\n"); +	mutex_unlock(&dev->mode_config.mutex); +	mutex_lock(&dev->mode_config.fb_lock);  	list_for_each_entry(fb, &dev->mode_config.fb_list, base.head) {  		if (&fb->base == ifbdev->helper.fb)  			continue; -		seq_printf(m, "user size: %d x %d, depth %d, %d bpp, obj ", +		seq_printf(m, "user size: %d x %d, depth %d, %d bpp, refcount %d, obj ",  			   fb->base.width,  			   fb->base.height,  			   fb->base.depth, -			   fb->base.bits_per_pixel); +			   fb->base.bits_per_pixel, +			   atomic_read(&fb->base.refcount.refcount));  		describe_obj(m, fb->obj);  		seq_printf(m, "\n");  	} - -	mutex_unlock(&dev->mode_config.mutex); +	mutex_unlock(&dev->mode_config.fb_lock);  	return 0;  } diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d7461771be2..62be74899c2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1700,7 +1700,8 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)  }  static long -i915_gem_purge(struct drm_i915_private *dev_priv, long target) +__i915_gem_shrink(struct drm_i915_private *dev_priv, long target, +		  bool purgeable_only)  {  	struct drm_i915_gem_object *obj, *next;  	long count = 0; @@ -1708,7 +1709,7 @@ i915_gem_purge(struct drm_i915_private *dev_priv, long target)  	list_for_each_entry_safe(obj, next,  				 &dev_priv->mm.unbound_list,  				 gtt_list) { -		if (i915_gem_object_is_purgeable(obj) && +		if ((i915_gem_object_is_purgeable(obj) || !purgeable_only) &&  		    i915_gem_object_put_pages(obj) == 0) {  			count += obj->base.size >> PAGE_SHIFT;  			if (count >= target) @@ -1719,7 +1720,7 @@ i915_gem_purge(struct drm_i915_private *dev_priv, long target)  	list_for_each_entry_safe(obj, next,  				 &dev_priv->mm.inactive_list,  				 mm_list) { -		if (i915_gem_object_is_purgeable(obj) && +		if ((i915_gem_object_is_purgeable(obj) || !purgeable_only) &&  		    i915_gem_object_unbind(obj) == 0 &&  		    i915_gem_object_put_pages(obj) == 0) {  			count += obj->base.size >> PAGE_SHIFT; @@ -1731,6 +1732,12 @@ i915_gem_purge(struct drm_i915_private *dev_priv, long target)  	return count;  } +static long +i915_gem_purge(struct drm_i915_private *dev_priv, long target) +{ +	return __i915_gem_shrink(dev_priv, target, true); +} +  static void  i915_gem_shrink_all(struct drm_i915_private *dev_priv)  { @@ -3538,14 +3545,15 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,  		goto out;  	} -	obj->user_pin_count++; -	obj->pin_filp = file; -	if (obj->user_pin_count == 1) { +	if (obj->user_pin_count == 0) {  		ret = i915_gem_object_pin(obj, args->alignment, true, false);  		if (ret)  			goto out;  	} +	obj->user_pin_count++; +	obj->pin_filp = file; +  	/* XXX - flush the CPU caches for pinned objects  	 * as the X server doesn't manage domains yet  	 */ @@ -4377,6 +4385,9 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)  	if (nr_to_scan) {  		nr_to_scan -= i915_gem_purge(dev_priv, nr_to_scan);  		if (nr_to_scan > 0) +			nr_to_scan -= __i915_gem_shrink(dev_priv, nr_to_scan, +							false); +		if (nr_to_scan > 0)  			i915_gem_shrink_all(dev_priv);  	} @@ -4384,7 +4395,7 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)  	list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list)  		if (obj->pages_pin_count == 0)  			cnt += obj->base.size >> PAGE_SHIFT; -	list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) +	list_for_each_entry(obj, &dev_priv->mm.inactive_list, gtt_list)  		if (obj->pin_count == 0 && obj->pages_pin_count == 0)  			cnt += obj->base.size >> PAGE_SHIFT; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d75c6a0a4bd..0dfecaf599f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6467,6 +6467,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,  	if (encoder->crtc) {  		crtc = encoder->crtc; +		mutex_lock(&crtc->mutex); +  		old->dpms_mode = connector->dpms;  		old->load_detect_temp = false; @@ -6496,6 +6498,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,  		return false;  	} +	mutex_lock(&crtc->mutex);  	intel_encoder->new_crtc = to_intel_crtc(crtc);  	to_intel_connector(connector)->new_encoder = intel_encoder; @@ -6523,6 +6526,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,  		DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");  	if (IS_ERR(fb)) {  		DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); +		mutex_unlock(&crtc->mutex);  		return false;  	} @@ -6530,6 +6534,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,  		DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");  		if (old->release_fb)  			old->release_fb->funcs->destroy(old->release_fb); +		mutex_unlock(&crtc->mutex);  		return false;  	} @@ -6544,27 +6549,31 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,  	struct intel_encoder *intel_encoder =  		intel_attached_encoder(connector);  	struct drm_encoder *encoder = &intel_encoder->base; +	struct drm_crtc *crtc = encoder->crtc;  	DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",  		      connector->base.id, drm_get_connector_name(connector),  		      encoder->base.id, drm_get_encoder_name(encoder));  	if (old->load_detect_temp) { -		struct drm_crtc *crtc = encoder->crtc; -  		to_intel_connector(connector)->new_encoder = NULL;  		intel_encoder->new_crtc = NULL;  		intel_set_mode(crtc, NULL, 0, 0, NULL); -		if (old->release_fb) -			old->release_fb->funcs->destroy(old->release_fb); +		if (old->release_fb) { +			drm_framebuffer_unregister_private(old->release_fb); +			drm_framebuffer_unreference(old->release_fb); +		} +		mutex_unlock(&crtc->mutex);  		return;  	}  	/* Switch crtc and encoder back off if necessary */  	if (old->dpms_mode != DRM_MODE_DPMS_ON)  		connector->funcs->dpms(connector, old->dpms_mode); + +	mutex_unlock(&crtc->mutex);  }  /* Returns the clock of the currently programmed mode of the given pipe. */ @@ -8359,19 +8368,30 @@ int intel_framebuffer_init(struct drm_device *dev,  {  	int ret; -	if (obj->tiling_mode == I915_TILING_Y) +	if (obj->tiling_mode == I915_TILING_Y) { +		DRM_DEBUG("hardware does not support tiling Y\n");  		return -EINVAL; +	} -	if (mode_cmd->pitches[0] & 63) +	if (mode_cmd->pitches[0] & 63) { +		DRM_DEBUG("pitch (%d) must be at least 64 byte aligned\n", +			  mode_cmd->pitches[0]);  		return -EINVAL; +	}  	/* FIXME <= Gen4 stride limits are bit unclear */ -	if (mode_cmd->pitches[0] > 32768) +	if (mode_cmd->pitches[0] > 32768) { +		DRM_DEBUG("pitch (%d) must be at less than 32768\n", +			  mode_cmd->pitches[0]);  		return -EINVAL; +	}  	if (obj->tiling_mode != I915_TILING_NONE && -	    mode_cmd->pitches[0] != obj->stride) +	    mode_cmd->pitches[0] != obj->stride) { +		DRM_DEBUG("pitch (%d) must match tiling stride (%d)\n", +			  mode_cmd->pitches[0], obj->stride);  		return -EINVAL; +	}  	/* Reject formats not supported by any plane early. */  	switch (mode_cmd->pixel_format) { @@ -8382,8 +8402,10 @@ int intel_framebuffer_init(struct drm_device *dev,  		break;  	case DRM_FORMAT_XRGB1555:  	case DRM_FORMAT_ARGB1555: -		if (INTEL_INFO(dev)->gen > 3) +		if (INTEL_INFO(dev)->gen > 3) { +			DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format);  			return -EINVAL; +		}  		break;  	case DRM_FORMAT_XBGR8888:  	case DRM_FORMAT_ABGR8888: @@ -8391,18 +8413,22 @@ int intel_framebuffer_init(struct drm_device *dev,  	case DRM_FORMAT_ARGB2101010:  	case DRM_FORMAT_XBGR2101010:  	case DRM_FORMAT_ABGR2101010: -		if (INTEL_INFO(dev)->gen < 4) +		if (INTEL_INFO(dev)->gen < 4) { +			DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format);  			return -EINVAL; +		}  		break;  	case DRM_FORMAT_YUYV:  	case DRM_FORMAT_UYVY:  	case DRM_FORMAT_YVYU:  	case DRM_FORMAT_VYUY: -		if (INTEL_INFO(dev)->gen < 6) +		if (INTEL_INFO(dev)->gen < 5) { +			DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format);  			return -EINVAL; +		}  		break;  	default: -		DRM_DEBUG_KMS("unsupported pixel format 0x%08x\n", mode_cmd->pixel_format); +		DRM_DEBUG("unsupported pixel format 0x%08x\n", mode_cmd->pixel_format);  		return -EINVAL;  	} @@ -8410,14 +8436,15 @@ int intel_framebuffer_init(struct drm_device *dev,  	if (mode_cmd->offsets[0] != 0)  		return -EINVAL; +	drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); +	intel_fb->obj = obj; +  	ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);  	if (ret) {  		DRM_ERROR("framebuffer init failed %d\n", ret);  		return ret;  	} -	drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); -	intel_fb->obj = obj;  	return 0;  } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1b76b04787c..15afcf86ad6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1118,6 +1118,8 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp)  	struct drm_i915_private *dev_priv = dev->dev_private;  	u32 pp; +	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); +  	if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) {  		pp = ironlake_get_pp_control(dev_priv);  		pp &= ~EDP_FORCE_VDD; diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 6591029ac0d..1c510da04d1 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -220,6 +220,7 @@ static void intel_fbdev_destroy(struct drm_device *dev,  	drm_fb_helper_fini(&ifbdev->helper); +	drm_framebuffer_unregister_private(&ifb->base);  	drm_framebuffer_cleanup(&ifb->base);  	if (ifb->obj) {  		drm_gem_object_unreference_unlocked(&ifb->obj->base); @@ -296,7 +297,7 @@ void intel_fb_restore_mode(struct drm_device *dev)  	struct drm_mode_config *config = &dev->mode_config;  	struct drm_plane *plane; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);  	if (ret) @@ -307,5 +308,5 @@ void intel_fb_restore_mode(struct drm_device *dev)  		if (plane->enabled)  			plane->funcs->disable_plane(plane); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 8c61876dbe9..5e3f08e3fd8 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -586,9 +586,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,  	dev_priv->modeset_on_lid = 0; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	intel_modeset_setup_hw_state(dev, true); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return NOTIFY_OK;  } @@ -830,14 +830,6 @@ static const struct dmi_system_id intel_no_lvds[] = {  	},  	{  		.callback = intel_no_lvds_dmi_callback, -		.ident = "ZOTAC ZBOXSD-ID12/ID13", -		.matches = { -			DMI_MATCH(DMI_BOARD_VENDOR, "ZOTAC"), -			DMI_MATCH(DMI_BOARD_NAME, "ZBOXSD-ID12/ID13"), -		}, -	}, -	{ -		.callback = intel_no_lvds_dmi_callback,  		.ident = "Gigabyte GA-D525TUD",  		.matches = {  			DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index ba978bf93a2..67a2501d519 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1045,13 +1045,13 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,  	}  	if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) { -		mutex_lock(&dev->mode_config.mutex); +		drm_modeset_lock_all(dev);  		mutex_lock(&dev->struct_mutex);  		ret = intel_overlay_switch_off(overlay);  		mutex_unlock(&dev->struct_mutex); -		mutex_unlock(&dev->mode_config.mutex); +		drm_modeset_unlock_all(dev);  		return ret;  	} @@ -1075,7 +1075,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,  		goto out_free;  	} -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	mutex_lock(&dev->struct_mutex);  	if (new_bo->tiling_mode) { @@ -1157,7 +1157,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,  		goto out_unlock;  	mutex_unlock(&dev->struct_mutex); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	kfree(params); @@ -1165,7 +1165,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,  out_unlock:  	mutex_unlock(&dev->struct_mutex); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	drm_gem_object_unreference_unlocked(&new_bo->base);  out_free:  	kfree(params); @@ -1241,7 +1241,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,  		return -ENODEV;  	} -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	mutex_lock(&dev->struct_mutex);  	ret = -EINVAL; @@ -1307,7 +1307,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,  	ret = 0;  out_unlock:  	mutex_unlock(&dev->struct_mutex); -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f024e7dcc2c..f7f67360e74 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -44,6 +44,14 @@   * i915.i915_enable_fbc parameter   */ +static bool intel_crtc_active(struct drm_crtc *crtc) +{ +	/* Be paranoid as we can arrive here with only partial +	 * state retrieved from the hardware during setup. +	 */ +	return to_intel_crtc(crtc)->active && crtc->fb && crtc->mode.clock; +} +  static void i8xx_disable_fbc(struct drm_device *dev)  {  	struct drm_i915_private *dev_priv = dev->dev_private; @@ -405,9 +413,8 @@ void intel_update_fbc(struct drm_device *dev)  	 *   - going to an unsupported config (interlace, pixel multiply, etc.)  	 */  	list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) { -		if (to_intel_crtc(tmp_crtc)->active && -		    !to_intel_crtc(tmp_crtc)->primary_disabled && -		    tmp_crtc->fb) { +		if (intel_crtc_active(tmp_crtc) && +		    !to_intel_crtc(tmp_crtc)->primary_disabled) {  			if (crtc) {  				DRM_DEBUG_KMS("more than one pipe active, disabling compression\n");  				dev_priv->no_fbc_reason = FBC_MULTIPLE_PIPES; @@ -995,7 +1002,7 @@ static struct drm_crtc *single_enabled_crtc(struct drm_device *dev)  	struct drm_crtc *crtc, *enabled = NULL;  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -		if (to_intel_crtc(crtc)->active && crtc->fb) { +		if (intel_crtc_active(crtc)) {  			if (enabled)  				return NULL;  			enabled = crtc; @@ -1089,7 +1096,7 @@ static bool g4x_compute_wm0(struct drm_device *dev,  	int entries, tlb_miss;  	crtc = intel_get_crtc_for_plane(dev, plane); -	if (crtc->fb == NULL || !to_intel_crtc(crtc)->active) { +	if (!intel_crtc_active(crtc)) {  		*cursor_wm = cursor->guard_size;  		*plane_wm = display->guard_size;  		return false; @@ -1218,7 +1225,7 @@ static bool vlv_compute_drain_latency(struct drm_device *dev,  	int entries;  	crtc = intel_get_crtc_for_plane(dev, plane); -	if (crtc->fb == NULL || !to_intel_crtc(crtc)->active) +	if (!intel_crtc_active(crtc))  		return false;  	clock = crtc->mode.clock;	/* VESA DOT Clock */ @@ -1479,7 +1486,7 @@ static void i9xx_update_wm(struct drm_device *dev)  	fifo_size = dev_priv->display.get_fifo_size(dev, 0);  	crtc = intel_get_crtc_for_plane(dev, 0); -	if (to_intel_crtc(crtc)->active && crtc->fb) { +	if (intel_crtc_active(crtc)) {  		int cpp = crtc->fb->bits_per_pixel / 8;  		if (IS_GEN2(dev))  			cpp = 4; @@ -1493,7 +1500,7 @@ static void i9xx_update_wm(struct drm_device *dev)  	fifo_size = dev_priv->display.get_fifo_size(dev, 1);  	crtc = intel_get_crtc_for_plane(dev, 1); -	if (to_intel_crtc(crtc)->active && crtc->fb) { +	if (intel_crtc_active(crtc)) {  		int cpp = crtc->fb->bits_per_pixel / 8;  		if (IS_GEN2(dev))  			cpp = 4; @@ -2047,7 +2054,7 @@ sandybridge_compute_sprite_wm(struct drm_device *dev, int plane,  	int entries, tlb_miss;  	crtc = intel_get_crtc_for_plane(dev, plane); -	if (crtc->fb == NULL || !to_intel_crtc(crtc)->active) { +	if (!intel_crtc_active(crtc)) {  		*sprite_wm = display->guard_size;  		return false;  	} diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 827dcd4edf1..f8293061d6b 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -120,11 +120,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,  	I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);  	I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); -	linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); +	linear_offset = y * fb->pitches[0] + x * pixel_size;  	sprsurf_offset =  		intel_gen4_compute_offset_xtiled(&x, &y, -						 fb->bits_per_pixel / 8, -						 fb->pitches[0]); +						 pixel_size, fb->pitches[0]);  	linear_offset -= sprsurf_offset;  	/* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET @@ -286,11 +285,10 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,  	I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);  	I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); -	linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); +	linear_offset = y * fb->pitches[0] + x * pixel_size;  	dvssurf_offset =  		intel_gen4_compute_offset_xtiled(&x, &y, -						 fb->bits_per_pixel / 8, -						 fb->pitches[0]); +						 pixel_size, fb->pitches[0]);  	linear_offset -= dvssurf_offset;  	if (obj->tiling_mode != I915_TILING_NONE) @@ -595,7 +593,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,  	if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE))  		return -EINVAL; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE);  	if (!obj) { @@ -608,7 +606,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,  	ret = intel_plane->update_colorkey(plane, set);  out_unlock: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } @@ -624,7 +622,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_MODESET))  		return -ENODEV; -	mutex_lock(&dev->mode_config.mutex); +	drm_modeset_lock_all(dev);  	obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE);  	if (!obj) { @@ -637,7 +635,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,  	intel_plane->get_colorkey(plane, get);  out_unlock: -	mutex_unlock(&dev->mode_config.mutex); +	drm_modeset_unlock_all(dev);  	return ret;  } diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 2f486481d79..5c69b432f99 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -247,6 +247,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,  	}  	drm_fb_helper_fini(&mfbdev->helper);  	vfree(mfbdev->sysram); +	drm_framebuffer_unregister_private(&mfb->base);  	drm_framebuffer_cleanup(&mfb->base);  	return 0; diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 70dd3c5529d..64297c72464 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -23,16 +23,8 @@ static void mga_user_framebuffer_destroy(struct drm_framebuffer *fb)  	kfree(fb);  } -static int mga_user_framebuffer_create_handle(struct drm_framebuffer *fb, -						 struct drm_file *file_priv, -						 unsigned int *handle) -{ -	return 0; -} -  static const struct drm_framebuffer_funcs mga_fb_funcs = {  	.destroy = mga_user_framebuffer_destroy, -	.create_handle = mga_user_framebuffer_create_handle,  };  int mgag200_framebuffer_init(struct drm_device *dev, @@ -40,13 +32,15 @@ int mgag200_framebuffer_init(struct drm_device *dev,  			     struct drm_mode_fb_cmd2 *mode_cmd,  			     struct drm_gem_object *obj)  { -	int ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); +	int ret; +	 +	drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); +	gfb->obj = obj; +	ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs);  	if (ret) {  		DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);  		return ret;  	} -	drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); -	gfb->obj = obj;  	return 0;  } diff --git a/drivers/gpu/drm/nouveau/core/core/client.c b/drivers/gpu/drm/nouveau/core/core/client.c index c617f048007..8bbb58f94a1 100644 --- a/drivers/gpu/drm/nouveau/core/core/client.c +++ b/drivers/gpu/drm/nouveau/core/core/client.c @@ -66,10 +66,8 @@ nouveau_client_create_(const char *name, u64 devname, const char *cfg,  	ret = nouveau_handle_create(nv_object(client), ~0, ~0,  				    nv_object(client), &client->root); -	if (ret) { -		nouveau_namedb_destroy(&client->base); +	if (ret)  		return ret; -	}  	/* prevent init/fini being called, os in in charge of this */  	atomic_set(&nv_object(client)->usecount, 2); diff --git a/drivers/gpu/drm/nouveau/core/core/handle.c b/drivers/gpu/drm/nouveau/core/core/handle.c index b8d2cbf8a7a..264c2b338ac 100644 --- a/drivers/gpu/drm/nouveau/core/core/handle.c +++ b/drivers/gpu/drm/nouveau/core/core/handle.c @@ -109,7 +109,7 @@ nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle,  	while (!nv_iclass(namedb, NV_NAMEDB_CLASS))  		namedb = namedb->parent; -	handle = *phandle = kzalloc(sizeof(*handle), GFP_KERNEL); +	handle = kzalloc(sizeof(*handle), GFP_KERNEL);  	if (!handle)  		return -ENOMEM; @@ -146,6 +146,9 @@ nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle,  	}  	hprintk(handle, TRACE, "created\n"); + +	*phandle = handle; +  	return 0;  } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 0f09af13541..ca1a7d76a95 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -851,20 +851,23 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)  	for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)  		ctrl = nv_rd32(priv, 0x610b5c + (i * 8)); -	if (nv_device(priv)->chipset  < 0x90 || -	    nv_device(priv)->chipset == 0x92 || -	    nv_device(priv)->chipset == 0xa0) { -		for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) -			ctrl = nv_rd32(priv, 0x610b74 + (i * 8)); -		i += 3; -	} else { -		for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) -			ctrl = nv_rd32(priv, 0x610798 + (i * 8)); -		i += 3; +	if (!(ctrl & (1 << head))) { +		if (nv_device(priv)->chipset  < 0x90 || +		    nv_device(priv)->chipset == 0x92 || +		    nv_device(priv)->chipset == 0xa0) { +			for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) +				ctrl = nv_rd32(priv, 0x610b74 + (i * 8)); +			i += 4; +		} else { +			for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) +				ctrl = nv_rd32(priv, 0x610798 + (i * 8)); +			i += 4; +		}  	}  	if (!(ctrl & (1 << head)))  		return false; +	i--;  	data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);  	if (data) { @@ -898,20 +901,23 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,  	for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)  		ctrl = nv_rd32(priv, 0x610b58 + (i * 8)); -	if (nv_device(priv)->chipset  < 0x90 || -	    nv_device(priv)->chipset == 0x92 || -	    nv_device(priv)->chipset == 0xa0) { -		for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) -			ctrl = nv_rd32(priv, 0x610b70 + (i * 8)); -		i += 3; -	} else { -		for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) -			ctrl = nv_rd32(priv, 0x610794 + (i * 8)); -		i += 3; +	if (!(ctrl & (1 << head))) { +		if (nv_device(priv)->chipset  < 0x90 || +		    nv_device(priv)->chipset == 0x92 || +		    nv_device(priv)->chipset == 0xa0) { +			for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) +				ctrl = nv_rd32(priv, 0x610b70 + (i * 8)); +			i += 4; +		} else { +			for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) +				ctrl = nv_rd32(priv, 0x610794 + (i * 8)); +			i += 4; +		}  	}  	if (!(ctrl & (1 << head)))  		return 0x0000; +	i--;  	data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1);  	if (!data) diff --git a/drivers/gpu/drm/nouveau/core/include/core/client.h b/drivers/gpu/drm/nouveau/core/include/core/client.h index 0193532ceac..63acc0346ff 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/client.h +++ b/drivers/gpu/drm/nouveau/core/include/core/client.h @@ -36,6 +36,9 @@ nouveau_client(void *obj)  int  nouveau_client_create_(const char *name, u64 device, const char *cfg,  			    const char *dbg, int, void **); +#define nouveau_client_destroy(p)                                              \ +	nouveau_namedb_destroy(&(p)->base) +  int  nouveau_client_init(struct nouveau_client *);  int  nouveau_client_fini(struct nouveau_client *, bool suspend); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h index c345097592f..b2f3d4d0aa4 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h @@ -38,6 +38,8 @@ enum nvbios_pll_type {  	PLL_UNK42  = 0x42,  	PLL_VPLL0  = 0x80,  	PLL_VPLL1  = 0x81, +	PLL_VPLL2  = 0x82, +	PLL_VPLL3  = 0x83,  	PLL_MAX    = 0xff  }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 2917d552689..690ed438b2a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -1534,7 +1534,6 @@ init_io(struct nvbios_init *init)  		mdelay(10);  		init_wr32(init, 0x614100, 0x10000018);  		init_wr32(init, 0x614900, 0x10000018); -		return;  	}  	value = init_rdport(init, port) & mask; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c index f6962c9b6c3..7c9626258a4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c @@ -52,6 +52,8 @@ nvc0_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)  	switch (info.type) {  	case PLL_VPLL0:  	case PLL_VPLL1: +	case PLL_VPLL2: +	case PLL_VPLL3:  		nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100);  		nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M);  		nv_wr32(priv, info.reg + 0x10, fN << 16); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c index 306bdf12145..7606ed15b6f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c @@ -145,14 +145,14 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,  	mem->memtype = type;  	mem->size = size; -	mutex_lock(&mm->mutex); +	mutex_lock(&pfb->base.mutex);  	do {  		if (back)  			ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);  		else  			ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r);  		if (ret) { -			mutex_unlock(&mm->mutex); +			mutex_unlock(&pfb->base.mutex);  			pfb->ram.put(pfb, &mem);  			return ret;  		} @@ -160,7 +160,7 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,  		list_add_tail(&r->rl_entry, &mem->regions);  		size -= r->length;  	} while (size); -	mutex_unlock(&mm->mutex); +	mutex_unlock(&pfb->base.mutex);  	r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);  	mem->offset = (u64)r->offset << 12; diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/base.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/base.c index 1188227ca6a..6565f3dbbe0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/instmem/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/base.c @@ -40,15 +40,21 @@ nouveau_instobj_create_(struct nouveau_object *parent,  	if (ret)  		return ret; +	mutex_lock(&imem->base.mutex);  	list_add(&iobj->head, &imem->list); +	mutex_unlock(&imem->base.mutex);  	return 0;  }  void  nouveau_instobj_destroy(struct nouveau_instobj *iobj)  { -	if (iobj->head.prev) -		list_del(&iobj->head); +	struct nouveau_subdev *subdev = nv_subdev(iobj->base.engine); + +	mutex_lock(&subdev->mutex); +	list_del(&iobj->head); +	mutex_unlock(&subdev->mutex); +  	return nouveau_object_destroy(&iobj->base);  } @@ -88,6 +94,8 @@ nouveau_instmem_init(struct nouveau_instmem *imem)  	if (ret)  		return ret; +	mutex_lock(&imem->base.mutex); +  	list_for_each_entry(iobj, &imem->list, head) {  		if (iobj->suspend) {  			for (i = 0; i < iobj->size; i += 4) @@ -97,6 +105,8 @@ nouveau_instmem_init(struct nouveau_instmem *imem)  		}  	} +	mutex_unlock(&imem->base.mutex); +  	return 0;  } @@ -104,17 +114,26 @@ int  nouveau_instmem_fini(struct nouveau_instmem *imem, bool suspend)  {  	struct nouveau_instobj *iobj; -	int i; +	int i, ret = 0;  	if (suspend) { +		mutex_lock(&imem->base.mutex); +  		list_for_each_entry(iobj, &imem->list, head) {  			iobj->suspend = vmalloc(iobj->size); -			if (iobj->suspend) { -				for (i = 0; i < iobj->size; i += 4) -					iobj->suspend[i / 4] = nv_ro32(iobj, i); -			} else -				return -ENOMEM; +			if (!iobj->suspend) { +				ret = -ENOMEM; +				break; +			} + +			for (i = 0; i < iobj->size; i += 4) +				iobj->suspend[i / 4] = nv_ro32(iobj, i);  		} + +		mutex_unlock(&imem->base.mutex); + +		if (ret) +			return ret;  	}  	return nouveau_subdev_fini(&imem->base, suspend); diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c index 082c11b75ac..77c67fc970e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c @@ -352,7 +352,7 @@ nouveau_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length,  	u64 mm_length = (offset + length) - mm_offset;  	int ret; -	vm = *pvm = kzalloc(sizeof(*vm), GFP_KERNEL); +	vm = kzalloc(sizeof(*vm), GFP_KERNEL);  	if (!vm)  		return -ENOMEM; @@ -376,6 +376,8 @@ nouveau_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length,  		return ret;  	} +	*pvm = vm; +  	return 0;  } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 69d7b1d0b9d..64d6e3047de 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -300,17 +300,18 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)  	struct ttm_buffer_object *bo = &nvbo->bo;  	int ret; +	ret = ttm_bo_reserve(bo, false, false, false, 0); +	if (ret) +		goto out; +  	if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) {  		NV_ERROR(drm, "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo,  			 1 << bo->mem.mem_type, memtype); -		return -EINVAL; +		ret = -EINVAL; +		goto out;  	}  	if (nvbo->pin_refcnt++) -		return 0; - -	ret = ttm_bo_reserve(bo, false, false, false, 0); -	if (ret)  		goto out;  	nouveau_bo_placement_set(nvbo, memtype, 0); @@ -328,10 +329,8 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)  			break;  		}  	} -	ttm_bo_unreserve(bo);  out: -	if (unlikely(ret)) -		nvbo->pin_refcnt--; +	ttm_bo_unreserve(bo);  	return ret;  } @@ -342,13 +341,13 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)  	struct ttm_buffer_object *bo = &nvbo->bo;  	int ret; -	if (--nvbo->pin_refcnt) -		return 0; -  	ret = ttm_bo_reserve(bo, false, false, false, 0);  	if (ret)  		return ret; +	if (--nvbo->pin_refcnt) +		goto out; +  	nouveau_bo_placement_set(nvbo, bo->mem.placement, 0);  	ret = nouveau_bo_validate(nvbo, false, false); @@ -365,6 +364,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)  		}  	} +out:  	ttm_bo_unreserve(bo);  	return ret;  } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 25ca37989d2..81d00fe03b5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -28,6 +28,8 @@ struct nouveau_bo {  	struct nouveau_drm_tile *tile;  	struct drm_gem_object *gem; + +	/* protect by the ttm reservation lock */  	int pin_refcnt;  	struct ttm_bo_kmap_obj dma_buf_vmap; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index ac340ba3201..e620ba8271b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -127,12 +127,26 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,  			     struct nouveau_encoder **pnv_encoder)  {  	struct drm_device *dev = connector->dev; +	struct nouveau_connector *nv_connector = nouveau_connector(connector);  	struct nouveau_drm *drm = nouveau_drm(dev); +	struct nouveau_gpio *gpio = nouveau_gpio(drm->device);  	struct nouveau_i2c *i2c = nouveau_i2c(drm->device); -	int i; +	struct nouveau_i2c_port *port = NULL; +	int i, panel = -ENODEV; + +	/* eDP panels need powering on by us (if the VBIOS doesn't default it +	 * to on) before doing any AUX channel transactions.  LVDS panel power +	 * is handled by the SOR itself, and not required for LVDS DDC. +	 */ +	if (nv_connector->type == DCB_CONNECTOR_eDP) { +		panel = gpio->get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); +		if (panel == 0) { +			gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); +			msleep(300); +		} +	}  	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { -		struct nouveau_i2c_port *port = NULL;  		struct nouveau_encoder *nv_encoder;  		struct drm_mode_object *obj;  		int id; @@ -150,11 +164,19 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,  			port = i2c->find(i2c, nv_encoder->dcb->i2c_index);  		if (port && nv_probe_i2c(port, 0x50)) {  			*pnv_encoder = nv_encoder; -			return port; +			break;  		} + +		port = NULL;  	} -	return NULL; +	/* eDP panel not detected, restore panel power GPIO to previous +	 * state to avoid confusing the SOR for other output types. +	 */ +	if (!port && panel == 0) +		gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel); + +	return port;  }  static struct nouveau_encoder * diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index e4188f24fc7..d42c9e860c1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -78,11 +78,6 @@ nouveau_framebuffer_init(struct drm_device *dev,  	struct drm_framebuffer *fb = &nv_fb->base;  	int ret; -	ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); -	if (ret) { -		return ret; -	} -  	drm_helper_mode_fill_fb_struct(fb, mode_cmd);  	nv_fb->nvbo = nvbo; @@ -125,6 +120,11 @@ nouveau_framebuffer_init(struct drm_device *dev,  		}  	} +	ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); +	if (ret) { +		return ret; +	} +  	return 0;  } @@ -225,15 +225,6 @@ nouveau_display_init(struct drm_device *dev)  	if (ret)  		return ret; -	/* power on internal panel if it's not already.  the init tables of -	 * some vbios default this to off for some reason, causing the -	 * panel to not work after resume -	 */ -	if (gpio && gpio->get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff) == 0) { -		gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); -		msleep(300); -	} -  	/* enable polling for external displays */  	drm_kms_helper_poll_enable(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 180a45e3b52..8b090f1eb51 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -84,11 +84,16 @@ nouveau_cli_create(struct pci_dev *pdev, const char *name,  	struct nouveau_cli *cli;  	int ret; +	*pcli = NULL;  	ret = nouveau_client_create_(name, nouveau_name(pdev), nouveau_config,  				     nouveau_debug, size, pcli);  	cli = *pcli; -	if (ret) +	if (ret) { +		if (cli) +			nouveau_client_destroy(&cli->base); +		*pcli = NULL;  		return ret; +	}  	mutex_init(&cli->mutex);  	return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 67a1a069de2..d4ecb4deb48 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -433,6 +433,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)  		nouveau_fb->nvbo = NULL;  	}  	drm_fb_helper_fini(&fbcon->helper); +	drm_framebuffer_unregister_private(&nouveau_fb->base);  	drm_framebuffer_cleanup(&nouveau_fb->base);  	return 0;  } diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index bedafd1c953..cdb83acdffe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -60,6 +60,7 @@ u32  nv10_fence_read(struct nouveau_channel *);  void nv10_fence_context_del(struct nouveau_channel *);  void nv10_fence_destroy(struct nouveau_drm *);  int  nv10_fence_create(struct nouveau_drm *); +void nv17_fence_resume(struct nouveau_drm *drm);  int nv50_fence_create(struct nouveau_drm *);  int nv84_fence_create(struct nouveau_drm *); diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c index 184cdf80676..39ffc07f906 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -505,7 +505,7 @@ static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode)  static inline bool is_powersaving_dpms(int mode)  { -	return (mode != DRM_MODE_DPMS_ON); +	return mode != DRM_MODE_DPMS_ON && mode != NV_DPMS_CLEARED;  }  static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode) diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 2cd6fb8c548..4c6e9f83fe8 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -140,7 +140,7 @@ nv04_display_destroy(struct drm_device *dev)  			.crtc = crtc,  		}; -		crtc->funcs->set_config(&modeset); +		drm_mode_set_config_internal(&modeset);  	}  	/* Restore state */ diff --git a/drivers/gpu/drm/nouveau/nv10_fence.c b/drivers/gpu/drm/nouveau/nv10_fence.c index 7ae7f97a6d4..03017f24d59 100644 --- a/drivers/gpu/drm/nouveau/nv10_fence.c +++ b/drivers/gpu/drm/nouveau/nv10_fence.c @@ -162,6 +162,13 @@ nv10_fence_destroy(struct nouveau_drm *drm)  	kfree(priv);  } +void nv17_fence_resume(struct nouveau_drm *drm) +{ +	struct nv10_fence_priv *priv = drm->fence; + +	nouveau_bo_wr32(priv->bo, 0, priv->sequence); +} +  int  nv10_fence_create(struct nouveau_drm *drm)  { @@ -197,6 +204,7 @@ nv10_fence_create(struct nouveau_drm *drm)  		if (ret == 0) {  			nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);  			priv->base.sync = nv17_fence_sync; +			priv->base.resume = nv17_fence_resume;  		}  	} diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 2ca276ada50..977e42be205 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -768,7 +768,7 @@ static int nv17_tv_set_property(struct drm_encoder *encoder,  				.crtc = crtc,  			}; -			crtc->funcs->set_config(&modeset); +			drm_mode_set_config_internal(&modeset);  		}  	} diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 35874085a61..d4cbea19b89 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -128,6 +128,11 @@ struct nv50_dmac {  	struct nv50_chan base;  	dma_addr_t handle;  	u32 *ptr; + +	/* Protects against concurrent pushbuf access to this channel, lock is +	 * grabbed by evo_wait (if the pushbuf reservation is successful) and +	 * dropped again by evo_kick. */ +	struct mutex lock;  };  static void @@ -271,6 +276,8 @@ nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head,  	u32 pushbuf = *(u32 *)data;  	int ret; +	mutex_init(&dmac->lock); +  	dmac->ptr = pci_alloc_consistent(nv_device(core)->pdev, PAGE_SIZE,  					&dmac->handle);  	if (!dmac->ptr) @@ -395,11 +402,13 @@ evo_wait(void *evoc, int nr)  	struct nv50_dmac *dmac = evoc;  	u32 put = nv_ro32(dmac->base.user, 0x0000) / 4; +	mutex_lock(&dmac->lock);  	if (put + nr >= (PAGE_SIZE / 4) - 8) {  		dmac->ptr[put] = 0x20000000;  		nv_wo32(dmac->base.user, 0x0000, 0x00000000);  		if (!nv_wait(dmac->base.user, 0x0004, ~0, 0x00000000)) { +			mutex_unlock(&dmac->lock);  			NV_ERROR(dmac->base.user, "channel stalled\n");  			return NULL;  		} @@ -415,6 +424,7 @@ evo_kick(u32 *push, void *evoc)  {  	struct nv50_dmac *dmac = evoc;  	nv_wo32(dmac->base.user, 0x0000, (push - dmac->ptr) << 2); +	mutex_unlock(&dmac->lock);  }  #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) diff --git a/drivers/gpu/drm/nouveau/nv50_fence.c b/drivers/gpu/drm/nouveau/nv50_fence.c index c20f2727ea0..d889f3ac0d4 100644 --- a/drivers/gpu/drm/nouveau/nv50_fence.c +++ b/drivers/gpu/drm/nouveau/nv50_fence.c @@ -122,6 +122,7 @@ nv50_fence_create(struct nouveau_drm *drm)  	if (ret == 0) {  		nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);  		priv->base.sync = nv17_fence_sync; +		priv->base.resume = nv17_fence_resume;  	}  	if (ret) diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 03191a56eb4..69ec24ab8d6 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -2476,8 +2476,10 @@ static void r600_cs_parser_fini(struct radeon_cs_parser *parser, int error)  	kfree(parser->relocs);  	for (i = 0; i < parser->nchunks; i++) {  		kfree(parser->chunks[i].kdata); -		kfree(parser->chunks[i].kpage[0]); -		kfree(parser->chunks[i].kpage[1]); +		if (parser->rdev && (parser->rdev->flags & RADEON_IS_AGP)) { +			kfree(parser->chunks[i].kpage[0]); +			kfree(parser->chunks[i].kpage[1]); +		}  	}  	kfree(parser->chunks);  	kfree(parser->chunks_array); @@ -2561,16 +2563,16 @@ int r600_dma_cs_next_reloc(struct radeon_cs_parser *p,  	struct radeon_cs_chunk *relocs_chunk;  	unsigned idx; +	*cs_reloc = NULL;  	if (p->chunk_relocs_idx == -1) {  		DRM_ERROR("No relocation chunk !\n");  		return -EINVAL;  	} -	*cs_reloc = NULL;  	relocs_chunk = &p->chunks[p->chunk_relocs_idx];  	idx = p->dma_reloc_idx; -	if (idx >= relocs_chunk->length_dw) { +	if (idx >= p->nrelocs) {  		DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", -			  idx, relocs_chunk->length_dw); +			  idx, p->nrelocs);  		return -EINVAL;  	}  	*cs_reloc = p->relocs_ptr[idx]; diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 396baba0141..469661fd190 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -279,13 +279,13 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)  				  p->chunks[p->chunk_ib_idx].length_dw);  			return -EINVAL;  		} -		if ((p->rdev->flags & RADEON_IS_AGP)) { +		if (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) {  			p->chunks[p->chunk_ib_idx].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL);  			p->chunks[p->chunk_ib_idx].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL);  			if (p->chunks[p->chunk_ib_idx].kpage[0] == NULL ||  			    p->chunks[p->chunk_ib_idx].kpage[1] == NULL) { -				kfree(p->chunks[i].kpage[0]); -				kfree(p->chunks[i].kpage[1]); +				kfree(p->chunks[p->chunk_ib_idx].kpage[0]); +				kfree(p->chunks[p->chunk_ib_idx].kpage[1]);  				return -ENOMEM;  			}  		} @@ -583,7 +583,8 @@ static int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx)  	struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];  	int i;  	int size = PAGE_SIZE; -	bool copy1 = (p->rdev->flags & RADEON_IS_AGP) ? false : true; +	bool copy1 = (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) ? +		false : true;  	for (i = ibc->last_copied_page + 1; i < pg_idx; i++) {  		if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)), diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index ad6df625e8b..c1680e6d76a 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -245,8 +245,14 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc,  		int i = 0;  		struct drm_crtc *crtc_p; -		/* avivo cursor image can't end on 128 pixel boundary or +		/* +		 * avivo cursor image can't end on 128 pixel boundary or  		 * go past the end of the frame if both crtcs are enabled +		 * +		 * NOTE: It is safe to access crtc->enabled of other crtcs +		 * without holding either the mode_config lock or the other +		 * crtc's lock as long as write access to this flag _always_ +		 * grabs all locks.  		 */  		list_for_each_entry(crtc_p, &crtc->dev->mode_config.crtc_list, head) {  			if (crtc_p->enabled) diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 1da2386d7cf..12ec3effb40 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1089,12 +1089,12 @@ radeon_framebuffer_init(struct drm_device *dev,  {  	int ret;  	rfb->obj = obj; +	drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd);  	ret = drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs);  	if (ret) {  		rfb->obj = NULL;  		return ret;  	} -	drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd);  	return 0;  } diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index cc8489d8c6d..515e5ee1f9e 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -293,6 +293,7 @@ out_unref:  	}  	if (fb && ret) {  		drm_gem_object_unreference(gobj); +		drm_framebuffer_unregister_private(fb);  		drm_framebuffer_cleanup(fb);  		kfree(fb);  	} @@ -339,6 +340,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb  		rfb->obj = NULL;  	}  	drm_fb_helper_fini(&rfbdev->helper); +	drm_framebuffer_unregister_private(&rfb->base);  	drm_framebuffer_cleanup(&rfb->base);  	return 0; diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index f5ba2241dac..62cd512f5c8 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -640,6 +640,14 @@ static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_enc  	enum drm_connector_status found = connector_status_disconnected;  	bool color = true; +	/* just don't bother on RN50 those chip are often connected to remoting +	 * console hw and often we get failure to load detect those. So to make +	 * everyone happy report the encoder as always connected. +	 */ +	if (ASIC_IS_RN50(rdev)) { +		return connector_status_connected; +	} +  	/* save the regs we need */  	vclk_ecp_cntl = RREG32_PLL(RADEON_VCLK_ECP_CNTL);  	crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index d1d5306ebf2..f6e0b539505 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -313,9 +313,9 @@ static int shmob_drm_pm_resume(struct device *dev)  {  	struct shmob_drm_device *sdev = dev_get_drvdata(dev); -	mutex_lock(&sdev->ddev->mode_config.mutex); +	drm_modeset_lock_all(sdev->ddev);  	shmob_drm_crtc_resume(&sdev->crtc); -	mutex_unlock(&sdev->ddev->mode_config.mutex); +	drm_modeset_unlock_all(sdev->ddev);  	drm_kms_helper_poll_enable(sdev->ddev);  	return 0; diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index 512f44add89..fe5cdbcf263 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -22,13 +22,17 @@  static u8 *udl_get_edid(struct udl_device *udl)  {  	u8 *block; -	char rbuf[3]; +	char *rbuf;  	int ret, i;  	block = kmalloc(EDID_LENGTH, GFP_KERNEL);  	if (block == NULL)  		return NULL; +	rbuf = kmalloc(2, GFP_KERNEL); +	if (rbuf == NULL) +		goto error; +  	for (i = 0; i < EDID_LENGTH; i++) {  		ret = usb_control_msg(udl->ddev->usbdev,  				      usb_rcvctrlpipe(udl->ddev->usbdev, 0), (0x02), @@ -36,16 +40,17 @@ static u8 *udl_get_edid(struct udl_device *udl)  				      HZ);  		if (ret < 1) {  			DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); -			i--;  			goto error;  		}  		block[i] = rbuf[1];  	} +	kfree(rbuf);  	return block;  error:  	kfree(block); +	kfree(rbuf);  	return NULL;  } @@ -57,6 +62,14 @@ static int udl_get_modes(struct drm_connector *connector)  	edid = (struct edid *)udl_get_edid(udl); +	/* +	 * We only read the main block, but if the monitor reports extension +	 * blocks then the drm edid code expects them to be present, so patch +	 * the extension count to 0. +	 */ +	edid->checksum += edid->extensions; +	edid->extensions = 0; +  	drm_mode_connector_update_edid_property(connector, edid);  	ret = drm_add_edid_modes(connector, edid);  	kfree(edid); diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index d4ab3beaada..caa84f1de9c 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -422,7 +422,6 @@ static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb)  static const struct drm_framebuffer_funcs udlfb_funcs = {  	.destroy = udl_user_framebuffer_destroy,  	.dirty = udl_user_framebuffer_dirty, -	.create_handle = NULL,  }; @@ -435,8 +434,8 @@ udl_framebuffer_init(struct drm_device *dev,  	int ret;  	ufb->obj = obj; -	ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs);  	drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd); +	ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs);  	return ret;  } @@ -556,6 +555,7 @@ static void udl_fbdev_destroy(struct drm_device *dev,  		framebuffer_release(info);  	}  	drm_fb_helper_fini(&ufbdev->helper); +	drm_framebuffer_unregister_private(&ufbdev->ufb.base);  	drm_framebuffer_cleanup(&ufbdev->ufb.base);  	drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base);  } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 161f8b2549a..07dfd823cc3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -829,7 +829,7 @@ static void vmw_lastclose(struct drm_device *dev)  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {  		set.crtc = crtc; -		ret = crtc->funcs->set_config(&set); +		ret = drm_mode_set_config_internal(&set);  		WARN_ON(ret != 0);  	} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index d9fbbe19107..c509d40c489 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -131,7 +131,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,  	struct vmw_master *vmaster = vmw_master(file_priv->master);  	struct drm_vmw_rect __user *clips_ptr;  	struct drm_vmw_rect *clips = NULL; -	struct drm_mode_object *obj; +	struct drm_framebuffer *fb;  	struct vmw_framebuffer *vfb;  	struct vmw_resource *res;  	uint32_t num_clips; @@ -163,19 +163,15 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,  		goto out_no_copy;  	} -	ret = mutex_lock_interruptible(&dev->mode_config.mutex); -	if (unlikely(ret != 0)) { -		ret = -ERESTARTSYS; -		goto out_no_mode_mutex; -	} +	drm_modeset_lock_all(dev); -	obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); -	if (!obj) { +	fb = drm_framebuffer_lookup(dev, arg->fb_id); +	if (!fb) {  		DRM_ERROR("Invalid framebuffer id.\n");  		ret = -EINVAL;  		goto out_no_fb;  	} -	vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); +	vfb = vmw_framebuffer_to_vfb(fb);  	ret = ttm_read_lock(&vmaster->lock, true);  	if (unlikely(ret != 0)) @@ -199,9 +195,9 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,  out_no_surface:  	ttm_read_unlock(&vmaster->lock);  out_no_ttm_lock: +	drm_framebuffer_unreference(fb);  out_no_fb: -	mutex_unlock(&dev->mode_config.mutex); -out_no_mode_mutex: +	drm_modeset_unlock_all(dev);  out_no_copy:  	kfree(clips);  out_clips: @@ -220,7 +216,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,  	struct vmw_master *vmaster = vmw_master(file_priv->master);  	struct drm_vmw_rect __user *clips_ptr;  	struct drm_vmw_rect *clips = NULL; -	struct drm_mode_object *obj; +	struct drm_framebuffer *fb;  	struct vmw_framebuffer *vfb;  	uint32_t num_clips;  	int ret; @@ -251,24 +247,20 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,  		goto out_no_copy;  	} -	ret = mutex_lock_interruptible(&dev->mode_config.mutex); -	if (unlikely(ret != 0)) { -		ret = -ERESTARTSYS; -		goto out_no_mode_mutex; -	} +	drm_modeset_lock_all(dev); -	obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); -	if (!obj) { +	fb = drm_framebuffer_lookup(dev, arg->fb_id); +	if (!fb) {  		DRM_ERROR("Invalid framebuffer id.\n");  		ret = -EINVAL;  		goto out_no_fb;  	} -	vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); +	vfb = vmw_framebuffer_to_vfb(fb);  	if (!vfb->dmabuf) {  		DRM_ERROR("Framebuffer not dmabuf backed.\n");  		ret = -EINVAL; -		goto out_no_fb; +		goto out_no_ttm_lock;  	}  	ret = ttm_read_lock(&vmaster->lock, true); @@ -281,9 +273,9 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,  	ttm_read_unlock(&vmaster->lock);  out_no_ttm_lock: +	drm_framebuffer_unreference(fb);  out_no_fb: -	mutex_unlock(&dev->mode_config.mutex); -out_no_mode_mutex: +	drm_modeset_unlock_all(dev);  out_no_copy:  	kfree(clips);  out_clips: diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 54743943d8b..3e3c7ab33ca 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -180,16 +180,29 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,  	struct vmw_dma_buffer *dmabuf = NULL;  	int ret; +	/* +	 * FIXME: Unclear whether there's any global state touched by the +	 * cursor_set function, especially vmw_cursor_update_position looks +	 * suspicious. For now take the easy route and reacquire all locks. We +	 * can do this since the caller in the drm core doesn't check anything +	 * which is protected by any looks. +	 */ +	mutex_unlock(&crtc->mutex); +	drm_modeset_lock_all(dev_priv->dev); +  	/* A lot of the code assumes this */ -	if (handle && (width != 64 || height != 64)) -		return -EINVAL; +	if (handle && (width != 64 || height != 64)) { +		ret = -EINVAL; +		goto out; +	}  	if (handle) {  		ret = vmw_user_lookup_handle(dev_priv, tfile,  					     handle, &surface, &dmabuf);  		if (ret) {  			DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); -			return -EINVAL; +			ret = -EINVAL; +			goto out;  		}  	} @@ -197,7 +210,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,  	if (surface && !surface->snooper.image) {  		DRM_ERROR("surface not suitable for cursor\n");  		vmw_surface_unreference(&surface); -		return -EINVAL; +		ret = -EINVAL; +		goto out;  	}  	/* takedown old cursor */ @@ -225,14 +239,20 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,  					       du->hotspot_x, du->hotspot_y);  	} else {  		vmw_cursor_update_position(dev_priv, false, 0, 0); -		return 0; +		ret = 0; +		goto out;  	}  	vmw_cursor_update_position(dev_priv, true,  				   du->cursor_x + du->hotspot_x,  				   du->cursor_y + du->hotspot_y); -	return 0; +	ret = 0; +out: +	drm_modeset_unlock_all(dev_priv->dev); +	mutex_lock(&crtc->mutex); + +	return ret;  }  int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) @@ -244,10 +264,23 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)  	du->cursor_x = x + crtc->x;  	du->cursor_y = y + crtc->y; +	/* +	 * FIXME: Unclear whether there's any global state touched by the +	 * cursor_set function, especially vmw_cursor_update_position looks +	 * suspicious. For now take the easy route and reacquire all locks. We +	 * can do this since the caller in the drm core doesn't check anything +	 * which is protected by any looks. +	 */ +	mutex_unlock(&crtc->mutex); +	drm_modeset_lock_all(dev_priv->dev); +  	vmw_cursor_update_position(dev_priv, shown,  				   du->cursor_x + du->hotspot_x,  				   du->cursor_y + du->hotspot_y); +	drm_modeset_unlock_all(dev_priv->dev); +	mutex_lock(&crtc->mutex); +  	return 0;  } @@ -373,16 +406,6 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)   * Generic framebuffer code   */ -int vmw_framebuffer_create_handle(struct drm_framebuffer *fb, -				  struct drm_file *file_priv, -				  unsigned int *handle) -{ -	if (handle) -		*handle = 0; - -	return 0; -} -  /*   * Surface framebuffer code   */ @@ -610,7 +633,6 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,  static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {  	.destroy = vmw_framebuffer_surface_destroy,  	.dirty = vmw_framebuffer_surface_dirty, -	.create_handle = vmw_framebuffer_create_handle,  };  static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, @@ -681,14 +703,10 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,  		goto out_err1;  	} -	ret = drm_framebuffer_init(dev, &vfbs->base.base, -				   &vmw_framebuffer_surface_funcs); -	if (ret) -		goto out_err2; -  	if (!vmw_surface_reference(surface)) {  		DRM_ERROR("failed to reference surface %p\n", surface); -		goto out_err3; +		ret = -EINVAL; +		goto out_err2;  	}  	/* XXX get the first 3 from the surface info */ @@ -707,10 +725,15 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,  	*out = &vfbs->base; +	ret = drm_framebuffer_init(dev, &vfbs->base.base, +				   &vmw_framebuffer_surface_funcs); +	if (ret) +		goto out_err3; +  	return 0;  out_err3: -	drm_framebuffer_cleanup(&vfbs->base.base); +	vmw_surface_unreference(&surface);  out_err2:  	kfree(vfbs);  out_err1: @@ -960,7 +983,6 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,  static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {  	.destroy = vmw_framebuffer_dmabuf_destroy,  	.dirty = vmw_framebuffer_dmabuf_dirty, -	.create_handle = vmw_framebuffer_create_handle,  };  /** @@ -1053,14 +1075,10 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,  		goto out_err1;  	} -	ret = drm_framebuffer_init(dev, &vfbd->base.base, -				   &vmw_framebuffer_dmabuf_funcs); -	if (ret) -		goto out_err2; -  	if (!vmw_dmabuf_reference(dmabuf)) {  		DRM_ERROR("failed to reference dmabuf %p\n", dmabuf); -		goto out_err3; +		ret = -EINVAL; +		goto out_err2;  	}  	vfbd->base.base.bits_per_pixel = mode_cmd->bpp; @@ -1077,10 +1095,15 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,  	vfbd->base.user_handle = mode_cmd->handle;  	*out = &vfbd->base; +	ret = drm_framebuffer_init(dev, &vfbd->base.base, +				   &vmw_framebuffer_dmabuf_funcs); +	if (ret) +		goto out_err3; +  	return 0;  out_err3: -	drm_framebuffer_cleanup(&vfbd->base.base); +	vmw_dmabuf_unreference(&dmabuf);  out_err2:  	kfree(vfbd);  out_err1:  |