summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2014-05-31 01:48:06 +1000
committerBen Skeggs <bskeggs@redhat.com>2014-06-11 16:11:10 +1000
commit4874322e78d505d38c8d4481118af5c9f0e8306d (patch)
treee5e995df297cb2ad3b15723afd546237b30b2a7f
parent8894f4919bc43f821775db2cfff4b917871b2102 (diff)
drm/nouveau/dp: fix support for dpms
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>
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c39
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c15
2 files changed, 50 insertions, 4 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 7535a8700fe..7dac5e21679 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -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;
}
@@ -915,6 +916,40 @@ nouveau_connector_funcs_lvds = {
};
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)
{
struct nouveau_connector *nv_connector =
@@ -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;
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 3c58854aff0..21c72480df8 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -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