diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_fbdev.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_fbdev.c | 83 |
1 files changed, 59 insertions, 24 deletions
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index f475414671d8..9b584f3fbb99 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c | |||
@@ -24,8 +24,10 @@ | |||
24 | * David Airlie | 24 | * David Airlie |
25 | */ | 25 | */ |
26 | 26 | ||
27 | #include <linux/async.h> | ||
27 | #include <linux/module.h> | 28 | #include <linux/module.h> |
28 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
30 | #include <linux/console.h> | ||
29 | #include <linux/errno.h> | 31 | #include <linux/errno.h> |
30 | #include <linux/string.h> | 32 | #include <linux/string.h> |
31 | #include <linux/mm.h> | 33 | #include <linux/mm.h> |
@@ -331,24 +333,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, | |||
331 | int num_connectors_enabled = 0; | 333 | int num_connectors_enabled = 0; |
332 | int num_connectors_detected = 0; | 334 | int num_connectors_detected = 0; |
333 | 335 | ||
334 | /* | ||
335 | * If the user specified any force options, just bail here | ||
336 | * and use that config. | ||
337 | */ | ||
338 | for (i = 0; i < fb_helper->connector_count; i++) { | ||
339 | struct drm_fb_helper_connector *fb_conn; | ||
340 | struct drm_connector *connector; | ||
341 | |||
342 | fb_conn = fb_helper->connector_info[i]; | ||
343 | connector = fb_conn->connector; | ||
344 | |||
345 | if (!enabled[i]) | ||
346 | continue; | ||
347 | |||
348 | if (connector->force != DRM_FORCE_UNSPECIFIED) | ||
349 | return false; | ||
350 | } | ||
351 | |||
352 | save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), | 336 | save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), |
353 | GFP_KERNEL); | 337 | GFP_KERNEL); |
354 | if (!save_enabled) | 338 | if (!save_enabled) |
@@ -374,8 +358,18 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, | |||
374 | continue; | 358 | continue; |
375 | } | 359 | } |
376 | 360 | ||
361 | if (connector->force == DRM_FORCE_OFF) { | ||
362 | DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", | ||
363 | connector->name); | ||
364 | enabled[i] = false; | ||
365 | continue; | ||
366 | } | ||
367 | |||
377 | encoder = connector->encoder; | 368 | encoder = connector->encoder; |
378 | if (!encoder || WARN_ON(!encoder->crtc)) { | 369 | if (!encoder || WARN_ON(!encoder->crtc)) { |
370 | if (connector->force > DRM_FORCE_OFF) | ||
371 | goto bail; | ||
372 | |||
379 | DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", | 373 | DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", |
380 | connector->name); | 374 | connector->name); |
381 | enabled[i] = false; | 375 | enabled[i] = false; |
@@ -394,8 +388,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, | |||
394 | for (j = 0; j < fb_helper->connector_count; j++) { | 388 | for (j = 0; j < fb_helper->connector_count; j++) { |
395 | if (crtcs[j] == new_crtc) { | 389 | if (crtcs[j] == new_crtc) { |
396 | DRM_DEBUG_KMS("fallback: cloned configuration\n"); | 390 | DRM_DEBUG_KMS("fallback: cloned configuration\n"); |
397 | fallback = true; | 391 | goto bail; |
398 | goto out; | ||
399 | } | 392 | } |
400 | } | 393 | } |
401 | 394 | ||
@@ -466,8 +459,8 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, | |||
466 | fallback = true; | 459 | fallback = true; |
467 | } | 460 | } |
468 | 461 | ||
469 | out: | ||
470 | if (fallback) { | 462 | if (fallback) { |
463 | bail: | ||
471 | DRM_DEBUG_KMS("Not using firmware configuration\n"); | 464 | DRM_DEBUG_KMS("Not using firmware configuration\n"); |
472 | memcpy(enabled, save_enabled, dev->mode_config.num_connector); | 465 | memcpy(enabled, save_enabled, dev->mode_config.num_connector); |
473 | kfree(save_enabled); | 466 | kfree(save_enabled); |
@@ -636,6 +629,15 @@ out: | |||
636 | return false; | 629 | return false; |
637 | } | 630 | } |
638 | 631 | ||
632 | static void intel_fbdev_suspend_worker(struct work_struct *work) | ||
633 | { | ||
634 | intel_fbdev_set_suspend(container_of(work, | ||
635 | struct drm_i915_private, | ||
636 | fbdev_suspend_work)->dev, | ||
637 | FBINFO_STATE_RUNNING, | ||
638 | true); | ||
639 | } | ||
640 | |||
639 | int intel_fbdev_init(struct drm_device *dev) | 641 | int intel_fbdev_init(struct drm_device *dev) |
640 | { | 642 | { |
641 | struct intel_fbdev *ifbdev; | 643 | struct intel_fbdev *ifbdev; |
@@ -662,14 +664,16 @@ int intel_fbdev_init(struct drm_device *dev) | |||
662 | } | 664 | } |
663 | 665 | ||
664 | dev_priv->fbdev = ifbdev; | 666 | dev_priv->fbdev = ifbdev; |
667 | INIT_WORK(&dev_priv->fbdev_suspend_work, intel_fbdev_suspend_worker); | ||
668 | |||
665 | drm_fb_helper_single_add_all_connectors(&ifbdev->helper); | 669 | drm_fb_helper_single_add_all_connectors(&ifbdev->helper); |
666 | 670 | ||
667 | return 0; | 671 | return 0; |
668 | } | 672 | } |
669 | 673 | ||
670 | void intel_fbdev_initial_config(struct drm_device *dev) | 674 | void intel_fbdev_initial_config(void *data, async_cookie_t cookie) |
671 | { | 675 | { |
672 | struct drm_i915_private *dev_priv = dev->dev_private; | 676 | struct drm_i915_private *dev_priv = data; |
673 | struct intel_fbdev *ifbdev = dev_priv->fbdev; | 677 | struct intel_fbdev *ifbdev = dev_priv->fbdev; |
674 | 678 | ||
675 | /* Due to peculiar init order wrt to hpd handling this is separate. */ | 679 | /* Due to peculiar init order wrt to hpd handling this is separate. */ |
@@ -682,12 +686,15 @@ void intel_fbdev_fini(struct drm_device *dev) | |||
682 | if (!dev_priv->fbdev) | 686 | if (!dev_priv->fbdev) |
683 | return; | 687 | return; |
684 | 688 | ||
689 | flush_work(&dev_priv->fbdev_suspend_work); | ||
690 | |||
691 | async_synchronize_full(); | ||
685 | intel_fbdev_destroy(dev, dev_priv->fbdev); | 692 | intel_fbdev_destroy(dev, dev_priv->fbdev); |
686 | kfree(dev_priv->fbdev); | 693 | kfree(dev_priv->fbdev); |
687 | dev_priv->fbdev = NULL; | 694 | dev_priv->fbdev = NULL; |
688 | } | 695 | } |
689 | 696 | ||
690 | void intel_fbdev_set_suspend(struct drm_device *dev, int state) | 697 | void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous) |
691 | { | 698 | { |
692 | struct drm_i915_private *dev_priv = dev->dev_private; | 699 | struct drm_i915_private *dev_priv = dev->dev_private; |
693 | struct intel_fbdev *ifbdev = dev_priv->fbdev; | 700 | struct intel_fbdev *ifbdev = dev_priv->fbdev; |
@@ -698,6 +705,33 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state) | |||
698 | 705 | ||
699 | info = ifbdev->helper.fbdev; | 706 | info = ifbdev->helper.fbdev; |
700 | 707 | ||
708 | if (synchronous) { | ||
709 | /* Flush any pending work to turn the console on, and then | ||
710 | * wait to turn it off. It must be synchronous as we are | ||
711 | * about to suspend or unload the driver. | ||
712 | * | ||
713 | * Note that from within the work-handler, we cannot flush | ||
714 | * ourselves, so only flush outstanding work upon suspend! | ||
715 | */ | ||
716 | if (state != FBINFO_STATE_RUNNING) | ||
717 | flush_work(&dev_priv->fbdev_suspend_work); | ||
718 | console_lock(); | ||
719 | } else { | ||
720 | /* | ||
721 | * The console lock can be pretty contented on resume due | ||
722 | * to all the printk activity. Try to keep it out of the hot | ||
723 | * path of resume if possible. | ||
724 | */ | ||
725 | WARN_ON(state != FBINFO_STATE_RUNNING); | ||
726 | if (!console_trylock()) { | ||
727 | /* Don't block our own workqueue as this can | ||
728 | * be run in parallel with other i915.ko tasks. | ||
729 | */ | ||
730 | schedule_work(&dev_priv->fbdev_suspend_work); | ||
731 | return; | ||
732 | } | ||
733 | } | ||
734 | |||
701 | /* On resume from hibernation: If the object is shmemfs backed, it has | 735 | /* On resume from hibernation: If the object is shmemfs backed, it has |
702 | * been restored from swap. If the object is stolen however, it will be | 736 | * been restored from swap. If the object is stolen however, it will be |
703 | * full of whatever garbage was left in there. | 737 | * full of whatever garbage was left in there. |
@@ -706,6 +740,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state) | |||
706 | memset_io(info->screen_base, 0, info->screen_size); | 740 | memset_io(info->screen_base, 0, info->screen_size); |
707 | 741 | ||
708 | fb_set_suspend(info, state); | 742 | fb_set_suspend(info, state); |
743 | console_unlock(); | ||
709 | } | 744 | } |
710 | 745 | ||
711 | void intel_fbdev_output_poll_changed(struct drm_device *dev) | 746 | void intel_fbdev_output_poll_changed(struct drm_device *dev) |