1 | /* |
2 | * Copyright © 2007 David Airlie |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice (including the next |
12 | * paragraph) shall be included in all copies or substantial portions of the |
13 | * Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
21 | * DEALINGS IN THE SOFTWARE. |
22 | * |
23 | * Authors: |
24 | * David Airlie |
25 | */ |
26 | |
27 | #include <linux/module.h> |
28 | #include <linux/kernel.h> |
29 | #include <linux/errno.h> |
30 | #include <linux/string.h> |
31 | #include <linux/mm.h> |
32 | #include <linux/tty.h> |
33 | #include <linux/sysrq.h> |
34 | #include <linux/delay.h> |
35 | #include <linux/fb.h> |
36 | #include <linux/init.h> |
37 | #include <linux/vga_switcheroo.h> |
38 | #include <linux/err.h> |
39 | |
40 | #include <drm/drmP.h> |
41 | #include <drm/drm_crtc.h> |
42 | #include <drm/drm_fb_helper.h> |
43 | #include "intel_drv.h" |
44 | #include <drm/i915_drm.h> |
45 | #include "i915_drv.h" |
46 | |
47 | #ifdef __NetBSD__ |
48 | #include "intelfb.h" |
49 | #endif |
50 | |
51 | #ifndef __NetBSD__ |
52 | static struct fb_ops intelfb_ops = { |
53 | .owner = THIS_MODULE, |
54 | .fb_check_var = drm_fb_helper_check_var, |
55 | .fb_set_par = drm_fb_helper_set_par, |
56 | .fb_fillrect = cfb_fillrect, |
57 | .fb_copyarea = cfb_copyarea, |
58 | .fb_imageblit = cfb_imageblit, |
59 | .fb_pan_display = drm_fb_helper_pan_display, |
60 | .fb_blank = drm_fb_helper_blank, |
61 | .fb_setcmap = drm_fb_helper_setcmap, |
62 | .fb_debug_enter = drm_fb_helper_debug_enter, |
63 | .fb_debug_leave = drm_fb_helper_debug_leave, |
64 | }; |
65 | #endif |
66 | |
67 | static int intelfb_alloc(struct drm_fb_helper *helper, |
68 | struct drm_fb_helper_surface_size *sizes) |
69 | { |
70 | struct intel_fbdev *ifbdev = |
71 | container_of(helper, struct intel_fbdev, helper); |
72 | struct drm_framebuffer *fb; |
73 | struct drm_device *dev = helper->dev; |
74 | static const struct drm_mode_fb_cmd2 zero_mode_cmd; |
75 | struct drm_mode_fb_cmd2 mode_cmd = zero_mode_cmd; |
76 | struct drm_i915_gem_object *obj; |
77 | int size, ret; |
78 | |
79 | /* we don't do packed 24bpp */ |
80 | if (sizes->surface_bpp == 24) |
81 | sizes->surface_bpp = 32; |
82 | |
83 | mode_cmd.width = sizes->surface_width; |
84 | mode_cmd.height = sizes->surface_height; |
85 | |
86 | #ifdef __NetBSD__ |
87 | mode_cmd.pitches[0] = round_up(mode_cmd.width * |
88 | DIV_ROUND_UP(sizes->surface_bpp, 8), 64); |
89 | #else |
90 | mode_cmd.pitches[0] = ALIGN(mode_cmd.width * |
91 | DIV_ROUND_UP(sizes->surface_bpp, 8), 64); |
92 | #endif |
93 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, |
94 | sizes->surface_depth); |
95 | |
96 | size = mode_cmd.pitches[0] * mode_cmd.height; |
97 | #ifdef __NetBSD__ |
98 | size = round_up(size, PAGE_SIZE); |
99 | #else |
100 | size = ALIGN(size, PAGE_SIZE); |
101 | #endif |
102 | obj = i915_gem_object_create_stolen(dev, size); |
103 | if (obj == NULL) |
104 | obj = i915_gem_alloc_object(dev, size); |
105 | if (!obj) { |
106 | DRM_ERROR("failed to allocate framebuffer\n" ); |
107 | ret = -ENOMEM; |
108 | goto out; |
109 | } |
110 | |
111 | /* Flush everything out, we'll be doing GTT only from now on */ |
112 | ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); |
113 | if (ret) { |
114 | DRM_ERROR("failed to pin obj: %d\n" , ret); |
115 | goto out_unref; |
116 | } |
117 | |
118 | fb = __intel_framebuffer_create(dev, &mode_cmd, obj); |
119 | if (IS_ERR(fb)) { |
120 | ret = PTR_ERR(fb); |
121 | goto out_unpin; |
122 | } |
123 | |
124 | ifbdev->fb = to_intel_framebuffer(fb); |
125 | |
126 | return 0; |
127 | |
128 | out_unpin: |
129 | i915_gem_object_ggtt_unpin(obj); |
130 | out_unref: |
131 | drm_gem_object_unreference(&obj->base); |
132 | out: |
133 | return ret; |
134 | } |
135 | |
136 | static int intelfb_create(struct drm_fb_helper *helper, |
137 | struct drm_fb_helper_surface_size *sizes) |
138 | { |
139 | struct intel_fbdev *ifbdev = |
140 | container_of(helper, struct intel_fbdev, helper); |
141 | struct intel_framebuffer *intel_fb = ifbdev->fb; |
142 | struct drm_device *dev = helper->dev; |
143 | struct drm_i915_private *dev_priv = dev->dev_private; |
144 | #ifndef __NetBSD__ |
145 | struct fb_info *info; |
146 | #endif |
147 | struct drm_framebuffer *fb; |
148 | struct drm_i915_gem_object *obj; |
149 | int size, ret; |
150 | bool prealloc = false; |
151 | |
152 | mutex_lock(&dev->struct_mutex); |
153 | |
154 | if (intel_fb && |
155 | (sizes->fb_width > intel_fb->base.width || |
156 | sizes->fb_height > intel_fb->base.height)) { |
157 | DRM_DEBUG_KMS("BIOS fb too small (%dx%d), we require (%dx%d)," |
158 | " releasing it\n" , |
159 | intel_fb->base.width, intel_fb->base.height, |
160 | sizes->fb_width, sizes->fb_height); |
161 | drm_framebuffer_unreference(&intel_fb->base); |
162 | intel_fb = ifbdev->fb = NULL; |
163 | } |
164 | if (!intel_fb || WARN_ON(!intel_fb->obj)) { |
165 | DRM_DEBUG_KMS("no BIOS fb, allocating a new one\n" ); |
166 | ret = intelfb_alloc(helper, sizes); |
167 | if (ret) |
168 | goto out_unlock; |
169 | intel_fb = ifbdev->fb; |
170 | } else { |
171 | DRM_DEBUG_KMS("re-using BIOS fb\n" ); |
172 | prealloc = true; |
173 | sizes->fb_width = intel_fb->base.width; |
174 | sizes->fb_height = intel_fb->base.height; |
175 | } |
176 | |
177 | obj = intel_fb->obj; |
178 | size = obj->base.size; |
179 | |
180 | #ifdef __NetBSD__ |
181 | { |
182 | static const struct intelfb_attach_args zero_ifa; |
183 | struct intelfb_attach_args ifa = zero_ifa; |
184 | |
185 | ifa.ifa_drm_dev = dev; |
186 | ifa.ifa_fb_helper = helper; |
187 | ifa.ifa_fb_sizes = *sizes; |
188 | ifa.ifa_fb_bst = dev->pdev->pd_pa.pa_memt; |
189 | ifa.ifa_fb_addr = (dev_priv->gtt.mappable_base + |
190 | i915_gem_obj_ggtt_offset(obj)); |
191 | ifa.ifa_fb_size = size; |
192 | ifa.ifa_fb_zero = (ifbdev->fb->obj->stolen && !prealloc); |
193 | |
194 | /* |
195 | * XXX Should do this asynchronously, since we hold |
196 | * dev->struct_mutex. |
197 | */ |
198 | helper->fbdev = config_found_ia(dev->dev, "intelfbbus" , &ifa, NULL); |
199 | if (helper->fbdev == NULL) { |
200 | DRM_ERROR("unable to attach intelfb\n" ); |
201 | ret = -ENXIO; |
202 | goto out_unpin; |
203 | } |
204 | fb = &ifbdev->fb->base; |
205 | ifbdev->helper.fb = fb; |
206 | } |
207 | #else |
208 | info = framebuffer_alloc(0, &dev->pdev->dev); |
209 | if (!info) { |
210 | ret = -ENOMEM; |
211 | goto out_unpin; |
212 | } |
213 | |
214 | info->par = helper; |
215 | |
216 | fb = &ifbdev->fb->base; |
217 | |
218 | ifbdev->helper.fb = fb; |
219 | ifbdev->helper.fbdev = info; |
220 | |
221 | strcpy(info->fix.id, "inteldrmfb" ); |
222 | |
223 | info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; |
224 | info->fbops = &intelfb_ops; |
225 | |
226 | ret = fb_alloc_cmap(&info->cmap, 256, 0); |
227 | if (ret) { |
228 | ret = -ENOMEM; |
229 | goto out_unpin; |
230 | } |
231 | /* setup aperture base/size for vesafb takeover */ |
232 | info->apertures = alloc_apertures(1); |
233 | if (!info->apertures) { |
234 | ret = -ENOMEM; |
235 | goto out_unpin; |
236 | } |
237 | info->apertures->ranges[0].base = dev->mode_config.fb_base; |
238 | info->apertures->ranges[0].size = dev_priv->gtt.mappable_end; |
239 | |
240 | info->fix.smem_start = dev->mode_config.fb_base + i915_gem_obj_ggtt_offset(obj); |
241 | info->fix.smem_len = size; |
242 | |
243 | info->screen_base = |
244 | ioremap_wc(dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj), |
245 | size); |
246 | if (!info->screen_base) { |
247 | ret = -ENOSPC; |
248 | goto out_unpin; |
249 | } |
250 | info->screen_size = size; |
251 | |
252 | /* This driver doesn't need a VT switch to restore the mode on resume */ |
253 | info->skip_vt_switch = true; |
254 | |
255 | drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); |
256 | drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); |
257 | |
258 | /* If the object is shmemfs backed, it will have given us zeroed pages. |
259 | * If the object is stolen however, it will be full of whatever |
260 | * garbage was left in there. |
261 | */ |
262 | if (ifbdev->fb->obj->stolen && !prealloc) |
263 | memset_io(info->screen_base, 0, info->screen_size); |
264 | #endif |
265 | |
266 | /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ |
267 | |
268 | DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08lx, bo %p\n" , |
269 | fb->width, fb->height, |
270 | i915_gem_obj_ggtt_offset(obj), obj); |
271 | |
272 | mutex_unlock(&dev->struct_mutex); |
273 | #ifndef __NetBSD__ |
274 | vga_switcheroo_client_fb_set(dev->pdev, info); |
275 | #endif |
276 | return 0; |
277 | |
278 | out_unpin: |
279 | i915_gem_object_ggtt_unpin(obj); |
280 | drm_gem_object_unreference(&obj->base); |
281 | out_unlock: |
282 | mutex_unlock(&dev->struct_mutex); |
283 | return ret; |
284 | } |
285 | |
286 | /** Sets the color ramps on behalf of RandR */ |
287 | static void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, |
288 | u16 blue, int regno) |
289 | { |
290 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
291 | |
292 | intel_crtc->lut_r[regno] = red >> 8; |
293 | intel_crtc->lut_g[regno] = green >> 8; |
294 | intel_crtc->lut_b[regno] = blue >> 8; |
295 | } |
296 | |
297 | static void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, |
298 | u16 *blue, int regno) |
299 | { |
300 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
301 | |
302 | *red = intel_crtc->lut_r[regno] << 8; |
303 | *green = intel_crtc->lut_g[regno] << 8; |
304 | *blue = intel_crtc->lut_b[regno] << 8; |
305 | } |
306 | |
307 | static struct drm_fb_helper_crtc * |
308 | intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc) |
309 | { |
310 | int i; |
311 | |
312 | for (i = 0; i < fb_helper->crtc_count; i++) |
313 | if (fb_helper->crtc_info[i].mode_set.crtc == crtc) |
314 | return &fb_helper->crtc_info[i]; |
315 | |
316 | return NULL; |
317 | } |
318 | |
319 | /* |
320 | * Try to read the BIOS display configuration and use it for the initial |
321 | * fb configuration. |
322 | * |
323 | * The BIOS or boot loader will generally create an initial display |
324 | * configuration for us that includes some set of active pipes and displays. |
325 | * This routine tries to figure out which pipes and connectors are active |
326 | * and stuffs them into the crtcs and modes array given to us by the |
327 | * drm_fb_helper code. |
328 | * |
329 | * The overall sequence is: |
330 | * intel_fbdev_init - from driver load |
331 | * intel_fbdev_init_bios - initialize the intel_fbdev using BIOS data |
332 | * drm_fb_helper_init - build fb helper structs |
333 | * drm_fb_helper_single_add_all_connectors - more fb helper structs |
334 | * intel_fbdev_initial_config - apply the config |
335 | * drm_fb_helper_initial_config - call ->probe then register_framebuffer() |
336 | * drm_setup_crtcs - build crtc config for fbdev |
337 | * intel_fb_initial_config - find active connectors etc |
338 | * drm_fb_helper_single_fb_probe - set up fbdev |
339 | * intelfb_create - re-use or alloc fb, build out fbdev structs |
340 | * |
341 | * Note that we don't make special consideration whether we could actually |
342 | * switch to the selected modes without a full modeset. E.g. when the display |
343 | * is in VGA mode we need to recalculate watermarks and set a new high-res |
344 | * framebuffer anyway. |
345 | */ |
346 | static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, |
347 | struct drm_fb_helper_crtc **crtcs, |
348 | struct drm_display_mode **modes, |
349 | bool *enabled, int width, int height) |
350 | { |
351 | struct drm_device *dev = fb_helper->dev; |
352 | int i, j; |
353 | bool *save_enabled; |
354 | bool fallback = true; |
355 | int num_connectors_enabled = 0; |
356 | int num_connectors_detected = 0; |
357 | |
358 | /* |
359 | * If the user specified any force options, just bail here |
360 | * and use that config. |
361 | */ |
362 | for (i = 0; i < fb_helper->connector_count; i++) { |
363 | struct drm_fb_helper_connector *fb_conn; |
364 | struct drm_connector *connector; |
365 | |
366 | fb_conn = fb_helper->connector_info[i]; |
367 | connector = fb_conn->connector; |
368 | |
369 | if (!enabled[i]) |
370 | continue; |
371 | |
372 | if (connector->force != DRM_FORCE_UNSPECIFIED) |
373 | return false; |
374 | } |
375 | |
376 | save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), |
377 | GFP_KERNEL); |
378 | if (!save_enabled) |
379 | return false; |
380 | |
381 | memcpy(save_enabled, enabled, dev->mode_config.num_connector); |
382 | |
383 | for (i = 0; i < fb_helper->connector_count; i++) { |
384 | struct drm_fb_helper_connector *fb_conn; |
385 | struct drm_connector *connector; |
386 | struct drm_encoder *encoder; |
387 | struct drm_fb_helper_crtc *new_crtc; |
388 | |
389 | fb_conn = fb_helper->connector_info[i]; |
390 | connector = fb_conn->connector; |
391 | |
392 | if (connector->status == connector_status_connected) |
393 | num_connectors_detected++; |
394 | |
395 | if (!enabled[i]) { |
396 | DRM_DEBUG_KMS("connector %d not enabled, skipping\n" , |
397 | connector->base.id); |
398 | continue; |
399 | } |
400 | |
401 | encoder = connector->encoder; |
402 | if (!encoder || WARN_ON(!encoder->crtc)) { |
403 | DRM_DEBUG_KMS("connector %d has no encoder or crtc, skipping\n" , |
404 | connector->base.id); |
405 | enabled[i] = false; |
406 | continue; |
407 | } |
408 | |
409 | num_connectors_enabled++; |
410 | |
411 | new_crtc = intel_fb_helper_crtc(fb_helper, encoder->crtc); |
412 | |
413 | /* |
414 | * Make sure we're not trying to drive multiple connectors |
415 | * with a single CRTC, since our cloning support may not |
416 | * match the BIOS. |
417 | */ |
418 | for (j = 0; j < fb_helper->connector_count; j++) { |
419 | if (crtcs[j] == new_crtc) { |
420 | DRM_DEBUG_KMS("fallback: cloned configuration\n" ); |
421 | fallback = true; |
422 | goto out; |
423 | } |
424 | } |
425 | |
426 | DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n" , |
427 | fb_conn->connector->base.id); |
428 | |
429 | /* go for command line mode first */ |
430 | modes[i] = drm_pick_cmdline_mode(fb_conn, width, height); |
431 | |
432 | /* try for preferred next */ |
433 | if (!modes[i]) { |
434 | DRM_DEBUG_KMS("looking for preferred mode on connector %d\n" , |
435 | fb_conn->connector->base.id); |
436 | modes[i] = drm_has_preferred_mode(fb_conn, width, |
437 | height); |
438 | } |
439 | |
440 | /* No preferred mode marked by the EDID? Are there any modes? */ |
441 | if (!modes[i] && !list_empty(&connector->modes)) { |
442 | DRM_DEBUG_KMS("using first mode listed on connector %s\n" , |
443 | drm_get_connector_name(connector)); |
444 | modes[i] = list_first_entry(&connector->modes, |
445 | struct drm_display_mode, |
446 | head); |
447 | } |
448 | |
449 | /* last resort: use current mode */ |
450 | if (!modes[i]) { |
451 | /* |
452 | * IMPORTANT: We want to use the adjusted mode (i.e. |
453 | * after the panel fitter upscaling) as the initial |
454 | * config, not the input mode, which is what crtc->mode |
455 | * usually contains. But since our current fastboot |
456 | * code puts a mode derived from the post-pfit timings |
457 | * into crtc->mode this works out correctly. We don't |
458 | * use hwmode anywhere right now, so use it for this |
459 | * since the fb helper layer wants a pointer to |
460 | * something we own. |
461 | */ |
462 | intel_mode_from_pipe_config(&encoder->crtc->hwmode, |
463 | &to_intel_crtc(encoder->crtc)->config); |
464 | modes[i] = &encoder->crtc->hwmode; |
465 | } |
466 | crtcs[i] = new_crtc; |
467 | |
468 | DRM_DEBUG_KMS("connector %s on crtc %d: %s\n" , |
469 | drm_get_connector_name(connector), |
470 | encoder->crtc->base.id, |
471 | modes[i]->name); |
472 | |
473 | fallback = false; |
474 | } |
475 | |
476 | /* |
477 | * If the BIOS didn't enable everything it could, fall back to have the |
478 | * same user experiencing of lighting up as much as possible like the |
479 | * fbdev helper library. |
480 | */ |
481 | if (num_connectors_enabled != num_connectors_detected && |
482 | num_connectors_enabled < INTEL_INFO(dev)->num_pipes) { |
483 | DRM_DEBUG_KMS("fallback: Not all outputs enabled\n" ); |
484 | DRM_DEBUG_KMS("Enabled: %i, detected: %i\n" , num_connectors_enabled, |
485 | num_connectors_detected); |
486 | fallback = true; |
487 | } |
488 | |
489 | out: |
490 | if (fallback) { |
491 | DRM_DEBUG_KMS("Not using firmware configuration\n" ); |
492 | memcpy(enabled, save_enabled, dev->mode_config.num_connector); |
493 | kfree(save_enabled); |
494 | return false; |
495 | } |
496 | |
497 | kfree(save_enabled); |
498 | return true; |
499 | } |
500 | |
501 | static struct drm_fb_helper_funcs intel_fb_helper_funcs = { |
502 | .initial_config = intel_fb_initial_config, |
503 | .gamma_set = intel_crtc_fb_gamma_set, |
504 | .gamma_get = intel_crtc_fb_gamma_get, |
505 | .fb_probe = intelfb_create, |
506 | }; |
507 | |
508 | static void intel_fbdev_destroy(struct drm_device *dev, |
509 | struct intel_fbdev *ifbdev) |
510 | { |
511 | #ifdef __NetBSD__ |
512 | int ret; |
513 | #endif |
514 | |
515 | #ifdef __NetBSD__ |
516 | /* XXX errno NetBSD->Linux */ |
517 | ret = -config_detach(ifbdev->helper.fbdev, DETACH_FORCE); |
518 | if (ret) |
519 | DRM_ERROR("failed to detach intelfb: %d\n" , ret); |
520 | ifbdev->helper.fbdev = NULL; |
521 | #else |
522 | if (ifbdev->helper.fbdev) { |
523 | struct fb_info *info = ifbdev->helper.fbdev; |
524 | |
525 | unregister_framebuffer(info); |
526 | iounmap(info->screen_base); |
527 | if (info->cmap.len) |
528 | fb_dealloc_cmap(&info->cmap); |
529 | |
530 | framebuffer_release(info); |
531 | } |
532 | #endif |
533 | |
534 | drm_fb_helper_fini(&ifbdev->helper); |
535 | |
536 | drm_framebuffer_unregister_private(&ifbdev->fb->base); |
537 | drm_framebuffer_remove(&ifbdev->fb->base); |
538 | } |
539 | |
540 | /* |
541 | * Build an intel_fbdev struct using a BIOS allocated framebuffer, if possible. |
542 | * The core display code will have read out the current plane configuration, |
543 | * so we use that to figure out if there's an object for us to use as the |
544 | * fb, and if so, we re-use it for the fbdev configuration. |
545 | * |
546 | * Note we only support a single fb shared across pipes for boot (mostly for |
547 | * fbcon), so we just find the biggest and use that. |
548 | */ |
549 | static bool intel_fbdev_init_bios(struct drm_device *dev, |
550 | struct intel_fbdev *ifbdev) |
551 | { |
552 | struct intel_framebuffer *fb = NULL; |
553 | struct drm_crtc *crtc; |
554 | struct intel_crtc *intel_crtc; |
555 | struct intel_plane_config *plane_config = NULL; |
556 | unsigned int max_size = 0; |
557 | |
558 | if (!i915.fastboot) |
559 | return false; |
560 | |
561 | /* Find the largest fb */ |
562 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
563 | intel_crtc = to_intel_crtc(crtc); |
564 | |
565 | if (!intel_crtc->active || !crtc->primary->fb) { |
566 | DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n" , |
567 | pipe_name(intel_crtc->pipe)); |
568 | continue; |
569 | } |
570 | |
571 | if (intel_crtc->plane_config.size > max_size) { |
572 | DRM_DEBUG_KMS("found possible fb from plane %c\n" , |
573 | pipe_name(intel_crtc->pipe)); |
574 | plane_config = &intel_crtc->plane_config; |
575 | fb = to_intel_framebuffer(crtc->primary->fb); |
576 | max_size = plane_config->size; |
577 | } |
578 | } |
579 | |
580 | if (!fb) { |
581 | DRM_DEBUG_KMS("no active fbs found, not using BIOS config\n" ); |
582 | goto out; |
583 | } |
584 | |
585 | /* Now make sure all the pipes will fit into it */ |
586 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
587 | unsigned int cur_size; |
588 | |
589 | intel_crtc = to_intel_crtc(crtc); |
590 | |
591 | if (!intel_crtc->active) { |
592 | DRM_DEBUG_KMS("pipe %c not active, skipping\n" , |
593 | pipe_name(intel_crtc->pipe)); |
594 | continue; |
595 | } |
596 | |
597 | DRM_DEBUG_KMS("checking plane %c for BIOS fb\n" , |
598 | pipe_name(intel_crtc->pipe)); |
599 | |
600 | /* |
601 | * See if the plane fb we found above will fit on this |
602 | * pipe. Note we need to use the selected fb's pitch and bpp |
603 | * rather than the current pipe's, since they differ. |
604 | */ |
605 | cur_size = intel_crtc->config.adjusted_mode.crtc_hdisplay; |
606 | cur_size = cur_size * fb->base.bits_per_pixel / 8; |
607 | if (fb->base.pitches[0] < cur_size) { |
608 | DRM_DEBUG_KMS("fb not wide enough for plane %c (%d vs %d)\n" , |
609 | pipe_name(intel_crtc->pipe), |
610 | cur_size, fb->base.pitches[0]); |
611 | plane_config = NULL; |
612 | fb = NULL; |
613 | break; |
614 | } |
615 | |
616 | cur_size = intel_crtc->config.adjusted_mode.crtc_vdisplay; |
617 | #ifdef __NetBSD__ |
618 | cur_size = round_up(cur_size, plane_config->tiled ? (IS_GEN2(dev) ? 16 : 8) : 1); |
619 | #else |
620 | cur_size = ALIGN(cur_size, plane_config->tiled ? (IS_GEN2(dev) ? 16 : 8) : 1); |
621 | #endif |
622 | cur_size *= fb->base.pitches[0]; |
623 | DRM_DEBUG_KMS("pipe %c area: %dx%d, bpp: %d, size: %d\n" , |
624 | pipe_name(intel_crtc->pipe), |
625 | intel_crtc->config.adjusted_mode.crtc_hdisplay, |
626 | intel_crtc->config.adjusted_mode.crtc_vdisplay, |
627 | fb->base.bits_per_pixel, |
628 | cur_size); |
629 | |
630 | if (cur_size > max_size) { |
631 | DRM_DEBUG_KMS("fb not big enough for plane %c (%d vs %d)\n" , |
632 | pipe_name(intel_crtc->pipe), |
633 | cur_size, max_size); |
634 | plane_config = NULL; |
635 | fb = NULL; |
636 | break; |
637 | } |
638 | |
639 | DRM_DEBUG_KMS("fb big enough for plane %c (%d >= %d)\n" , |
640 | pipe_name(intel_crtc->pipe), |
641 | max_size, cur_size); |
642 | } |
643 | |
644 | if (!fb) { |
645 | DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n" ); |
646 | goto out; |
647 | } |
648 | |
649 | ifbdev->preferred_bpp = fb->base.bits_per_pixel; |
650 | ifbdev->fb = fb; |
651 | |
652 | drm_framebuffer_reference(&ifbdev->fb->base); |
653 | |
654 | /* Final pass to check if any active pipes don't have fbs */ |
655 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
656 | intel_crtc = to_intel_crtc(crtc); |
657 | |
658 | if (!intel_crtc->active) |
659 | continue; |
660 | |
661 | WARN(!crtc->primary->fb, |
662 | "re-used BIOS config but lost an fb on crtc %d\n" , |
663 | crtc->base.id); |
664 | } |
665 | |
666 | |
667 | DRM_DEBUG_KMS("using BIOS fb for initial console\n" ); |
668 | return true; |
669 | |
670 | out: |
671 | |
672 | return false; |
673 | } |
674 | |
675 | int intel_fbdev_init(struct drm_device *dev) |
676 | { |
677 | struct intel_fbdev *ifbdev; |
678 | struct drm_i915_private *dev_priv = dev->dev_private; |
679 | int ret; |
680 | |
681 | if (WARN_ON(INTEL_INFO(dev)->num_pipes == 0)) |
682 | return -ENODEV; |
683 | |
684 | ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL); |
685 | if (ifbdev == NULL) |
686 | return -ENOMEM; |
687 | |
688 | ifbdev->helper.funcs = &intel_fb_helper_funcs; |
689 | if (!intel_fbdev_init_bios(dev, ifbdev)) |
690 | ifbdev->preferred_bpp = 32; |
691 | |
692 | ret = drm_fb_helper_init(dev, &ifbdev->helper, |
693 | INTEL_INFO(dev)->num_pipes, 4); |
694 | if (ret) { |
695 | kfree(ifbdev); |
696 | return ret; |
697 | } |
698 | |
699 | dev_priv->fbdev = ifbdev; |
700 | drm_fb_helper_single_add_all_connectors(&ifbdev->helper); |
701 | |
702 | return 0; |
703 | } |
704 | |
705 | void intel_fbdev_initial_config(struct drm_device *dev) |
706 | { |
707 | struct drm_i915_private *dev_priv = dev->dev_private; |
708 | struct intel_fbdev *ifbdev = dev_priv->fbdev; |
709 | |
710 | /* Due to peculiar init order wrt to hpd handling this is separate. */ |
711 | drm_fb_helper_initial_config(&ifbdev->helper, ifbdev->preferred_bpp); |
712 | } |
713 | |
714 | void intel_fbdev_fini(struct drm_device *dev) |
715 | { |
716 | struct drm_i915_private *dev_priv = dev->dev_private; |
717 | if (!dev_priv->fbdev) |
718 | return; |
719 | |
720 | intel_fbdev_destroy(dev, dev_priv->fbdev); |
721 | kfree(dev_priv->fbdev); |
722 | dev_priv->fbdev = NULL; |
723 | } |
724 | |
725 | #ifndef __NetBSD__ /* XXX fb suspend */ |
726 | void intel_fbdev_set_suspend(struct drm_device *dev, int state) |
727 | { |
728 | struct drm_i915_private *dev_priv = dev->dev_private; |
729 | struct intel_fbdev *ifbdev = dev_priv->fbdev; |
730 | struct fb_info *info; |
731 | |
732 | if (!ifbdev) |
733 | return; |
734 | |
735 | info = ifbdev->helper.fbdev; |
736 | |
737 | /* On resume from hibernation: If the object is shmemfs backed, it has |
738 | * been restored from swap. If the object is stolen however, it will be |
739 | * full of whatever garbage was left in there. |
740 | */ |
741 | if (state == FBINFO_STATE_RUNNING && ifbdev->fb->obj->stolen) |
742 | memset_io(info->screen_base, 0, info->screen_size); |
743 | |
744 | fb_set_suspend(info, state); |
745 | } |
746 | #endif |
747 | |
748 | void intel_fbdev_output_poll_changed(struct drm_device *dev) |
749 | { |
750 | struct drm_i915_private *dev_priv = dev->dev_private; |
751 | if (dev_priv->fbdev) |
752 | drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); |
753 | } |
754 | |
755 | void intel_fbdev_restore_mode(struct drm_device *dev) |
756 | { |
757 | int ret; |
758 | struct drm_i915_private *dev_priv = dev->dev_private; |
759 | |
760 | if (!dev_priv->fbdev) |
761 | return; |
762 | |
763 | drm_modeset_lock_all(dev); |
764 | |
765 | ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); |
766 | if (ret) |
767 | DRM_DEBUG("failed to restore crtc mode\n" ); |
768 | |
769 | drm_modeset_unlock_all(dev); |
770 | } |
771 | |