NV50: s/FALSE/false/
[platform/upstream/libdrm.git] / linux-core / nv50_kms_wrapper.c
index b0d6434..8ae72f4 100644 (file)
@@ -236,8 +236,10 @@ static const struct drm_mode_config_funcs nv50_kms_mode_funcs = {
  * CRTC functions.
  */
 
-static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc, uint32_t buffer_handle,
-                         uint32_t width, uint32_t height)
+static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc, 
+                                   struct drm_file *file_priv,
+                                   uint32_t buffer_handle,
+                                   uint32_t width, uint32_t height)
 {
        struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
        struct nv50_display *display = nv50_get_display(crtc->dev);
@@ -386,7 +388,7 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set)
                        /* This is to ensure it knows the connector subtype. */
                        drm_connector->funcs->fill_modes(drm_connector, 0, 0);
 
-                       output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+                       output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
                        if (!output) {
                                DRM_ERROR("No output\n");
                                goto out;
@@ -458,7 +460,7 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set)
                                goto out;
                        }
 
-                       output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+                       output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
                        if (!output) {
                                DRM_ERROR("No output\n");
                                goto out;
@@ -635,12 +637,12 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set)
                                if (connector->output != output)
                                        continue;
 
-                               crtc->scaling_mode = connector->scaling_mode;
+                               crtc->requested_scaling_mode = connector->requested_scaling_mode;
                                crtc->use_dithering = connector->use_dithering;
                                break;
                        }
 
-                       if (crtc->scaling_mode == SCALE_NON_GPU)
+                       if (crtc->requested_scaling_mode == SCALE_NON_GPU)
                                crtc->use_native_mode = false;
                        else
                                crtc->use_native_mode = true;
@@ -835,7 +837,9 @@ static int nv50_kms_encoders_init(struct drm_device *dev)
  * Connector functions
  */
 
-bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector)
+
+/* These 2 functions wrap the connector properties that deal with multiple encoders per connector. */
+bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector)
 {
        struct drm_device *dev = drm_connector->dev;
 
@@ -892,6 +896,33 @@ bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector)
        return false;
 }
 
+static void nv50_kms_connector_set_digital(struct drm_connector *drm_connector, int digital, bool force)
+{
+       struct drm_device *dev = drm_connector->dev;
+
+       if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+               uint64_t cur_value, new_value;
+
+               int rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &cur_value);
+               if (rval) {
+                       DRM_ERROR("Unable to find subconnector property\n");
+                       return;
+               }
+
+               /* Only set when unknown or when forced to do so. */
+               if (cur_value != DRM_MODE_SUBCONNECTOR_Unknown && !force)
+                       return;
+
+               if (digital == 1)
+                       new_value = DRM_MODE_SUBCONNECTOR_DVID;
+               else if (digital == 0)
+                       new_value = DRM_MODE_SUBCONNECTOR_DVIA;
+               else
+                       new_value = DRM_MODE_SUBCONNECTOR_Unknown;
+               drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, new_value);
+       }
+}
+
 void nv50_kms_connector_detect_all(struct drm_device *dev)
 {
        struct drm_connector *drm_connector = NULL;
@@ -903,16 +934,32 @@ void nv50_kms_connector_detect_all(struct drm_device *dev)
 
 static enum drm_connector_status nv50_kms_connector_detect(struct drm_connector *drm_connector)
 {
-       struct nv50_connector *connector = to_nv50_connector(drm_connector);
        struct drm_device *dev = drm_connector->dev;
-       bool connected;
-       int old_status;
+       struct nv50_connector *connector = to_nv50_connector(drm_connector);
+       struct nv50_output *output = NULL;
+       int hpd_detect = 0, load_detect = 0, i2c_detect = 0;
+       int old_status = drm_connector->status;
 
-       connected = connector->detect(connector);
+       /* hotplug detect */
+       hpd_detect = connector->hpd_detect(connector);
 
-       old_status = drm_connector->status;
+       /* load detect */
+       output = connector->to_output(connector, false); /* analog */
+       if (output && output->detect)
+               load_detect = output->detect(output);
 
-       if (connected)
+       if (hpd_detect < 0 || load_detect < 0) /* did an error occur? */
+               i2c_detect = connector->i2c_detect(connector);
+
+       if (load_detect == 1) {
+               nv50_kms_connector_set_digital(drm_connector, 0, true); /* analog, forced */
+       } else if (hpd_detect == 1 && load_detect == 0) {
+               nv50_kms_connector_set_digital(drm_connector, 1, true); /* digital, forced */
+       } else {
+               nv50_kms_connector_set_digital(drm_connector, -1, true); /* unknown, forced */
+       }
+
+       if (hpd_detect == 1 || load_detect == 1 || i2c_detect == 1)
                drm_connector->status = connector_status_connected;
        else
                drm_connector->status = connector_status_disconnected;
@@ -956,7 +1003,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
        struct nv50_connector *connector = to_nv50_connector(drm_connector);
        struct drm_device *dev = drm_connector->dev;
        int rval = 0;
-       bool connected;
+       bool connected = false;
        struct drm_display_mode *mode, *t;
        struct edid *edid = NULL;
 
@@ -965,16 +1012,13 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
        list_for_each_entry_safe(mode, t, &drm_connector->modes, head)
                mode->status = MODE_UNVERIFIED;
 
-       connected = connector->detect(connector);
+       if (nv50_kms_connector_detect(drm_connector) == connector_status_connected)
+               connected = true;
 
        if (connected)
-               drm_connector->status = connector_status_connected;
+               NV50_DEBUG("%s is connected\n", drm_get_connector_name(drm_connector));
        else
-               drm_connector->status = connector_status_disconnected;
-
-       if (!connected) {
                NV50_DEBUG("%s is disconnected\n", drm_get_connector_name(drm_connector));
-       }
 
        /* Not all connnectors have an i2c channel. */
        if (connected && connector->i2c_chan)
@@ -986,16 +1030,8 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
        if (edid) {
                rval = drm_add_edid_modes(drm_connector, edid);
 
-               /* 2 encoders per connector */
-               /* eventually do this based on load detect and hot plug detect */
-               if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
-                       uint64_t subtype = 0;
-                       if (edid->digital)
-                               subtype = DRM_MODE_SUBCONNECTOR_DVID;
-                       else
-                               subtype = DRM_MODE_SUBCONNECTOR_DVIA;
-                       drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, subtype);
-               }
+               /* Only update when relevant and when detect couldn't determine type. */
+               nv50_kms_connector_set_digital(drm_connector, edid->digital ? 1 : 0, false);
 
                kfree(edid);
        }
@@ -1009,7 +1045,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
        list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
                if (mode->status == MODE_OK) {
                        struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode);
-                       struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+                       struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
 
                        mode->status = output->validate_mode(output, hw_mode);
                        /* find native mode, TODO: also check if we actually found one */
@@ -1025,7 +1061,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
        list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
                if (mode->status == MODE_OK) {
                        struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode);
-                       struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+                       struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
 
                        mode->status = output->validate_mode(output, hw_mode);
                        kfree(hw_mode);
@@ -1045,6 +1081,10 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
 
                NV50_DEBUG("No valid modes on %s\n", drm_get_connector_name(drm_connector));
 
+               /* Making up native modes for LVDS is a bad idea. */
+               if (drm_connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+                       return;
+
                /* Should we do this here ???
                 * When no valid EDID modes are available we end up
                 * here and bailed in the past, now we add a standard
@@ -1057,7 +1097,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
 
                /* also add it as native mode */
                hw_mode = nv50_kms_to_hw_mode(mode);
-               output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+               output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
 
                if (hw_mode)
                        *output->native_mode = *hw_mode;
@@ -1079,21 +1119,22 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
        }
 }
 
-static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector,
+static int nv50_kms_connector_set_property(struct drm_connector *drm_connector,
                                        struct drm_property *property,
                                        uint64_t value)
 {
        struct drm_device *dev = drm_connector->dev;
        struct nv50_connector *connector = to_nv50_connector(drm_connector);
+       int rval = 0;
+       bool delay_change = false;
 
        /* DPMS */
        if (property == dev->mode_config.dpms_property && drm_connector->encoder) {
                struct nv50_output *output = to_nv50_output(drm_connector->encoder);
 
-               if (!output->set_power_mode(output, (int) value))
-                       return true;
-               else
-                       return false;
+               rval = output->set_power_mode(output, (int) value);
+
+               return rval;
        }
 
        /* Scaling mode */
@@ -1101,7 +1142,6 @@ static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector,
                struct nv50_crtc *crtc = NULL;
                struct nv50_display *display = nv50_get_display(dev);
                int internal_value = 0;
-               int rval = 0;
 
                switch (value) {
                        case DRM_MODE_SCALE_NON_GPU:
@@ -1120,30 +1160,43 @@ static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector,
                                break;
                }
 
-               connector->scaling_mode = internal_value;
+               /* LVDS always needs gpu scaling */
+               if (connector->type == CONNECTOR_LVDS && internal_value == SCALE_NON_GPU)
+                       return -EINVAL;
+
+               connector->requested_scaling_mode = internal_value;
 
                if (drm_connector->encoder && drm_connector->encoder->crtc)
                        crtc = to_nv50_crtc(drm_connector->encoder->crtc);
 
                if (!crtc)
-                       return true;
+                       return 0;
+
+               crtc->requested_scaling_mode = connector->requested_scaling_mode;
+
+               /* going from and to a gpu scaled regime requires a modesetting, so wait until next modeset */
+               if (crtc->scaling_mode == SCALE_NON_GPU || internal_value == SCALE_NON_GPU) {
+                       DRM_INFO("Moving from or to a non-gpu scaled mode, this will be processed upon next modeset.");
+                       delay_change = true;
+               }
+
+               if (delay_change)
+                       return 0;
 
-               crtc->scaling_mode = connector->scaling_mode;
                rval = crtc->set_scale(crtc);
                if (rval)
-                       return false;
+                       return rval;
 
                /* process command buffer */
                display->update(display);
 
-               return true;
+               return 0;
        }
 
        /* Dithering */
        if (property == dev->mode_config.dithering_mode_property) {
                struct nv50_crtc *crtc = NULL;
                struct nv50_display *display = nv50_get_display(dev);
-               int rval = 0;
 
                if (value == DRM_MODE_DITHERING_ON)
                        connector->use_dithering = true;
@@ -1154,21 +1207,21 @@ static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector,
                        crtc = to_nv50_crtc(drm_connector->encoder->crtc);
 
                if (!crtc)
-                       return true;
+                       return 0;
 
                /* update hw state */
                crtc->use_dithering = connector->use_dithering;
                rval = crtc->set_dither(crtc);
                if (rval)
-                       return false;
+                       return rval;
 
                /* process command buffer */
                display->update(display);
 
-               return true;
+               return 0;
        }
 
-       return false;
+       return -EINVAL;
 }
 
 static const struct drm_connector_funcs nv50_kms_connector_funcs = {
@@ -1192,7 +1245,7 @@ static int nv50_kms_get_scaling_mode(struct drm_connector *drm_connector)
 
        connector = to_nv50_connector(drm_connector);
 
-       switch (connector->scaling_mode) {
+       switch (connector->requested_scaling_mode) {
                case SCALE_NON_GPU:
                        drm_mode = DRM_MODE_SCALE_NON_GPU;
                        break;