diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_fb.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_fb.c | 926 |
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 | |||
46 | struct 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 | |||
55 | static 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 | |||
109 | static 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? */ | ||
199 | static 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 | |||
238 | static 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 | |||
277 | static 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 | |||
309 | static 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 | |||
341 | int 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 | |||
363 | static 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 | */ | ||
382 | int 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 | } | ||
413 | EXPORT_SYMBOL(intelfb_resize); | ||
414 | |||
415 | static struct drm_mode_set kernelfb_mode; | ||
416 | |||
417 | int 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 | } | ||
425 | EXPORT_SYMBOL(intelfb_panic); | ||
426 | |||
427 | static struct notifier_block paniced = { | ||
428 | .notifier_call = intelfb_panic, | ||
429 | }; | ||
430 | |||
431 | int 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 | |||
612 | out_unref: | ||
613 | drm_gem_object_unreference(fbo); | ||
614 | mutex_unlock(&dev->struct_mutex); | ||
615 | out: | ||
616 | return ret; | ||
617 | } | ||
618 | |||
619 | static 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 | |||
699 | static 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 | |||
713 | static 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 | */ | ||
854 | void intelfb_restore(void) | ||
855 | { | ||
856 | drm_crtc_helper_set_config(&kernelfb_mode); | ||
857 | } | ||
858 | |||
859 | static void intelfb_sysrq(int dummy1, struct tty_struct *dummy3) | ||
860 | { | ||
861 | intelfb_restore(); | ||
862 | } | ||
863 | |||
864 | static 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 | |||
870 | int 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 | } | ||
904 | EXPORT_SYMBOL(intelfb_probe); | ||
905 | |||
906 | int 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 | } | ||
925 | EXPORT_SYMBOL(intelfb_remove); | ||
926 | MODULE_LICENSE("GPL and additional rights"); | ||