drm/nv50-/kms: allow disabling of gpu scaling on fixed panels
authorBen Skeggs <bskeggs@redhat.com>
Mon, 22 Dec 2014 07:19:26 +0000 (17:19 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Thu, 22 Jan 2015 02:14:58 +0000 (12:14 +1000)
The hilarious part is that, under X, this won't work anyway because the
server decides to construct its own modes for some reason.

Tested with modetest, which isn't quite as insane.  I'd hope that
wayland is more sensible.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nv50_display.c

index c8ac9482cf2ed12de114636b004bdd66813bc4ba..d13f50db765d8f4a2c05c841828175c10e1c7ebb 100644 (file)
@@ -458,6 +458,28 @@ nouveau_connector_set_property(struct drm_connector *connector,
 
                switch (value) {
                case DRM_MODE_SCALE_NONE:
+                       /* We allow 'None' for EDID modes, even on a fixed
+                        * panel (some exist with support for lower refresh
+                        * rates, which people might want to use for power
+                        * saving purposes).
+                        *
+                        * Non-EDID modes will force the use of GPU scaling
+                        * to the native mode regardless of this setting.
+                        */
+                       switch (nv_connector->type) {
+                       case DCB_CONNECTOR_LVDS:
+                       case DCB_CONNECTOR_LVDS_SPWG:
+                       case DCB_CONNECTOR_eDP:
+                               /* ... except prior to G80, where the code
+                                * doesn't support such things.
+                                */
+                               if (disp->disp.oclass < NV50_DISP)
+                                       return -EINVAL;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
                case DRM_MODE_SCALE_FULLSCREEN:
                case DRM_MODE_SCALE_CENTER:
                case DRM_MODE_SCALE_ASPECT:
@@ -466,11 +488,6 @@ nouveau_connector_set_property(struct drm_connector *connector,
                        return -EINVAL;
                }
 
-               /* LVDS always needs gpu scaling */
-               if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
-                   value == DRM_MODE_SCALE_NONE)
-                       return -EINVAL;
-
                /* Changing between GPU and panel scaling requires a full
                 * modeset
                 */
@@ -662,8 +679,6 @@ nouveau_connector_scaler_modes_add(struct drm_connector *connector)
                        if (!m)
                                continue;
 
-                       m->type |= DRM_MODE_TYPE_DRIVER;
-
                        drm_mode_probed_add(connector, m);
                        modes++;
                }
index 629a380c708555b26522987036b458a01fb2983f..a2d099142d96cb159211d173e4cf8a6cbe038a30 100644 (file)
@@ -72,6 +72,7 @@ struct nouveau_connector {
        int dithering_mode;
        int dithering_depth;
        int scaling_mode;
+       bool scaling_full;
        enum nouveau_underscan_type underscan;
        u32 underscan_hborder;
        u32 underscan_vborder;
index 4f544f3683db44374196ec5dd53db29b0724d46b..85cc667cfb0f0311143ad4cfff03b8e6fa6987e3 100644 (file)
@@ -729,8 +729,11 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
         * effectively handles NONE/FULL scaling
         */
        nv_connector = nouveau_crtc_connector_get(nv_crtc);
-       if (nv_connector && nv_connector->native_mode)
+       if (nv_connector && nv_connector->native_mode) {
                mode = nv_connector->scaling_mode;
+               if (nv_connector->scaling_full) /* non-EDID LVDS/eDP mode */
+                       mode = DRM_MODE_SCALE_FULLSCREEN;
+       }
 
        if (mode != DRM_MODE_SCALE_NONE)
                omode = nv_connector->native_mode;
@@ -1478,8 +1481,23 @@ nv50_encoder_mode_fixup(struct drm_encoder *encoder,
 
        nv_connector = nouveau_encoder_connector_get(nv_encoder);
        if (nv_connector && nv_connector->native_mode) {
-               if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE)
-                       drm_mode_copy(adjusted_mode, nv_connector->native_mode);
+               nv_connector->scaling_full = false;
+               if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE) {
+                       switch (nv_connector->type) {
+                       case DCB_CONNECTOR_LVDS:
+                       case DCB_CONNECTOR_LVDS_SPWG:
+                       case DCB_CONNECTOR_eDP:
+                               /* force use of scaler for non-edid modes */
+                               if (adjusted_mode->type & DRM_MODE_TYPE_DRIVER)
+                                       return true;
+                               nv_connector->scaling_full = true;
+                               break;
+                       default:
+                               return true;
+                       }
+               }
+
+               drm_mode_copy(adjusted_mode, nv_connector->native_mode);
        }
 
        return true;