Merge branch 'getname2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[deliverable/linux.git] / drivers / gpu / drm / drm_probe_helper.c
index 7483a47de8e41630a37e7db22c5e9585dff11013..6591d48c1b9d0f3bcc4a74a8da730833cbc4e839 100644 (file)
 static bool drm_kms_helper_poll = true;
 module_param_named(poll, drm_kms_helper_poll, bool, 0600);
 
-static void drm_mode_validate_flag(struct drm_connector *connector,
-                                  int flags)
+static enum drm_mode_status
+drm_mode_validate_flag(const struct drm_display_mode *mode,
+                      int flags)
 {
-       struct drm_display_mode *mode;
+       if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+           !(flags & DRM_MODE_FLAG_INTERLACE))
+               return MODE_NO_INTERLACE;
 
-       if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE |
-                     DRM_MODE_FLAG_3D_MASK))
-               return;
+       if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) &&
+           !(flags & DRM_MODE_FLAG_DBLSCAN))
+               return MODE_NO_DBLESCAN;
 
-       list_for_each_entry(mode, &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;
-               if ((mode->flags & DRM_MODE_FLAG_3D_MASK) &&
-                               !(flags & DRM_MODE_FLAG_3D_MASK))
-                       mode->status = MODE_NO_STEREO;
-       }
+       if ((mode->flags & DRM_MODE_FLAG_3D_MASK) &&
+           !(flags & DRM_MODE_FLAG_3D_MASK))
+               return MODE_NO_STEREO;
 
-       return;
+       return MODE_OK;
 }
 
 static int drm_helper_probe_add_cmdline_mode(struct drm_connector *connector)
@@ -108,6 +103,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
        int count = 0;
        int mode_flags = 0;
        bool verbose_prune = true;
+       enum drm_connector_status old_status;
 
        WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 
@@ -126,7 +122,33 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
                if (connector->funcs->force)
                        connector->funcs->force(connector);
        } else {
+               old_status = connector->status;
+
                connector->status = connector->funcs->detect(connector, true);
+
+               /*
+                * Normally either the driver's hpd code or the poll loop should
+                * pick up any changes and fire the hotplug event. But if
+                * userspace sneaks in a probe, we might miss a change. Hence
+                * check here, and if anything changed start the hotplug code.
+                */
+               if (old_status != connector->status) {
+                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
+                                     connector->base.id,
+                                     connector->name,
+                                     old_status, connector->status);
+
+                       /*
+                        * The hotplug event code might call into the fb
+                        * helpers, and so expects that we do not hold any
+                        * locks. Fire up the poll struct instead, it will
+                        * disable itself again.
+                        */
+                       dev->mode_config.delayed_event = true;
+                       if (dev->mode_config.poll_enabled)
+                               schedule_delayed_work(&dev->mode_config.output_poll_work,
+                                                     0);
+               }
        }
 
        /* Re-enable polling in case the global poll config changed. */
@@ -164,18 +186,22 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
 
        drm_mode_connector_list_update(connector, merge_type_bits);
 
-       if (maxX && maxY)
-               drm_mode_validate_size(dev, &connector->modes, maxX, maxY);
-
        if (connector->interlace_allowed)
                mode_flags |= DRM_MODE_FLAG_INTERLACE;
        if (connector->doublescan_allowed)
                mode_flags |= DRM_MODE_FLAG_DBLSCAN;
        if (connector->stereo_allowed)
                mode_flags |= DRM_MODE_FLAG_3D_MASK;
-       drm_mode_validate_flag(connector, mode_flags);
 
        list_for_each_entry(mode, &connector->modes, head) {
+               mode->status = drm_mode_validate_basic(mode);
+
+               if (mode->status == MODE_OK)
+                       mode->status = drm_mode_validate_size(mode, maxX, maxY);
+
+               if (mode->status == MODE_OK)
+                       mode->status = drm_mode_validate_flag(mode, mode_flags);
+
                if (mode->status == MODE_OK && connector_funcs->mode_valid)
                        mode->status = connector_funcs->mode_valid(connector,
                                                                   mode);
@@ -275,10 +301,14 @@ static void output_poll_execute(struct work_struct *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;
-       bool repoll = false, changed = false;
+       bool repoll = false, changed;
+
+       /* Pick up any changes detected by the probe functions. */
+       changed = dev->mode_config.delayed_event;
+       dev->mode_config.delayed_event = false;
 
        if (!drm_kms_helper_poll)
-               return;
+               goto out;
 
        mutex_lock(&dev->mode_config.mutex);
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -305,6 +335,24 @@ static void output_poll_execute(struct work_struct *work)
                if (old_status != connector->status) {
                        const char *old, *new;
 
+                       /*
+                        * The poll work sets force=false when calling detect so
+                        * that drivers can avoid to do disruptive tests (e.g.
+                        * when load detect cycles could cause flickering on
+                        * other, running displays). This bears the risk that we
+                        * flip-flop between unknown here in the poll work and
+                        * the real state when userspace forces a full detect
+                        * call after receiving a hotplug event due to this
+                        * change.
+                        *
+                        * Hence clamp an unknown detect status to the old
+                        * value.
+                        */
+                       if (connector->status == connector_status_unknown) {
+                               connector->status = old_status;
+                               continue;
+                       }
+
                        old = drm_get_connector_status_name(old_status);
                        new = drm_get_connector_status_name(connector->status);
 
@@ -320,6 +368,7 @@ static void output_poll_execute(struct work_struct *work)
 
        mutex_unlock(&dev->mode_config.mutex);
 
+out:
        if (changed)
                drm_kms_helper_hotplug_event(dev);
 
This page took 0.03313 seconds and 5 git commands to generate.