From: Jesse Barnes <jbarnes at virtuousgeek.org> If the BIOS hands us a multi-pipe configuration, try to allocate an fb large enough for the largest screen and preserve the mode. Still need to fix the other initial_config function in case one or more of the pipes was rejected due to an incompatibility with the others. Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org> --- drivers/gpu/drm/i915/intel_fb.c | 190 ++++++++++++++++++++++++++------------- 1 file changed, 129 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 74bd22e..e69567d 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -288,23 +288,49 @@ static void intel_fbdev_destroy(struct drm_device *dev, } } +/* + * Try to read the BIOS display configuration and use it for the initial + * fb configuration. + * + * The BIOS or boot loader will generally create an initial display + * configuration for us that includes some set of active pipes and displays. + * This routine tries to figure out which pipes are active, what resolutions + * are being displayed, and then allocates a framebuffer and initial fb + * config based on that data. + * + * If the BIOS or boot loader leaves the display in VGA mode, there's not + * much we can do; switching out of that mode involves allocating a new, + * high res buffer, and also recalculating bandwidth requirements for the + * new bpp configuration. + * + * However, if we're loaded into an existing, high res mode, we should + * be able to allocate a buffer big enough to handle the largest active + * mode, create a mode_set for it, and pass it to the fb helper to create + * the configuration. + */ void intel_fbdev_init_bios(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_fbdev *ifbdev; - int pipe, ret; - - for_each_pipe(pipe) { - struct intel_crtc *crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); - struct drm_mode_fb_cmd2 mode_cmd; - struct drm_i915_gem_object *obj; - struct fb_info *info; - u32 val, bpp, depth; - u32 size, offset; - - val = I915_READ(DSPCNTR(crtc->plane)); - if ((val & DISPLAY_PLANE_ENABLE) == 0) + struct drm_crtc *crtc; + struct drm_mode_fb_cmd2 mode_cmd; + struct drm_i915_gem_object *obj; + struct fb_info *info; + u32 obj_size = 0, obj_offset = 0, last_bpp = 0, last_depth = 0; + int ret; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe, plane = intel_crtc->plane; + u32 val, bpp, depth, offset; + int pitch, width, height, size; + + if (!intel_crtc->active) { + DRM_DEBUG_KMS("pipe %d not active, skipping\n", pipe); continue; + } + + val = I915_READ(DSPCNTR(plane)); if (INTEL_INFO(dev)->gen >= 4) { if (val & DISPPLANE_TILED) { @@ -329,77 +355,119 @@ void intel_fbdev_init_bios(struct drm_device *dev) break; } + if (!last_bpp) + last_bpp = bpp; + if (!last_depth) + last_depth = depth; + + if (bpp != last_bpp || depth != last_depth) { + DRM_DEBUG_KMS("pipe %d has depth/bpp mismatch: " + "(%d/%d vs %d/%d), skipping\n", + pipe, bpp, depth, last_bpp, last_depth); + continue; + } + + last_bpp = bpp; + last_depth = depth; + if (INTEL_INFO(dev)->gen >= 4) - offset = I915_READ(DSPSURF(crtc->plane)); + offset = I915_READ(DSPSURF(plane)); else - offset = I915_READ(DSPADDR(crtc->plane)); - mode_cmd.pitches[0] = I915_READ(DSPSTRIDE(crtc->plane)); + offset = I915_READ(DSPADDR(plane)); - val = I915_READ(PIPESRC(crtc->pipe)); - mode_cmd.width = ((val >> 16) & 0xfff) + 1; - mode_cmd.height = ((val >> 0) & 0xfff) + 1; - mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth); + pitch = I915_READ(DSPSTRIDE(plane)); + + val = I915_READ(PIPESRC(pipe)); + width = ((val >> 16) & 0xfff) + 1; + height = ((val >> 0) & 0xfff) + 1; DRM_DEBUG_KMS("Found active pipe [%d/%d]: size=%dx%d@%d, offset=%x\n", - crtc->pipe, crtc->plane, - mode_cmd.width, mode_cmd.height, depth, offset); + pipe, plane, width, height, depth, offset); - size = mode_cmd.pitches[0] * mode_cmd.height; + size = pitch * height; size = ALIGN(size, PAGE_SIZE); - ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL); - if (!ifbdev) - continue; + /* In case we're handed multiple fbs, inherit the largest one */ + if (!obj_size || size > obj_size) { + obj_size = size; + obj_offset = offset; - ifbdev->helper.funcs = &intel_fb_helper_funcs; - ret = drm_fb_helper_init(dev, &ifbdev->helper, - dev_priv->num_pipe, - INTELFB_CONN_LIMIT); - if (ret) { - kfree(ifbdev); - continue; + mode_cmd.pitches[0] = pitch; + mode_cmd.width = width; + mode_cmd.height = height; + mode_cmd.pixel_format = + drm_mode_legacy_fb_format(bpp, depth); } + } - /* assume a 1:1 linear mapping between stolen and GTT */ - obj = i915_gem_object_create_stolen_for_preallocated - (dev, offset, offset, size); - if (obj == NULL) { - DRM_DEBUG_KMS("failed to create stolen fb\n"); - kfree(ifbdev); - continue; - } + if (!obj_size) { + DRM_DEBUG_KMS("no active pipes found, not using BIOS config\n"); + goto out_fail; + } - ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj); - if (ret) { - drm_gem_object_unreference(&obj->base); - kfree(ifbdev); - continue; - } + ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL); + if (!ifbdev) { + DRM_DEBUG_KMS("failed to alloc intel fbdev\n"); + goto out_fail; + } + + ifbdev->helper.funcs = &intel_fb_helper_funcs; + ret = drm_fb_helper_init(dev, &ifbdev->helper, + dev_priv->num_pipe, + INTELFB_CONN_LIMIT); + if (ret) { + DRM_DEBUG_KMS("drm fb init failed\n"); + goto out_free_ifbdev; + } + + /* assume a 1:1 linear mapping between stolen and GTT */ + obj = i915_gem_object_create_stolen_for_preallocated(dev, obj_offset, + obj_offset, + obj_size); + if (obj == NULL) { + DRM_DEBUG_KMS("failed to create stolen fb\n"); + goto out_free_ifbdev; + } - info = intelfb_create_info(ifbdev); - if (info == NULL) { - drm_gem_object_unreference(&obj->base); - kfree(ifbdev); + ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj); + if (ret) { + DRM_DEBUG_KMS("intel fb init failed\n"); + goto out_unref_obj; + } + + info = intelfb_create_info(ifbdev); + if (info == NULL) { + DRM_DEBUG_KMS("intelfb fb creation failed\n"); + goto out_unref_obj; + } + + /* FIXME: omit any crtcs left out in the above loop */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + if (!intel_crtc->active) continue; - } - crtc->base.fb = &ifbdev->ifb.base; + crtc->fb = &ifbdev->ifb.base; obj->pin_count++; - ifbdev->bios_fb = true; + } + ifbdev->bios_fb = true; - drm_fb_helper_single_add_all_connectors(&ifbdev->helper); - drm_fb_helper_initial_config(&ifbdev->helper, bpp); + drm_fb_helper_single_add_all_connectors(&ifbdev->helper); + drm_fb_helper_initial_config(&ifbdev->helper, last_bpp); - vga_switcheroo_client_fb_set(dev->pdev, info); - dev_priv->fbdev = ifbdev; + vga_switcheroo_client_fb_set(dev->pdev, info); + dev_priv->fbdev = ifbdev; - DRM_DEBUG_KMS("using BIOS config for initial console\n"); + DRM_DEBUG_KMS("using BIOS config for initial console\n"); - return; - } + return; +out_unref_obj: + drm_gem_object_unreference(&obj->base); +out_free_ifbdev: + kfree(ifbdev); +out_fail: /* otherwise disable all the possible crtcs before KMS */ - DRM_DEBUG_KMS("No active planes found\n"); drm_helper_disable_unused_functions(dev); } -- 1.7.10