diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_fbdev.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_fbdev.c | 144 |
1 files changed, 135 insertions, 9 deletions
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 6b5beed28d70..2b1d42dbfe13 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c | |||
@@ -128,6 +128,7 @@ static int intelfb_create(struct drm_fb_helper *helper, | |||
128 | struct drm_framebuffer *fb; | 128 | struct drm_framebuffer *fb; |
129 | struct drm_i915_gem_object *obj; | 129 | struct drm_i915_gem_object *obj; |
130 | int size, ret; | 130 | int size, ret; |
131 | bool prealloc = false; | ||
131 | 132 | ||
132 | mutex_lock(&dev->struct_mutex); | 133 | mutex_lock(&dev->struct_mutex); |
133 | 134 | ||
@@ -139,6 +140,7 @@ static int intelfb_create(struct drm_fb_helper *helper, | |||
139 | intel_fb = ifbdev->fb; | 140 | intel_fb = ifbdev->fb; |
140 | } else { | 141 | } else { |
141 | DRM_DEBUG_KMS("re-using BIOS fb\n"); | 142 | DRM_DEBUG_KMS("re-using BIOS fb\n"); |
143 | prealloc = true; | ||
142 | sizes->fb_width = intel_fb->base.width; | 144 | sizes->fb_width = intel_fb->base.width; |
143 | sizes->fb_height = intel_fb->base.height; | 145 | sizes->fb_height = intel_fb->base.height; |
144 | } | 146 | } |
@@ -200,7 +202,7 @@ static int intelfb_create(struct drm_fb_helper *helper, | |||
200 | * If the object is stolen however, it will be full of whatever | 202 | * If the object is stolen however, it will be full of whatever |
201 | * garbage was left in there. | 203 | * garbage was left in there. |
202 | */ | 204 | */ |
203 | if (ifbdev->fb->obj->stolen) | 205 | if (ifbdev->fb->obj->stolen && !prealloc) |
204 | memset_io(info->screen_base, 0, info->screen_size); | 206 | memset_io(info->screen_base, 0, info->screen_size); |
205 | 207 | ||
206 | /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ | 208 | /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ |
@@ -454,27 +456,149 @@ static void intel_fbdev_destroy(struct drm_device *dev, | |||
454 | drm_framebuffer_remove(&ifbdev->fb->base); | 456 | drm_framebuffer_remove(&ifbdev->fb->base); |
455 | } | 457 | } |
456 | 458 | ||
459 | /* | ||
460 | * Build an intel_fbdev struct using a BIOS allocated framebuffer, if possible. | ||
461 | * The core display code will have read out the current plane configuration, | ||
462 | * so we use that to figure out if there's an object for us to use as the | ||
463 | * fb, and if so, we re-use it for the fbdev configuration. | ||
464 | * | ||
465 | * Note we only support a single fb shared across pipes for boot (mostly for | ||
466 | * fbcon), so we just find the biggest and use that. | ||
467 | */ | ||
468 | static bool intel_fbdev_init_bios(struct drm_device *dev, | ||
469 | struct intel_fbdev *ifbdev) | ||
470 | { | ||
471 | struct intel_framebuffer *fb = NULL; | ||
472 | struct drm_crtc *crtc; | ||
473 | struct intel_crtc *intel_crtc; | ||
474 | struct intel_plane_config *plane_config = NULL; | ||
475 | unsigned int max_size = 0; | ||
476 | |||
477 | if (!i915.fastboot) | ||
478 | return false; | ||
479 | |||
480 | /* Find the largest fb */ | ||
481 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
482 | intel_crtc = to_intel_crtc(crtc); | ||
483 | |||
484 | if (!intel_crtc->active || !crtc->primary->fb) { | ||
485 | DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n", | ||
486 | pipe_name(intel_crtc->pipe)); | ||
487 | continue; | ||
488 | } | ||
489 | |||
490 | if (intel_crtc->plane_config.size > max_size) { | ||
491 | DRM_DEBUG_KMS("found possible fb from plane %c\n", | ||
492 | pipe_name(intel_crtc->pipe)); | ||
493 | plane_config = &intel_crtc->plane_config; | ||
494 | fb = to_intel_framebuffer(crtc->primary->fb); | ||
495 | max_size = plane_config->size; | ||
496 | } | ||
497 | } | ||
498 | |||
499 | if (!fb) { | ||
500 | DRM_DEBUG_KMS("no active fbs found, not using BIOS config\n"); | ||
501 | goto out; | ||
502 | } | ||
503 | |||
504 | /* Now make sure all the pipes will fit into it */ | ||
505 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
506 | unsigned int cur_size; | ||
507 | |||
508 | intel_crtc = to_intel_crtc(crtc); | ||
509 | |||
510 | if (!intel_crtc->active) { | ||
511 | DRM_DEBUG_KMS("pipe %c not active, skipping\n", | ||
512 | pipe_name(intel_crtc->pipe)); | ||
513 | continue; | ||
514 | } | ||
515 | |||
516 | DRM_DEBUG_KMS("checking plane %c for BIOS fb\n", | ||
517 | pipe_name(intel_crtc->pipe)); | ||
518 | |||
519 | /* | ||
520 | * See if the plane fb we found above will fit on this | ||
521 | * pipe. Note we need to use the selected fb's bpp rather | ||
522 | * than the current pipe's, since they could be different. | ||
523 | */ | ||
524 | cur_size = intel_crtc->config.adjusted_mode.crtc_hdisplay * | ||
525 | intel_crtc->config.adjusted_mode.crtc_vdisplay; | ||
526 | DRM_DEBUG_KMS("pipe %c area: %d\n", pipe_name(intel_crtc->pipe), | ||
527 | cur_size); | ||
528 | cur_size *= fb->base.bits_per_pixel / 8; | ||
529 | DRM_DEBUG_KMS("total size %d (bpp %d)\n", cur_size, | ||
530 | fb->base.bits_per_pixel / 8); | ||
531 | |||
532 | if (cur_size > max_size) { | ||
533 | DRM_DEBUG_KMS("fb not big enough for plane %c (%d vs %d)\n", | ||
534 | pipe_name(intel_crtc->pipe), | ||
535 | cur_size, max_size); | ||
536 | plane_config = NULL; | ||
537 | fb = NULL; | ||
538 | break; | ||
539 | } | ||
540 | |||
541 | DRM_DEBUG_KMS("fb big enough for plane %c (%d >= %d)\n", | ||
542 | pipe_name(intel_crtc->pipe), | ||
543 | max_size, cur_size); | ||
544 | } | ||
545 | |||
546 | if (!fb) { | ||
547 | DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n"); | ||
548 | goto out; | ||
549 | } | ||
550 | |||
551 | ifbdev->preferred_bpp = fb->base.bits_per_pixel; | ||
552 | ifbdev->fb = fb; | ||
553 | |||
554 | drm_framebuffer_reference(&ifbdev->fb->base); | ||
555 | |||
556 | /* Final pass to check if any active pipes don't have fbs */ | ||
557 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
558 | intel_crtc = to_intel_crtc(crtc); | ||
559 | |||
560 | if (!intel_crtc->active) | ||
561 | continue; | ||
562 | |||
563 | WARN(!crtc->primary->fb, | ||
564 | "re-used BIOS config but lost an fb on crtc %d\n", | ||
565 | crtc->base.id); | ||
566 | } | ||
567 | |||
568 | |||
569 | DRM_DEBUG_KMS("using BIOS fb for initial console\n"); | ||
570 | return true; | ||
571 | |||
572 | out: | ||
573 | |||
574 | return false; | ||
575 | } | ||
576 | |||
457 | int intel_fbdev_init(struct drm_device *dev) | 577 | int intel_fbdev_init(struct drm_device *dev) |
458 | { | 578 | { |
459 | struct intel_fbdev *ifbdev; | 579 | struct intel_fbdev *ifbdev; |
460 | struct drm_i915_private *dev_priv = dev->dev_private; | 580 | struct drm_i915_private *dev_priv = dev->dev_private; |
461 | int ret; | 581 | int ret; |
462 | 582 | ||
463 | ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL); | 583 | if (WARN_ON(INTEL_INFO(dev)->num_pipes == 0)) |
464 | if (!ifbdev) | 584 | return -ENODEV; |
585 | |||
586 | ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL); | ||
587 | if (ifbdev == NULL) | ||
465 | return -ENOMEM; | 588 | return -ENOMEM; |
466 | 589 | ||
467 | dev_priv->fbdev = ifbdev; | ||
468 | ifbdev->helper.funcs = &intel_fb_helper_funcs; | 590 | ifbdev->helper.funcs = &intel_fb_helper_funcs; |
591 | if (!intel_fbdev_init_bios(dev, ifbdev)) | ||
592 | ifbdev->preferred_bpp = 32; | ||
469 | 593 | ||
470 | ret = drm_fb_helper_init(dev, &ifbdev->helper, | 594 | ret = drm_fb_helper_init(dev, &ifbdev->helper, |
471 | INTEL_INFO(dev)->num_pipes, | 595 | INTEL_INFO(dev)->num_pipes, 4); |
472 | 4); | ||
473 | if (ret) { | 596 | if (ret) { |
474 | kfree(ifbdev); | 597 | kfree(ifbdev); |
475 | return ret; | 598 | return ret; |
476 | } | 599 | } |
477 | 600 | ||
601 | dev_priv->fbdev = ifbdev; | ||
478 | drm_fb_helper_single_add_all_connectors(&ifbdev->helper); | 602 | drm_fb_helper_single_add_all_connectors(&ifbdev->helper); |
479 | 603 | ||
480 | return 0; | 604 | return 0; |
@@ -483,9 +607,10 @@ int intel_fbdev_init(struct drm_device *dev) | |||
483 | void intel_fbdev_initial_config(struct drm_device *dev) | 607 | void intel_fbdev_initial_config(struct drm_device *dev) |
484 | { | 608 | { |
485 | struct drm_i915_private *dev_priv = dev->dev_private; | 609 | struct drm_i915_private *dev_priv = dev->dev_private; |
610 | struct intel_fbdev *ifbdev = dev_priv->fbdev; | ||
486 | 611 | ||
487 | /* Due to peculiar init order wrt to hpd handling this is separate. */ | 612 | /* Due to peculiar init order wrt to hpd handling this is separate. */ |
488 | drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); | 613 | drm_fb_helper_initial_config(&ifbdev->helper, ifbdev->preferred_bpp); |
489 | } | 614 | } |
490 | 615 | ||
491 | void intel_fbdev_fini(struct drm_device *dev) | 616 | void intel_fbdev_fini(struct drm_device *dev) |
@@ -523,7 +648,8 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state) | |||
523 | void intel_fbdev_output_poll_changed(struct drm_device *dev) | 648 | void intel_fbdev_output_poll_changed(struct drm_device *dev) |
524 | { | 649 | { |
525 | struct drm_i915_private *dev_priv = dev->dev_private; | 650 | struct drm_i915_private *dev_priv = dev->dev_private; |
526 | drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); | 651 | if (dev_priv->fbdev) |
652 | drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); | ||
527 | } | 653 | } |
528 | 654 | ||
529 | void intel_fbdev_restore_mode(struct drm_device *dev) | 655 | void intel_fbdev_restore_mode(struct drm_device *dev) |
@@ -531,7 +657,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev) | |||
531 | int ret; | 657 | int ret; |
532 | struct drm_i915_private *dev_priv = dev->dev_private; | 658 | struct drm_i915_private *dev_priv = dev->dev_private; |
533 | 659 | ||
534 | if (INTEL_INFO(dev)->num_pipes == 0) | 660 | if (!dev_priv->fbdev) |
535 | return; | 661 | return; |
536 | 662 | ||
537 | drm_modeset_lock_all(dev); | 663 | drm_modeset_lock_all(dev); |