diff options
Diffstat (limited to 'drivers/gpu/drm/drm_crtc.c')
| -rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 607 | 
1 files changed, 478 insertions, 129 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fe94cc10cd35..90e773019eac 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -41,6 +41,10 @@  #include "drm_crtc_internal.h" +static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, +							struct drm_mode_fb_cmd2 *r, +							struct drm_file *file_priv); +  /**   * drm_modeset_lock_all - take all modeset locks   * @dev: drm device @@ -178,6 +182,12 @@ static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] =  	{ DRM_MODE_SCALE_ASPECT, "Full aspect" },  }; +static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { +	{ DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, +	{ DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, +	{ DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, +}; +  /*   * Non-global properties, but "required" for certain connectors.   */ @@ -357,6 +367,32 @@ const char *drm_get_format_name(uint32_t format)  }  EXPORT_SYMBOL(drm_get_format_name); +/* + * Internal function to assign a slot in the object idr and optionally + * register the object into the idr. + */ +static int drm_mode_object_get_reg(struct drm_device *dev, +				   struct drm_mode_object *obj, +				   uint32_t obj_type, +				   bool register_obj) +{ +	int ret; + +	mutex_lock(&dev->mode_config.idr_mutex); +	ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); +	if (ret >= 0) { +		/* +		 * Set up the object linking under the protection of the idr +		 * lock so that other users can't see inconsistent state. +		 */ +		obj->id = ret; +		obj->type = obj_type; +	} +	mutex_unlock(&dev->mode_config.idr_mutex); + +	return ret < 0 ? ret : 0; +} +  /**   * drm_mode_object_get - allocate a new modeset identifier   * @dev: DRM device @@ -375,21 +411,15 @@ EXPORT_SYMBOL(drm_get_format_name);  int drm_mode_object_get(struct drm_device *dev,  			struct drm_mode_object *obj, uint32_t obj_type)  { -	int ret; +	return drm_mode_object_get_reg(dev, obj, obj_type, true); +} +static void drm_mode_object_register(struct drm_device *dev, +				     struct drm_mode_object *obj) +{  	mutex_lock(&dev->mode_config.idr_mutex); -	ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL); -	if (ret >= 0) { -		/* -		 * Set up the object linking under the protection of the idr -		 * lock so that other users can't see inconsistent state. -		 */ -		obj->id = ret; -		obj->type = obj_type; -	} +	idr_replace(&dev->mode_config.crtc_idr, obj, obj->id);  	mutex_unlock(&dev->mode_config.idr_mutex); - -	return ret < 0 ? ret : 0;  }  /** @@ -416,8 +446,12 @@ static struct drm_mode_object *_object_find(struct drm_device *dev,  	mutex_lock(&dev->mode_config.idr_mutex);  	obj = idr_find(&dev->mode_config.crtc_idr, id); -	if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) || -	    (obj->id != id)) +	if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) +		obj = NULL; +	if (obj && obj->id != id) +		obj = NULL; +	/* don't leak out unref'd fb's */ +	if (obj && (obj->type == DRM_MODE_OBJECT_FB))  		obj = NULL;  	mutex_unlock(&dev->mode_config.idr_mutex); @@ -444,9 +478,6 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,  	 * function.*/  	WARN_ON(type == DRM_MODE_OBJECT_FB);  	obj = _object_find(dev, id, type); -	/* don't leak out unref'd fb's */ -	if (obj && (obj->type == DRM_MODE_OBJECT_FB)) -		obj = NULL;  	return obj;  }  EXPORT_SYMBOL(drm_mode_object_find); @@ -723,7 +754,7 @@ DEFINE_WW_CLASS(crtc_ww_class);   */  int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,  			      struct drm_plane *primary, -			      void *cursor, +			      struct drm_plane *cursor,  			      const struct drm_crtc_funcs *funcs)  {  	struct drm_mode_config *config = &dev->mode_config; @@ -748,8 +779,11 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,  	config->num_crtc++;  	crtc->primary = primary; +	crtc->cursor = cursor;  	if (primary)  		primary->possible_crtcs = 1 << drm_crtc_index(crtc); +	if (cursor) +		cursor->possible_crtcs = 1 << drm_crtc_index(crtc);   out:  	drm_modeset_unlock_all(dev); @@ -842,7 +876,7 @@ int drm_connector_init(struct drm_device *dev,  	drm_modeset_lock_all(dev); -	ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); +	ret = drm_mode_object_get_reg(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR, false);  	if (ret)  		goto out_unlock; @@ -881,6 +915,8 @@ int drm_connector_init(struct drm_device *dev,  	drm_object_attach_property(&connector->base,  				      dev->mode_config.dpms_property, 0); +	connector->debugfs_entry = NULL; +  out_put:  	if (ret)  		drm_mode_object_put(dev, &connector->base); @@ -921,6 +957,49 @@ void drm_connector_cleanup(struct drm_connector *connector)  EXPORT_SYMBOL(drm_connector_cleanup);  /** + * drm_connector_register - register a connector + * @connector: the connector to register + * + * Register userspace interfaces for a connector + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_connector_register(struct drm_connector *connector) +{ +	int ret; + +	drm_mode_object_register(connector->dev, &connector->base); + +	ret = drm_sysfs_connector_add(connector); +	if (ret) +		return ret; + +	ret = drm_debugfs_connector_add(connector); +	if (ret) { +		drm_sysfs_connector_remove(connector); +		return ret; +	} + +	return 0; +} +EXPORT_SYMBOL(drm_connector_register); + +/** + * drm_connector_unregister - unregister a connector + * @connector: the connector to unregister + * + * Unregister userspace interfaces for a connector + */ +void drm_connector_unregister(struct drm_connector *connector) +{ +	drm_sysfs_connector_remove(connector); +	drm_debugfs_connector_remove(connector); +} +EXPORT_SYMBOL(drm_connector_unregister); + + +/**   * drm_connector_unplug_all - unregister connector userspace interfaces   * @dev: drm device   * @@ -934,7 +1013,7 @@ void drm_connector_unplug_all(struct drm_device *dev)  	/* taking the mode config mutex ends up in a clash with sysfs */  	list_for_each_entry(connector, &dev->mode_config.connector_list, head) -		drm_sysfs_connector_remove(connector); +		drm_connector_unregister(connector);  }  EXPORT_SYMBOL(drm_connector_unplug_all); @@ -1214,6 +1293,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)  {  	struct drm_property *edid;  	struct drm_property *dpms; +	struct drm_property *dev_path;  	/*  	 * Standard properties (apply to all connectors) @@ -1228,6 +1308,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)  				   ARRAY_SIZE(drm_dpms_enum_list));  	dev->mode_config.dpms_property = dpms; +	dev_path = drm_property_create(dev, +				       DRM_MODE_PROP_BLOB | +				       DRM_MODE_PROP_IMMUTABLE, +				       "PATH", 0); +	dev->mode_config.path_property = dev_path; +  	return 0;  } @@ -1384,6 +1470,33 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev)  EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);  /** + * drm_mode_create_aspect_ratio_property - create aspect ratio property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired + * connectors. + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_create_aspect_ratio_property(struct drm_device *dev) +{ +	if (dev->mode_config.aspect_ratio_property) +		return 0; + +	dev->mode_config.aspect_ratio_property = +		drm_property_create_enum(dev, 0, "aspect ratio", +				drm_aspect_ratio_enum_list, +				ARRAY_SIZE(drm_aspect_ratio_enum_list)); + +	if (dev->mode_config.aspect_ratio_property == NULL) +		return -ENOMEM; + +	return 0; +} +EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); + +/**   * drm_mode_create_dirty_property - create dirty property   * @dev: DRM device   * @@ -1470,6 +1583,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,  }  EXPORT_SYMBOL(drm_mode_group_init_legacy_group); +void drm_reinit_primary_mode_group(struct drm_device *dev) +{ +	drm_modeset_lock_all(dev); +	drm_mode_group_destroy(&dev->primary->mode_group); +	drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); +	drm_modeset_unlock_all(dev); +} +EXPORT_SYMBOL(drm_reinit_primary_mode_group); +  /**   * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo   * @out: drm_mode_modeinfo struct to return to the user @@ -2118,45 +2240,32 @@ out:  	return ret;  } -/** - * drm_mode_setplane - configure a plane's configuration - * @dev: DRM device - * @data: ioctl data* - * @file_priv: DRM file info +/* + * setplane_internal - setplane handler for internal callers   * - * Set plane configuration, including placement, fb, scaling, and other factors. - * Or pass a NULL fb to disable. + * Note that we assume an extra reference has already been taken on fb.  If the + * update fails, this reference will be dropped before return; if it succeeds, + * the previous framebuffer (if any) will be unreferenced instead.   * - * Returns: - * Zero on success, errno on failure. + * src_{x,y,w,h} are provided in 16.16 fixed point format   */ -int drm_mode_setplane(struct drm_device *dev, void *data, -		      struct drm_file *file_priv) +static int setplane_internal(struct drm_plane *plane, +			     struct drm_crtc *crtc, +			     struct drm_framebuffer *fb, +			     int32_t crtc_x, int32_t crtc_y, +			     uint32_t crtc_w, uint32_t crtc_h, +			     /* src_{x,y,w,h} values are 16.16 fixed point */ +			     uint32_t src_x, uint32_t src_y, +			     uint32_t src_w, uint32_t src_h)  { -	struct drm_mode_set_plane *plane_req = data; -	struct drm_plane *plane; -	struct drm_crtc *crtc; -	struct drm_framebuffer *fb = NULL, *old_fb = NULL; +	struct drm_device *dev = plane->dev; +	struct drm_framebuffer *old_fb = NULL;  	int ret = 0;  	unsigned int fb_width, fb_height;  	int i; -	if (!drm_core_check_feature(dev, DRIVER_MODESET)) -		return -EINVAL; - -	/* -	 * First, find the plane, crtc, and fb objects.  If not available, -	 * we don't bother to call the driver. -	 */ -	plane = drm_plane_find(dev, plane_req->plane_id); -	if (!plane) { -		DRM_DEBUG_KMS("Unknown plane ID %d\n", -			      plane_req->plane_id); -		return -ENOENT; -	} -  	/* No fb means shut it down */ -	if (!plane_req->fb_id) { +	if (!fb) {  		drm_modeset_lock_all(dev);  		old_fb = plane->fb;  		ret = plane->funcs->disable_plane(plane); @@ -2170,14 +2279,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data,  		goto out;  	} -	crtc = drm_crtc_find(dev, plane_req->crtc_id); -	if (!crtc) { -		DRM_DEBUG_KMS("Unknown crtc ID %d\n", -			      plane_req->crtc_id); -		ret = -ENOENT; -		goto out; -	} -  	/* Check whether this plane is usable on this CRTC */  	if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {  		DRM_DEBUG_KMS("Invalid crtc for plane\n"); @@ -2185,14 +2286,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data,  		goto out;  	} -	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; -	} -  	/* Check whether this plane supports the fb pixel format. */  	for (i = 0; i < plane->format_count; i++)  		if (fb->pixel_format == plane->format_types[i]) @@ -2208,43 +2301,25 @@ int drm_mode_setplane(struct drm_device *dev, void *data,  	fb_height = fb->height << 16;  	/* Make sure source coordinates are inside the fb. */ -	if (plane_req->src_w > fb_width || -	    plane_req->src_x > fb_width - plane_req->src_w || -	    plane_req->src_h > fb_height || -	    plane_req->src_y > fb_height - plane_req->src_h) { +	if (src_w > fb_width || +	    src_x > fb_width - src_w || +	    src_h > fb_height || +	    src_y > fb_height - src_h) {  		DRM_DEBUG_KMS("Invalid source coordinates "  			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", -			      plane_req->src_w >> 16, -			      ((plane_req->src_w & 0xffff) * 15625) >> 10, -			      plane_req->src_h >> 16, -			      ((plane_req->src_h & 0xffff) * 15625) >> 10, -			      plane_req->src_x >> 16, -			      ((plane_req->src_x & 0xffff) * 15625) >> 10, -			      plane_req->src_y >> 16, -			      ((plane_req->src_y & 0xffff) * 15625) >> 10); +			      src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, +			      src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, +			      src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, +			      src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);  		ret = -ENOSPC;  		goto out;  	} -	/* Give drivers some help against integer overflows */ -	if (plane_req->crtc_w > INT_MAX || -	    plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || -	    plane_req->crtc_h > INT_MAX || -	    plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { -		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", -			      plane_req->crtc_w, plane_req->crtc_h, -			      plane_req->crtc_x, plane_req->crtc_y); -		ret = -ERANGE; -		goto out; -	} -  	drm_modeset_lock_all(dev);  	old_fb = plane->fb;  	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); +					 crtc_x, crtc_y, crtc_w, crtc_h, +					 src_x, src_y, src_w, src_h);  	if (!ret) {  		plane->crtc = crtc;  		plane->fb = fb; @@ -2261,6 +2336,85 @@ out:  		drm_framebuffer_unreference(old_fb);  	return ret; + +} + +/** + * drm_mode_setplane - configure a plane's configuration + * @dev: DRM device + * @data: ioctl data* + * @file_priv: DRM file info + * + * Set plane configuration, including placement, fb, scaling, and other factors. + * Or pass a NULL fb to disable (planes may be disabled without providing a + * valid crtc). + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_setplane(struct drm_device *dev, void *data, +		      struct drm_file *file_priv) +{ +	struct drm_mode_set_plane *plane_req = data; +	struct drm_mode_object *obj; +	struct drm_plane *plane; +	struct drm_crtc *crtc = NULL; +	struct drm_framebuffer *fb = NULL; + +	if (!drm_core_check_feature(dev, DRIVER_MODESET)) +		return -EINVAL; + +	/* Give drivers some help against integer overflows */ +	if (plane_req->crtc_w > INT_MAX || +	    plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || +	    plane_req->crtc_h > INT_MAX || +	    plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { +		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", +			      plane_req->crtc_w, plane_req->crtc_h, +			      plane_req->crtc_x, plane_req->crtc_y); +		return -ERANGE; +	} + +	/* +	 * First, find the plane, crtc, and fb objects.  If not available, +	 * we don't bother to call the driver. +	 */ +	obj = drm_mode_object_find(dev, plane_req->plane_id, +				   DRM_MODE_OBJECT_PLANE); +	if (!obj) { +		DRM_DEBUG_KMS("Unknown plane ID %d\n", +			      plane_req->plane_id); +		return -ENOENT; +	} +	plane = obj_to_plane(obj); + +	if (plane_req->fb_id) { +		fb = drm_framebuffer_lookup(dev, plane_req->fb_id); +		if (!fb) { +			DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", +				      plane_req->fb_id); +			return -ENOENT; +		} + +		obj = drm_mode_object_find(dev, plane_req->crtc_id, +					   DRM_MODE_OBJECT_CRTC); +		if (!obj) { +			DRM_DEBUG_KMS("Unknown crtc ID %d\n", +				      plane_req->crtc_id); +			return -ENOENT; +		} +		crtc = obj_to_crtc(obj); +	} + +	/* +	 * setplane_internal will take care of deref'ing either the old or new +	 * framebuffer depending on success. +	 */ +	return setplane_internal(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);  }  /** @@ -2509,6 +2663,102 @@ out:  	return ret;  } +/** + * drm_mode_cursor_universal - translate legacy cursor ioctl call into a + *     universal plane handler call + * @crtc: crtc to update cursor for + * @req: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Legacy cursor ioctl's work directly with driver buffer handles.  To + * translate legacy ioctl calls into universal plane handler calls, we need to + * wrap the native buffer handle in a drm_framebuffer. + * + * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB + * buffer with a pitch of 4*width; the universal plane interface should be used + * directly in cases where the hardware can support other buffer settings and + * userspace wants to make use of these capabilities. + * + * Returns: + * Zero on success, errno on failure. + */ +static int drm_mode_cursor_universal(struct drm_crtc *crtc, +				     struct drm_mode_cursor2 *req, +				     struct drm_file *file_priv) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_framebuffer *fb = NULL; +	struct drm_mode_fb_cmd2 fbreq = { +		.width = req->width, +		.height = req->height, +		.pixel_format = DRM_FORMAT_ARGB8888, +		.pitches = { req->width * 4 }, +		.handles = { req->handle }, +	}; +	int32_t crtc_x, crtc_y; +	uint32_t crtc_w = 0, crtc_h = 0; +	uint32_t src_w = 0, src_h = 0; +	int ret = 0; + +	BUG_ON(!crtc->cursor); + +	/* +	 * Obtain fb we'll be using (either new or existing) and take an extra +	 * reference to it if fb != null.  setplane will take care of dropping +	 * the reference if the plane update fails. +	 */ +	if (req->flags & DRM_MODE_CURSOR_BO) { +		if (req->handle) { +			fb = add_framebuffer_internal(dev, &fbreq, file_priv); +			if (IS_ERR(fb)) { +				DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); +				return PTR_ERR(fb); +			} + +			drm_framebuffer_reference(fb); +		} else { +			fb = NULL; +		} +	} else { +		mutex_lock(&dev->mode_config.mutex); +		fb = crtc->cursor->fb; +		if (fb) +			drm_framebuffer_reference(fb); +		mutex_unlock(&dev->mode_config.mutex); +	} + +	if (req->flags & DRM_MODE_CURSOR_MOVE) { +		crtc_x = req->x; +		crtc_y = req->y; +	} else { +		crtc_x = crtc->cursor_x; +		crtc_y = crtc->cursor_y; +	} + +	if (fb) { +		crtc_w = fb->width; +		crtc_h = fb->height; +		src_w = fb->width << 16; +		src_h = fb->height << 16; +	} + +	/* +	 * setplane_internal will take care of deref'ing either the old or new +	 * framebuffer depending on success. +	 */ +	ret = setplane_internal(crtc->cursor, crtc, fb, +				crtc_x, crtc_y, crtc_w, crtc_h, +				0, 0, src_w, src_h); + +	/* Update successful; save new cursor position, if necessary */ +	if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { +		crtc->cursor_x = req->x; +		crtc->cursor_y = req->y; +	} + +	return ret; +} +  static int drm_mode_cursor_common(struct drm_device *dev,  				  struct drm_mode_cursor2 *req,  				  struct drm_file *file_priv) @@ -2528,6 +2778,13 @@ static int drm_mode_cursor_common(struct drm_device *dev,  		return -ENOENT;  	} +	/* +	 * If this crtc has a universal cursor plane, call that plane's update +	 * handler rather than using legacy cursor handlers. +	 */ +	if (crtc->cursor) +		return drm_mode_cursor_universal(crtc, req, file_priv); +  	drm_modeset_lock(&crtc->mutex, NULL);  	if (req->flags & DRM_MODE_CURSOR_BO) {  		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { @@ -2827,56 +3084,38 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)  	return 0;  } -/** - * drm_mode_addfb2 - add an FB to the graphics configuration - * @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. This is - * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers - * and uses fourcc codes as pixel format specifiers. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, errno on failure. - */ -int drm_mode_addfb2(struct drm_device *dev, -		    void *data, struct drm_file *file_priv) +static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, +							struct drm_mode_fb_cmd2 *r, +							struct drm_file *file_priv)  { -	struct drm_mode_fb_cmd2 *r = data;  	struct drm_mode_config *config = &dev->mode_config;  	struct drm_framebuffer *fb;  	int ret; -	if (!drm_core_check_feature(dev, DRIVER_MODESET)) -		return -EINVAL; -  	if (r->flags & ~DRM_MODE_FB_INTERLACED) {  		DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); -		return -EINVAL; +		return ERR_PTR(-EINVAL);  	}  	if ((config->min_width > r->width) || (r->width > config->max_width)) {  		DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",  			  r->width, config->min_width, config->max_width); -		return -EINVAL; +		return ERR_PTR(-EINVAL);  	}  	if ((config->min_height > r->height) || (r->height > config->max_height)) {  		DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",  			  r->height, config->min_height, config->max_height); -		return -EINVAL; +		return ERR_PTR(-EINVAL);  	}  	ret = framebuffer_check(r);  	if (ret) -		return ret; +		return ERR_PTR(ret);  	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);  	if (IS_ERR(fb)) {  		DRM_DEBUG_KMS("could not create framebuffer\n"); -		return PTR_ERR(fb); +		return fb;  	}  	mutex_lock(&file_priv->fbs_lock); @@ -2885,8 +3124,37 @@ int drm_mode_addfb2(struct drm_device *dev,  	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);  	mutex_unlock(&file_priv->fbs_lock); +	return fb; +} -	return ret; +/** + * drm_mode_addfb2 - add an FB to the graphics configuration + * @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. This is + * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers + * and uses fourcc codes as pixel format specifiers. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_addfb2(struct drm_device *dev, +		    void *data, struct drm_file *file_priv) +{ +	struct drm_framebuffer *fb; + +	if (!drm_core_check_feature(dev, DRIVER_MODESET)) +		return -EINVAL; + +	fb = add_framebuffer_internal(dev, data, file_priv); +	if (IS_ERR(fb)) +		return PTR_ERR(fb); + +	return 0;  }  /** @@ -3176,7 +3444,7 @@ fail:  EXPORT_SYMBOL(drm_property_create);  /** - * drm_property_create - create a new enumeration property type + * drm_property_create_enum - create a new enumeration property type   * @dev: drm device   * @flags: flags specifying the property type   * @name: name of the property @@ -3222,7 +3490,7 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,  EXPORT_SYMBOL(drm_property_create_enum);  /** - * drm_property_create - create a new bitmask property type + * drm_property_create_bitmask - create a new bitmask property type   * @dev: drm device   * @flags: flags specifying the property type   * @name: name of the property @@ -3242,19 +3510,28 @@ EXPORT_SYMBOL(drm_property_create_enum);  struct drm_property *drm_property_create_bitmask(struct drm_device *dev,  					 int flags, const char *name,  					 const struct drm_prop_enum_list *props, -					 int num_values) +					 int num_props, +					 uint64_t supported_bits)  {  	struct drm_property *property; -	int i, ret; +	int i, ret, index = 0; +	int num_values = hweight64(supported_bits);  	flags |= DRM_MODE_PROP_BITMASK;  	property = drm_property_create(dev, flags, name, num_values);  	if (!property)  		return NULL; +	for (i = 0; i < num_props; i++) { +		if (!(supported_bits & (1ULL << props[i].type))) +			continue; -	for (i = 0; i < num_values; i++) { -		ret = drm_property_add_enum(property, i, +		if (WARN_ON(index >= num_values)) { +			drm_property_destroy(dev, property); +			return NULL; +		} + +		ret = drm_property_add_enum(property, index++,  				      props[i].type,  				      props[i].name);  		if (ret) { @@ -3284,7 +3561,7 @@ static struct drm_property *property_create_range(struct drm_device *dev,  }  /** - * drm_property_create - create a new ranged property type + * drm_property_create_range - create a new ranged property type   * @dev: drm device   * @flags: flags specifying the property type   * @name: name of the property @@ -3703,6 +3980,25 @@ done:  	return ret;  } +int drm_mode_connector_set_path_property(struct drm_connector *connector, +					 char *path) +{ +	struct drm_device *dev = connector->dev; +	int ret, size; +	size = strlen(path) + 1; + +	connector->path_blob_ptr = drm_property_create_blob(connector->dev, +							    size, path); +	if (!connector->path_blob_ptr) +		return -EINVAL; + +	ret = drm_object_property_set_value(&connector->base, +					    dev->mode_config.path_property, +					    connector->path_blob_ptr->base.id); +	return ret; +} +EXPORT_SYMBOL(drm_mode_connector_set_path_property); +  /**   * drm_mode_connector_update_edid_property - update the edid property of a connector   * @connector: drm connector @@ -3720,6 +4016,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,  	struct drm_device *dev = connector->dev;  	int ret, size; +	/* ignore requests to set edid when overridden */ +	if (connector->override_edid) +		return 0; +  	if (connector->edid_blob_ptr)  		drm_property_destroy_blob(dev, connector->edid_blob_ptr); @@ -4396,8 +4696,9 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,  		return -EINVAL;  	/* overflow checks for 32bit size calculations */ +	/* NOTE: DIV_ROUND_UP() can overflow */  	cpp = DIV_ROUND_UP(args->bpp, 8); -	if (cpp > 0xffffffffU / args->width) +	if (!cpp || cpp > 0xffffffffU / args->width)  		return -EINVAL;  	stride = cpp * args->width;  	if (args->height > 0xffffffffU / stride) @@ -4680,6 +4981,36 @@ int drm_format_vert_chroma_subsampling(uint32_t format)  EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);  /** + * drm_rotation_simplify() - Try to simplify the rotation + * @rotation: Rotation to be simplified + * @supported_rotations: Supported rotations + * + * Attempt to simplify the rotation to a form that is supported. + * Eg. if the hardware supports everything except DRM_REFLECT_X + * one could call this function like this: + * + * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) | + *                       BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) | + *                       BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y)); + * + * to eliminate the DRM_ROTATE_X flag. Depending on what kind of + * transforms the hardware supports, this function may not + * be able to produce a supported transform, so the caller should + * check the result afterwards. + */ +unsigned int drm_rotation_simplify(unsigned int rotation, +				   unsigned int supported_rotations) +{ +	if (rotation & ~supported_rotations) { +		rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y); +		rotation = (rotation & ~0xf) | BIT((ffs(rotation & 0xf) + 1) % 4); +	} + +	return rotation; +} +EXPORT_SYMBOL(drm_rotation_simplify); + +/**   * drm_mode_config_init - initialize DRM mode_configuration structure   * @dev: DRM device   * @@ -4797,3 +5128,21 @@ void drm_mode_config_cleanup(struct drm_device *dev)  	drm_modeset_lock_fini(&dev->mode_config.connection_mutex);  }  EXPORT_SYMBOL(drm_mode_config_cleanup); + +struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, +						       unsigned int supported_rotations) +{ +	static const struct drm_prop_enum_list props[] = { +		{ DRM_ROTATE_0,   "rotate-0" }, +		{ DRM_ROTATE_90,  "rotate-90" }, +		{ DRM_ROTATE_180, "rotate-180" }, +		{ DRM_ROTATE_270, "rotate-270" }, +		{ DRM_REFLECT_X,  "reflect-x" }, +		{ DRM_REFLECT_Y,  "reflect-y" }, +	}; + +	return drm_property_create_bitmask(dev, 0, "rotation", +					   props, ARRAY_SIZE(props), +					   supported_rotations); +} +EXPORT_SYMBOL(drm_mode_create_rotation_property);  |