1717f4a47SSascha Wildner /*
2717f4a47SSascha Wildner * Copyright © 2007 David Airlie
3717f4a47SSascha Wildner *
4717f4a47SSascha Wildner * Permission is hereby granted, free of charge, to any person obtaining a
5717f4a47SSascha Wildner * copy of this software and associated documentation files (the "Software"),
6717f4a47SSascha Wildner * to deal in the Software without restriction, including without limitation
7717f4a47SSascha Wildner * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8717f4a47SSascha Wildner * and/or sell copies of the Software, and to permit persons to whom the
9717f4a47SSascha Wildner * Software is furnished to do so, subject to the following conditions:
10717f4a47SSascha Wildner *
11717f4a47SSascha Wildner * The above copyright notice and this permission notice (including the next
12717f4a47SSascha Wildner * paragraph) shall be included in all copies or substantial portions of the
13717f4a47SSascha Wildner * Software.
14717f4a47SSascha Wildner *
15717f4a47SSascha Wildner * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16717f4a47SSascha Wildner * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17717f4a47SSascha Wildner * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18717f4a47SSascha Wildner * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19717f4a47SSascha Wildner * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20717f4a47SSascha Wildner * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21717f4a47SSascha Wildner * DEALINGS IN THE SOFTWARE.
22717f4a47SSascha Wildner *
23717f4a47SSascha Wildner * Authors:
24717f4a47SSascha Wildner * David Airlie
25717f4a47SSascha Wildner */
26717f4a47SSascha Wildner
271b13d190SFrançois Tigeot #include <linux/async.h>
28717f4a47SSascha Wildner #include <linux/module.h>
29717f4a47SSascha Wildner #include <linux/kernel.h>
308621f407SFrançois Tigeot #include <linux/console.h>
31717f4a47SSascha Wildner #include <linux/errno.h>
32717f4a47SSascha Wildner #include <linux/string.h>
33717f4a47SSascha Wildner #include <linux/mm.h>
341487f786SFrançois Tigeot #include <linux/tty.h>
351487f786SFrançois Tigeot #include <linux/sysrq.h>
36717f4a47SSascha Wildner #include <linux/delay.h>
371487f786SFrançois Tigeot #include <linux/init.h>
388621f407SFrançois Tigeot #include <linux/vga_switcheroo.h>
39717f4a47SSascha Wildner
40ba55f2f5SFrançois Tigeot #include <drm/drmP.h>
41717f4a47SSascha Wildner #include <drm/drm_crtc.h>
42717f4a47SSascha Wildner #include <drm/drm_fb_helper.h>
43717f4a47SSascha Wildner #include "intel_drv.h"
4471f41f3eSFrançois Tigeot #include "intel_frontbuffer.h"
45717f4a47SSascha Wildner #include <drm/i915_drm.h>
46717f4a47SSascha Wildner #include "i915_drv.h"
47717f4a47SSascha Wildner
intel_fbdev_invalidate(struct intel_fbdev * ifbdev)48a85cb24fSFrançois Tigeot static void intel_fbdev_invalidate(struct intel_fbdev *ifbdev)
49a85cb24fSFrançois Tigeot {
50a85cb24fSFrançois Tigeot struct drm_i915_gem_object *obj = ifbdev->fb->obj;
51a85cb24fSFrançois Tigeot unsigned int origin = ifbdev->vma->fence ? ORIGIN_GTT : ORIGIN_CPU;
52a85cb24fSFrançois Tigeot
53a85cb24fSFrançois Tigeot intel_fb_obj_invalidate(obj, origin);
54a85cb24fSFrançois Tigeot }
55a85cb24fSFrançois Tigeot
intel_fbdev_set_par(struct fb_info * info)5624edb884SFrançois Tigeot static int intel_fbdev_set_par(struct fb_info *info)
5724edb884SFrançois Tigeot {
5824edb884SFrançois Tigeot struct drm_fb_helper *fb_helper = info->par;
5924edb884SFrançois Tigeot struct intel_fbdev *ifbdev =
6024edb884SFrançois Tigeot container_of(fb_helper, struct intel_fbdev, helper);
6124edb884SFrançois Tigeot int ret;
6224edb884SFrançois Tigeot
6324edb884SFrançois Tigeot ret = drm_fb_helper_set_par(info);
64a85cb24fSFrançois Tigeot if (ret == 0)
65a85cb24fSFrançois Tigeot intel_fbdev_invalidate(ifbdev);
6624edb884SFrançois Tigeot
6724edb884SFrançois Tigeot return ret;
6824edb884SFrançois Tigeot }
6924edb884SFrançois Tigeot
intel_fbdev_blank(int blank,struct fb_info * info)70477eb7f9SFrançois Tigeot static int intel_fbdev_blank(int blank, struct fb_info *info)
71477eb7f9SFrançois Tigeot {
72477eb7f9SFrançois Tigeot struct drm_fb_helper *fb_helper = info->par;
73477eb7f9SFrançois Tigeot struct intel_fbdev *ifbdev =
74477eb7f9SFrançois Tigeot container_of(fb_helper, struct intel_fbdev, helper);
75477eb7f9SFrançois Tigeot int ret;
76477eb7f9SFrançois Tigeot
77477eb7f9SFrançois Tigeot ret = drm_fb_helper_blank(blank, info);
78a85cb24fSFrançois Tigeot if (ret == 0)
79a85cb24fSFrançois Tigeot intel_fbdev_invalidate(ifbdev);
80477eb7f9SFrançois Tigeot
81477eb7f9SFrançois Tigeot return ret;
82477eb7f9SFrançois Tigeot }
83477eb7f9SFrançois Tigeot
84ba409a88SImre Vadász #if 0
8519c468b4SFrançois Tigeot static int intel_fbdev_pan_display(struct fb_var_screeninfo *var,
8619c468b4SFrançois Tigeot struct fb_info *info)
8719c468b4SFrançois Tigeot {
8819c468b4SFrançois Tigeot struct drm_fb_helper *fb_helper = info->par;
8919c468b4SFrançois Tigeot struct intel_fbdev *ifbdev =
9019c468b4SFrançois Tigeot container_of(fb_helper, struct intel_fbdev, helper);
9119c468b4SFrançois Tigeot int ret;
9219c468b4SFrançois Tigeot
93a85cb24fSFrançois Tigeot ret = drm_fb_helper_pan_display(var, info);
94a85cb24fSFrançois Tigeot if (ret == 0)
95a85cb24fSFrançois Tigeot intel_fbdev_invalidate(ifbdev);
9619c468b4SFrançois Tigeot
9719c468b4SFrançois Tigeot return ret;
9819c468b4SFrançois Tigeot }
99ba409a88SImre Vadász #endif
10019c468b4SFrançois Tigeot
101717f4a47SSascha Wildner static struct fb_ops intelfb_ops = {
102ba409a88SImre Vadász #if 0
103717f4a47SSascha Wildner .owner = THIS_MODULE,
104ba409a88SImre Vadász #endif
105a85cb24fSFrançois Tigeot DRM_FB_HELPER_DEFAULT_OPS,
10624edb884SFrançois Tigeot .fb_set_par = intel_fbdev_set_par,
107ba409a88SImre Vadász #if 0
108a05eeebfSFrançois Tigeot .fb_fillrect = drm_fb_helper_cfb_fillrect,
109a05eeebfSFrançois Tigeot .fb_copyarea = drm_fb_helper_cfb_copyarea,
110a05eeebfSFrançois Tigeot .fb_imageblit = drm_fb_helper_cfb_imageblit,
11119c468b4SFrançois Tigeot .fb_pan_display = intel_fbdev_pan_display,
112717f4a47SSascha Wildner #endif
113ba409a88SImre Vadász .fb_blank = intel_fbdev_blank,
114ba409a88SImre Vadász };
115717f4a47SSascha Wildner
intelfb_alloc(struct drm_fb_helper * helper,struct drm_fb_helper_surface_size * sizes)116717f4a47SSascha Wildner static int intelfb_alloc(struct drm_fb_helper *helper,
117717f4a47SSascha Wildner struct drm_fb_helper_surface_size *sizes)
118717f4a47SSascha Wildner {
119717f4a47SSascha Wildner struct intel_fbdev *ifbdev =
120717f4a47SSascha Wildner container_of(helper, struct intel_fbdev, helper);
121c0e85e96SFrançois Tigeot struct drm_framebuffer *fb;
122717f4a47SSascha Wildner struct drm_device *dev = helper->dev;
123352ff8bdSFrançois Tigeot struct drm_i915_private *dev_priv = to_i915(dev);
1248621f407SFrançois Tigeot struct i915_ggtt *ggtt = &dev_priv->ggtt;
125717f4a47SSascha Wildner struct drm_mode_fb_cmd2 mode_cmd = {};
126a85cb24fSFrançois Tigeot struct drm_i915_gem_object *obj;
127717f4a47SSascha Wildner int size, ret;
128717f4a47SSascha Wildner
129717f4a47SSascha Wildner /* we don't do packed 24bpp */
130717f4a47SSascha Wildner if (sizes->surface_bpp == 24)
131717f4a47SSascha Wildner sizes->surface_bpp = 32;
132717f4a47SSascha Wildner
133717f4a47SSascha Wildner mode_cmd.width = sizes->surface_width;
134717f4a47SSascha Wildner mode_cmd.height = sizes->surface_height;
135717f4a47SSascha Wildner
136717f4a47SSascha Wildner mode_cmd.pitches[0] = ALIGN(mode_cmd.width *
137717f4a47SSascha Wildner DIV_ROUND_UP(sizes->surface_bpp, 8), 64);
138717f4a47SSascha Wildner mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
139717f4a47SSascha Wildner sizes->surface_depth);
140717f4a47SSascha Wildner
141717f4a47SSascha Wildner size = mode_cmd.pitches[0] * mode_cmd.height;
14224edb884SFrançois Tigeot size = PAGE_ALIGN(size);
143352ff8bdSFrançois Tigeot
144352ff8bdSFrançois Tigeot /* If the FB is too big, just don't use it since fbdev is not very
145352ff8bdSFrançois Tigeot * important and we should probably use that space with FBC or other
146352ff8bdSFrançois Tigeot * features. */
147a85cb24fSFrançois Tigeot obj = NULL;
1488621f407SFrançois Tigeot if (size * 2 < ggtt->stolen_usable_size)
149a85cb24fSFrançois Tigeot obj = i915_gem_object_create_stolen(dev_priv, size);
150717f4a47SSascha Wildner if (obj == NULL)
151a85cb24fSFrançois Tigeot obj = i915_gem_object_create(dev_priv, size);
1521487f786SFrançois Tigeot if (IS_ERR(obj)) {
153717f4a47SSascha Wildner DRM_ERROR("failed to allocate framebuffer\n");
1541487f786SFrançois Tigeot ret = PTR_ERR(obj);
155a85cb24fSFrançois Tigeot goto err;
156717f4a47SSascha Wildner }
157717f4a47SSascha Wildner
158a85cb24fSFrançois Tigeot fb = intel_framebuffer_create(obj, &mode_cmd);
159ba55f2f5SFrançois Tigeot if (IS_ERR(fb)) {
160ba55f2f5SFrançois Tigeot ret = PTR_ERR(fb);
161a85cb24fSFrançois Tigeot goto err_obj;
1622c9916cdSFrançois Tigeot }
1632c9916cdSFrançois Tigeot
164ba55f2f5SFrançois Tigeot ifbdev->fb = to_intel_framebuffer(fb);
165717f4a47SSascha Wildner
166717f4a47SSascha Wildner return 0;
167717f4a47SSascha Wildner
168a85cb24fSFrançois Tigeot err_obj:
169a85cb24fSFrançois Tigeot i915_gem_object_put(obj);
170a85cb24fSFrançois Tigeot err:
171717f4a47SSascha Wildner return ret;
172717f4a47SSascha Wildner }
173717f4a47SSascha Wildner
intelfb_create(struct drm_fb_helper * helper,struct drm_fb_helper_surface_size * sizes)174717f4a47SSascha Wildner static int intelfb_create(struct drm_fb_helper *helper,
175717f4a47SSascha Wildner struct drm_fb_helper_surface_size *sizes)
176717f4a47SSascha Wildner {
177717f4a47SSascha Wildner struct intel_fbdev *ifbdev =
178717f4a47SSascha Wildner container_of(helper, struct intel_fbdev, helper);
179ba55f2f5SFrançois Tigeot struct intel_framebuffer *intel_fb = ifbdev->fb;
180717f4a47SSascha Wildner struct drm_device *dev = helper->dev;
1818621f407SFrançois Tigeot struct drm_i915_private *dev_priv = to_i915(dev);
1821e12ee3bSFrançois Tigeot struct pci_dev *pdev = dev_priv->drm.pdev;
1831487f786SFrançois Tigeot struct i915_ggtt *ggtt = &dev_priv->ggtt;
184717f4a47SSascha Wildner struct fb_info *info;
185717f4a47SSascha Wildner struct drm_framebuffer *fb;
1861487f786SFrançois Tigeot struct i915_vma *vma;
187ba55f2f5SFrançois Tigeot bool prealloc = false;
18887df8fc6SFrançois Tigeot void __iomem *vaddr;
1891487f786SFrançois Tigeot int ret;
1901487f786SFrançois Tigeot device_t vga_dev;
191717f4a47SSascha Wildner
192ba55f2f5SFrançois Tigeot if (intel_fb &&
193ba55f2f5SFrançois Tigeot (sizes->fb_width > intel_fb->base.width ||
194ba55f2f5SFrançois Tigeot sizes->fb_height > intel_fb->base.height)) {
195ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("BIOS fb too small (%dx%d), we require (%dx%d),"
196ba55f2f5SFrançois Tigeot " releasing it\n",
197ba55f2f5SFrançois Tigeot intel_fb->base.width, intel_fb->base.height,
198ba55f2f5SFrançois Tigeot sizes->fb_width, sizes->fb_height);
199*3f2dd94aSFrançois Tigeot drm_framebuffer_put(&intel_fb->base);
200ba55f2f5SFrançois Tigeot intel_fb = ifbdev->fb = NULL;
201ba55f2f5SFrançois Tigeot }
202ba55f2f5SFrançois Tigeot if (!intel_fb || WARN_ON(!intel_fb->obj)) {
203717f4a47SSascha Wildner DRM_DEBUG_KMS("no BIOS fb, allocating a new one\n");
204717f4a47SSascha Wildner ret = intelfb_alloc(helper, sizes);
205717f4a47SSascha Wildner if (ret)
206aee94f86SFrançois Tigeot return ret;
207ba55f2f5SFrançois Tigeot intel_fb = ifbdev->fb;
208717f4a47SSascha Wildner } else {
209717f4a47SSascha Wildner DRM_DEBUG_KMS("re-using BIOS fb\n");
210ba55f2f5SFrançois Tigeot prealloc = true;
211717f4a47SSascha Wildner sizes->fb_width = intel_fb->base.width;
212717f4a47SSascha Wildner sizes->fb_height = intel_fb->base.height;
213717f4a47SSascha Wildner }
214717f4a47SSascha Wildner
215aee94f86SFrançois Tigeot mutex_lock(&dev->struct_mutex);
216*3f2dd94aSFrançois Tigeot intel_runtime_pm_get(dev_priv);
217aee94f86SFrançois Tigeot
218aee94f86SFrançois Tigeot /* Pin the GGTT vma for our access via info->screen_base.
219aee94f86SFrançois Tigeot * This also validates that any existing fb inherited from the
220aee94f86SFrançois Tigeot * BIOS is suitable for own access.
221aee94f86SFrançois Tigeot */
222*3f2dd94aSFrançois Tigeot vma = intel_pin_and_fence_fb_obj(&ifbdev->fb->base, DRM_MODE_ROTATE_0);
2231e12ee3bSFrançois Tigeot if (IS_ERR(vma)) {
2241e12ee3bSFrançois Tigeot ret = PTR_ERR(vma);
225aee94f86SFrançois Tigeot goto out_unlock;
2261e12ee3bSFrançois Tigeot }
227aee94f86SFrançois Tigeot
228a05eeebfSFrançois Tigeot info = drm_fb_helper_alloc_fbi(helper);
229a05eeebfSFrançois Tigeot if (IS_ERR(info)) {
230aee94f86SFrançois Tigeot DRM_ERROR("Failed to allocate fb_info\n");
231a05eeebfSFrançois Tigeot ret = PTR_ERR(info);
232717f4a47SSascha Wildner goto out_unpin;
233717f4a47SSascha Wildner }
2342c9916cdSFrançois Tigeot
235717f4a47SSascha Wildner info->par = helper;
236ba55f2f5SFrançois Tigeot
237f9dc6f44SImre Vadász fb = &ifbdev->fb->base;
238f9dc6f44SImre Vadász
239a05eeebfSFrançois Tigeot ifbdev->helper.fb = fb;
240a05eeebfSFrançois Tigeot
241ba55f2f5SFrançois Tigeot #ifdef __DragonFly__
242fb572d17SFrançois Tigeot vga_dev = device_get_parent(dev->dev->bsddev);
243717f4a47SSascha Wildner info->width = sizes->fb_width;
244717f4a47SSascha Wildner info->height = sizes->fb_height;
245f9dc6f44SImre Vadász info->stride = fb->pitches[0];
246717f4a47SSascha Wildner info->depth = sizes->surface_bpp;
2471e12ee3bSFrançois Tigeot info->paddr = ggtt->mappable_base + vma->node.start;
248717f4a47SSascha Wildner info->is_vga_boot_display = vga_pci_is_boot_display(vga_dev);
249ba409a88SImre Vadász info->fbops = intelfb_ops;
2501487f786SFrançois Tigeot
251a85cb24fSFrançois Tigeot #else
252717f4a47SSascha Wildner strcpy(info->fix.id, "inteldrmfb");
253717f4a47SSascha Wildner
254717f4a47SSascha Wildner info->fbops = &intelfb_ops;
2551487f786SFrançois Tigeot
256717f4a47SSascha Wildner /* setup aperture base/size for vesafb takeover */
257717f4a47SSascha Wildner info->apertures->ranges[0].base = dev->mode_config.fb_base;
2588621f407SFrançois Tigeot info->apertures->ranges[0].size = ggtt->mappable_end;
259717f4a47SSascha Wildner
2601e12ee3bSFrançois Tigeot info->fix.smem_start = dev->mode_config.fb_base + i915_ggtt_offset(vma);
2611487f786SFrançois Tigeot info->fix.smem_len = vma->node.size;
2621487f786SFrançois Tigeot #endif
263717f4a47SSascha Wildner
2641487f786SFrançois Tigeot vaddr = i915_vma_pin_iomap(vma);
2651487f786SFrançois Tigeot if (IS_ERR(vaddr)) {
266aee94f86SFrançois Tigeot DRM_ERROR("Failed to remap framebuffer into virtual memory\n");
2671487f786SFrançois Tigeot ret = PTR_ERR(vaddr);
268a85cb24fSFrançois Tigeot goto out_unpin;
269717f4a47SSascha Wildner }
2701487f786SFrançois Tigeot #ifdef __DragonFly__
2711487f786SFrançois Tigeot info->vaddr = (vm_offset_t)vaddr;
2721487f786SFrançois Tigeot #else
2731487f786SFrançois Tigeot info->screen_base = vaddr;
2741487f786SFrançois Tigeot info->screen_size = vma->node.size;
275717f4a47SSascha Wildner
276717f4a47SSascha Wildner /* This driver doesn't need a VT switch to restore the mode on resume */
277717f4a47SSascha Wildner info->skip_vt_switch = true;
278717f4a47SSascha Wildner
279a85cb24fSFrançois Tigeot drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
280717f4a47SSascha Wildner drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height);
281717f4a47SSascha Wildner
282717f4a47SSascha Wildner /* If the object is shmemfs backed, it will have given us zeroed pages.
283717f4a47SSascha Wildner * If the object is stolen however, it will be full of whatever
284717f4a47SSascha Wildner * garbage was left in there.
285717f4a47SSascha Wildner */
2861e12ee3bSFrançois Tigeot if (intel_fb->obj->stolen && !prealloc)
287717f4a47SSascha Wildner memset_io(info->screen_base, 0, info->screen_size);
288a85cb24fSFrançois Tigeot #endif
289717f4a47SSascha Wildner
290717f4a47SSascha Wildner /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
291717f4a47SSascha Wildner
2921e12ee3bSFrançois Tigeot DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x\n",
2931e12ee3bSFrançois Tigeot fb->width, fb->height, i915_ggtt_offset(vma));
2941e12ee3bSFrançois Tigeot ifbdev->vma = vma;
295717f4a47SSascha Wildner
296*3f2dd94aSFrançois Tigeot intel_runtime_pm_put(dev_priv);
297717f4a47SSascha Wildner mutex_unlock(&dev->struct_mutex);
2981e12ee3bSFrançois Tigeot vga_switcheroo_client_fb_set(pdev, info);
299717f4a47SSascha Wildner return 0;
300717f4a47SSascha Wildner
301717f4a47SSascha Wildner out_unpin:
3024be47400SFrançois Tigeot intel_unpin_fb_vma(vma);
303717f4a47SSascha Wildner out_unlock:
304*3f2dd94aSFrançois Tigeot intel_runtime_pm_put(dev_priv);
305717f4a47SSascha Wildner mutex_unlock(&dev->struct_mutex);
306717f4a47SSascha Wildner return ret;
307717f4a47SSascha Wildner }
308717f4a47SSascha Wildner
309ba55f2f5SFrançois Tigeot static struct drm_fb_helper_crtc *
intel_fb_helper_crtc(struct drm_fb_helper * fb_helper,struct drm_crtc * crtc)310ba55f2f5SFrançois Tigeot intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc)
311ba55f2f5SFrançois Tigeot {
312ba55f2f5SFrançois Tigeot int i;
313ba55f2f5SFrançois Tigeot
314ba55f2f5SFrançois Tigeot for (i = 0; i < fb_helper->crtc_count; i++)
315ba55f2f5SFrançois Tigeot if (fb_helper->crtc_info[i].mode_set.crtc == crtc)
316ba55f2f5SFrançois Tigeot return &fb_helper->crtc_info[i];
317ba55f2f5SFrançois Tigeot
318ba55f2f5SFrançois Tigeot return NULL;
319ba55f2f5SFrançois Tigeot }
320ba55f2f5SFrançois Tigeot
321ba55f2f5SFrançois Tigeot /*
322ba55f2f5SFrançois Tigeot * Try to read the BIOS display configuration and use it for the initial
323ba55f2f5SFrançois Tigeot * fb configuration.
324ba55f2f5SFrançois Tigeot *
325ba55f2f5SFrançois Tigeot * The BIOS or boot loader will generally create an initial display
326ba55f2f5SFrançois Tigeot * configuration for us that includes some set of active pipes and displays.
327ba55f2f5SFrançois Tigeot * This routine tries to figure out which pipes and connectors are active
328ba55f2f5SFrançois Tigeot * and stuffs them into the crtcs and modes array given to us by the
329ba55f2f5SFrançois Tigeot * drm_fb_helper code.
330ba55f2f5SFrançois Tigeot *
331ba55f2f5SFrançois Tigeot * The overall sequence is:
332ba55f2f5SFrançois Tigeot * intel_fbdev_init - from driver load
333ba55f2f5SFrançois Tigeot * intel_fbdev_init_bios - initialize the intel_fbdev using BIOS data
334ba55f2f5SFrançois Tigeot * drm_fb_helper_init - build fb helper structs
335ba55f2f5SFrançois Tigeot * drm_fb_helper_single_add_all_connectors - more fb helper structs
336ba55f2f5SFrançois Tigeot * intel_fbdev_initial_config - apply the config
337ba55f2f5SFrançois Tigeot * drm_fb_helper_initial_config - call ->probe then register_framebuffer()
338ba55f2f5SFrançois Tigeot * drm_setup_crtcs - build crtc config for fbdev
339ba55f2f5SFrançois Tigeot * intel_fb_initial_config - find active connectors etc
340ba55f2f5SFrançois Tigeot * drm_fb_helper_single_fb_probe - set up fbdev
341ba55f2f5SFrançois Tigeot * intelfb_create - re-use or alloc fb, build out fbdev structs
342ba55f2f5SFrançois Tigeot *
343ba55f2f5SFrançois Tigeot * Note that we don't make special consideration whether we could actually
344ba55f2f5SFrançois Tigeot * switch to the selected modes without a full modeset. E.g. when the display
345ba55f2f5SFrançois Tigeot * is in VGA mode we need to recalculate watermarks and set a new high-res
346ba55f2f5SFrançois Tigeot * framebuffer anyway.
347ba55f2f5SFrançois Tigeot */
intel_fb_initial_config(struct drm_fb_helper * fb_helper,struct drm_fb_helper_crtc ** crtcs,struct drm_display_mode ** modes,struct drm_fb_offset * offsets,bool * enabled,int width,int height)348ba55f2f5SFrançois Tigeot static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
349ba55f2f5SFrançois Tigeot struct drm_fb_helper_crtc **crtcs,
350ba55f2f5SFrançois Tigeot struct drm_display_mode **modes,
3512c9916cdSFrançois Tigeot struct drm_fb_offset *offsets,
352ba55f2f5SFrançois Tigeot bool *enabled, int width, int height)
353ba55f2f5SFrançois Tigeot {
3544be47400SFrançois Tigeot struct drm_i915_private *dev_priv = to_i915(fb_helper->dev);
3554be47400SFrançois Tigeot unsigned long conn_configured, conn_seq, mask;
356bf017597SFrançois Tigeot unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG);
357ba55f2f5SFrançois Tigeot int i, j;
358ba55f2f5SFrançois Tigeot bool *save_enabled;
359*3f2dd94aSFrançois Tigeot bool fallback = true, ret = true;
360ba55f2f5SFrançois Tigeot int num_connectors_enabled = 0;
361ba55f2f5SFrançois Tigeot int num_connectors_detected = 0;
362*3f2dd94aSFrançois Tigeot struct drm_modeset_acquire_ctx ctx;
363ba55f2f5SFrançois Tigeot
364bf017597SFrançois Tigeot save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL);
365ba55f2f5SFrançois Tigeot if (!save_enabled)
366ba55f2f5SFrançois Tigeot return false;
367ba55f2f5SFrançois Tigeot
368*3f2dd94aSFrançois Tigeot drm_modeset_acquire_init(&ctx, 0);
369*3f2dd94aSFrançois Tigeot
370*3f2dd94aSFrançois Tigeot while (drm_modeset_lock_all_ctx(fb_helper->dev, &ctx) != 0)
371*3f2dd94aSFrançois Tigeot drm_modeset_backoff(&ctx);
372*3f2dd94aSFrançois Tigeot
373bf017597SFrançois Tigeot memcpy(save_enabled, enabled, count);
374a85cb24fSFrançois Tigeot mask = GENMASK(count - 1, 0);
375bf017597SFrançois Tigeot conn_configured = 0;
3762c9916cdSFrançois Tigeot retry:
3774be47400SFrançois Tigeot conn_seq = conn_configured;
378bf017597SFrançois Tigeot for (i = 0; i < count; i++) {
379ba55f2f5SFrançois Tigeot struct drm_fb_helper_connector *fb_conn;
380ba55f2f5SFrançois Tigeot struct drm_connector *connector;
381ba55f2f5SFrançois Tigeot struct drm_encoder *encoder;
382ba55f2f5SFrançois Tigeot struct drm_fb_helper_crtc *new_crtc;
383ba55f2f5SFrançois Tigeot
384ba55f2f5SFrançois Tigeot fb_conn = fb_helper->connector_info[i];
385ba55f2f5SFrançois Tigeot connector = fb_conn->connector;
386ba55f2f5SFrançois Tigeot
387bf017597SFrançois Tigeot if (conn_configured & BIT(i))
3882c9916cdSFrançois Tigeot continue;
3892c9916cdSFrançois Tigeot
3904be47400SFrançois Tigeot if (conn_seq == 0 && !connector->has_tile)
3912c9916cdSFrançois Tigeot continue;
3922c9916cdSFrançois Tigeot
393ba55f2f5SFrançois Tigeot if (connector->status == connector_status_connected)
394ba55f2f5SFrançois Tigeot num_connectors_detected++;
395ba55f2f5SFrançois Tigeot
396ba55f2f5SFrançois Tigeot if (!enabled[i]) {
397ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("connector %s not enabled, skipping\n",
398b4efbf42Szrj connector->name);
399bf017597SFrançois Tigeot conn_configured |= BIT(i);
400ba55f2f5SFrançois Tigeot continue;
401ba55f2f5SFrançois Tigeot }
402ba55f2f5SFrançois Tigeot
4031b13d190SFrançois Tigeot if (connector->force == DRM_FORCE_OFF) {
4041b13d190SFrançois Tigeot DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n",
4051b13d190SFrançois Tigeot connector->name);
4061b13d190SFrançois Tigeot enabled[i] = false;
4071b13d190SFrançois Tigeot continue;
4081b13d190SFrançois Tigeot }
4091b13d190SFrançois Tigeot
410c0e85e96SFrançois Tigeot encoder = connector->state->best_encoder;
411c0e85e96SFrançois Tigeot if (!encoder || WARN_ON(!connector->state->crtc)) {
4121b13d190SFrançois Tigeot if (connector->force > DRM_FORCE_OFF)
4131b13d190SFrançois Tigeot goto bail;
4141b13d190SFrançois Tigeot
415ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
416b4efbf42Szrj connector->name);
417ba55f2f5SFrançois Tigeot enabled[i] = false;
418bf017597SFrançois Tigeot conn_configured |= BIT(i);
419ba55f2f5SFrançois Tigeot continue;
420ba55f2f5SFrançois Tigeot }
421ba55f2f5SFrançois Tigeot
422ba55f2f5SFrançois Tigeot num_connectors_enabled++;
423ba55f2f5SFrançois Tigeot
424bf017597SFrançois Tigeot new_crtc = intel_fb_helper_crtc(fb_helper,
425bf017597SFrançois Tigeot connector->state->crtc);
426ba55f2f5SFrançois Tigeot
427ba55f2f5SFrançois Tigeot /*
428ba55f2f5SFrançois Tigeot * Make sure we're not trying to drive multiple connectors
429ba55f2f5SFrançois Tigeot * with a single CRTC, since our cloning support may not
430ba55f2f5SFrançois Tigeot * match the BIOS.
431ba55f2f5SFrançois Tigeot */
432bf017597SFrançois Tigeot for (j = 0; j < count; j++) {
433ba55f2f5SFrançois Tigeot if (crtcs[j] == new_crtc) {
434ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("fallback: cloned configuration\n");
4351b13d190SFrançois Tigeot goto bail;
436ba55f2f5SFrançois Tigeot }
437ba55f2f5SFrançois Tigeot }
438ba55f2f5SFrançois Tigeot
439ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n",
440b4efbf42Szrj connector->name);
441ba55f2f5SFrançois Tigeot
442ba55f2f5SFrançois Tigeot /* go for command line mode first */
443a85cb24fSFrançois Tigeot modes[i] = drm_pick_cmdline_mode(fb_conn);
444ba55f2f5SFrançois Tigeot
445ba55f2f5SFrançois Tigeot /* try for preferred next */
446ba55f2f5SFrançois Tigeot if (!modes[i]) {
4472c9916cdSFrançois Tigeot DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n",
4482c9916cdSFrançois Tigeot connector->name, connector->has_tile);
449ba55f2f5SFrançois Tigeot modes[i] = drm_has_preferred_mode(fb_conn, width,
450ba55f2f5SFrançois Tigeot height);
451ba55f2f5SFrançois Tigeot }
452ba55f2f5SFrançois Tigeot
453ba55f2f5SFrançois Tigeot /* No preferred mode marked by the EDID? Are there any modes? */
454ba55f2f5SFrançois Tigeot if (!modes[i] && !list_empty(&connector->modes)) {
455ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("using first mode listed on connector %s\n",
456ba55f2f5SFrançois Tigeot connector->name);
457ba55f2f5SFrançois Tigeot modes[i] = list_first_entry(&connector->modes,
458ba55f2f5SFrançois Tigeot struct drm_display_mode,
459ba55f2f5SFrançois Tigeot head);
460ba55f2f5SFrançois Tigeot }
461ba55f2f5SFrançois Tigeot
462ba55f2f5SFrançois Tigeot /* last resort: use current mode */
463ba55f2f5SFrançois Tigeot if (!modes[i]) {
464ba55f2f5SFrançois Tigeot /*
465ba55f2f5SFrançois Tigeot * IMPORTANT: We want to use the adjusted mode (i.e.
466ba55f2f5SFrançois Tigeot * after the panel fitter upscaling) as the initial
467ba55f2f5SFrançois Tigeot * config, not the input mode, which is what crtc->mode
468a05eeebfSFrançois Tigeot * usually contains. But since our current
469ba55f2f5SFrançois Tigeot * code puts a mode derived from the post-pfit timings
470a05eeebfSFrançois Tigeot * into crtc->mode this works out correctly.
471c0e85e96SFrançois Tigeot *
472c0e85e96SFrançois Tigeot * This is crtc->mode and not crtc->state->mode for the
473c0e85e96SFrançois Tigeot * fastboot check to work correctly. crtc_state->mode has
474c0e85e96SFrançois Tigeot * I915_MODE_FLAG_INHERITED, which we clear to force check
475c0e85e96SFrançois Tigeot * state.
476ba55f2f5SFrançois Tigeot */
477ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("looking for current mode on connector %s\n",
478b4efbf42Szrj connector->name);
479c0e85e96SFrançois Tigeot modes[i] = &connector->state->crtc->mode;
480ba55f2f5SFrançois Tigeot }
481ba55f2f5SFrançois Tigeot crtcs[i] = new_crtc;
482ba55f2f5SFrançois Tigeot
4831487f786SFrançois Tigeot DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n",
484b4efbf42Szrj connector->name,
485c0e85e96SFrançois Tigeot connector->state->crtc->base.id,
4861487f786SFrançois Tigeot connector->state->crtc->name,
487ba55f2f5SFrançois Tigeot modes[i]->hdisplay, modes[i]->vdisplay,
488ba55f2f5SFrançois Tigeot modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :"");
489ba55f2f5SFrançois Tigeot
490ba55f2f5SFrançois Tigeot fallback = false;
491bf017597SFrançois Tigeot conn_configured |= BIT(i);
4922c9916cdSFrançois Tigeot }
4932c9916cdSFrançois Tigeot
4944be47400SFrançois Tigeot if ((conn_configured & mask) != mask && conn_configured != conn_seq)
4952c9916cdSFrançois Tigeot goto retry;
496ba55f2f5SFrançois Tigeot
497ba55f2f5SFrançois Tigeot /*
498ba55f2f5SFrançois Tigeot * If the BIOS didn't enable everything it could, fall back to have the
499ba55f2f5SFrançois Tigeot * same user experiencing of lighting up as much as possible like the
500ba55f2f5SFrançois Tigeot * fbdev helper library.
501ba55f2f5SFrançois Tigeot */
502ba55f2f5SFrançois Tigeot if (num_connectors_enabled != num_connectors_detected &&
5034be47400SFrançois Tigeot num_connectors_enabled < INTEL_INFO(dev_priv)->num_pipes) {
504ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("fallback: Not all outputs enabled\n");
505ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled,
506ba55f2f5SFrançois Tigeot num_connectors_detected);
507ba55f2f5SFrançois Tigeot fallback = true;
508ba55f2f5SFrançois Tigeot }
509ba55f2f5SFrançois Tigeot
510ba55f2f5SFrançois Tigeot if (fallback) {
5111b13d190SFrançois Tigeot bail:
512ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("Not using firmware configuration\n");
513bf017597SFrançois Tigeot memcpy(enabled, save_enabled, count);
514*3f2dd94aSFrançois Tigeot ret = false;
515ba55f2f5SFrançois Tigeot }
516ba55f2f5SFrançois Tigeot
517*3f2dd94aSFrançois Tigeot drm_modeset_drop_locks(&ctx);
518*3f2dd94aSFrançois Tigeot drm_modeset_acquire_fini(&ctx);
519*3f2dd94aSFrançois Tigeot
520ba55f2f5SFrançois Tigeot kfree(save_enabled);
521*3f2dd94aSFrançois Tigeot return ret;
522ba55f2f5SFrançois Tigeot }
523ba55f2f5SFrançois Tigeot
524c6f73aabSFrançois Tigeot static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
525ba55f2f5SFrançois Tigeot .initial_config = intel_fb_initial_config,
526717f4a47SSascha Wildner .fb_probe = intelfb_create,
527717f4a47SSascha Wildner };
528717f4a47SSascha Wildner
intel_fbdev_destroy(struct intel_fbdev * ifbdev)5291487f786SFrançois Tigeot static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
530717f4a47SSascha Wildner {
531aee94f86SFrançois Tigeot /* We rely on the object-free to release the VMA pinning for
532aee94f86SFrançois Tigeot * the info->screen_base mmaping. Leaking the VMA is simpler than
533aee94f86SFrançois Tigeot * trying to rectify all the possible error paths leading here.
534aee94f86SFrançois Tigeot */
535aee94f86SFrançois Tigeot
536717f4a47SSascha Wildner drm_fb_helper_fini(&ifbdev->helper);
537717f4a47SSascha Wildner
538a85cb24fSFrançois Tigeot if (ifbdev->vma) {
5391487f786SFrançois Tigeot mutex_lock(&ifbdev->helper.dev->struct_mutex);
5404be47400SFrançois Tigeot intel_unpin_fb_vma(ifbdev->vma);
5411487f786SFrançois Tigeot mutex_unlock(&ifbdev->helper.dev->struct_mutex);
542ba55f2f5SFrançois Tigeot }
5431487f786SFrançois Tigeot
544a85cb24fSFrançois Tigeot if (ifbdev->fb)
545a85cb24fSFrançois Tigeot drm_framebuffer_remove(&ifbdev->fb->base);
546a85cb24fSFrançois Tigeot
5471487f786SFrançois Tigeot kfree(ifbdev);
548aee94f86SFrançois Tigeot }
549ba55f2f5SFrançois Tigeot
550ba55f2f5SFrançois Tigeot /*
551ba55f2f5SFrançois Tigeot * Build an intel_fbdev struct using a BIOS allocated framebuffer, if possible.
552ba55f2f5SFrançois Tigeot * The core display code will have read out the current plane configuration,
553ba55f2f5SFrançois Tigeot * so we use that to figure out if there's an object for us to use as the
554ba55f2f5SFrançois Tigeot * fb, and if so, we re-use it for the fbdev configuration.
555ba55f2f5SFrançois Tigeot *
556ba55f2f5SFrançois Tigeot * Note we only support a single fb shared across pipes for boot (mostly for
557ba55f2f5SFrançois Tigeot * fbcon), so we just find the biggest and use that.
558ba55f2f5SFrançois Tigeot */
intel_fbdev_init_bios(struct drm_device * dev,struct intel_fbdev * ifbdev)559ba55f2f5SFrançois Tigeot static bool intel_fbdev_init_bios(struct drm_device *dev,
560ba55f2f5SFrançois Tigeot struct intel_fbdev *ifbdev)
561ba55f2f5SFrançois Tigeot {
562ba55f2f5SFrançois Tigeot struct intel_framebuffer *fb = NULL;
563ba55f2f5SFrançois Tigeot struct drm_crtc *crtc;
564ba55f2f5SFrançois Tigeot struct intel_crtc *intel_crtc;
565ba55f2f5SFrançois Tigeot unsigned int max_size = 0;
566ba55f2f5SFrançois Tigeot
567ba55f2f5SFrançois Tigeot /* Find the largest fb */
568ba55f2f5SFrançois Tigeot for_each_crtc(dev, crtc) {
569a05eeebfSFrançois Tigeot struct drm_i915_gem_object *obj =
570a05eeebfSFrançois Tigeot intel_fb_obj(crtc->primary->state->fb);
571ba55f2f5SFrançois Tigeot intel_crtc = to_intel_crtc(crtc);
572ba55f2f5SFrançois Tigeot
573352ff8bdSFrançois Tigeot if (!crtc->state->active || !obj) {
574ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n",
575ba55f2f5SFrançois Tigeot pipe_name(intel_crtc->pipe));
576ba55f2f5SFrançois Tigeot continue;
577ba55f2f5SFrançois Tigeot }
578ba55f2f5SFrançois Tigeot
579a05eeebfSFrançois Tigeot if (obj->base.size > max_size) {
580ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("found possible fb from plane %c\n",
581ba55f2f5SFrançois Tigeot pipe_name(intel_crtc->pipe));
582a05eeebfSFrançois Tigeot fb = to_intel_framebuffer(crtc->primary->state->fb);
583a05eeebfSFrançois Tigeot max_size = obj->base.size;
584ba55f2f5SFrançois Tigeot }
585ba55f2f5SFrançois Tigeot }
586ba55f2f5SFrançois Tigeot
587ba55f2f5SFrançois Tigeot if (!fb) {
588ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("no active fbs found, not using BIOS config\n");
589ba55f2f5SFrançois Tigeot goto out;
590ba55f2f5SFrançois Tigeot }
591ba55f2f5SFrançois Tigeot
592ba55f2f5SFrançois Tigeot /* Now make sure all the pipes will fit into it */
593ba55f2f5SFrançois Tigeot for_each_crtc(dev, crtc) {
594ba55f2f5SFrançois Tigeot unsigned int cur_size;
595ba55f2f5SFrançois Tigeot
596ba55f2f5SFrançois Tigeot intel_crtc = to_intel_crtc(crtc);
597ba55f2f5SFrançois Tigeot
598352ff8bdSFrançois Tigeot if (!crtc->state->active) {
599ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("pipe %c not active, skipping\n",
600ba55f2f5SFrançois Tigeot pipe_name(intel_crtc->pipe));
601ba55f2f5SFrançois Tigeot continue;
602ba55f2f5SFrançois Tigeot }
603ba55f2f5SFrançois Tigeot
604ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("checking plane %c for BIOS fb\n",
605ba55f2f5SFrançois Tigeot pipe_name(intel_crtc->pipe));
606ba55f2f5SFrançois Tigeot
607ba55f2f5SFrançois Tigeot /*
608ba55f2f5SFrançois Tigeot * See if the plane fb we found above will fit on this
609ba55f2f5SFrançois Tigeot * pipe. Note we need to use the selected fb's pitch and bpp
610ba55f2f5SFrançois Tigeot * rather than the current pipe's, since they differ.
611ba55f2f5SFrançois Tigeot */
6122c9916cdSFrançois Tigeot cur_size = intel_crtc->config->base.adjusted_mode.crtc_hdisplay;
613a85cb24fSFrançois Tigeot cur_size = cur_size * fb->base.format->cpp[0];
614ba55f2f5SFrançois Tigeot if (fb->base.pitches[0] < cur_size) {
615ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("fb not wide enough for plane %c (%d vs %d)\n",
616ba55f2f5SFrançois Tigeot pipe_name(intel_crtc->pipe),
617ba55f2f5SFrançois Tigeot cur_size, fb->base.pitches[0]);
618ba55f2f5SFrançois Tigeot fb = NULL;
619ba55f2f5SFrançois Tigeot break;
620ba55f2f5SFrançois Tigeot }
621ba55f2f5SFrançois Tigeot
6222c9916cdSFrançois Tigeot cur_size = intel_crtc->config->base.adjusted_mode.crtc_vdisplay;
623a85cb24fSFrançois Tigeot cur_size = intel_fb_align_height(&fb->base, 0, cur_size);
624ba55f2f5SFrançois Tigeot cur_size *= fb->base.pitches[0];
625ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("pipe %c area: %dx%d, bpp: %d, size: %d\n",
626ba55f2f5SFrançois Tigeot pipe_name(intel_crtc->pipe),
6272c9916cdSFrançois Tigeot intel_crtc->config->base.adjusted_mode.crtc_hdisplay,
6282c9916cdSFrançois Tigeot intel_crtc->config->base.adjusted_mode.crtc_vdisplay,
629a85cb24fSFrançois Tigeot fb->base.format->cpp[0] * 8,
630ba55f2f5SFrançois Tigeot cur_size);
631ba55f2f5SFrançois Tigeot
632ba55f2f5SFrançois Tigeot if (cur_size > max_size) {
633ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("fb not big enough for plane %c (%d vs %d)\n",
634ba55f2f5SFrançois Tigeot pipe_name(intel_crtc->pipe),
635ba55f2f5SFrançois Tigeot cur_size, max_size);
636ba55f2f5SFrançois Tigeot fb = NULL;
637ba55f2f5SFrançois Tigeot break;
638ba55f2f5SFrançois Tigeot }
639ba55f2f5SFrançois Tigeot
640ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("fb big enough for plane %c (%d >= %d)\n",
641ba55f2f5SFrançois Tigeot pipe_name(intel_crtc->pipe),
642ba55f2f5SFrançois Tigeot max_size, cur_size);
643ba55f2f5SFrançois Tigeot }
644ba55f2f5SFrançois Tigeot
645ba55f2f5SFrançois Tigeot if (!fb) {
646ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n");
647ba55f2f5SFrançois Tigeot goto out;
648ba55f2f5SFrançois Tigeot }
649ba55f2f5SFrançois Tigeot
650a85cb24fSFrançois Tigeot ifbdev->preferred_bpp = fb->base.format->cpp[0] * 8;
651ba55f2f5SFrançois Tigeot ifbdev->fb = fb;
652ba55f2f5SFrançois Tigeot
653*3f2dd94aSFrançois Tigeot drm_framebuffer_get(&ifbdev->fb->base);
654ba55f2f5SFrançois Tigeot
655ba55f2f5SFrançois Tigeot /* Final pass to check if any active pipes don't have fbs */
656ba55f2f5SFrançois Tigeot for_each_crtc(dev, crtc) {
657ba55f2f5SFrançois Tigeot intel_crtc = to_intel_crtc(crtc);
658ba55f2f5SFrançois Tigeot
659352ff8bdSFrançois Tigeot if (!crtc->state->active)
660ba55f2f5SFrançois Tigeot continue;
661ba55f2f5SFrançois Tigeot
662ba55f2f5SFrançois Tigeot WARN(!crtc->primary->fb,
663ba55f2f5SFrançois Tigeot "re-used BIOS config but lost an fb on crtc %d\n",
664ba55f2f5SFrançois Tigeot crtc->base.id);
665ba55f2f5SFrançois Tigeot }
666ba55f2f5SFrançois Tigeot
667ba55f2f5SFrançois Tigeot
668ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("using BIOS fb for initial console\n");
669ba55f2f5SFrançois Tigeot return true;
670ba55f2f5SFrançois Tigeot
671ba55f2f5SFrançois Tigeot out:
672ba55f2f5SFrançois Tigeot
673ba55f2f5SFrançois Tigeot return false;
674717f4a47SSascha Wildner }
675717f4a47SSascha Wildner
intel_fbdev_suspend_worker(struct work_struct * work)6761b13d190SFrançois Tigeot static void intel_fbdev_suspend_worker(struct work_struct *work)
6771b13d190SFrançois Tigeot {
678303bf270SFrançois Tigeot intel_fbdev_set_suspend(&container_of(work,
6791b13d190SFrançois Tigeot struct drm_i915_private,
680303bf270SFrançois Tigeot fbdev_suspend_work)->drm,
6811b13d190SFrançois Tigeot FBINFO_STATE_RUNNING,
6821b13d190SFrançois Tigeot true);
6831b13d190SFrançois Tigeot }
6841b13d190SFrançois Tigeot
intel_fbdev_init(struct drm_device * dev)685717f4a47SSascha Wildner int intel_fbdev_init(struct drm_device *dev)
686717f4a47SSascha Wildner {
687bf017597SFrançois Tigeot struct drm_i915_private *dev_priv = to_i915(dev);
6884be47400SFrançois Tigeot struct intel_fbdev *ifbdev;
689717f4a47SSascha Wildner int ret;
690717f4a47SSascha Wildner
6914be47400SFrançois Tigeot if (WARN_ON(INTEL_INFO(dev_priv)->num_pipes == 0))
692ba55f2f5SFrançois Tigeot return -ENODEV;
693ba55f2f5SFrançois Tigeot
694ba55f2f5SFrançois Tigeot ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL);
695ba55f2f5SFrançois Tigeot if (ifbdev == NULL)
696717f4a47SSascha Wildner return -ENOMEM;
697717f4a47SSascha Wildner
698c6f73aabSFrançois Tigeot drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
699c6f73aabSFrançois Tigeot
700ba55f2f5SFrançois Tigeot if (!intel_fbdev_init_bios(dev, ifbdev))
701ba55f2f5SFrançois Tigeot ifbdev->preferred_bpp = 32;
702717f4a47SSascha Wildner
703a85cb24fSFrançois Tigeot ret = drm_fb_helper_init(dev, &ifbdev->helper, 4);
704717f4a47SSascha Wildner if (ret) {
705717f4a47SSascha Wildner kfree(ifbdev);
706717f4a47SSascha Wildner return ret;
707717f4a47SSascha Wildner }
708717f4a47SSascha Wildner
709ba55f2f5SFrançois Tigeot dev_priv->fbdev = ifbdev;
7101b13d190SFrançois Tigeot INIT_WORK(&dev_priv->fbdev_suspend_work, intel_fbdev_suspend_worker);
7111b13d190SFrançois Tigeot
712717f4a47SSascha Wildner drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
713717f4a47SSascha Wildner
714717f4a47SSascha Wildner return 0;
715717f4a47SSascha Wildner }
716717f4a47SSascha Wildner
intel_fbdev_initial_config(void * data,async_cookie_t cookie)717aee94f86SFrançois Tigeot static void intel_fbdev_initial_config(void *data, async_cookie_t cookie)
718717f4a47SSascha Wildner {
7191487f786SFrançois Tigeot struct intel_fbdev *ifbdev = data;
720717f4a47SSascha Wildner
721717f4a47SSascha Wildner /* Due to peculiar init order wrt to hpd handling this is separate. */
722aee94f86SFrançois Tigeot if (drm_fb_helper_initial_config(&ifbdev->helper,
723aee94f86SFrançois Tigeot ifbdev->preferred_bpp))
724*3f2dd94aSFrançois Tigeot intel_fbdev_unregister(to_i915(ifbdev->helper.dev));
725aee94f86SFrançois Tigeot }
726aee94f86SFrançois Tigeot
intel_fbdev_initial_config_async(struct drm_device * dev)727aee94f86SFrançois Tigeot void intel_fbdev_initial_config_async(struct drm_device *dev)
728aee94f86SFrançois Tigeot {
7291487f786SFrançois Tigeot struct intel_fbdev *ifbdev = to_i915(dev)->fbdev;
7301487f786SFrançois Tigeot
7314be47400SFrançois Tigeot if (!ifbdev)
7324be47400SFrançois Tigeot return;
7334be47400SFrançois Tigeot
7341487f786SFrançois Tigeot ifbdev->cookie = async_schedule(intel_fbdev_initial_config, ifbdev);
7351487f786SFrançois Tigeot }
7361487f786SFrançois Tigeot
intel_fbdev_sync(struct intel_fbdev * ifbdev)7371487f786SFrançois Tigeot static void intel_fbdev_sync(struct intel_fbdev *ifbdev)
7381487f786SFrançois Tigeot {
7391487f786SFrançois Tigeot if (!ifbdev->cookie)
7401487f786SFrançois Tigeot return;
7411487f786SFrançois Tigeot
7421487f786SFrançois Tigeot /* Only serialises with all preceding async calls, hence +1 */
7431487f786SFrançois Tigeot async_synchronize_cookie(ifbdev->cookie + 1);
7441487f786SFrançois Tigeot ifbdev->cookie = 0;
745717f4a47SSascha Wildner }
746717f4a47SSascha Wildner
intel_fbdev_unregister(struct drm_i915_private * dev_priv)747*3f2dd94aSFrançois Tigeot void intel_fbdev_unregister(struct drm_i915_private *dev_priv)
748717f4a47SSascha Wildner {
7491487f786SFrançois Tigeot struct intel_fbdev *ifbdev = dev_priv->fbdev;
7501487f786SFrançois Tigeot
7511487f786SFrançois Tigeot if (!ifbdev)
752717f4a47SSascha Wildner return;
753717f4a47SSascha Wildner
75487df8fc6SFrançois Tigeot cancel_work_sync(&dev_priv->fbdev_suspend_work);
755aee94f86SFrançois Tigeot if (!current_is_async())
7561487f786SFrançois Tigeot intel_fbdev_sync(ifbdev);
7571487f786SFrançois Tigeot
758*3f2dd94aSFrançois Tigeot drm_fb_helper_unregister_fbi(&ifbdev->helper);
759*3f2dd94aSFrançois Tigeot }
760*3f2dd94aSFrançois Tigeot
intel_fbdev_fini(struct drm_i915_private * dev_priv)761*3f2dd94aSFrançois Tigeot void intel_fbdev_fini(struct drm_i915_private *dev_priv)
762*3f2dd94aSFrançois Tigeot {
763*3f2dd94aSFrançois Tigeot struct intel_fbdev *ifbdev = fetch_and_zero(&dev_priv->fbdev);
764*3f2dd94aSFrançois Tigeot
765*3f2dd94aSFrançois Tigeot if (!ifbdev)
766*3f2dd94aSFrançois Tigeot return;
767*3f2dd94aSFrançois Tigeot
7681487f786SFrançois Tigeot intel_fbdev_destroy(ifbdev);
769717f4a47SSascha Wildner }
770717f4a47SSascha Wildner
intel_fbdev_set_suspend(struct drm_device * dev,int state,bool synchronous)7711b13d190SFrançois Tigeot void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous)
772717f4a47SSascha Wildner {
773717f4a47SSascha Wildner #if 0
774bf017597SFrançois Tigeot struct drm_i915_private *dev_priv = to_i915(dev);
775717f4a47SSascha Wildner struct intel_fbdev *ifbdev = dev_priv->fbdev;
776717f4a47SSascha Wildner struct fb_info *info;
777717f4a47SSascha Wildner
778a85cb24fSFrançois Tigeot if (!ifbdev || !ifbdev->vma)
779717f4a47SSascha Wildner return;
780717f4a47SSascha Wildner
781717f4a47SSascha Wildner info = ifbdev->helper.fbdev;
782717f4a47SSascha Wildner
7831b13d190SFrançois Tigeot if (synchronous) {
7841b13d190SFrançois Tigeot /* Flush any pending work to turn the console on, and then
7851b13d190SFrançois Tigeot * wait to turn it off. It must be synchronous as we are
7861b13d190SFrançois Tigeot * about to suspend or unload the driver.
7871b13d190SFrançois Tigeot *
7881b13d190SFrançois Tigeot * Note that from within the work-handler, we cannot flush
7891b13d190SFrançois Tigeot * ourselves, so only flush outstanding work upon suspend!
7901b13d190SFrançois Tigeot */
7911b13d190SFrançois Tigeot if (state != FBINFO_STATE_RUNNING)
7921b13d190SFrançois Tigeot flush_work(&dev_priv->fbdev_suspend_work);
7931b13d190SFrançois Tigeot console_lock();
7941b13d190SFrançois Tigeot } else {
7951b13d190SFrançois Tigeot /*
7961b13d190SFrançois Tigeot * The console lock can be pretty contented on resume due
7971b13d190SFrançois Tigeot * to all the printk activity. Try to keep it out of the hot
7981b13d190SFrançois Tigeot * path of resume if possible.
7991b13d190SFrançois Tigeot */
8001b13d190SFrançois Tigeot WARN_ON(state != FBINFO_STATE_RUNNING);
8011b13d190SFrançois Tigeot if (!console_trylock()) {
8021b13d190SFrançois Tigeot /* Don't block our own workqueue as this can
8031b13d190SFrançois Tigeot * be run in parallel with other i915.ko tasks.
8041b13d190SFrançois Tigeot */
8051b13d190SFrançois Tigeot schedule_work(&dev_priv->fbdev_suspend_work);
8061b13d190SFrançois Tigeot return;
8071b13d190SFrançois Tigeot }
8081b13d190SFrançois Tigeot }
8091b13d190SFrançois Tigeot
810717f4a47SSascha Wildner /* On resume from hibernation: If the object is shmemfs backed, it has
811717f4a47SSascha Wildner * been restored from swap. If the object is stolen however, it will be
812717f4a47SSascha Wildner * full of whatever garbage was left in there.
813717f4a47SSascha Wildner */
8141b13d190SFrançois Tigeot if (state == FBINFO_STATE_RUNNING && ifbdev->fb->obj->stolen)
815717f4a47SSascha Wildner memset_io(info->screen_base, 0, info->screen_size);
816717f4a47SSascha Wildner
817a05eeebfSFrançois Tigeot drm_fb_helper_set_suspend(&ifbdev->helper, state);
8181b13d190SFrançois Tigeot console_unlock();
819717f4a47SSascha Wildner #endif
820717f4a47SSascha Wildner }
821717f4a47SSascha Wildner
intel_fbdev_output_poll_changed(struct drm_device * dev)822717f4a47SSascha Wildner void intel_fbdev_output_poll_changed(struct drm_device *dev)
823717f4a47SSascha Wildner {
824bf017597SFrançois Tigeot struct intel_fbdev *ifbdev = to_i915(dev)->fbdev;
825bf017597SFrançois Tigeot
826*3f2dd94aSFrançois Tigeot if (!ifbdev)
827*3f2dd94aSFrançois Tigeot return;
828*3f2dd94aSFrançois Tigeot
829*3f2dd94aSFrançois Tigeot intel_fbdev_sync(ifbdev);
830*3f2dd94aSFrançois Tigeot if (ifbdev->vma)
831bf017597SFrançois Tigeot drm_fb_helper_hotplug_event(&ifbdev->helper);
832717f4a47SSascha Wildner }
833717f4a47SSascha Wildner
intel_fbdev_restore_mode(struct drm_device * dev)834717f4a47SSascha Wildner void intel_fbdev_restore_mode(struct drm_device *dev)
835717f4a47SSascha Wildner {
836bf017597SFrançois Tigeot struct intel_fbdev *ifbdev = to_i915(dev)->fbdev;
837717f4a47SSascha Wildner
838a05eeebfSFrançois Tigeot if (!ifbdev)
839717f4a47SSascha Wildner return;
840717f4a47SSascha Wildner
8411487f786SFrançois Tigeot intel_fbdev_sync(ifbdev);
842a85cb24fSFrançois Tigeot if (!ifbdev->vma)
843bf017597SFrançois Tigeot return;
844a05eeebfSFrançois Tigeot
8451487f786SFrançois Tigeot #ifdef __DragonFly__
8469c52345dSzrj /* XXX: avoid dead-locking the Xorg on exit */
8479c52345dSzrj if (mutex_is_locked(&dev->mode_config.mutex)) {
8489c52345dSzrj DRM_ERROR("fubar while trying to restore kms_console\n");
8499c52345dSzrj return; /* drm_modeset_unlock_all(dev) ? */
8509c52345dSzrj }
8511487f786SFrançois Tigeot #endif
8529c52345dSzrj
853a85cb24fSFrançois Tigeot if (drm_fb_helper_restore_fbdev_mode_unlocked(&ifbdev->helper) == 0)
854a85cb24fSFrançois Tigeot intel_fbdev_invalidate(ifbdev);
855717f4a47SSascha Wildner }
856