diff options
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_fb.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_fb.c | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c new file mode 100644 index 000000000000..fa86d398945e --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_fb.c | |||
@@ -0,0 +1,825 @@ | |||
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 | * Modularization | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/errno.h> | ||
33 | #include <linux/string.h> | ||
34 | #include <linux/mm.h> | ||
35 | #include <linux/tty.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include <linux/fb.h> | ||
39 | #include <linux/init.h> | ||
40 | |||
41 | #include "drmP.h" | ||
42 | #include "drm.h" | ||
43 | #include "drm_crtc.h" | ||
44 | #include "drm_crtc_helper.h" | ||
45 | #include "radeon_drm.h" | ||
46 | #include "radeon.h" | ||
47 | |||
48 | struct radeon_fb_device { | ||
49 | struct radeon_device *rdev; | ||
50 | struct drm_display_mode *mode; | ||
51 | struct radeon_framebuffer *rfb; | ||
52 | int crtc_count; | ||
53 | /* crtc currently bound to this */ | ||
54 | uint32_t crtc_ids[2]; | ||
55 | }; | ||
56 | |||
57 | static int radeonfb_setcolreg(unsigned regno, | ||
58 | unsigned red, | ||
59 | unsigned green, | ||
60 | unsigned blue, | ||
61 | unsigned transp, | ||
62 | struct fb_info *info) | ||
63 | { | ||
64 | struct radeon_fb_device *rfbdev = info->par; | ||
65 | struct drm_device *dev = rfbdev->rdev->ddev; | ||
66 | struct drm_crtc *crtc; | ||
67 | int i; | ||
68 | |||
69 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
70 | struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); | ||
71 | struct drm_mode_set *modeset = &radeon_crtc->mode_set; | ||
72 | struct drm_framebuffer *fb = modeset->fb; | ||
73 | |||
74 | for (i = 0; i < rfbdev->crtc_count; i++) { | ||
75 | if (crtc->base.id == rfbdev->crtc_ids[i]) { | ||
76 | break; | ||
77 | } | ||
78 | } | ||
79 | if (i == rfbdev->crtc_count) { | ||
80 | continue; | ||
81 | } | ||
82 | if (regno > 255) { | ||
83 | return 1; | ||
84 | } | ||
85 | if (fb->depth == 8) { | ||
86 | radeon_crtc_fb_gamma_set(crtc, red, green, blue, regno); | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | if (regno < 16) { | ||
91 | switch (fb->depth) { | ||
92 | case 15: | ||
93 | fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | | ||
94 | ((green & 0xf800) >> 6) | | ||
95 | ((blue & 0xf800) >> 11); | ||
96 | break; | ||
97 | case 16: | ||
98 | fb->pseudo_palette[regno] = (red & 0xf800) | | ||
99 | ((green & 0xfc00) >> 5) | | ||
100 | ((blue & 0xf800) >> 11); | ||
101 | break; | ||
102 | case 24: | ||
103 | case 32: | ||
104 | fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | | ||
105 | (green & 0xff00) | | ||
106 | ((blue & 0xff00) >> 8); | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int radeonfb_check_var(struct fb_var_screeninfo *var, | ||
115 | struct fb_info *info) | ||
116 | { | ||
117 | struct radeon_fb_device *rfbdev = info->par; | ||
118 | struct radeon_framebuffer *rfb = rfbdev->rfb; | ||
119 | struct drm_framebuffer *fb = &rfb->base; | ||
120 | int depth; | ||
121 | |||
122 | if (var->pixclock == -1 || !var->pixclock) { | ||
123 | return -EINVAL; | ||
124 | } | ||
125 | /* Need to resize the fb object !!! */ | ||
126 | if (var->xres > fb->width || var->yres > fb->height) { | ||
127 | DRM_ERROR("Requested width/height is greater than current fb " | ||
128 | "object %dx%d > %dx%d\n", var->xres, var->yres, | ||
129 | fb->width, fb->height); | ||
130 | DRM_ERROR("Need resizing code.\n"); | ||
131 | return -EINVAL; | ||
132 | } | ||
133 | |||
134 | switch (var->bits_per_pixel) { | ||
135 | case 16: | ||
136 | depth = (var->green.length == 6) ? 16 : 15; | ||
137 | break; | ||
138 | case 32: | ||
139 | depth = (var->transp.length > 0) ? 32 : 24; | ||
140 | break; | ||
141 | default: | ||
142 | depth = var->bits_per_pixel; | ||
143 | break; | ||
144 | } | ||
145 | |||
146 | switch (depth) { | ||
147 | case 8: | ||
148 | var->red.offset = 0; | ||
149 | var->green.offset = 0; | ||
150 | var->blue.offset = 0; | ||
151 | var->red.length = 8; | ||
152 | var->green.length = 8; | ||
153 | var->blue.length = 8; | ||
154 | var->transp.length = 0; | ||
155 | var->transp.offset = 0; | ||
156 | break; | ||
157 | case 15: | ||
158 | var->red.offset = 10; | ||
159 | var->green.offset = 5; | ||
160 | var->blue.offset = 0; | ||
161 | var->red.length = 5; | ||
162 | var->green.length = 5; | ||
163 | var->blue.length = 5; | ||
164 | var->transp.length = 1; | ||
165 | var->transp.offset = 15; | ||
166 | break; | ||
167 | case 16: | ||
168 | var->red.offset = 11; | ||
169 | var->green.offset = 5; | ||
170 | var->blue.offset = 0; | ||
171 | var->red.length = 5; | ||
172 | var->green.length = 6; | ||
173 | var->blue.length = 5; | ||
174 | var->transp.length = 0; | ||
175 | var->transp.offset = 0; | ||
176 | break; | ||
177 | case 24: | ||
178 | var->red.offset = 16; | ||
179 | var->green.offset = 8; | ||
180 | var->blue.offset = 0; | ||
181 | var->red.length = 8; | ||
182 | var->green.length = 8; | ||
183 | var->blue.length = 8; | ||
184 | var->transp.length = 0; | ||
185 | var->transp.offset = 0; | ||
186 | break; | ||
187 | case 32: | ||
188 | var->red.offset = 16; | ||
189 | var->green.offset = 8; | ||
190 | var->blue.offset = 0; | ||
191 | var->red.length = 8; | ||
192 | var->green.length = 8; | ||
193 | var->blue.length = 8; | ||
194 | var->transp.length = 8; | ||
195 | var->transp.offset = 24; | ||
196 | break; | ||
197 | default: | ||
198 | return -EINVAL; | ||
199 | } | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | /* this will let fbcon do the mode init */ | ||
204 | static int radeonfb_set_par(struct fb_info *info) | ||
205 | { | ||
206 | struct radeon_fb_device *rfbdev = info->par; | ||
207 | struct drm_device *dev = rfbdev->rdev->ddev; | ||
208 | struct fb_var_screeninfo *var = &info->var; | ||
209 | struct drm_crtc *crtc; | ||
210 | int ret; | ||
211 | int i; | ||
212 | |||
213 | if (var->pixclock != -1) { | ||
214 | DRM_ERROR("PIXEL CLCOK SET\n"); | ||
215 | return -EINVAL; | ||
216 | } | ||
217 | |||
218 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
219 | struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); | ||
220 | |||
221 | for (i = 0; i < rfbdev->crtc_count; i++) { | ||
222 | if (crtc->base.id == rfbdev->crtc_ids[i]) { | ||
223 | break; | ||
224 | } | ||
225 | } | ||
226 | if (i == rfbdev->crtc_count) { | ||
227 | continue; | ||
228 | } | ||
229 | if (crtc->fb == radeon_crtc->mode_set.fb) { | ||
230 | mutex_lock(&dev->mode_config.mutex); | ||
231 | ret = crtc->funcs->set_config(&radeon_crtc->mode_set); | ||
232 | mutex_unlock(&dev->mode_config.mutex); | ||
233 | if (ret) { | ||
234 | return ret; | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static int radeonfb_pan_display(struct fb_var_screeninfo *var, | ||
242 | struct fb_info *info) | ||
243 | { | ||
244 | struct radeon_fb_device *rfbdev = info->par; | ||
245 | struct drm_device *dev = rfbdev->rdev->ddev; | ||
246 | struct drm_mode_set *modeset; | ||
247 | struct drm_crtc *crtc; | ||
248 | struct radeon_crtc *radeon_crtc; | ||
249 | int ret = 0; | ||
250 | int i; | ||
251 | |||
252 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
253 | for (i = 0; i < rfbdev->crtc_count; i++) { | ||
254 | if (crtc->base.id == rfbdev->crtc_ids[i]) { | ||
255 | break; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | if (i == rfbdev->crtc_count) { | ||
260 | continue; | ||
261 | } | ||
262 | |||
263 | radeon_crtc = to_radeon_crtc(crtc); | ||
264 | modeset = &radeon_crtc->mode_set; | ||
265 | |||
266 | modeset->x = var->xoffset; | ||
267 | modeset->y = var->yoffset; | ||
268 | |||
269 | if (modeset->num_connectors) { | ||
270 | mutex_lock(&dev->mode_config.mutex); | ||
271 | ret = crtc->funcs->set_config(modeset); | ||
272 | mutex_unlock(&dev->mode_config.mutex); | ||
273 | if (!ret) { | ||
274 | info->var.xoffset = var->xoffset; | ||
275 | info->var.yoffset = var->yoffset; | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | static void radeonfb_on(struct fb_info *info) | ||
283 | { | ||
284 | struct radeon_fb_device *rfbdev = info->par; | ||
285 | struct drm_device *dev = rfbdev->rdev->ddev; | ||
286 | struct drm_crtc *crtc; | ||
287 | struct drm_encoder *encoder; | ||
288 | int i; | ||
289 | |||
290 | /* | ||
291 | * For each CRTC in this fb, find all associated encoders | ||
292 | * and turn them off, then turn off the CRTC. | ||
293 | */ | ||
294 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
295 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
296 | |||
297 | for (i = 0; i < rfbdev->crtc_count; i++) { | ||
298 | if (crtc->base.id == rfbdev->crtc_ids[i]) { | ||
299 | break; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | mutex_lock(&dev->mode_config.mutex); | ||
304 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); | ||
305 | mutex_unlock(&dev->mode_config.mutex); | ||
306 | |||
307 | /* Found a CRTC on this fb, now find encoders */ | ||
308 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
309 | if (encoder->crtc == crtc) { | ||
310 | struct drm_encoder_helper_funcs *encoder_funcs; | ||
311 | |||
312 | encoder_funcs = encoder->helper_private; | ||
313 | mutex_lock(&dev->mode_config.mutex); | ||
314 | encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); | ||
315 | mutex_unlock(&dev->mode_config.mutex); | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | } | ||
320 | |||
321 | static void radeonfb_off(struct fb_info *info, int dpms_mode) | ||
322 | { | ||
323 | struct radeon_fb_device *rfbdev = info->par; | ||
324 | struct drm_device *dev = rfbdev->rdev->ddev; | ||
325 | struct drm_crtc *crtc; | ||
326 | struct drm_encoder *encoder; | ||
327 | int i; | ||
328 | |||
329 | /* | ||
330 | * For each CRTC in this fb, find all associated encoders | ||
331 | * and turn them off, then turn off the CRTC. | ||
332 | */ | ||
333 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
334 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
335 | |||
336 | for (i = 0; i < rfbdev->crtc_count; i++) { | ||
337 | if (crtc->base.id == rfbdev->crtc_ids[i]) { | ||
338 | break; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | /* Found a CRTC on this fb, now find encoders */ | ||
343 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
344 | if (encoder->crtc == crtc) { | ||
345 | struct drm_encoder_helper_funcs *encoder_funcs; | ||
346 | |||
347 | encoder_funcs = encoder->helper_private; | ||
348 | mutex_lock(&dev->mode_config.mutex); | ||
349 | encoder_funcs->dpms(encoder, dpms_mode); | ||
350 | mutex_unlock(&dev->mode_config.mutex); | ||
351 | } | ||
352 | } | ||
353 | if (dpms_mode == DRM_MODE_DPMS_OFF) { | ||
354 | mutex_lock(&dev->mode_config.mutex); | ||
355 | crtc_funcs->dpms(crtc, dpms_mode); | ||
356 | mutex_unlock(&dev->mode_config.mutex); | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
361 | int radeonfb_blank(int blank, struct fb_info *info) | ||
362 | { | ||
363 | switch (blank) { | ||
364 | case FB_BLANK_UNBLANK: | ||
365 | radeonfb_on(info); | ||
366 | break; | ||
367 | case FB_BLANK_NORMAL: | ||
368 | radeonfb_off(info, DRM_MODE_DPMS_STANDBY); | ||
369 | break; | ||
370 | case FB_BLANK_HSYNC_SUSPEND: | ||
371 | radeonfb_off(info, DRM_MODE_DPMS_STANDBY); | ||
372 | break; | ||
373 | case FB_BLANK_VSYNC_SUSPEND: | ||
374 | radeonfb_off(info, DRM_MODE_DPMS_SUSPEND); | ||
375 | break; | ||
376 | case FB_BLANK_POWERDOWN: | ||
377 | radeonfb_off(info, DRM_MODE_DPMS_OFF); | ||
378 | break; | ||
379 | } | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static struct fb_ops radeonfb_ops = { | ||
384 | .owner = THIS_MODULE, | ||
385 | .fb_check_var = radeonfb_check_var, | ||
386 | .fb_set_par = radeonfb_set_par, | ||
387 | .fb_setcolreg = radeonfb_setcolreg, | ||
388 | .fb_fillrect = cfb_fillrect, | ||
389 | .fb_copyarea = cfb_copyarea, | ||
390 | .fb_imageblit = cfb_imageblit, | ||
391 | .fb_pan_display = radeonfb_pan_display, | ||
392 | .fb_blank = radeonfb_blank, | ||
393 | }; | ||
394 | |||
395 | /** | ||
396 | * Curretly it is assumed that the old framebuffer is reused. | ||
397 | * | ||
398 | * LOCKING | ||
399 | * caller should hold the mode config lock. | ||
400 | * | ||
401 | */ | ||
402 | int radeonfb_resize(struct drm_device *dev, struct drm_crtc *crtc) | ||
403 | { | ||
404 | struct fb_info *info; | ||
405 | struct drm_framebuffer *fb; | ||
406 | struct drm_display_mode *mode = crtc->desired_mode; | ||
407 | |||
408 | fb = crtc->fb; | ||
409 | if (fb == NULL) { | ||
410 | return 1; | ||
411 | } | ||
412 | info = fb->fbdev; | ||
413 | if (info == NULL) { | ||
414 | return 1; | ||
415 | } | ||
416 | if (mode == NULL) { | ||
417 | return 1; | ||
418 | } | ||
419 | info->var.xres = mode->hdisplay; | ||
420 | info->var.right_margin = mode->hsync_start - mode->hdisplay; | ||
421 | info->var.hsync_len = mode->hsync_end - mode->hsync_start; | ||
422 | info->var.left_margin = mode->htotal - mode->hsync_end; | ||
423 | info->var.yres = mode->vdisplay; | ||
424 | info->var.lower_margin = mode->vsync_start - mode->vdisplay; | ||
425 | info->var.vsync_len = mode->vsync_end - mode->vsync_start; | ||
426 | info->var.upper_margin = mode->vtotal - mode->vsync_end; | ||
427 | info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100; | ||
428 | /* avoid overflow */ | ||
429 | info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | EXPORT_SYMBOL(radeonfb_resize); | ||
434 | |||
435 | static struct drm_mode_set panic_mode; | ||
436 | |||
437 | int radeonfb_panic(struct notifier_block *n, unsigned long ununsed, | ||
438 | void *panic_str) | ||
439 | { | ||
440 | DRM_ERROR("panic occurred, switching back to text console\n"); | ||
441 | drm_crtc_helper_set_config(&panic_mode); | ||
442 | return 0; | ||
443 | } | ||
444 | EXPORT_SYMBOL(radeonfb_panic); | ||
445 | |||
446 | static struct notifier_block paniced = { | ||
447 | .notifier_call = radeonfb_panic, | ||
448 | }; | ||
449 | |||
450 | static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp) | ||
451 | { | ||
452 | int aligned = width; | ||
453 | int align_large = (ASIC_IS_AVIVO(rdev)); | ||
454 | int pitch_mask = 0; | ||
455 | |||
456 | switch (bpp / 8) { | ||
457 | case 1: | ||
458 | pitch_mask = align_large ? 255 : 127; | ||
459 | break; | ||
460 | case 2: | ||
461 | pitch_mask = align_large ? 127 : 31; | ||
462 | break; | ||
463 | case 3: | ||
464 | case 4: | ||
465 | pitch_mask = align_large ? 63 : 15; | ||
466 | break; | ||
467 | } | ||
468 | |||
469 | aligned += pitch_mask; | ||
470 | aligned &= ~pitch_mask; | ||
471 | return aligned; | ||
472 | } | ||
473 | |||
474 | int radeonfb_create(struct radeon_device *rdev, | ||
475 | uint32_t fb_width, uint32_t fb_height, | ||
476 | uint32_t surface_width, uint32_t surface_height, | ||
477 | struct radeon_framebuffer **rfb_p) | ||
478 | { | ||
479 | struct fb_info *info; | ||
480 | struct radeon_fb_device *rfbdev; | ||
481 | struct drm_framebuffer *fb; | ||
482 | struct radeon_framebuffer *rfb; | ||
483 | struct drm_mode_fb_cmd mode_cmd; | ||
484 | struct drm_gem_object *gobj = NULL; | ||
485 | struct radeon_object *robj = NULL; | ||
486 | struct device *device = &rdev->pdev->dev; | ||
487 | int size, aligned_size, ret; | ||
488 | void *fbptr = NULL; | ||
489 | |||
490 | mode_cmd.width = surface_width; | ||
491 | mode_cmd.height = surface_height; | ||
492 | mode_cmd.bpp = 32; | ||
493 | /* need to align pitch with crtc limits */ | ||
494 | mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp) * ((mode_cmd.bpp + 1) / 8); | ||
495 | mode_cmd.depth = 24; | ||
496 | |||
497 | size = mode_cmd.pitch * mode_cmd.height; | ||
498 | aligned_size = ALIGN(size, PAGE_SIZE); | ||
499 | |||
500 | ret = radeon_gem_object_create(rdev, aligned_size, 0, | ||
501 | RADEON_GEM_DOMAIN_VRAM, | ||
502 | false, ttm_bo_type_kernel, | ||
503 | false, &gobj); | ||
504 | if (ret) { | ||
505 | printk(KERN_ERR "failed to allocate framebuffer\n"); | ||
506 | ret = -ENOMEM; | ||
507 | goto out; | ||
508 | } | ||
509 | robj = gobj->driver_private; | ||
510 | |||
511 | mutex_lock(&rdev->ddev->struct_mutex); | ||
512 | fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj); | ||
513 | if (fb == NULL) { | ||
514 | DRM_ERROR("failed to allocate fb.\n"); | ||
515 | ret = -ENOMEM; | ||
516 | goto out_unref; | ||
517 | } | ||
518 | |||
519 | list_add(&fb->filp_head, &rdev->ddev->mode_config.fb_kernel_list); | ||
520 | |||
521 | rfb = to_radeon_framebuffer(fb); | ||
522 | *rfb_p = rfb; | ||
523 | rdev->fbdev_rfb = rfb; | ||
524 | |||
525 | info = framebuffer_alloc(sizeof(struct radeon_fb_device), device); | ||
526 | if (info == NULL) { | ||
527 | ret = -ENOMEM; | ||
528 | goto out_unref; | ||
529 | } | ||
530 | rfbdev = info->par; | ||
531 | |||
532 | ret = radeon_object_kmap(robj, &fbptr); | ||
533 | if (ret) { | ||
534 | goto out_unref; | ||
535 | } | ||
536 | |||
537 | strcpy(info->fix.id, "radeondrmfb"); | ||
538 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
539 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
540 | info->fix.type_aux = 0; | ||
541 | info->fix.xpanstep = 1; /* doing it in hw */ | ||
542 | info->fix.ypanstep = 1; /* doing it in hw */ | ||
543 | info->fix.ywrapstep = 0; | ||
544 | info->fix.accel = FB_ACCEL_I830; | ||
545 | info->fix.type_aux = 0; | ||
546 | info->flags = FBINFO_DEFAULT; | ||
547 | info->fbops = &radeonfb_ops; | ||
548 | info->fix.line_length = fb->pitch; | ||
549 | info->screen_base = fbptr; | ||
550 | info->fix.smem_start = (unsigned long)fbptr; | ||
551 | info->fix.smem_len = size; | ||
552 | info->screen_base = fbptr; | ||
553 | info->screen_size = size; | ||
554 | info->pseudo_palette = fb->pseudo_palette; | ||
555 | info->var.xres_virtual = fb->width; | ||
556 | info->var.yres_virtual = fb->height; | ||
557 | info->var.bits_per_pixel = fb->bits_per_pixel; | ||
558 | info->var.xoffset = 0; | ||
559 | info->var.yoffset = 0; | ||
560 | info->var.activate = FB_ACTIVATE_NOW; | ||
561 | info->var.height = -1; | ||
562 | info->var.width = -1; | ||
563 | info->var.xres = fb_width; | ||
564 | info->var.yres = fb_height; | ||
565 | info->fix.mmio_start = pci_resource_start(rdev->pdev, 2); | ||
566 | info->fix.mmio_len = pci_resource_len(rdev->pdev, 2); | ||
567 | info->pixmap.size = 64*1024; | ||
568 | info->pixmap.buf_align = 8; | ||
569 | info->pixmap.access_align = 32; | ||
570 | info->pixmap.flags = FB_PIXMAP_SYSTEM; | ||
571 | info->pixmap.scan_align = 1; | ||
572 | if (info->screen_base == NULL) { | ||
573 | ret = -ENOSPC; | ||
574 | goto out_unref; | ||
575 | } | ||
576 | DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); | ||
577 | DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base); | ||
578 | DRM_INFO("size %lu\n", (unsigned long)size); | ||
579 | DRM_INFO("fb depth is %d\n", fb->depth); | ||
580 | DRM_INFO(" pitch is %d\n", fb->pitch); | ||
581 | |||
582 | switch (fb->depth) { | ||
583 | case 8: | ||
584 | info->var.red.offset = 0; | ||
585 | info->var.green.offset = 0; | ||
586 | info->var.blue.offset = 0; | ||
587 | info->var.red.length = 8; /* 8bit DAC */ | ||
588 | info->var.green.length = 8; | ||
589 | info->var.blue.length = 8; | ||
590 | info->var.transp.offset = 0; | ||
591 | info->var.transp.length = 0; | ||
592 | break; | ||
593 | case 15: | ||
594 | info->var.red.offset = 10; | ||
595 | info->var.green.offset = 5; | ||
596 | info->var.blue.offset = 0; | ||
597 | info->var.red.length = 5; | ||
598 | info->var.green.length = 5; | ||
599 | info->var.blue.length = 5; | ||
600 | info->var.transp.offset = 15; | ||
601 | info->var.transp.length = 1; | ||
602 | break; | ||
603 | case 16: | ||
604 | info->var.red.offset = 11; | ||
605 | info->var.green.offset = 5; | ||
606 | info->var.blue.offset = 0; | ||
607 | info->var.red.length = 5; | ||
608 | info->var.green.length = 6; | ||
609 | info->var.blue.length = 5; | ||
610 | info->var.transp.offset = 0; | ||
611 | break; | ||
612 | case 24: | ||
613 | info->var.red.offset = 16; | ||
614 | info->var.green.offset = 8; | ||
615 | info->var.blue.offset = 0; | ||
616 | info->var.red.length = 8; | ||
617 | info->var.green.length = 8; | ||
618 | info->var.blue.length = 8; | ||
619 | info->var.transp.offset = 0; | ||
620 | info->var.transp.length = 0; | ||
621 | break; | ||
622 | case 32: | ||
623 | info->var.red.offset = 16; | ||
624 | info->var.green.offset = 8; | ||
625 | info->var.blue.offset = 0; | ||
626 | info->var.red.length = 8; | ||
627 | info->var.green.length = 8; | ||
628 | info->var.blue.length = 8; | ||
629 | info->var.transp.offset = 24; | ||
630 | info->var.transp.length = 8; | ||
631 | break; | ||
632 | default: | ||
633 | break; | ||
634 | } | ||
635 | |||
636 | fb->fbdev = info; | ||
637 | rfbdev->rfb = rfb; | ||
638 | rfbdev->rdev = rdev; | ||
639 | |||
640 | mutex_unlock(&rdev->ddev->struct_mutex); | ||
641 | return 0; | ||
642 | |||
643 | out_unref: | ||
644 | if (robj) { | ||
645 | radeon_object_kunmap(robj); | ||
646 | } | ||
647 | if (ret) { | ||
648 | list_del(&fb->filp_head); | ||
649 | drm_gem_object_unreference(gobj); | ||
650 | drm_framebuffer_cleanup(fb); | ||
651 | kfree(fb); | ||
652 | } | ||
653 | drm_gem_object_unreference(gobj); | ||
654 | mutex_unlock(&rdev->ddev->struct_mutex); | ||
655 | out: | ||
656 | return ret; | ||
657 | } | ||
658 | |||
659 | static int radeonfb_single_fb_probe(struct radeon_device *rdev) | ||
660 | { | ||
661 | struct drm_crtc *crtc; | ||
662 | struct drm_connector *connector; | ||
663 | unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; | ||
664 | unsigned int surface_width = 0, surface_height = 0; | ||
665 | int new_fb = 0; | ||
666 | int crtc_count = 0; | ||
667 | int ret, i, conn_count = 0; | ||
668 | struct radeon_framebuffer *rfb; | ||
669 | struct fb_info *info; | ||
670 | struct radeon_fb_device *rfbdev; | ||
671 | struct drm_mode_set *modeset = NULL; | ||
672 | |||
673 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ | ||
674 | list_for_each_entry(crtc, &rdev->ddev->mode_config.crtc_list, head) { | ||
675 | if (drm_helper_crtc_in_use(crtc)) { | ||
676 | if (crtc->desired_mode) { | ||
677 | if (crtc->desired_mode->hdisplay < fb_width) | ||
678 | fb_width = crtc->desired_mode->hdisplay; | ||
679 | |||
680 | if (crtc->desired_mode->vdisplay < fb_height) | ||
681 | fb_height = crtc->desired_mode->vdisplay; | ||
682 | |||
683 | if (crtc->desired_mode->hdisplay > surface_width) | ||
684 | surface_width = crtc->desired_mode->hdisplay; | ||
685 | |||
686 | if (crtc->desired_mode->vdisplay > surface_height) | ||
687 | surface_height = crtc->desired_mode->vdisplay; | ||
688 | } | ||
689 | crtc_count++; | ||
690 | } | ||
691 | } | ||
692 | |||
693 | if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { | ||
694 | /* hmm everyone went away - assume VGA cable just fell out | ||
695 | and will come back later. */ | ||
696 | return 0; | ||
697 | } | ||
698 | |||
699 | /* do we have an fb already? */ | ||
700 | if (list_empty(&rdev->ddev->mode_config.fb_kernel_list)) { | ||
701 | /* create an fb if we don't have one */ | ||
702 | ret = radeonfb_create(rdev, fb_width, fb_height, surface_width, surface_height, &rfb); | ||
703 | if (ret) { | ||
704 | return -EINVAL; | ||
705 | } | ||
706 | new_fb = 1; | ||
707 | } else { | ||
708 | struct drm_framebuffer *fb; | ||
709 | fb = list_first_entry(&rdev->ddev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head); | ||
710 | rfb = to_radeon_framebuffer(fb); | ||
711 | |||
712 | /* if someone hotplugs something bigger than we have already allocated, we are pwned. | ||
713 | As really we can't resize an fbdev that is in the wild currently due to fbdev | ||
714 | not really being designed for the lower layers moving stuff around under it. | ||
715 | - so in the grand style of things - punt. */ | ||
716 | if ((fb->width < surface_width) || (fb->height < surface_height)) { | ||
717 | DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); | ||
718 | return -EINVAL; | ||
719 | } | ||
720 | } | ||
721 | |||
722 | info = rfb->base.fbdev; | ||
723 | rdev->fbdev_info = info; | ||
724 | rfbdev = info->par; | ||
725 | |||
726 | crtc_count = 0; | ||
727 | /* okay we need to setup new connector sets in the crtcs */ | ||
728 | list_for_each_entry(crtc, &rdev->ddev->mode_config.crtc_list, head) { | ||
729 | struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); | ||
730 | modeset = &radeon_crtc->mode_set; | ||
731 | modeset->fb = &rfb->base; | ||
732 | conn_count = 0; | ||
733 | list_for_each_entry(connector, &rdev->ddev->mode_config.connector_list, head) { | ||
734 | if (connector->encoder) | ||
735 | if (connector->encoder->crtc == modeset->crtc) { | ||
736 | modeset->connectors[conn_count] = connector; | ||
737 | conn_count++; | ||
738 | if (conn_count > RADEONFB_CONN_LIMIT) | ||
739 | BUG(); | ||
740 | } | ||
741 | } | ||
742 | |||
743 | for (i = conn_count; i < RADEONFB_CONN_LIMIT; i++) | ||
744 | modeset->connectors[i] = NULL; | ||
745 | |||
746 | |||
747 | rfbdev->crtc_ids[crtc_count++] = crtc->base.id; | ||
748 | |||
749 | modeset->num_connectors = conn_count; | ||
750 | if (modeset->crtc->desired_mode) { | ||
751 | if (modeset->mode) { | ||
752 | drm_mode_destroy(rdev->ddev, modeset->mode); | ||
753 | } | ||
754 | modeset->mode = drm_mode_duplicate(rdev->ddev, | ||
755 | modeset->crtc->desired_mode); | ||
756 | } | ||
757 | } | ||
758 | rfbdev->crtc_count = crtc_count; | ||
759 | |||
760 | if (new_fb) { | ||
761 | info->var.pixclock = -1; | ||
762 | if (register_framebuffer(info) < 0) | ||
763 | return -EINVAL; | ||
764 | } else { | ||
765 | radeonfb_set_par(info); | ||
766 | } | ||
767 | printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, | ||
768 | info->fix.id); | ||
769 | |||
770 | /* Switch back to kernel console on panic */ | ||
771 | panic_mode = *modeset; | ||
772 | atomic_notifier_chain_register(&panic_notifier_list, &paniced); | ||
773 | printk(KERN_INFO "registered panic notifier\n"); | ||
774 | |||
775 | return 0; | ||
776 | } | ||
777 | |||
778 | int radeonfb_probe(struct drm_device *dev) | ||
779 | { | ||
780 | int ret; | ||
781 | |||
782 | /* something has changed in the lower levels of hell - deal with it | ||
783 | here */ | ||
784 | |||
785 | /* two modes : a) 1 fb to rule all crtcs. | ||
786 | b) one fb per crtc. | ||
787 | two actions 1) new connected device | ||
788 | 2) device removed. | ||
789 | case a/1 : if the fb surface isn't big enough - resize the surface fb. | ||
790 | if the fb size isn't big enough - resize fb into surface. | ||
791 | if everything big enough configure the new crtc/etc. | ||
792 | case a/2 : undo the configuration | ||
793 | possibly resize down the fb to fit the new configuration. | ||
794 | case b/1 : see if it is on a new crtc - setup a new fb and add it. | ||
795 | case b/2 : teardown the new fb. | ||
796 | */ | ||
797 | ret = radeonfb_single_fb_probe(dev->dev_private); | ||
798 | return ret; | ||
799 | } | ||
800 | EXPORT_SYMBOL(radeonfb_probe); | ||
801 | |||
802 | int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) | ||
803 | { | ||
804 | struct fb_info *info; | ||
805 | struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb); | ||
806 | struct radeon_object *robj; | ||
807 | |||
808 | if (!fb) { | ||
809 | return -EINVAL; | ||
810 | } | ||
811 | info = fb->fbdev; | ||
812 | if (info) { | ||
813 | robj = rfb->obj->driver_private; | ||
814 | unregister_framebuffer(info); | ||
815 | radeon_object_kunmap(robj); | ||
816 | framebuffer_release(info); | ||
817 | } | ||
818 | |||
819 | printk(KERN_INFO "unregistered panic notifier\n"); | ||
820 | atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); | ||
821 | memset(&panic_mode, 0, sizeof(struct drm_mode_set)); | ||
822 | return 0; | ||
823 | } | ||
824 | EXPORT_SYMBOL(radeonfb_remove); | ||
825 | MODULE_LICENSE("GPL"); | ||