drm/kms: Init the CRTC info fields for modes forced from the command line.
[cascardo/linux.git] / drivers / gpu / drm / drm_crtc_helper.c
index eea5e6c..bbfd110 100644 (file)
 #include "drmP.h"
 #include "drm_crtc.h"
 #include "drm_crtc_helper.h"
-
-/*
- * Detailed mode info for 800x600@60Hz
- */
-static struct drm_display_mode std_modes[] = {
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840,
-                  968, 1056, 0, 600, 601, 605, 628, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-};
+#include "drm_fb_helper.h"
 
 static void drm_mode_validate_flag(struct drm_connector *connector,
                                   int flags)
@@ -99,18 +91,28 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
        list_for_each_entry_safe(mode, t, &connector->modes, head)
                mode->status = MODE_UNVERIFIED;
 
-       connector->status = connector->funcs->detect(connector);
+       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);
 
        if (connector->status == connector_status_disconnected) {
                DRM_DEBUG_KMS("%s is disconnected\n",
                          drm_get_connector_name(connector));
-               /* TODO set EDID to NULL */
-               return 0;
+               goto prune;
        }
 
        count = (*connector_funcs->get_modes)(connector);
-       if (!count)
-               return 0;
+       if (!count) {
+               count = drm_add_modes_noedid(connector, 800, 600);
+               if (!count)
+                       return 0;
+       }
 
        drm_mode_connector_list_update(connector);
 
@@ -130,7 +132,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
                                                                   mode);
        }
 
-
+prune:
        drm_mode_prune_invalid(dev, &connector->modes, true);
 
        if (list_empty(&connector->modes))
@@ -166,40 +168,6 @@ int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
 }
 EXPORT_SYMBOL(drm_helper_probe_connector_modes);
 
-static void drm_helper_add_std_modes(struct drm_device *dev,
-                                    struct drm_connector *connector)
-{
-       struct drm_display_mode *mode, *t;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(std_modes); i++) {
-               struct drm_display_mode *stdmode;
-
-               /*
-                * When no valid EDID modes are available we end up
-                * here and bailed in the past, now we add some standard
-                * modes and move on.
-                */
-               stdmode = drm_mode_duplicate(dev, &std_modes[i]);
-               drm_mode_probed_add(connector, stdmode);
-               drm_mode_list_concat(&connector->probed_modes,
-                                    &connector->modes);
-
-               DRM_DEBUG_KMS("Adding mode %s to %s\n", stdmode->name,
-                         drm_get_connector_name(connector));
-       }
-       drm_mode_sort(&connector->modes);
-
-       DRM_DEBUG_KMS("Added std modes on %s\n",
-                       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);
-       }
-}
-
 /**
  * drm_helper_encoder_in_use - check if a given encoder is in use
  * @encoder: encoder to check
@@ -278,9 +246,9 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
                                (*encoder_funcs->disable)(encoder);
                        else
                                (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
+                       /* disconnector encoder from any connector */
+                       encoder->crtc = NULL;
                }
-               /* disconnector encoder from any connector */
-               encoder->crtc = NULL;
        }
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -308,6 +276,66 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *con
        return NULL;
 }
 
+static bool drm_has_cmdline_mode(struct drm_connector *connector)
+{
+       struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
+       struct drm_fb_helper_cmdline_mode *cmdline_mode;
+
+       if (!fb_help_conn)
+               return false;
+
+       cmdline_mode = &fb_help_conn->cmdline_mode;
+       return cmdline_mode->specified;
+}
+
+static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height)
+{
+       struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
+       struct drm_fb_helper_cmdline_mode *cmdline_mode;
+       struct drm_display_mode *mode = NULL;
+
+       if (!fb_help_conn)
+               return mode;
+
+       cmdline_mode = &fb_help_conn->cmdline_mode;
+       if (cmdline_mode->specified == false)
+               return mode;
+
+       /* attempt to find a matching mode in the list of modes
+        *  we have gotten so far, if not add a CVT mode that conforms
+        */
+       if (cmdline_mode->rb || cmdline_mode->margins)
+               goto create_mode;
+
+       list_for_each_entry(mode, &connector->modes, head) {
+               /* check width/height */
+               if (mode->hdisplay != cmdline_mode->xres ||
+                   mode->vdisplay != cmdline_mode->yres)
+                       continue;
+
+               if (cmdline_mode->refresh_specified) {
+                       if (mode->vrefresh != cmdline_mode->refresh)
+                               continue;
+               }
+
+               if (cmdline_mode->interlace) {
+                       if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
+                               continue;
+               }
+               return mode;
+       }
+
+create_mode:
+       mode = drm_cvt_mode(connector->dev, cmdline_mode->xres,
+                           cmdline_mode->yres,
+                           cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
+                           cmdline_mode->rb, cmdline_mode->interlace,
+                           cmdline_mode->margins);
+       drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+       list_add(&mode->head, &connector->modes);
+       return mode;
+}
+
 static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
 {
        bool enable;
@@ -358,10 +386,16 @@ static bool drm_target_preferred(struct drm_device *dev,
                        continue;
                }
 
-               DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
-                         connector->base.id);
+               DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
+                             connector->base.id);
 
-               modes[i] = drm_has_preferred_mode(connector, width, height);
+               /* got for command line mode first */
+               modes[i] = drm_pick_cmdline_mode(connector, width, height);
+               if (!modes[i]) {
+                       DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
+                                     connector->base.id);
+                       modes[i] = drm_has_preferred_mode(connector, width, height);
+               }
                /* No preferred modes, pick one off the list */
                if (!modes[i] && !list_empty(&connector->modes)) {
                        list_for_each_entry(modes[i], &connector->modes, head)
@@ -410,6 +444,8 @@ static int drm_pick_crtcs(struct drm_device *dev,
        my_score = 1;
        if (connector->status == connector_status_connected)
                my_score++;
+       if (drm_has_cmdline_mode(connector))
+               my_score++;
        if (drm_has_preferred_mode(connector, width, height))
                my_score++;
 
@@ -842,7 +878,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        /* If the encoder is reused for another connector, then
                         * the appropriate crtc will be set later.
                         */
-                       connector->encoder->crtc = NULL;
+                       if (connector->encoder)
+                               connector->encoder->crtc = NULL;
                        connector->encoder = new_encoder;
                }
        }
@@ -909,6 +946,9 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                }
                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;
@@ -978,24 +1018,18 @@ bool drm_helper_plugged_event(struct drm_device *dev)
  */
 bool drm_helper_initial_config(struct drm_device *dev)
 {
-       struct drm_connector *connector;
        int count = 0;
 
+       drm_fb_helper_parse_command_line(dev);
+
        count = drm_helper_probe_connector_modes(dev,
                                                 dev->mode_config.max_width,
                                                 dev->mode_config.max_height);
 
        /*
-        * None of the available connectors had any modes, so add some
-        * and try to light them up anyway
+        * we shouldn't end up with no modes here.
         */
-       if (!count) {
-               DRM_ERROR("connectors have no modes, using standard modes\n");
-               list_for_each_entry(connector,
-                                   &dev->mode_config.connector_list,
-                                   head)
-                       drm_helper_add_std_modes(dev, connector);
-       }
+       WARN(!count, "No connectors reported connected with modes\n");
 
        drm_setup_crtcs(dev);