[Intel-gfx] [PATCH 2/3] drm/i915: Attach a fb to the load-detect pipe

Chris Wilson chris at chris-wilson.co.uk
Tue Apr 19 22:32:02 CEST 2011


We need to ensure that we feed valid memory into the display plane
attached to the pipe when switching the pipe on. Otherwise, the display
engine may read through an invalid PTE and so throw an PGTBL_ER
exception.

For bonus amusement value, we perform the first load detect before even
establishing our fbdev.

Reported-by: Knut Petersen <Knut_Petersen at t-online.de>
References: https://bugs.freedesktop.org/show_bug.cgi?id=36246
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/intel_crt.c     |   24 ++---
 drivers/gpu/drm/i915/intel_display.c |  182 +++++++++++++++++++---------------
 drivers/gpu/drm/i915/intel_drv.h     |   25 +++--
 drivers/gpu/drm/i915/intel_tv.c      |   11 +--
 4 files changed, 132 insertions(+), 110 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index d03fc05..2a32405 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -305,13 +305,11 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
 }
 
 static enum drm_connector_status
-intel_crt_load_detect(struct drm_crtc *crtc, struct intel_crt *crt)
+intel_crt_load_detect(struct intel_crt *crt)
 {
-	struct drm_encoder *encoder = &crt->base.base;
-	struct drm_device *dev = encoder->dev;
+	struct drm_device *dev = crt->base.base.dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
-	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-	uint32_t pipe = intel_crtc->pipe;
+	uint32_t pipe = to_intel_crtc(crt->base.base.crtc)->pipe;
 	uint32_t save_bclrpat;
 	uint32_t save_vtotal;
 	uint32_t vtotal, vactive;
@@ -432,7 +430,6 @@ intel_crt_detect(struct drm_connector *connector, bool force)
 	struct drm_device *dev = connector->dev;
 	struct intel_crt *crt = intel_attached_crt(connector);
 	struct drm_crtc *crtc;
-	int dpms_mode;
 	enum drm_connector_status status;
 
 	if (I915_HAS_HOTPLUG(dev)) {
@@ -454,17 +451,18 @@ intel_crt_detect(struct drm_connector *connector, bool force)
 	/* for pre-945g platforms use load detect */
 	crtc = crt->base.base.crtc;
 	if (crtc && crtc->enabled) {
-		status = intel_crt_load_detect(crtc, crt);
+		status = intel_crt_load_detect(crt);
 	} else {
-		crtc = intel_get_load_detect_pipe(&crt->base, connector,
-						  NULL, &dpms_mode);
-		if (crtc) {
+		struct intel_load_detect_pipe tmp;
+
+		if (intel_get_load_detect_pipe(&crt->base,
+					       connector, NULL,
+					       &tmp)) {
 			if (intel_crt_detect_ddc(connector))
 				status = connector_status_connected;
 			else
-				status = intel_crt_load_detect(crtc, crt);
-			intel_release_load_detect_pipe(&crt->base,
-						       connector, dpms_mode);
+				status = intel_crt_load_detect(crt);
+			intel_release_load_detect_pipe(&tmp);
 		} else
 			status = connector_status_unknown;
 	}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index b7d63a5..b9bb20d 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -5541,115 +5541,133 @@ static struct drm_display_mode load_detect_mode = {
 		 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
 };
 
-struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
-					    struct drm_connector *connector,
-					    struct drm_display_mode *mode,
-					    int *dpms_mode)
+bool intel_get_load_detect_pipe(struct intel_encoder *encoder,
+				struct drm_connector *connector,
+				struct drm_display_mode *mode,
+				struct intel_load_detect_pipe *old)
 {
-	struct intel_crtc *intel_crtc;
-	struct drm_crtc *possible_crtc;
-	struct drm_crtc *supported_crtc =NULL;
-	struct drm_encoder *encoder = &intel_encoder->base;
-	struct drm_crtc *crtc = NULL;
-	struct drm_device *dev = encoder->dev;
-	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-	struct drm_crtc_helper_funcs *crtc_funcs;
-	int i = -1;
-
-	/*
-	 * Algorithm gets a little messy:
-	 *   - if the connector already has an assigned crtc, use it (but make
-	 *     sure it's on first)
-	 *   - try to find the first unused crtc that can drive this connector,
-	 *     and use that if we find one
-	 *   - if there are no unused crtcs available, try to use the first
-	 *     one we found that supports the connector
-	 */
+	struct drm_device *dev = encoder->base.dev;
+	struct drm_crtc *crtc;
+	struct drm_framebuffer *fb, *old_fb;
+	int x, y;
 
 	/* See if we already have a CRTC for this connector */
-	if (encoder->crtc) {
-		crtc = encoder->crtc;
-		/* Make sure the crtc and connector are running */
-		intel_crtc = to_intel_crtc(crtc);
-		*dpms_mode = intel_crtc->dpms_mode;
-		if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
-			crtc_funcs = crtc->helper_private;
-			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
-			encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
-		}
-		return crtc;
-	}
+	crtc = NULL;
+	if (encoder->base.crtc)
+		crtc = encoder->base.crtc;
 
-	/* Find an unused one (if possible) */
-	list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
-		i++;
-		if (!(encoder->possible_crtcs & (1 << i)))
-			continue;
-		if (!possible_crtc->enabled) {
-			crtc = possible_crtc;
-			break;
+	if (crtc == NULL) {
+		struct drm_crtc *pos;
+		int i = 0;
+
+		/* Find an unused one (if possible) */
+		list_for_each_entry(pos, &dev->mode_config.crtc_list, head) {
+			if (!(encoder->base.possible_crtcs & (1 << i++)))
+				continue;
+
+			if (!pos->enabled) {
+				crtc = pos;
+				break;
+			}
 		}
-		if (!supported_crtc)
-			supported_crtc = possible_crtc;
 	}
 
 	/*
 	 * If we didn't find an unused CRTC, don't use any.
 	 */
-	if (!crtc) {
-		return NULL;
-	}
+	if (crtc == NULL)
+		return false;
 
-	encoder->crtc = crtc;
-	connector->encoder = encoder;
-	intel_encoder->load_detect_temp = true;
+	/* Reuse the existing crtc->fb unless absent */
+	old_fb = crtc->fb;
+	if (old_fb == NULL || !crtc->enabled) {
+		struct drm_i915_private *dev_priv = dev->dev_private;
+		struct intel_fbdev *ifbdev = dev_priv->fbdev;
 
-	intel_crtc = to_intel_crtc(crtc);
-	*dpms_mode = intel_crtc->dpms_mode;
+		fb = &ifbdev->ifb.base;
+		x = y = 0;
 
-	if (!crtc->enabled) {
 		if (!mode)
 			mode = &load_detect_mode;
-		drm_crtc_helper_set_mode(crtc, mode, 0, 0, crtc->fb);
 	} else {
-		if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
-			crtc_funcs = crtc->helper_private;
-			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
-		}
+		fb = crtc->fb;
+		x = crtc->x;
+		y = crtc->y;
+		mode = &crtc->mode;
+	}
+
+	/* We may be called during ifbdev init, before the fb is bound */
+	if (fb == NULL || to_intel_framebuffer(fb)->obj == NULL)
+		return false;
+
+	/* Save the current configuration to be restored after load-detect */
+	old->connector = connector;
+	old->encoder = connector->encoder;
+	old->crtc = connector->encoder ? connector->encoder->crtc : NULL;
+	if (old->crtc) {
+		struct intel_crtc *intel_crtc;
 
-		/* Add this connector to the crtc */
-		encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->mode);
-		encoder_funcs->commit(encoder);
+		intel_crtc = to_intel_crtc(connector->encoder->crtc);
+		old->crtc = &intel_crtc->base;
+		old->dpms_mode = intel_crtc->dpms_mode;
+		old->fb = intel_crtc->base.fb;
+		old->mode = intel_crtc->base.mode;
+		old->x = intel_crtc->base.x;
+		old->y = intel_crtc->base.y;
 	}
+
+	/* Set the new connector->encoder->crtc->fb configuration */
+	crtc->fb = fb;
+	encoder->base.crtc = crtc;
+	connector->encoder = &encoder->base;
+
+	if (!drm_crtc_helper_set_mode(crtc, mode, x, y, old_fb)) {
+		DRM_DEBUG_KMS("failed to set mode for load-detect\n");
+
+		old->connector->encoder = old->encoder;
+		old->encoder->crtc = old->crtc;
+		old->crtc->fb = old->fb;
+
+		crtc->fb = old_fb;
+
+		drm_helper_disable_unused_functions(dev);
+		return false;
+	}
+	connector->dpms = DRM_MODE_DPMS_ON;
+
 	/* let the connector get through one full cycle before testing */
-	intel_wait_for_vblank(dev, intel_crtc->pipe);
+	intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe);
 
-	return crtc;
+	return true;
 }
 
-void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
-				    struct drm_connector *connector, int dpms_mode)
+void intel_release_load_detect_pipe(struct intel_load_detect_pipe *old)
 {
-	struct drm_encoder *encoder = &intel_encoder->base;
-	struct drm_device *dev = encoder->dev;
-	struct drm_crtc *crtc = encoder->crtc;
-	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+	struct drm_crtc *crtc = old->crtc;
+
+	old->connector->encoder = old->encoder;
+	if (old->encoder != NULL)
+		old->encoder->crtc = crtc;
+	if (crtc != NULL) {
+		struct drm_framebuffer *fb;
+
+		fb = crtc->fb;
+		crtc->fb = old->fb;
 
-	if (intel_encoder->load_detect_temp) {
-		encoder->crtc = NULL;
-		connector->encoder = NULL;
-		intel_encoder->load_detect_temp = false;
 		crtc->enabled = drm_helper_crtc_in_use(crtc);
-		drm_helper_disable_unused_functions(dev);
+		if (crtc->enabled) {
+			if (!drm_crtc_helper_set_mode(crtc, &old->mode,
+						      old->x, old->y, fb))
+				DRM_ERROR("failed to restore mode after load-detect\n");
+
+			old->connector = DRM_MODE_DPMS_ON;
+			if (old->dpms_mode != DRM_MODE_DPMS_ON)
+				drm_helper_connector_dpms(old->connector,
+							  old->dpms_mode);
+		}
 	}
 
-	/* Switch crtc and encoder back off if necessary */
-	if (crtc->enabled && dpms_mode != DRM_MODE_DPMS_ON) {
-		if (encoder->crtc == crtc)
-			encoder_funcs->dpms(encoder, dpms_mode);
-		crtc_funcs->dpms(crtc, dpms_mode);
-	}
+	drm_helper_disable_unused_functions(crtc->dev);
 }
 
 /* Returns the clock of the currently programmed mode of the given pipe. */
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index aeb1b98..64cfc88 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -140,7 +140,6 @@ struct intel_fbdev {
 struct intel_encoder {
 	struct drm_encoder base;
 	int type;
-	bool load_detect_temp;
 	bool needs_tv_clock;
 	void (*hot_plug)(struct intel_encoder *);
 	int crtc_mask;
@@ -298,13 +297,23 @@ static inline void intel_wait_for_crtc_vblank_safe(struct drm_crtc *crtc)
 		msleep(50);
 }
 extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe);
-extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
-						   struct drm_connector *connector,
-						   struct drm_display_mode *mode,
-						   int *dpms_mode);
-extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
-					   struct drm_connector *connector,
-					   int dpms_mode);
+
+struct intel_load_detect_pipe {
+	struct drm_connector *connector;
+	struct drm_encoder *encoder;
+	struct drm_crtc *crtc;
+
+	int dpms_mode;
+	struct drm_framebuffer *fb;
+	struct drm_display_mode mode;
+	int x, y;
+};
+
+extern bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
+				       struct drm_connector *connector,
+				       struct drm_display_mode *mode,
+				       struct intel_load_detect_pipe *tmp);
+extern void intel_release_load_detect_pipe(struct intel_load_detect_pipe *tmp);
 
 extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB);
 extern int intel_sdvo_supports_hotplug(struct drm_connector *connector);
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 3047a66..1352acf 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1370,15 +1370,12 @@ intel_tv_detect(struct drm_connector *connector, bool force)
 	if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) {
 		type = intel_tv_detect_type(intel_tv, connector);
 	} else if (force) {
-		struct drm_crtc *crtc;
-		int dpms_mode;
+		struct intel_load_detect_pipe tmp;
 
-		crtc = intel_get_load_detect_pipe(&intel_tv->base, connector,
-						  &mode, &dpms_mode);
-		if (crtc) {
+		if (intel_get_load_detect_pipe(&intel_tv->base, connector,
+					       &mode, &tmp)) {
 			type = intel_tv_detect_type(intel_tv, connector);
-			intel_release_load_detect_pipe(&intel_tv->base, connector,
-						       dpms_mode);
+			intel_release_load_detect_pipe(&tmp);
 		} else
 			return connector_status_unknown;
 	} else
-- 
1.7.4.1




More information about the Intel-gfx mailing list