]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/drm_crtc_helper.c
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[karo-tx-linux.git] / drivers / gpu / drm / drm_crtc_helper.c
index f7a81209beb38b92291e616b0171eb9d3f94fa62..c43825e8f5c12c0ba4fd5e9b27181cfac8dfc4d0 100644 (file)
@@ -105,9 +105,6 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
  * @maxX: max width for modes
  * @maxY: max height for modes
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Based on the helper callbacks implemented by @connector try to detect all
  * valid modes.  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
@@ -117,8 +114,8 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
  * @connector vfunc for drivers that use the crtc helpers for output mode
  * filtering and detection.
  *
- * RETURNS:
- * Number of modes found on @connector.
+ * Returns:
+ * The number of modes found on @connector.
  */
 int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
                                            uint32_t maxX, uint32_t maxY)
@@ -131,6 +128,8 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
        int mode_flags = 0;
        bool verbose_prune = true;
 
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
                        drm_get_connector_name(connector));
        /* set all modes to the unverified state */
@@ -176,8 +175,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
        drm_mode_connector_list_update(connector);
 
        if (maxX && maxY)
-               drm_mode_validate_size(dev, &connector->modes, maxX,
-                                      maxY, 0);
+               drm_mode_validate_size(dev, &connector->modes, maxX, maxY);
 
        if (connector->interlace_allowed)
                mode_flags |= DRM_MODE_FLAG_INTERLACE;
@@ -219,18 +217,19 @@ EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
  * drm_helper_encoder_in_use - check if a given encoder is in use
  * @encoder: encoder to check
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
- * Walk @encoders's DRM device's mode_config and see if it's in use.
+ * 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.
  *
- * 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;
+
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder == encoder)
                        return true;
@@ -242,19 +241,19 @@ 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.
- *
- * Walk @crtc's DRM device's mode_config and see if it's in use.
+ * 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.
  *
- * 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? */
+
+       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;
@@ -279,27 +278,17 @@ drm_encoder_disable(struct drm_encoder *encoder)
                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;
 
+       drm_warn_on_modeset_not_all_locked(dev);
+
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                if (!connector->encoder)
                        continue;
-               if (connector->status == connector_status_disconnected)
-                       connector->encoder = NULL;
        }
 
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
@@ -318,10 +307,27 @@ 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;
                }
        }
 }
+
+/**
+ * drm_helper_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+ *
+ * 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.
+ */
+void drm_helper_disable_unused_functions(struct drm_device *dev)
+{
+       drm_modeset_lock_all(dev);
+       __drm_helper_disable_unused_functions(dev);
+       drm_modeset_unlock_all(dev);
+}
 EXPORT_SYMBOL(drm_helper_disable_unused_functions);
 
 /*
@@ -355,9 +361,6 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
  * @y: vertical offset into the surface
  * @old_fb: old framebuffer, for cleanup
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * 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. This is an internal
  * helper that drivers could e.g. use to update properties that require the
@@ -367,8 +370,8 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
  * 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,
@@ -384,6 +387,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
        struct drm_encoder *encoder;
        bool ret = true;
 
+       drm_warn_on_modeset_not_all_locked(dev);
+
        saved_enabled = crtc->enabled;
        crtc->enabled = drm_helper_crtc_in_use(crtc);
        if (!crtc->enabled)
@@ -552,7 +557,7 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
                }
        }
 
-       drm_helper_disable_unused_functions(dev);
+       __drm_helper_disable_unused_functions(dev);
        return 0;
 }
 
@@ -560,17 +565,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
  * drm_crtc_helper_set_config - set a new config from userspace
  * @set: mode set configuration
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * 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:
- * Returns 0 on success, -ERRNO on failure.
+ * Returns:
+ * Returns 0 on success, negative errno numbers on failure.
  */
 int drm_crtc_helper_set_config(struct drm_mode_set *set)
 {
@@ -612,6 +614,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
 
        dev = set->crtc->dev;
 
+       drm_warn_on_modeset_not_all_locked(dev);
+
        /*
         * Allocate space for the backup of all (non-pointer) encoder and
         * connector data.
@@ -647,19 +651,19 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
        save_set.mode = &set->crtc->mode;
        save_set.x = set->crtc->x;
        save_set.y = set->crtc->y;
-       save_set.fb = set->crtc->fb;
+       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->fb->pixel_format) {
+                          set->crtc->primary->fb->pixel_format) {
                        mode_changed = true;
                } else
                        fb_changed = true;
@@ -689,12 +693,13 @@ 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;
-                               break;
 
                                if (connector->dpms != DRM_MODE_DPMS_ON) {
                                        DRM_DEBUG_KMS("connector dpms not on, full mode switch\n");
                                        mode_changed = true;
                                }
+
+                               break;
                        }
                }
 
@@ -760,13 +765,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        DRM_DEBUG_KMS("attempting to set mode from"
                                        " userspace\n");
                        drm_mode_debug_printmodeline(set->mode);
-                       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,
                                                      save_set.fb)) {
                                DRM_ERROR("failed to set mode on [CRTC:%d]\n",
                                          set->crtc->base.id);
-                               set->crtc->fb = save_set.fb;
+                               set->crtc->primary->fb = save_set.fb;
                                ret = -EINVAL;
                                goto fail;
                        }
@@ -777,17 +782,17 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                                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;
-               set->crtc->fb = set->fb;
+               set->crtc->primary->fb = set->fb;
                ret = crtc_funcs->mode_set_base(set->crtc,
                                                set->x, set->y, save_set.fb);
                if (ret != 0) {
                        set->crtc->x = save_set.x;
                        set->crtc->y = save_set.y;
-                       set->crtc->fb = save_set.fb;
+                       set->crtc->primary->fb = save_set.fb;
                        goto fail;
                }
        }
@@ -924,8 +929,16 @@ 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_cmd2 *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;
 
@@ -938,26 +951,47 @@ int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
        drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
                                    &fb->bits_per_pixel);
        fb->pixel_format = mode_cmd->pixel_format;
-
-       return 0;
 }
 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_crtc_helper_funcs *crtc_funcs;
-       int ret, encoder_dpms;
+       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);
 
@@ -980,12 +1014,29 @@ 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);
 
+/**
+ * drm_kms_helper_hotplug_event - fire off KMS hotplug events
+ * @dev: drm_device whose connector state changed
+ *
+ * This function fires off the uevent for userspace and also calls the
+ * output_poll_changed function, which is most commonly used to inform the fbdev
+ * emulation code and allow it to update the fbcon output configuration.
+ *
+ * Drivers should call this from their hotplug handling code when a change is
+ * detected. Note that this function does not do any output detection of its
+ * own, like drm_helper_hpd_irq_event() does - this is assumed to be done by the
+ * driver already.
+ *
+ * This function must be called from process context with no mode
+ * setting locks held.
+ */
 void drm_kms_helper_hotplug_event(struct drm_device *dev)
 {
        /* send a uevent + call fbdev */
@@ -1054,6 +1105,16 @@ static void output_poll_execute(struct work_struct *work)
                schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
 }
 
+/**
+ * drm_kms_helper_poll_disable - disable output polling
+ * @dev: drm_device
+ *
+ * This function disables the output polling work.
+ *
+ * Drivers can call this helper from their device suspend implementation. It is
+ * not an error to call this even when output polling isn't enabled or arlready
+ * disabled.
+ */
 void drm_kms_helper_poll_disable(struct drm_device *dev)
 {
        if (!dev->mode_config.poll_enabled)
@@ -1062,6 +1123,16 @@ void drm_kms_helper_poll_disable(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_kms_helper_poll_disable);
 
+/**
+ * drm_kms_helper_poll_enable - re-enable output polling.
+ * @dev: drm_device
+ *
+ * This function re-enables the output polling work.
+ *
+ * Drivers can call this helper from their device resume implementation. It is
+ * an error to call this when the output polling support has not yet been set
+ * up.
+ */
 void drm_kms_helper_poll_enable(struct drm_device *dev)
 {
        bool poll = false;
@@ -1081,6 +1152,25 @@ void drm_kms_helper_poll_enable(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_kms_helper_poll_enable);
 
+/**
+ * drm_kms_helper_poll_init - initialize and enable output polling
+ * @dev: drm_device
+ *
+ * This function intializes and then also enables output polling support for
+ * @dev. Drivers which do not have reliable hotplug support in hardware can use
+ * this helper infrastructure to regularly poll such connectors for changes in
+ * their connection state.
+ *
+ * Drivers can control which connectors are polled by setting the
+ * DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT flags. On
+ * connectors where probing live outputs can result in visual distortion drivers
+ * should not set the DRM_CONNECTOR_POLL_DISCONNECT flag to avoid this.
+ * Connectors which have no flag or only DRM_CONNECTOR_POLL_HPD set are
+ * completely ignored by the polling logic.
+ *
+ * Note that a connector can be both polled and probed from the hotplug handler,
+ * in case the hotplug interrupt is known to be unreliable.
+ */
 void drm_kms_helper_poll_init(struct drm_device *dev)
 {
        INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);
@@ -1090,12 +1180,39 @@ void drm_kms_helper_poll_init(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_kms_helper_poll_init);
 
+/**
+ * drm_kms_helper_poll_fini - disable output polling and clean it up
+ * @dev: drm_device
+ */
 void drm_kms_helper_poll_fini(struct drm_device *dev)
 {
        drm_kms_helper_poll_disable(dev);
 }
 EXPORT_SYMBOL(drm_kms_helper_poll_fini);
 
+/**
+ * drm_helper_hpd_irq_event - hotplug processing
+ * @dev: drm_device
+ *
+ * Drivers can use this helper function to run a detect cycle on all connectors
+ * which have the DRM_CONNECTOR_POLL_HPD flag set in their &polled member. All
+ * other connectors are ignored, which is useful to avoid reprobing fixed
+ * panels.
+ *
+ * This helper function is useful for drivers which can't or don't track hotplug
+ * interrupts for each connector.
+ *
+ * Drivers which support hotplug interrupts for each connector individually and
+ * which have a more fine-grained detect logic should bypass this code and
+ * directly call drm_kms_helper_hotplug_event() in case the connector state
+ * changed.
+ *
+ * This function must be called from process context with no mode
+ * setting locks held.
+ *
+ * Note that a connector can be both polled and probed from the hotplug handler,
+ * in case the hotplug interrupt is known to be unreliable.
+ */
 bool drm_helper_hpd_irq_event(struct drm_device *dev)
 {
        struct drm_connector *connector;