diff options
Diffstat (limited to 'drivers/gpu/drm/drm_crtc_helper.c')
| -rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 729 | 
1 files changed, 340 insertions, 389 deletions
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index f7af91cb273..78b37f3febd 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -29,152 +29,75 @@   *      Jesse Barnes <jesse.barnes@intel.com>   */ -#include "drmP.h" -#include "drm_crtc.h" -#include "drm_crtc_helper.h" -#include "drm_fb_helper.h" +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/moduleparam.h> -static bool drm_kms_helper_poll = true; -module_param_named(poll, drm_kms_helper_poll, bool, 0600); +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_edid.h> -static void drm_mode_validate_flag(struct drm_connector *connector, -				   int flags) -{ -	struct drm_display_mode *mode, *t; - -	if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE)) -		return; - -	list_for_each_entry_safe(mode, t, &connector->modes, head) { -		if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && -				!(flags & DRM_MODE_FLAG_INTERLACE)) -			mode->status = MODE_NO_INTERLACE; -		if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && -				!(flags & DRM_MODE_FLAG_DBLSCAN)) -			mode->status = MODE_NO_DBLESCAN; -	} - -	return; -} +MODULE_AUTHOR("David Airlie, Jesse Barnes"); +MODULE_DESCRIPTION("DRM KMS helper"); +MODULE_LICENSE("GPL and additional rights");  /** - * drm_helper_probe_single_connector_modes - get complete set of display modes - * @dev: DRM device - * @maxX: max width for modes - * @maxY: max height for modes - * - * LOCKING: - * Caller must hold mode config lock. - * - * Based on @dev's mode_config layout, scan all the connectors and try to detect - * modes on them.  Modes will first be added to the connector's probed_modes - * list, then culled (based on validity and the @maxX, @maxY parameters) and - * put into the normal modes list. - * - * Intended to be used either at bootup time or when major configuration - * changes have occurred. - * - * FIXME: take into account monitor limits + * drm_helper_move_panel_connectors_to_head() - move panels to the front in the + * 						connector list + * @dev: drm device to operate on   * - * RETURNS: - * Number of modes found on @connector. + * Some userspace presumes that the first connected connector is the main + * display, where it's supposed to display e.g. the login screen. For + * laptops, this should be the main panel. Use this function to sort all + * (eDP/LVDS) panels to the front of the connector list, instead of + * painstakingly trying to initialize them in the right order.   */ -int drm_helper_probe_single_connector_modes(struct drm_connector *connector, -					    uint32_t maxX, uint32_t maxY) +void drm_helper_move_panel_connectors_to_head(struct drm_device *dev)  { -	struct drm_device *dev = connector->dev; -	struct drm_display_mode *mode, *t; -	struct drm_connector_helper_funcs *connector_funcs = -		connector->helper_private; -	int count = 0; -	int mode_flags = 0; - -	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, -			drm_get_connector_name(connector)); -	/* set all modes to the unverified state */ -	list_for_each_entry_safe(mode, t, &connector->modes, head) -		mode->status = MODE_UNVERIFIED; - -	if (connector->force) { -		if (connector->force == DRM_FORCE_ON) -			connector->status = connector_status_connected; -		else -			connector->status = connector_status_disconnected; -		if (connector->funcs->force) -			connector->funcs->force(connector); -	} else { -		connector->status = connector->funcs->detect(connector, true); -		drm_kms_helper_poll_enable(dev); -	} - -	if (connector->status == connector_status_disconnected) { -		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n", -			connector->base.id, drm_get_connector_name(connector)); -		drm_mode_connector_update_edid_property(connector, NULL); -		goto prune; -	} - -	count = (*connector_funcs->get_modes)(connector); -	if (count == 0 && connector->status == connector_status_connected) -		count = drm_add_modes_noedid(connector, 1024, 768); -	if (count == 0) -		goto prune; - -	drm_mode_connector_list_update(connector); +	struct drm_connector *connector, *tmp; +	struct list_head panel_list; -	if (maxX && maxY) -		drm_mode_validate_size(dev, &connector->modes, maxX, -				       maxY, 0); +	INIT_LIST_HEAD(&panel_list); -	if (connector->interlace_allowed) -		mode_flags |= DRM_MODE_FLAG_INTERLACE; -	if (connector->doublescan_allowed) -		mode_flags |= DRM_MODE_FLAG_DBLSCAN; -	drm_mode_validate_flag(connector, mode_flags); - -	list_for_each_entry_safe(mode, t, &connector->modes, head) { -		if (mode->status == MODE_OK) -			mode->status = connector_funcs->mode_valid(connector, -								   mode); +	list_for_each_entry_safe(connector, tmp, +				 &dev->mode_config.connector_list, head) { +		if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || +		    connector->connector_type == DRM_MODE_CONNECTOR_eDP) +			list_move_tail(&connector->head, &panel_list);  	} -prune: -	drm_mode_prune_invalid(dev, &connector->modes, true); - -	if (list_empty(&connector->modes)) -		return 0; - -	drm_mode_sort(&connector->modes); - -	DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id, -			drm_get_connector_name(connector)); -	list_for_each_entry_safe(mode, t, &connector->modes, head) { -		mode->vrefresh = drm_mode_vrefresh(mode); - -		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); -		drm_mode_debug_printmodeline(mode); -	} - -	return count; +	list_splice(&panel_list, &dev->mode_config.connector_list);  } -EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); +EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head);  /**   * drm_helper_encoder_in_use - check if a given encoder is in use   * @encoder: encoder to check   * - * LOCKING: - * Caller must hold mode config lock. + * Checks whether @encoder is with the current mode setting output configuration + * in use by any connector. This doesn't mean that it is actually enabled since + * the DPMS state is tracked separately.   * - * Walk @encoders's DRM device's mode_config and see if it's in use. - * - * RETURNS: - * True if @encoder is part of the mode_config, false otherwise. + * Returns: + * True if @encoder is used, false otherwise.   */  bool drm_helper_encoder_in_use(struct drm_encoder *encoder)  {  	struct drm_connector *connector;  	struct drm_device *dev = encoder->dev; + +	/* +	 * We can expect this mutex to be locked if we are not panicking. +	 * Locking is currently fubar in the panic handler. +	 */ +	if (!oops_in_progress) { +		WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); +		WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); +	} +  	list_for_each_entry(connector, &dev->mode_config.connector_list, head)  		if (connector->encoder == encoder)  			return true; @@ -186,19 +109,25 @@ EXPORT_SYMBOL(drm_helper_encoder_in_use);   * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config   * @crtc: CRTC to check   * - * LOCKING: - * Caller must hold mode config lock. + * Checks whether @crtc is with the current mode setting output configuration + * in use by any connector. This doesn't mean that it is actually enabled since + * the DPMS state is tracked separately.   * - * Walk @crtc's DRM device's mode_config and see if it's in use. - * - * RETURNS: - * True if @crtc is part of the mode_config, false otherwise. + * Returns: + * True if @crtc is used, false otherwise.   */  bool drm_helper_crtc_in_use(struct drm_crtc *crtc)  {  	struct drm_encoder *encoder;  	struct drm_device *dev = crtc->dev; -	/* FIXME: Locking around list access? */ + +	/* +	 * We can expect this mutex to be locked if we are not panicking. +	 * Locking is currently fubar in the panic handler. +	 */ +	if (!oops_in_progress) +		WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); +  	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)  		if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))  			return true; @@ -211,39 +140,29 @@ drm_encoder_disable(struct drm_encoder *encoder)  {  	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; +	if (encoder->bridge) +		encoder->bridge->funcs->disable(encoder->bridge); +  	if (encoder_funcs->disable)  		(*encoder_funcs->disable)(encoder);  	else  		(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); + +	if (encoder->bridge) +		encoder->bridge->funcs->post_disable(encoder->bridge);  } -/** - * drm_helper_disable_unused_functions - disable unused objects - * @dev: DRM device - * - * LOCKING: - * Caller must hold mode config lock. - * - * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled - * by calling its dpms function, which should power it off. - */ -void drm_helper_disable_unused_functions(struct drm_device *dev) +static void __drm_helper_disable_unused_functions(struct drm_device *dev)  {  	struct drm_encoder *encoder; -	struct drm_connector *connector;  	struct drm_crtc *crtc; -	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -		if (!connector->encoder) -			continue; -		if (connector->status == connector_status_disconnected) -			connector->encoder = NULL; -	} +	drm_warn_on_modeset_not_all_locked(dev);  	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {  		if (!drm_helper_encoder_in_use(encoder)) {  			drm_encoder_disable(encoder); -			/* disconnector encoder from any connector */ +			/* disconnect encoder from any connector */  			encoder->crtc = NULL;  		}  	} @@ -256,40 +175,28 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)  				(*crtc_funcs->disable)(crtc);  			else  				(*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); -			crtc->fb = NULL; +			crtc->primary->fb = NULL;  		}  	}  } -EXPORT_SYMBOL(drm_helper_disable_unused_functions);  /** - * drm_encoder_crtc_ok - can a given crtc drive a given encoder? - * @encoder: encoder to test - * @crtc: crtc to test + * drm_helper_disable_unused_functions - disable unused objects + * @dev: DRM device   * - * Return false if @encoder can't be driven by @crtc, true otherwise. + * This function walks through the entire mode setting configuration of @dev. It + * will remove any crtc links of unused encoders and encoder links of + * disconnected connectors. Then it will disable all unused encoders and crtcs + * either by calling their disable callback if available or by calling their + * dpms callback with DRM_MODE_DPMS_OFF.   */ -static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, -				struct drm_crtc *crtc) +void drm_helper_disable_unused_functions(struct drm_device *dev)  { -	struct drm_device *dev; -	struct drm_crtc *tmp; -	int crtc_mask = 1; - -	WARN(!crtc, "checking null crtc?\n"); - -	dev = crtc->dev; - -	list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { -		if (tmp == crtc) -			break; -		crtc_mask <<= 1; -	} - -	if (encoder->possible_crtcs & crtc_mask) -		return true; -	return false; +	drm_modeset_lock_all(dev); +	__drm_helper_disable_unused_functions(dev); +	drm_modeset_unlock_all(dev);  } +EXPORT_SYMBOL(drm_helper_disable_unused_functions);  /*   * Check the CRTC we're going to map each output to vs. its current @@ -315,20 +222,24 @@ drm_crtc_prepare_encoders(struct drm_device *dev)  }  /** - * drm_crtc_set_mode - set a mode + * drm_crtc_helper_set_mode - internal helper to set a mode   * @crtc: CRTC to program   * @mode: mode to use - * @x: width of mode - * @y: height of mode - * - * LOCKING: - * Caller must hold mode config lock. + * @x: horizontal offset into the surface + * @y: vertical offset into the surface + * @old_fb: old framebuffer, for cleanup   *   * Try to set @mode on @crtc.  Give @crtc and its associated connectors a chance - * to fixup or reject the mode prior to trying to set it. + * to fixup or reject the mode prior to trying to set it. This is an internal + * helper that drivers could e.g. use to update properties that require the + * entire output pipe to be disabled and re-enabled in a new configuration. For + * example for changing whether audio is enabled on a hdmi link or for changing + * panel fitter or dither attributes. It is also called by the + * drm_crtc_helper_set_config() helper function to drive the mode setting + * sequence.   * - * RETURNS: - * True if the mode was set successfully, or false otherwise. + * Returns: + * True if the mode was set successfully, false otherwise.   */  bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,  			      struct drm_display_mode *mode, @@ -340,16 +251,23 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,  	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;  	struct drm_encoder_helper_funcs *encoder_funcs;  	int saved_x, saved_y; +	bool saved_enabled;  	struct drm_encoder *encoder;  	bool ret = true; -	adjusted_mode = drm_mode_duplicate(dev, mode); +	drm_warn_on_modeset_not_all_locked(dev); +	saved_enabled = crtc->enabled;  	crtc->enabled = drm_helper_crtc_in_use(crtc); -  	if (!crtc->enabled)  		return true; +	adjusted_mode = drm_mode_duplicate(dev, mode); +	if (!adjusted_mode) { +		crtc->enabled = saved_enabled; +		return false; +	} +  	saved_mode = crtc->mode;  	saved_x = crtc->x;  	saved_y = crtc->y; @@ -369,14 +287,26 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,  		if (encoder->crtc != crtc)  			continue; + +		if (encoder->bridge && encoder->bridge->funcs->mode_fixup) { +			ret = encoder->bridge->funcs->mode_fixup( +					encoder->bridge, mode, adjusted_mode); +			if (!ret) { +				DRM_DEBUG_KMS("Bridge fixup failed\n"); +				goto done; +			} +		} +  		encoder_funcs = encoder->helper_private;  		if (!(ret = encoder_funcs->mode_fixup(encoder, mode,  						      adjusted_mode))) { +			DRM_DEBUG_KMS("Encoder fixup failed\n");  			goto done;  		}  	}  	if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { +		DRM_DEBUG_KMS("CRTC fixup failed\n");  		goto done;  	}  	DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); @@ -386,9 +316,16 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,  		if (encoder->crtc != crtc)  			continue; + +		if (encoder->bridge) +			encoder->bridge->funcs->disable(encoder->bridge); +  		encoder_funcs = encoder->helper_private;  		/* Disable the encoders as the first thing we do. */  		encoder_funcs->prepare(encoder); + +		if (encoder->bridge) +			encoder->bridge->funcs->post_disable(encoder->bridge);  	}  	drm_crtc_prepare_encoders(dev); @@ -408,10 +345,14 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,  			continue;  		DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", -			encoder->base.id, drm_get_encoder_name(encoder), +			encoder->base.id, encoder->name,  			mode->base.id, mode->name);  		encoder_funcs = encoder->helper_private;  		encoder_funcs->mode_set(encoder, mode, adjusted_mode); + +		if (encoder->bridge && encoder->bridge->funcs->mode_set) +			encoder->bridge->funcs->mode_set(encoder->bridge, mode, +					adjusted_mode);  	}  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */ @@ -422,16 +363,30 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,  		if (encoder->crtc != crtc)  			continue; +		if (encoder->bridge) +			encoder->bridge->funcs->pre_enable(encoder->bridge); +  		encoder_funcs = encoder->helper_private;  		encoder_funcs->commit(encoder); +		if (encoder->bridge) +			encoder->bridge->funcs->enable(encoder->bridge);  	} -	/* XXX free adjustedmode */ -	drm_mode_destroy(dev, adjusted_mode); +	/* Store real post-adjustment hardware mode. */ +	crtc->hwmode = *adjusted_mode; + +	/* Calculate and store various constants which +	 * are later needed by vblank and swap-completion +	 * timestamping. They are derived from true hwmode. +	 */ +	drm_calc_timestamping_constants(crtc, &crtc->hwmode); +  	/* FIXME: add subpixel order */  done: +	drm_mode_destroy(dev, adjusted_mode);  	if (!ret) { +		crtc->enabled = saved_enabled;  		crtc->mode = saved_mode;  		crtc->x = saved_x;  		crtc->y = saved_y; @@ -441,94 +396,115 @@ done:  }  EXPORT_SYMBOL(drm_crtc_helper_set_mode); +static void +drm_crtc_helper_disable(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_connector *connector; +	struct drm_encoder *encoder; + +	/* Decouple all encoders and their attached connectors from this crtc */ +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +		if (encoder->crtc != crtc) +			continue; + +		list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +			if (connector->encoder != encoder) +				continue; + +			connector->encoder = NULL; + +			/* +			 * drm_helper_disable_unused_functions() ought to be +			 * doing this, but since we've decoupled the encoder +			 * from the connector above, the required connection +			 * between them is henceforth no longer available. +			 */ +			connector->dpms = DRM_MODE_DPMS_OFF; +		} +	} + +	__drm_helper_disable_unused_functions(dev); +}  /**   * drm_crtc_helper_set_config - set a new config from userspace - * @crtc: CRTC to setup - * @crtc_info: user provided configuration - * @new_mode: new mode to set - * @connector_set: set of connectors for the new config - * @fb: new framebuffer - * - * LOCKING: - * Caller must hold mode config lock. + * @set: mode set configuration   * - * Setup a new configuration, provided by the user in @crtc_info, and enable - * it. + * Setup a new configuration, provided by the upper layers (either an ioctl call + * from userspace or internally e.g. from the fbdev support code) in @set, and + * enable it. This is the main helper functions for drivers that implement + * kernel mode setting with the crtc helper functions and the assorted + * ->prepare(), ->modeset() and ->commit() helper callbacks.   * - * RETURNS: - * Zero. (FIXME) + * Returns: + * Returns 0 on success, negative errno numbers on failure.   */  int drm_crtc_helper_set_config(struct drm_mode_set *set)  {  	struct drm_device *dev; -	struct drm_crtc *save_crtcs, *new_crtc, *crtc; +	struct drm_crtc *new_crtc;  	struct drm_encoder *save_encoders, *new_encoder, *encoder; -	struct drm_framebuffer *old_fb = NULL;  	bool mode_changed = false; /* if true do a full mode set */  	bool fb_changed = false; /* if true and !mode_changed just do a flip */  	struct drm_connector *save_connectors, *connector;  	int count = 0, ro, fail = 0;  	struct drm_crtc_helper_funcs *crtc_funcs; -	int ret = 0; +	struct drm_mode_set save_set; +	int ret; +	int i;  	DRM_DEBUG_KMS("\n"); -	if (!set) -		return -EINVAL; - -	if (!set->crtc) -		return -EINVAL; +	BUG_ON(!set); +	BUG_ON(!set->crtc); +	BUG_ON(!set->crtc->helper_private); -	if (!set->crtc->helper_private) -		return -EINVAL; +	/* Enforce sane interface api - has been abused by the fb helper. */ +	BUG_ON(!set->mode && set->fb); +	BUG_ON(set->fb && set->num_connectors == 0);  	crtc_funcs = set->crtc->helper_private; +	if (!set->mode) +		set->fb = NULL; +  	if (set->fb) {  		DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n",  				set->crtc->base.id, set->fb->base.id,  				(int)set->num_connectors, set->x, set->y);  	} else { -		DRM_DEBUG_KMS("[CRTC:%d] [NOFB] #connectors=%d (x y) (%i %i)\n", -				set->crtc->base.id, (int)set->num_connectors, -				set->x, set->y); +		DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); +		drm_crtc_helper_disable(set->crtc); +		return 0;  	}  	dev = set->crtc->dev; -	/* Allocate space for the backup of all (non-pointer) crtc, encoder and -	 * connector data. */ -	save_crtcs = kzalloc(dev->mode_config.num_crtc * -			     sizeof(struct drm_crtc), GFP_KERNEL); -	if (!save_crtcs) -		return -ENOMEM; +	drm_warn_on_modeset_not_all_locked(dev); +	/* +	 * Allocate space for the backup of all (non-pointer) encoder and +	 * connector data. +	 */  	save_encoders = kzalloc(dev->mode_config.num_encoder *  				sizeof(struct drm_encoder), GFP_KERNEL); -	if (!save_encoders) { -		kfree(save_crtcs); +	if (!save_encoders)  		return -ENOMEM; -	}  	save_connectors = kzalloc(dev->mode_config.num_connector *  				sizeof(struct drm_connector), GFP_KERNEL);  	if (!save_connectors) { -		kfree(save_crtcs);  		kfree(save_encoders);  		return -ENOMEM;  	} -	/* Copy data. Note that driver private data is not affected. +	/* +	 * Copy data. Note that driver private data is not affected.  	 * Should anything bad happen only the expected state is  	 * restored, not the drivers personal bookkeeping.  	 */  	count = 0; -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -		save_crtcs[count++] = *crtc; -	} - -	count = 0;  	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {  		save_encoders[count++] = *encoder;  	} @@ -538,15 +514,24 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)  		save_connectors[count++] = *connector;  	} +	save_set.crtc = set->crtc; +	save_set.mode = &set->crtc->mode; +	save_set.x = set->crtc->x; +	save_set.y = set->crtc->y; +	save_set.fb = set->crtc->primary->fb; +  	/* We should be able to check here if the fb has the same properties  	 * and then just flip_or_move it */ -	if (set->crtc->fb != set->fb) { +	if (set->crtc->primary->fb != set->fb) {  		/* If we have no fb then treat it as a full mode set */ -		if (set->crtc->fb == NULL) { +		if (set->crtc->primary->fb == NULL) {  			DRM_DEBUG_KMS("crtc has no fb, full mode set\n");  			mode_changed = true;  		} else if (set->fb == NULL) {  			mode_changed = true; +		} else if (set->fb->pixel_format != +			   set->crtc->primary->fb->pixel_format) { +			mode_changed = true;  		} else  			fb_changed = true;  	} @@ -575,6 +560,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)  				if (new_encoder == NULL)  					/* don't break so fail path works correct */  					fail = 1; + +				if (connector->dpms != DRM_MODE_DPMS_ON) { +					DRM_DEBUG_KMS("connector dpms not on, full mode switch\n"); +					mode_changed = true; +				} +  				break;  			}  		} @@ -624,11 +615,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)  		}  		if (new_crtc) {  			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", -				connector->base.id, drm_get_connector_name(connector), +				connector->base.id, connector->name,  				new_crtc->base.id);  		} else {  			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", -				connector->base.id, drm_get_connector_name(connector)); +				connector->base.id, connector->name);  		}  	} @@ -637,49 +628,49 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)  		mode_changed = true;  	if (mode_changed) { -		set->crtc->enabled = (set->mode != NULL); -		if (set->mode != NULL) { +		if (drm_helper_crtc_in_use(set->crtc)) {  			DRM_DEBUG_KMS("attempting to set mode from"  					" userspace\n");  			drm_mode_debug_printmodeline(set->mode); -			old_fb = set->crtc->fb; -			set->crtc->fb = set->fb; +			set->crtc->primary->fb = set->fb;  			if (!drm_crtc_helper_set_mode(set->crtc, set->mode,  						      set->x, set->y, -						      old_fb)) { +						      save_set.fb)) {  				DRM_ERROR("failed to set mode on [CRTC:%d]\n",  					  set->crtc->base.id); +				set->crtc->primary->fb = save_set.fb;  				ret = -EINVAL;  				goto fail;  			} +			DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); +			for (i = 0; i < set->num_connectors; i++) { +				DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, +					      set->connectors[i]->name); +				set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); +			}  		} -		drm_helper_disable_unused_functions(dev); +		__drm_helper_disable_unused_functions(dev);  	} else if (fb_changed) {  		set->crtc->x = set->x;  		set->crtc->y = set->y; - -		old_fb = set->crtc->fb; -		if (set->crtc->fb != set->fb) -			set->crtc->fb = set->fb; +		set->crtc->primary->fb = set->fb;  		ret = crtc_funcs->mode_set_base(set->crtc, -						set->x, set->y, old_fb); -		if (ret != 0) +						set->x, set->y, save_set.fb); +		if (ret != 0) { +			set->crtc->x = save_set.x; +			set->crtc->y = save_set.y; +			set->crtc->primary->fb = save_set.fb;  			goto fail; +		}  	}  	kfree(save_connectors);  	kfree(save_encoders); -	kfree(save_crtcs);  	return 0;  fail:  	/* Restore all previous data. */  	count = 0; -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -		*crtc = save_crtcs[count++]; -	} - -	count = 0;  	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {  		*encoder = save_encoders[count++];  	} @@ -689,9 +680,14 @@ fail:  		*connector = save_connectors[count++];  	} +	/* Try to restore the config */ +	if (mode_changed && +	    !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, +				      save_set.y, save_set.fb)) +		DRM_ERROR("failed to restore config after modeset failure\n"); +  	kfree(save_connectors);  	kfree(save_encoders); -	kfree(save_crtcs);  	return ret;  }  EXPORT_SYMBOL(drm_crtc_helper_set_config); @@ -709,6 +705,31 @@ static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder)  	return dpms;  } +/* Helper which handles bridge ordering around encoder dpms */ +static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_bridge *bridge = encoder->bridge; +	struct drm_encoder_helper_funcs *encoder_funcs; + +	if (bridge) { +		if (mode == DRM_MODE_DPMS_ON) +			bridge->funcs->pre_enable(bridge); +		else +			bridge->funcs->disable(bridge); +	} + +	encoder_funcs = encoder->helper_private; +	if (encoder_funcs->dpms) +		encoder_funcs->dpms(encoder, mode); + +	if (bridge) { +		if (mode == DRM_MODE_DPMS_ON) +			bridge->funcs->enable(bridge); +		else +			bridge->funcs->post_disable(bridge); +	} +} +  static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)  {  	int dpms = DRM_MODE_DPMS_OFF; @@ -723,18 +744,20 @@ static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)  }  /** - * drm_helper_connector_dpms - * @connector affected connector - * @mode DPMS mode + * drm_helper_connector_dpms() - connector dpms helper implementation + * @connector: affected connector + * @mode: DPMS mode   * - * Calls the low-level connector DPMS function, then - * calls appropriate encoder and crtc DPMS functions as well + * This is the main helper function provided by the crtc helper framework for + * implementing the DPMS connector attribute. It computes the new desired DPMS + * state for all encoders and crtcs in the output mesh and calls the ->dpms() + * callback provided by the driver appropriately.   */  void drm_helper_connector_dpms(struct drm_connector *connector, int mode)  {  	struct drm_encoder *encoder = connector->encoder;  	struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; -	int old_dpms; +	int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF;  	if (mode == connector->dpms)  		return; @@ -742,6 +765,9 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)  	old_dpms = connector->dpms;  	connector->dpms = mode; +	if (encoder) +		encoder_dpms = drm_helper_choose_encoder_dpms(encoder); +  	/* from off to on, do crtc then encoder */  	if (mode < old_dpms) {  		if (crtc) { @@ -750,22 +776,14 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)  				(*crtc_funcs->dpms) (crtc,  						     drm_helper_choose_crtc_dpms(crtc));  		} -		if (encoder) { -			struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; -			if (encoder_funcs->dpms) -				(*encoder_funcs->dpms) (encoder, -							drm_helper_choose_encoder_dpms(encoder)); -		} +		if (encoder) +			drm_helper_encoder_dpms(encoder, encoder_dpms);  	}  	/* from on to off, do encoder then crtc */  	if (mode > old_dpms) { -		if (encoder) { -			struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; -			if (encoder_funcs->dpms) -				(*encoder_funcs->dpms) (encoder, -							drm_helper_choose_encoder_dpms(encoder)); -		} +		if (encoder) +			drm_helper_encoder_dpms(encoder, encoder_dpms);  		if (crtc) {  			struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;  			if (crtc_funcs->dpms) @@ -778,35 +796,69 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)  }  EXPORT_SYMBOL(drm_helper_connector_dpms); -int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, -				   struct drm_mode_fb_cmd *mode_cmd) +/** + * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata + * @fb: drm_framebuffer object to fill out + * @mode_cmd: metadata from the userspace fb creation request + * + * This helper can be used in a drivers fb_create callback to pre-fill the fb's + * metadata fields. + */ +void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, +				    struct drm_mode_fb_cmd2 *mode_cmd)  { +	int i; +  	fb->width = mode_cmd->width;  	fb->height = mode_cmd->height; -	fb->pitch = mode_cmd->pitch; -	fb->bits_per_pixel = mode_cmd->bpp; -	fb->depth = mode_cmd->depth; - -	return 0; +	for (i = 0; i < 4; i++) { +		fb->pitches[i] = mode_cmd->pitches[i]; +		fb->offsets[i] = mode_cmd->offsets[i]; +	} +	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, +				    &fb->bits_per_pixel); +	fb->pixel_format = mode_cmd->pixel_format;  }  EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); -int drm_helper_resume_force_mode(struct drm_device *dev) +/** + * drm_helper_resume_force_mode - force-restore mode setting configuration + * @dev: drm_device which should be restored + * + * Drivers which use the mode setting helpers can use this function to + * force-restore the mode setting configuration e.g. on resume or when something + * else might have trampled over the hw state (like some overzealous old BIOSen + * tended to do). + * + * This helper doesn't provide a error return value since restoring the old + * config should never fail due to resource allocation issues since the driver + * has successfully set the restored configuration already. Hence this should + * boil down to the equivalent of a few dpms on calls, which also don't provide + * an error code. + * + * Drivers where simply restoring an old configuration again might fail (e.g. + * due to slight differences in allocating shared resources when the + * configuration is restored in a different order than when userspace set it up) + * need to use their own restore logic. + */ +void drm_helper_resume_force_mode(struct drm_device *dev)  {  	struct drm_crtc *crtc;  	struct drm_encoder *encoder; -	struct drm_encoder_helper_funcs *encoder_funcs;  	struct drm_crtc_helper_funcs *crtc_funcs; -	int ret; +	int encoder_dpms; +	bool ret; +	drm_modeset_lock_all(dev);  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {  		if (!crtc->enabled)  			continue;  		ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, -					       crtc->x, crtc->y, crtc->fb); +					       crtc->x, crtc->y, crtc->primary->fb); +		/* Restoring the old config should never fail! */  		if (ret == false)  			DRM_ERROR("failed to set mode on crtc %p\n", crtc); @@ -817,10 +869,10 @@ int drm_helper_resume_force_mode(struct drm_device *dev)  				if(encoder->crtc != crtc)  					continue; -				encoder_funcs = encoder->helper_private; -				if (encoder_funcs->dpms) -					(*encoder_funcs->dpms) (encoder, -								drm_helper_choose_encoder_dpms(encoder)); +				encoder_dpms = drm_helper_choose_encoder_dpms( +							encoder); + +				drm_helper_encoder_dpms(encoder, encoder_dpms);  			}  			crtc_funcs = crtc->helper_private; @@ -829,110 +881,9 @@ int drm_helper_resume_force_mode(struct drm_device *dev)  						     drm_helper_choose_crtc_dpms(crtc));  		}  	} +  	/* disable the unused connectors while restoring the modesetting */ -	drm_helper_disable_unused_functions(dev); -	return 0; +	__drm_helper_disable_unused_functions(dev); +	drm_modeset_unlock_all(dev);  }  EXPORT_SYMBOL(drm_helper_resume_force_mode); - -#define DRM_OUTPUT_POLL_PERIOD (10*HZ) -static void output_poll_execute(struct work_struct *work) -{ -	struct delayed_work *delayed_work = to_delayed_work(work); -	struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); -	struct drm_connector *connector; -	enum drm_connector_status old_status, status; -	bool repoll = false, changed = false; - -	if (!drm_kms_helper_poll) -		return; - -	mutex_lock(&dev->mode_config.mutex); -	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - -		/* if this is HPD or polled don't check it - -		   TV out for instance */ -		if (!connector->polled) -			continue; - -		else if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT)) -			repoll = true; - -		old_status = connector->status; -		/* if we are connected and don't want to poll for disconnect -		   skip it */ -		if (old_status == connector_status_connected && -		    !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT) && -		    !(connector->polled & DRM_CONNECTOR_POLL_HPD)) -			continue; - -		status = connector->funcs->detect(connector, false); -		if (old_status != status) -			changed = true; -	} - -	mutex_unlock(&dev->mode_config.mutex); - -	if (changed) { -		/* send a uevent + call fbdev */ -		drm_sysfs_hotplug_event(dev); -		if (dev->mode_config.funcs->output_poll_changed) -			dev->mode_config.funcs->output_poll_changed(dev); -	} - -	if (repoll) -		queue_delayed_work(system_nrt_wq, delayed_work, DRM_OUTPUT_POLL_PERIOD); -} - -void drm_kms_helper_poll_disable(struct drm_device *dev) -{ -	if (!dev->mode_config.poll_enabled) -		return; -	cancel_delayed_work_sync(&dev->mode_config.output_poll_work); -} -EXPORT_SYMBOL(drm_kms_helper_poll_disable); - -void drm_kms_helper_poll_enable(struct drm_device *dev) -{ -	bool poll = false; -	struct drm_connector *connector; - -	if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll) -		return; - -	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -		if (connector->polled) -			poll = true; -	} - -	if (poll) -		queue_delayed_work(system_nrt_wq, &dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD); -} -EXPORT_SYMBOL(drm_kms_helper_poll_enable); - -void drm_kms_helper_poll_init(struct drm_device *dev) -{ -	INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute); -	dev->mode_config.poll_enabled = true; - -	drm_kms_helper_poll_enable(dev); -} -EXPORT_SYMBOL(drm_kms_helper_poll_init); - -void drm_kms_helper_poll_fini(struct drm_device *dev) -{ -	drm_kms_helper_poll_disable(dev); -} -EXPORT_SYMBOL(drm_kms_helper_poll_fini); - -void drm_helper_hpd_irq_event(struct drm_device *dev) -{ -	if (!dev->mode_config.poll_enabled) -		return; - -	/* kill timer and schedule immediate execution, this doesn't block */ -	cancel_delayed_work(&dev->mode_config.output_poll_work); -	if (drm_kms_helper_poll) -		queue_delayed_work(system_nrt_wq, &dev->mode_config.output_poll_work, 0); -} -EXPORT_SYMBOL(drm_helper_hpd_irq_event);  | 
