aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_fb.c
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2008-11-07 17:24:08 -0500
committerDave Airlie <airlied@linux.ie>2008-12-29 02:47:23 -0500
commit79e539453b34e35f39299a899d263b0a1f1670bd (patch)
tree6d1285f2b78fab399aab75a3557b7d6bc0dbd112 /drivers/gpu/drm/i915/intel_fb.c
parentf453ba0460742ad027ae0c4c7d61e62817b3e7ef (diff)
DRM: i915: add mode setting support
This commit adds i915 driver support for the DRM mode setting APIs. Currently, VGA, LVDS, SDVO DVI & VGA, TV and DVO LVDS outputs are supported. HDMI, DisplayPort and additional SDVO output support will follow. Support for the mode setting code is controlled by the new 'modeset' module option. A new config option, CONFIG_DRM_I915_KMS controls the default behavior, and whether a PCI ID list is built into the module for use by user level module utilities. Note that if mode setting is enabled, user level drivers that access display registers directly or that don't use the kernel graphics memory manager will likely corrupt kernel graphics memory, disrupt output configuration (possibly leading to hangs and/or blank displays), and prevent panic/oops messages from appearing. So use caution when enabling this code; be sure your user level code supports the new interfaces. A new SysRq key, 'g', provides emergency support for switching back to the kernel's framebuffer console; which is useful for testing. Co-authors: Dave Airlie <airlied@linux.ie>, Hong Liu <hong.liu@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_fb.c')
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c926
1 files changed, 926 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
new file mode 100644
index 000000000000..a89ebea7b76d
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -0,0 +1,926 @@
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/slab.h>
34#include <linux/sysrq.h>
35#include <linux/delay.h>
36#include <linux/fb.h>
37#include <linux/init.h>
38
39#include "drmP.h"
40#include "drm.h"
41#include "drm_crtc.h"
42#include "intel_drv.h"
43#include "i915_drm.h"
44#include "i915_drv.h"
45
46struct intelfb_par {
47 struct drm_device *dev;
48 struct drm_display_mode *our_mode;
49 struct intel_framebuffer *intel_fb;
50 int crtc_count;
51 /* crtc currently bound to this */
52 uint32_t crtc_ids[2];
53};
54
55static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
56 unsigned blue, unsigned transp,
57 struct fb_info *info)
58{
59 struct intelfb_par *par = info->par;
60 struct drm_device *dev = par->dev;
61 struct drm_crtc *crtc;
62 int i;
63
64 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
65 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
66 struct drm_mode_set *modeset = &intel_crtc->mode_set;
67 struct drm_framebuffer *fb = modeset->fb;
68
69 for (i = 0; i < par->crtc_count; i++)
70 if (crtc->base.id == par->crtc_ids[i])
71 break;
72
73 if (i == par->crtc_count)
74 continue;
75
76
77 if (regno > 255)
78 return 1;
79
80 if (fb->depth == 8) {
81 intel_crtc_fb_gamma_set(crtc, red, green, blue, regno);
82 return 0;
83 }
84
85 if (regno < 16) {
86 switch (fb->depth) {
87 case 15:
88 fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
89 ((green & 0xf800) >> 6) |
90 ((blue & 0xf800) >> 11);
91 break;
92 case 16:
93 fb->pseudo_palette[regno] = (red & 0xf800) |
94 ((green & 0xfc00) >> 5) |
95 ((blue & 0xf800) >> 11);
96 break;
97 case 24:
98 case 32:
99 fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
100 (green & 0xff00) |
101 ((blue & 0xff00) >> 8);
102 break;
103 }
104 }
105 }
106 return 0;
107}
108
109static int intelfb_check_var(struct fb_var_screeninfo *var,
110 struct fb_info *info)
111{
112 struct intelfb_par *par = info->par;
113 struct intel_framebuffer *intel_fb = par->intel_fb;
114 struct drm_framebuffer *fb = &intel_fb->base;
115 int depth;
116
117 if (var->pixclock == -1 || !var->pixclock)
118 return -EINVAL;
119
120 /* Need to resize the fb object !!! */
121 if (var->xres > fb->width || var->yres > fb->height) {
122 DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height);
123 DRM_ERROR("Need resizing code.\n");
124 return -EINVAL;
125 }
126
127 switch (var->bits_per_pixel) {
128 case 16:
129 depth = (var->green.length == 6) ? 16 : 15;
130 break;
131 case 32:
132 depth = (var->transp.length > 0) ? 32 : 24;
133 break;
134 default:
135 depth = var->bits_per_pixel;
136 break;
137 }
138
139 switch (depth) {
140 case 8:
141 var->red.offset = 0;
142 var->green.offset = 0;
143 var->blue.offset = 0;
144 var->red.length = 8;
145 var->green.length = 8;
146 var->blue.length = 8;
147 var->transp.length = 0;
148 var->transp.offset = 0;
149 break;
150 case 15:
151 var->red.offset = 10;
152 var->green.offset = 5;
153 var->blue.offset = 0;
154 var->red.length = 5;
155 var->green.length = 5;
156 var->blue.length = 5;
157 var->transp.length = 1;
158 var->transp.offset = 15;
159 break;
160 case 16:
161 var->red.offset = 11;
162 var->green.offset = 5;
163 var->blue.offset = 0;
164 var->red.length = 5;
165 var->green.length = 6;
166 var->blue.length = 5;
167 var->transp.length = 0;
168 var->transp.offset = 0;
169 break;
170 case 24:
171 var->red.offset = 16;
172 var->green.offset = 8;
173 var->blue.offset = 0;
174 var->red.length = 8;
175 var->green.length = 8;
176 var->blue.length = 8;
177 var->transp.length = 0;
178 var->transp.offset = 0;
179 break;
180 case 32:
181 var->red.offset = 16;
182 var->green.offset = 8;
183 var->blue.offset = 0;
184 var->red.length = 8;
185 var->green.length = 8;
186 var->blue.length = 8;
187 var->transp.length = 8;
188 var->transp.offset = 24;
189 break;
190 default:
191 return -EINVAL;
192 }
193
194 return 0;
195}
196
197/* this will let fbcon do the mode init */
198/* FIXME: take mode config lock? */
199static int intelfb_set_par(struct fb_info *info)
200{
201 struct intelfb_par *par = info->par;
202 struct drm_device *dev = par->dev;
203 struct fb_var_screeninfo *var = &info->var;
204 int i;
205
206 DRM_DEBUG("%d %d\n", var->xres, var->pixclock);
207
208 if (var->pixclock != -1) {
209
210 DRM_ERROR("PIXEL CLCOK SET\n");
211 return -EINVAL;
212 } else {
213 struct drm_crtc *crtc;
214 int ret;
215
216 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
217 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
218
219 for (i = 0; i < par->crtc_count; i++)
220 if (crtc->base.id == par->crtc_ids[i])
221 break;
222
223 if (i == par->crtc_count)
224 continue;
225
226 if (crtc->fb == intel_crtc->mode_set.fb) {
227 mutex_lock(&dev->mode_config.mutex);
228 ret = crtc->funcs->set_config(&intel_crtc->mode_set);
229 mutex_unlock(&dev->mode_config.mutex);
230 if (ret)
231 return ret;
232 }
233 }
234 return 0;
235 }
236}
237
238static int intelfb_pan_display(struct fb_var_screeninfo *var,
239 struct fb_info *info)
240{
241 struct intelfb_par *par = info->par;
242 struct drm_device *dev = par->dev;
243 struct drm_mode_set *modeset;
244 struct drm_crtc *crtc;
245 struct intel_crtc *intel_crtc;
246 int ret = 0;
247 int i;
248
249 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
250 for (i = 0; i < par->crtc_count; i++)
251 if (crtc->base.id == par->crtc_ids[i])
252 break;
253
254 if (i == par->crtc_count)
255 continue;
256
257 intel_crtc = to_intel_crtc(crtc);
258 modeset = &intel_crtc->mode_set;
259
260 modeset->x = var->xoffset;
261 modeset->y = var->yoffset;
262
263 if (modeset->num_connectors) {
264 mutex_lock(&dev->mode_config.mutex);
265 ret = crtc->funcs->set_config(modeset);
266 mutex_unlock(&dev->mode_config.mutex);
267 if (!ret) {
268 info->var.xoffset = var->xoffset;
269 info->var.yoffset = var->yoffset;
270 }
271 }
272 }
273
274 return ret;
275}
276
277static void intelfb_on(struct fb_info *info)
278{
279 struct intelfb_par *par = info->par;
280 struct drm_device *dev = par->dev;
281 struct drm_crtc *crtc;
282 struct drm_encoder *encoder;
283 int i;
284
285 /*
286 * For each CRTC in this fb, find all associated encoders
287 * and turn them off, then turn off the CRTC.
288 */
289 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
290 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
291
292 for (i = 0; i < par->crtc_count; i++)
293 if (crtc->base.id == par->crtc_ids[i])
294 break;
295
296 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
297
298 /* Found a CRTC on this fb, now find encoders */
299 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
300 if (encoder->crtc == crtc) {
301 struct drm_encoder_helper_funcs *encoder_funcs;
302 encoder_funcs = encoder->helper_private;
303 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
304 }
305 }
306 }
307}
308
309static void intelfb_off(struct fb_info *info, int dpms_mode)
310{
311 struct intelfb_par *par = info->par;
312 struct drm_device *dev = par->dev;
313 struct drm_crtc *crtc;
314 struct drm_encoder *encoder;
315 int i;
316
317 /*
318 * For each CRTC in this fb, find all associated encoders
319 * and turn them off, then turn off the CRTC.
320 */
321 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
322 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
323
324 for (i = 0; i < par->crtc_count; i++)
325 if (crtc->base.id == par->crtc_ids[i])
326 break;
327
328 /* Found a CRTC on this fb, now find encoders */
329 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
330 if (encoder->crtc == crtc) {
331 struct drm_encoder_helper_funcs *encoder_funcs;
332 encoder_funcs = encoder->helper_private;
333 encoder_funcs->dpms(encoder, dpms_mode);
334 }
335 }
336 if (dpms_mode == DRM_MODE_DPMS_OFF)
337 crtc_funcs->dpms(crtc, dpms_mode);
338 }
339}
340
341int intelfb_blank(int blank, struct fb_info *info)
342{
343 switch (blank) {
344 case FB_BLANK_UNBLANK:
345 intelfb_on(info);
346 break;
347 case FB_BLANK_NORMAL:
348 intelfb_off(info, DRM_MODE_DPMS_STANDBY);
349 break;
350 case FB_BLANK_HSYNC_SUSPEND:
351 intelfb_off(info, DRM_MODE_DPMS_STANDBY);
352 break;
353 case FB_BLANK_VSYNC_SUSPEND:
354 intelfb_off(info, DRM_MODE_DPMS_SUSPEND);
355 break;
356 case FB_BLANK_POWERDOWN:
357 intelfb_off(info, DRM_MODE_DPMS_OFF);
358 break;
359 }
360 return 0;
361}
362
363static struct fb_ops intelfb_ops = {
364 .owner = THIS_MODULE,
365 .fb_check_var = intelfb_check_var,
366 .fb_set_par = intelfb_set_par,
367 .fb_setcolreg = intelfb_setcolreg,
368 .fb_fillrect = cfb_fillrect,
369 .fb_copyarea = cfb_copyarea,
370 .fb_imageblit = cfb_imageblit,
371 .fb_pan_display = intelfb_pan_display,
372 .fb_blank = intelfb_blank,
373};
374
375/**
376 * Curretly it is assumed that the old framebuffer is reused.
377 *
378 * LOCKING
379 * caller should hold the mode config lock.
380 *
381 */
382int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
383{
384 struct fb_info *info;
385 struct drm_framebuffer *fb;
386 struct drm_display_mode *mode = crtc->desired_mode;
387
388 fb = crtc->fb;
389 if (!fb)
390 return 1;
391
392 info = fb->fbdev;
393 if (!info)
394 return 1;
395
396 if (!mode)
397 return 1;
398
399 info->var.xres = mode->hdisplay;
400 info->var.right_margin = mode->hsync_start - mode->hdisplay;
401 info->var.hsync_len = mode->hsync_end - mode->hsync_start;
402 info->var.left_margin = mode->htotal - mode->hsync_end;
403 info->var.yres = mode->vdisplay;
404 info->var.lower_margin = mode->vsync_start - mode->vdisplay;
405 info->var.vsync_len = mode->vsync_end - mode->vsync_start;
406 info->var.upper_margin = mode->vtotal - mode->vsync_end;
407 info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
408 /* avoid overflow */
409 info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
410
411 return 0;
412}
413EXPORT_SYMBOL(intelfb_resize);
414
415static struct drm_mode_set kernelfb_mode;
416
417int intelfb_panic(struct notifier_block *n, unsigned long ununsed,
418 void *panic_str)
419{
420 DRM_ERROR("panic occurred, switching back to text console\n");
421
422 intelfb_restore();
423 return 0;
424}
425EXPORT_SYMBOL(intelfb_panic);
426
427static struct notifier_block paniced = {
428 .notifier_call = intelfb_panic,
429};
430
431int intelfb_create(struct drm_device *dev, uint32_t fb_width,
432 uint32_t fb_height, uint32_t surface_width,
433 uint32_t surface_height,
434 struct intel_framebuffer **intel_fb_p)
435{
436 struct fb_info *info;
437 struct intelfb_par *par;
438 struct drm_framebuffer *fb;
439 struct intel_framebuffer *intel_fb;
440 struct drm_mode_fb_cmd mode_cmd;
441 struct drm_gem_object *fbo = NULL;
442 struct drm_i915_gem_object *obj_priv;
443 struct device *device = &dev->pdev->dev;
444 int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1;
445
446 mode_cmd.width = surface_width;
447 mode_cmd.height = surface_height;
448
449 mode_cmd.bpp = 32;
450 mode_cmd.pitch = mode_cmd.width * ((mode_cmd.bpp + 1) / 8);
451 mode_cmd.depth = 24;
452
453 size = mode_cmd.pitch * mode_cmd.height;
454 size = ALIGN(size, PAGE_SIZE);
455 fbo = drm_gem_object_alloc(dev, size);
456 if (!fbo) {
457 printk(KERN_ERR "failed to allocate framebuffer\n");
458 ret = -ENOMEM;
459 goto out;
460 }
461 obj_priv = fbo->driver_private;
462
463 mutex_lock(&dev->struct_mutex);
464
465 ret = i915_gem_object_pin(fbo, PAGE_SIZE);
466 if (ret) {
467 DRM_ERROR("failed to pin fb: %d\n", ret);
468 goto out_unref;
469 }
470
471 /* Flush everything out, we'll be doing GTT only from now on */
472 i915_gem_object_set_to_gtt_domain(fbo, 1);
473
474 ret = intel_framebuffer_create(dev, &mode_cmd, &fb, fbo);
475 if (ret) {
476 DRM_ERROR("failed to allocate fb.\n");
477 goto out_unref;
478 }
479
480 list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
481
482 intel_fb = to_intel_framebuffer(fb);
483 *intel_fb_p = intel_fb;
484
485 info = framebuffer_alloc(sizeof(struct intelfb_par), device);
486 if (!info) {
487 ret = -ENOMEM;
488 goto out_unref;
489 }
490
491 par = info->par;
492
493 strcpy(info->fix.id, "inteldrmfb");
494 info->fix.type = FB_TYPE_PACKED_PIXELS;
495 info->fix.visual = FB_VISUAL_TRUECOLOR;
496 info->fix.type_aux = 0;
497 info->fix.xpanstep = 1; /* doing it in hw */
498 info->fix.ypanstep = 1; /* doing it in hw */
499 info->fix.ywrapstep = 0;
500 info->fix.accel = FB_ACCEL_I830;
501 info->fix.type_aux = 0;
502
503 info->flags = FBINFO_DEFAULT;
504
505 info->fbops = &intelfb_ops;
506
507 info->fix.line_length = fb->pitch;
508 info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
509 info->fix.smem_len = size;
510
511 info->flags = FBINFO_DEFAULT;
512
513 info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
514 size);
515 if (!info->screen_base) {
516 ret = -ENOSPC;
517 goto out_unref;
518 }
519 info->screen_size = size;
520
521// memset(info->screen_base, 0, size);
522
523 info->pseudo_palette = fb->pseudo_palette;
524 info->var.xres_virtual = fb->width;
525 info->var.yres_virtual = fb->height;
526 info->var.bits_per_pixel = fb->bits_per_pixel;
527 info->var.xoffset = 0;
528 info->var.yoffset = 0;
529 info->var.activate = FB_ACTIVATE_NOW;
530 info->var.height = -1;
531 info->var.width = -1;
532
533 info->var.xres = fb_width;
534 info->var.yres = fb_height;
535
536 /* FIXME: we really shouldn't expose mmio space at all */
537 info->fix.mmio_start = pci_resource_start(dev->pdev, mmio_bar);
538 info->fix.mmio_len = pci_resource_len(dev->pdev, mmio_bar);
539
540 info->pixmap.size = 64*1024;
541 info->pixmap.buf_align = 8;
542 info->pixmap.access_align = 32;
543 info->pixmap.flags = FB_PIXMAP_SYSTEM;
544 info->pixmap.scan_align = 1;
545
546 switch(fb->depth) {
547 case 8:
548 info->var.red.offset = 0;
549 info->var.green.offset = 0;
550 info->var.blue.offset = 0;
551 info->var.red.length = 8; /* 8bit DAC */
552 info->var.green.length = 8;
553 info->var.blue.length = 8;
554 info->var.transp.offset = 0;
555 info->var.transp.length = 0;
556 break;
557 case 15:
558 info->var.red.offset = 10;
559 info->var.green.offset = 5;
560 info->var.blue.offset = 0;
561 info->var.red.length = 5;
562 info->var.green.length = 5;
563 info->var.blue.length = 5;
564 info->var.transp.offset = 15;
565 info->var.transp.length = 1;
566 break;
567 case 16:
568 info->var.red.offset = 11;
569 info->var.green.offset = 5;
570 info->var.blue.offset = 0;
571 info->var.red.length = 5;
572 info->var.green.length = 6;
573 info->var.blue.length = 5;
574 info->var.transp.offset = 0;
575 break;
576 case 24:
577 info->var.red.offset = 16;
578 info->var.green.offset = 8;
579 info->var.blue.offset = 0;
580 info->var.red.length = 8;
581 info->var.green.length = 8;
582 info->var.blue.length = 8;
583 info->var.transp.offset = 0;
584 info->var.transp.length = 0;
585 break;
586 case 32:
587 info->var.red.offset = 16;
588 info->var.green.offset = 8;
589 info->var.blue.offset = 0;
590 info->var.red.length = 8;
591 info->var.green.length = 8;
592 info->var.blue.length = 8;
593 info->var.transp.offset = 24;
594 info->var.transp.length = 8;
595 break;
596 default:
597 break;
598 }
599
600 fb->fbdev = info;
601
602 par->intel_fb = intel_fb;
603 par->dev = dev;
604
605 /* To allow resizeing without swapping buffers */
606 printk("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width,
607 intel_fb->base.height, obj_priv->gtt_offset, fbo);
608
609 mutex_unlock(&dev->struct_mutex);
610 return 0;
611
612out_unref:
613 drm_gem_object_unreference(fbo);
614 mutex_unlock(&dev->struct_mutex);
615out:
616 return ret;
617}
618
619static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc)
620{
621 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
622 struct intel_framebuffer *intel_fb;
623 struct drm_framebuffer *fb;
624 struct drm_connector *connector;
625 struct fb_info *info;
626 struct intelfb_par *par;
627 struct drm_mode_set *modeset;
628 unsigned int width, height;
629 int new_fb = 0;
630 int ret, i, conn_count;
631
632 if (!drm_helper_crtc_in_use(crtc))
633 return 0;
634
635 if (!crtc->desired_mode)
636 return 0;
637
638 width = crtc->desired_mode->hdisplay;
639 height = crtc->desired_mode->vdisplay;
640
641 /* is there an fb bound to this crtc already */
642 if (!intel_crtc->mode_set.fb) {
643 ret = intelfb_create(dev, width, height, width, height, &intel_fb);
644 if (ret)
645 return -EINVAL;
646 new_fb = 1;
647 } else {
648 fb = intel_crtc->mode_set.fb;
649 intel_fb = to_intel_framebuffer(fb);
650 if ((intel_fb->base.width < width) || (intel_fb->base.height < height))
651 return -EINVAL;
652 }
653
654 info = intel_fb->base.fbdev;
655 par = info->par;
656
657 modeset = &intel_crtc->mode_set;
658 modeset->fb = &intel_fb->base;
659 conn_count = 0;
660 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
661 if (connector->encoder)
662 if (connector->encoder->crtc == modeset->crtc) {
663 modeset->connectors[conn_count] = connector;
664 conn_count++;
665 if (conn_count > INTELFB_CONN_LIMIT)
666 BUG();
667 }
668 }
669
670 for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
671 modeset->connectors[i] = NULL;
672
673 par->crtc_ids[0] = crtc->base.id;
674
675 modeset->num_connectors = conn_count;
676 if (modeset->mode != modeset->crtc->desired_mode)
677 modeset->mode = modeset->crtc->desired_mode;
678
679 par->crtc_count = 1;
680
681 if (new_fb) {
682 info->var.pixclock = -1;
683 if (register_framebuffer(info) < 0)
684 return -EINVAL;
685 } else
686 intelfb_set_par(info);
687
688 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
689 info->fix.id);
690
691 /* Switch back to kernel console on panic */
692 kernelfb_mode = *modeset;
693 atomic_notifier_chain_register(&panic_notifier_list, &paniced);
694 printk(KERN_INFO "registered panic notifier\n");
695
696 return 0;
697}
698
699static int intelfb_multi_fb_probe(struct drm_device *dev)
700{
701
702 struct drm_crtc *crtc;
703 int ret = 0;
704
705 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
706 ret = intelfb_multi_fb_probe_crtc(dev, crtc);
707 if (ret)
708 return ret;
709 }
710 return ret;
711}
712
713static int intelfb_single_fb_probe(struct drm_device *dev)
714{
715 struct drm_crtc *crtc;
716 struct drm_connector *connector;
717 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
718 unsigned int surface_width = 0, surface_height = 0;
719 int new_fb = 0;
720 int crtc_count = 0;
721 int ret, i, conn_count = 0;
722 struct intel_framebuffer *intel_fb;
723 struct fb_info *info;
724 struct intelfb_par *par;
725 struct drm_mode_set *modeset = NULL;
726
727 DRM_DEBUG("\n");
728
729 /* Get a count of crtcs now in use and new min/maxes width/heights */
730 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
731 if (!drm_helper_crtc_in_use(crtc))
732 continue;
733
734 crtc_count++;
735 if (!crtc->desired_mode)
736 continue;
737
738 /* Smallest mode determines console size... */
739 if (crtc->desired_mode->hdisplay < fb_width)
740 fb_width = crtc->desired_mode->hdisplay;
741
742 if (crtc->desired_mode->vdisplay < fb_height)
743 fb_height = crtc->desired_mode->vdisplay;
744
745 /* ... but largest for memory allocation dimensions */
746 if (crtc->desired_mode->hdisplay > surface_width)
747 surface_width = crtc->desired_mode->hdisplay;
748
749 if (crtc->desired_mode->vdisplay > surface_height)
750 surface_height = crtc->desired_mode->vdisplay;
751 }
752
753 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
754 /* hmm everyone went away - assume VGA cable just fell out
755 and will come back later. */
756 DRM_DEBUG("no CRTCs available?\n");
757 return 0;
758 }
759
760//fail
761 /* Find the fb for our new config */
762 if (list_empty(&dev->mode_config.fb_kernel_list)) {
763 DRM_DEBUG("creating new fb (console size %dx%d, "
764 "buffer size %dx%d)\n", fb_width, fb_height,
765 surface_width, surface_height);
766 ret = intelfb_create(dev, fb_width, fb_height, surface_width,
767 surface_height, &intel_fb);
768 if (ret)
769 return -EINVAL;
770 new_fb = 1;
771 } else {
772 struct drm_framebuffer *fb;
773
774 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
775 struct drm_framebuffer, filp_head);
776 intel_fb = to_intel_framebuffer(fb);
777
778 /* if someone hotplugs something bigger than we have already
779 * allocated, we are pwned. As really we can't resize an
780 * fbdev that is in the wild currently due to fbdev not really
781 * being designed for the lower layers moving stuff around
782 * under it.
783 * - so in the grand style of things - punt.
784 */
785 if ((fb->width < surface_width) ||
786 (fb->height < surface_height)) {
787 DRM_ERROR("fb not large enough for console\n");
788 return -EINVAL;
789 }
790 }
791// fail
792
793 info = intel_fb->base.fbdev;
794 par = info->par;
795
796 crtc_count = 0;
797 /*
798 * For each CRTC, set up the connector list for the CRTC's mode
799 * set configuration.
800 */
801 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
802 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
803
804 modeset = &intel_crtc->mode_set;
805 modeset->fb = &intel_fb->base;
806 conn_count = 0;
807 list_for_each_entry(connector, &dev->mode_config.connector_list,
808 head) {
809 if (!connector->encoder)
810 continue;
811
812 if(connector->encoder->crtc == modeset->crtc) {
813 modeset->connectors[conn_count++] = connector;
814 if (conn_count > INTELFB_CONN_LIMIT)
815 BUG();
816 }
817 }
818
819 /* Zero out remaining connector pointers */
820 for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
821 modeset->connectors[i] = NULL;
822
823 par->crtc_ids[crtc_count++] = crtc->base.id;
824
825 modeset->num_connectors = conn_count;
826 if (modeset->mode != modeset->crtc->desired_mode)
827 modeset->mode = modeset->crtc->desired_mode;
828 }
829 par->crtc_count = crtc_count;
830
831 if (new_fb) {
832 info->var.pixclock = -1;
833 if (register_framebuffer(info) < 0)
834 return -EINVAL;
835 } else
836 intelfb_set_par(info);
837
838 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
839 info->fix.id);
840
841 /* Switch back to kernel console on panic */
842 kernelfb_mode = *modeset;
843 atomic_notifier_chain_register(&panic_notifier_list, &paniced);
844 printk(KERN_INFO "registered panic notifier\n");
845
846 return 0;
847}
848
849/**
850 * intelfb_restore - restore the framebuffer console (kernel) config
851 *
852 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
853 */
854void intelfb_restore(void)
855{
856 drm_crtc_helper_set_config(&kernelfb_mode);
857}
858
859static void intelfb_sysrq(int dummy1, struct tty_struct *dummy3)
860{
861 intelfb_restore();
862}
863
864static struct sysrq_key_op sysrq_intelfb_restore_op = {
865 .handler = intelfb_sysrq,
866 .help_msg = "force fb",
867 .action_msg = "force restore of fb console",
868};
869
870int intelfb_probe(struct drm_device *dev)
871{
872 int ret;
873
874 DRM_DEBUG("\n");
875
876 /* something has changed in the lower levels of hell - deal with it
877 here */
878
879 /* two modes : a) 1 fb to rule all crtcs.
880 b) one fb per crtc.
881 two actions 1) new connected device
882 2) device removed.
883 case a/1 : if the fb surface isn't big enough - resize the surface fb.
884 if the fb size isn't big enough - resize fb into surface.
885 if everything big enough configure the new crtc/etc.
886 case a/2 : undo the configuration
887 possibly resize down the fb to fit the new configuration.
888 case b/1 : see if it is on a new crtc - setup a new fb and add it.
889 case b/2 : teardown the new fb.
890 */
891
892 /* mode a first */
893 /* search for an fb */
894 if (i915_fbpercrtc == 1) {
895 ret = intelfb_multi_fb_probe(dev);
896 } else {
897 ret = intelfb_single_fb_probe(dev);
898 }
899
900 register_sysrq_key('g', &sysrq_intelfb_restore_op);
901
902 return ret;
903}
904EXPORT_SYMBOL(intelfb_probe);
905
906int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
907{
908 struct fb_info *info;
909
910 if (!fb)
911 return -EINVAL;
912
913 info = fb->fbdev;
914
915 if (info) {
916 unregister_framebuffer(info);
917 iounmap(info->screen_base);
918 framebuffer_release(info);
919 }
920
921 atomic_notifier_chain_unregister(&panic_notifier_list, &paniced);
922 memset(&kernelfb_mode, 0, sizeof(struct drm_mode_set));
923 return 0;
924}
925EXPORT_SYMBOL(intelfb_remove);
926MODULE_LICENSE("GPL and additional rights");