aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2011-04-19 03:36:26 -0400
committerKeith Packard <keithp@keithp.com>2011-05-10 16:19:18 -0400
commitd2dff872ac44540622ef77a2b7d6ce4a1b145931 (patch)
treec66cb740139e9793b39d6d0731eebc6b3a53b85f
parent0622a53c60fbf48d3b85efc4995f4c152c254cf4 (diff)
drm/i915: Attach a fb to the load-detect pipe
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. As we need to perform load detection before even the first object is allocated for the fbdev, there is no pre-existing object large enough for us to borrow to use as the framebuffer. So we need to create one and cleanup afterwards. At other times, the current fbcon may be large enough for us to borrow it for duration of load detection. Found by assert_fb_bound_for_plane(). Reported-by: Knut Petersen <Knut_Petersen@t-online.de> References: https://bugs.freedesktop.org/show_bug.cgi?id=36246 Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Keith Packard <keithp@keithp.com>
-rw-r--r--drivers/gpu/drm/i915/intel_display.c144
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h1
2 files changed, 128 insertions, 17 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index e1fcd89562df..a47e5ee5dcaa 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -5481,6 +5481,92 @@ static struct drm_display_mode load_detect_mode = {
5481 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 5481 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
5482}; 5482};
5483 5483
5484static struct drm_framebuffer *
5485intel_framebuffer_create(struct drm_device *dev,
5486 struct drm_mode_fb_cmd *mode_cmd,
5487 struct drm_i915_gem_object *obj)
5488{
5489 struct intel_framebuffer *intel_fb;
5490 int ret;
5491
5492 intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
5493 if (!intel_fb) {
5494 drm_gem_object_unreference_unlocked(&obj->base);
5495 return ERR_PTR(-ENOMEM);
5496 }
5497
5498 ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
5499 if (ret) {
5500 drm_gem_object_unreference_unlocked(&obj->base);
5501 kfree(intel_fb);
5502 return ERR_PTR(ret);
5503 }
5504
5505 return &intel_fb->base;
5506}
5507
5508static u32
5509intel_framebuffer_pitch_for_width(int width, int bpp)
5510{
5511 u32 pitch = DIV_ROUND_UP(width * bpp, 8);
5512 return ALIGN(pitch, 64);
5513}
5514
5515static u32
5516intel_framebuffer_size_for_mode(struct drm_display_mode *mode, int bpp)
5517{
5518 u32 pitch = intel_framebuffer_pitch_for_width(mode->hdisplay, bpp);
5519 return ALIGN(pitch * mode->vdisplay, PAGE_SIZE);
5520}
5521
5522static struct drm_framebuffer *
5523intel_framebuffer_create_for_mode(struct drm_device *dev,
5524 struct drm_display_mode *mode,
5525 int depth, int bpp)
5526{
5527 struct drm_i915_gem_object *obj;
5528 struct drm_mode_fb_cmd mode_cmd;
5529
5530 obj = i915_gem_alloc_object(dev,
5531 intel_framebuffer_size_for_mode(mode, bpp));
5532 if (obj == NULL)
5533 return ERR_PTR(-ENOMEM);
5534
5535 mode_cmd.width = mode->hdisplay;
5536 mode_cmd.height = mode->vdisplay;
5537 mode_cmd.depth = depth;
5538 mode_cmd.bpp = bpp;
5539 mode_cmd.pitch = intel_framebuffer_pitch_for_width(mode_cmd.width, bpp);
5540
5541 return intel_framebuffer_create(dev, &mode_cmd, obj);
5542}
5543
5544static struct drm_framebuffer *
5545mode_fits_in_fbdev(struct drm_device *dev,
5546 struct drm_display_mode *mode)
5547{
5548 struct drm_i915_private *dev_priv = dev->dev_private;
5549 struct drm_i915_gem_object *obj;
5550 struct drm_framebuffer *fb;
5551
5552 if (dev_priv->fbdev == NULL)
5553 return NULL;
5554
5555 obj = dev_priv->fbdev->ifb.obj;
5556 if (obj == NULL)
5557 return NULL;
5558
5559 fb = &dev_priv->fbdev->ifb.base;
5560 if (fb->pitch < intel_framebuffer_pitch_for_width(mode->hdisplay,
5561 fb->bits_per_pixel))
5562 return NULL;
5563
5564 if (obj->base.size < mode->vdisplay * fb->pitch)
5565 return NULL;
5566
5567 return fb;
5568}
5569
5484bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, 5570bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
5485 struct drm_connector *connector, 5571 struct drm_connector *connector,
5486 struct drm_display_mode *mode, 5572 struct drm_display_mode *mode,
@@ -5491,8 +5577,13 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
5491 struct drm_encoder *encoder = &intel_encoder->base; 5577 struct drm_encoder *encoder = &intel_encoder->base;
5492 struct drm_crtc *crtc = NULL; 5578 struct drm_crtc *crtc = NULL;
5493 struct drm_device *dev = encoder->dev; 5579 struct drm_device *dev = encoder->dev;
5580 struct drm_framebuffer *old_fb;
5494 int i = -1; 5581 int i = -1;
5495 5582
5583 DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
5584 connector->base.id, drm_get_connector_name(connector),
5585 encoder->base.id, drm_get_encoder_name(encoder));
5586
5496 /* 5587 /*
5497 * Algorithm gets a little messy: 5588 * Algorithm gets a little messy:
5498 * 5589 *
@@ -5551,12 +5642,38 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
5551 intel_crtc = to_intel_crtc(crtc); 5642 intel_crtc = to_intel_crtc(crtc);
5552 old->dpms_mode = intel_crtc->dpms_mode; 5643 old->dpms_mode = intel_crtc->dpms_mode;
5553 old->load_detect_temp = true; 5644 old->load_detect_temp = true;
5645 old->release_fb = NULL;
5554 5646
5555 if (!mode) 5647 if (!mode)
5556 mode = &load_detect_mode; 5648 mode = &load_detect_mode;
5557 5649
5558 if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, crtc->fb)) { 5650 old_fb = crtc->fb;
5651
5652 /* We need a framebuffer large enough to accommodate all accesses
5653 * that the plane may generate whilst we perform load detection.
5654 * We can not rely on the fbcon either being present (we get called
5655 * during its initialisation to detect all boot displays, or it may
5656 * not even exist) or that it is large enough to satisfy the
5657 * requested mode.
5658 */
5659 crtc->fb = mode_fits_in_fbdev(dev, mode);
5660 if (crtc->fb == NULL) {
5661 DRM_DEBUG_KMS("creating tmp fb for load-detection\n");
5662 crtc->fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32);
5663 old->release_fb = crtc->fb;
5664 } else
5665 DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
5666 if (IS_ERR(crtc->fb)) {
5667 DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
5668 crtc->fb = old_fb;
5669 return false;
5670 }
5671
5672 if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) {
5559 DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); 5673 DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");
5674 if (old->release_fb)
5675 old->release_fb->funcs->destroy(old->release_fb);
5676 crtc->fb = old_fb;
5560 return false; 5677 return false;
5561 } 5678 }
5562 5679
@@ -5576,9 +5693,17 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
5576 struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; 5693 struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
5577 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 5694 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
5578 5695
5696 DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
5697 connector->base.id, drm_get_connector_name(connector),
5698 encoder->base.id, drm_get_encoder_name(encoder));
5699
5579 if (old->load_detect_temp) { 5700 if (old->load_detect_temp) {
5580 connector->encoder = NULL; 5701 connector->encoder = NULL;
5581 drm_helper_disable_unused_functions(dev); 5702 drm_helper_disable_unused_functions(dev);
5703
5704 if (old->release_fb)
5705 old->release_fb->funcs->destroy(old->release_fb);
5706
5582 return; 5707 return;
5583 } 5708 }
5584 5709
@@ -6566,27 +6691,12 @@ intel_user_framebuffer_create(struct drm_device *dev,
6566 struct drm_mode_fb_cmd *mode_cmd) 6691 struct drm_mode_fb_cmd *mode_cmd)
6567{ 6692{
6568 struct drm_i915_gem_object *obj; 6693 struct drm_i915_gem_object *obj;
6569 struct intel_framebuffer *intel_fb;
6570 int ret;
6571 6694
6572 obj = to_intel_bo(drm_gem_object_lookup(dev, filp, mode_cmd->handle)); 6695 obj = to_intel_bo(drm_gem_object_lookup(dev, filp, mode_cmd->handle));
6573 if (&obj->base == NULL) 6696 if (&obj->base == NULL)
6574 return ERR_PTR(-ENOENT); 6697 return ERR_PTR(-ENOENT);
6575 6698
6576 intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); 6699 return intel_framebuffer_create(dev, mode_cmd, obj);
6577 if (!intel_fb) {
6578 drm_gem_object_unreference_unlocked(&obj->base);
6579 return ERR_PTR(-ENOMEM);
6580 }
6581
6582 ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
6583 if (ret) {
6584 drm_gem_object_unreference_unlocked(&obj->base);
6585 kfree(intel_fb);
6586 return ERR_PTR(ret);
6587 }
6588
6589 return &intel_fb->base;
6590} 6700}
6591 6701
6592static const struct drm_mode_config_funcs intel_mode_funcs = { 6702static const struct drm_mode_config_funcs intel_mode_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 3ceb3daeb61f..2e49b62da9a5 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -292,6 +292,7 @@ extern void intel_wait_for_vblank(struct drm_device *dev, int pipe);
292extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe); 292extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe);
293 293
294struct intel_load_detect_pipe { 294struct intel_load_detect_pipe {
295 struct drm_framebuffer *release_fb;
295 bool load_detect_temp; 296 bool load_detect_temp;
296 int dpms_mode; 297 int dpms_mode;
297}; 298};