drm/nouveau/dp: fix support for dpms
authorBen Skeggs <bskeggs@redhat.com>
Fri, 30 May 2014 15:48:06 +0000 (01:48 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 11 Jun 2014 06:11:10 +0000 (16:11 +1000)
SOR_PWR has no effect to power-off DP links, unlike other SOR protocols.

Instead, on the source side, we cut power to the lanes after having put
the sink into D3.  Link training takes care of everything required to
bring it back again.

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

index 7535a8700fe9865ffa17e46fa1fe4e2eafab935e..7dac5e21679998786c17584cb6c75d9c79fce260 100644 (file)
@@ -76,7 +76,8 @@ find_encoder(struct drm_connector *connector, int type)
                        continue;
                nv_encoder = nouveau_encoder(obj_to_encoder(obj));
 
-               if (type == DCB_OUTPUT_ANY || nv_encoder->dcb->type == type)
+               if (type == DCB_OUTPUT_ANY ||
+                   (nv_encoder->dcb && nv_encoder->dcb->type == type))
                        return nv_encoder;
        }
 
@@ -914,6 +915,40 @@ nouveau_connector_funcs_lvds = {
        .force = nouveau_connector_force
 };
 
+static void
+nouveau_connector_dp_dpms(struct drm_connector *connector, int mode)
+{
+       struct nouveau_encoder *nv_encoder = NULL;
+
+       if (connector->encoder)
+               nv_encoder = nouveau_encoder(connector->encoder);
+       if (nv_encoder && nv_encoder->dcb &&
+           nv_encoder->dcb->type == DCB_OUTPUT_DP) {
+               if (mode == DRM_MODE_DPMS_ON) {
+                       u8 data = DP_SET_POWER_D0;
+                       nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1);
+                       usleep_range(1000, 2000);
+               } else {
+                       u8 data = DP_SET_POWER_D3;
+                       nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1);
+               }
+       }
+
+       drm_helper_connector_dpms(connector, mode);
+}
+
+static const struct drm_connector_funcs
+nouveau_connector_funcs_dp = {
+       .dpms = nouveau_connector_dp_dpms,
+       .save = NULL,
+       .restore = NULL,
+       .detect = nouveau_connector_detect,
+       .destroy = nouveau_connector_destroy,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .set_property = nouveau_connector_set_property,
+       .force = nouveau_connector_force
+};
+
 static void
 nouveau_connector_hotplug_work(struct work_struct *work)
 {
@@ -1122,7 +1157,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
                        return ERR_PTR(ret);
                }
 
-               funcs = &nouveau_connector_funcs;
+               funcs = &nouveau_connector_funcs_dp;
                break;
        default:
                funcs = &nouveau_connector_funcs;
index 3c58854aff03832a539586f7985b79b2f8f9327a..21c72480df826a0167c3f442f0dd1482d14045fe 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
 
 #include "nouveau_drm.h"
 #include "nouveau_dma.h"
@@ -1722,7 +1723,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
        struct drm_device *dev = encoder->dev;
        struct nv50_disp *disp = nv50_disp(dev);
        struct drm_encoder *partner;
-       int or = nv_encoder->or;
+       u32 mthd;
 
        nv_encoder->last_dpms = mode;
 
@@ -1740,7 +1741,17 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
                }
        }
 
-       nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON));
+       mthd  = (ffs(nv_encoder->dcb->sorconf.link) - 1) << 2;
+       mthd |= nv_encoder->or;
+
+       if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
+               nv_call(disp->core, NV50_DISP_SOR_PWR | mthd, 1);
+               mthd |= NV94_DISP_SOR_DP_PWR;
+       } else {
+               mthd |= NV50_DISP_SOR_PWR;
+       }
+
+       nv_call(disp->core, mthd, (mode == DRM_MODE_DPMS_ON));
 }
 
 static bool