blob: 3206c0ad7b6c3c9a292c7b6ba13fc38e9e2badcb [file] [log] [blame]
Jerome Glisse771fe6b2009-06-05 14:42:42 +02001/*
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
48struct 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
57static 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:
Michel Dänzer61b576d2009-06-24 00:12:55 +1000104 fb->pseudo_palette[regno] =
105 (((red >> 8) & 0xff) << info->var.red.offset) |
106 (((green >> 8) & 0xff) << info->var.green.offset) |
107 (((blue >> 8) & 0xff) << info->var.blue.offset);
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200108 break;
109 }
110 }
111 }
112 return 0;
113}
114
115static int radeonfb_check_var(struct fb_var_screeninfo *var,
116 struct fb_info *info)
117{
118 struct radeon_fb_device *rfbdev = info->par;
119 struct radeon_framebuffer *rfb = rfbdev->rfb;
120 struct drm_framebuffer *fb = &rfb->base;
121 int depth;
122
123 if (var->pixclock == -1 || !var->pixclock) {
124 return -EINVAL;
125 }
126 /* Need to resize the fb object !!! */
127 if (var->xres > fb->width || var->yres > fb->height) {
128 DRM_ERROR("Requested width/height is greater than current fb "
129 "object %dx%d > %dx%d\n", var->xres, var->yres,
130 fb->width, fb->height);
131 DRM_ERROR("Need resizing code.\n");
132 return -EINVAL;
133 }
134
135 switch (var->bits_per_pixel) {
136 case 16:
137 depth = (var->green.length == 6) ? 16 : 15;
138 break;
139 case 32:
140 depth = (var->transp.length > 0) ? 32 : 24;
141 break;
142 default:
143 depth = var->bits_per_pixel;
144 break;
145 }
146
147 switch (depth) {
148 case 8:
149 var->red.offset = 0;
150 var->green.offset = 0;
151 var->blue.offset = 0;
152 var->red.length = 8;
153 var->green.length = 8;
154 var->blue.length = 8;
155 var->transp.length = 0;
156 var->transp.offset = 0;
157 break;
Michel Dänzer61b576d2009-06-24 00:12:55 +1000158#ifdef __LITTLE_ENDIAN
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200159 case 15:
160 var->red.offset = 10;
161 var->green.offset = 5;
162 var->blue.offset = 0;
163 var->red.length = 5;
164 var->green.length = 5;
165 var->blue.length = 5;
166 var->transp.length = 1;
167 var->transp.offset = 15;
168 break;
169 case 16:
170 var->red.offset = 11;
171 var->green.offset = 5;
172 var->blue.offset = 0;
173 var->red.length = 5;
174 var->green.length = 6;
175 var->blue.length = 5;
176 var->transp.length = 0;
177 var->transp.offset = 0;
178 break;
179 case 24:
180 var->red.offset = 16;
181 var->green.offset = 8;
182 var->blue.offset = 0;
183 var->red.length = 8;
184 var->green.length = 8;
185 var->blue.length = 8;
186 var->transp.length = 0;
187 var->transp.offset = 0;
188 break;
189 case 32:
190 var->red.offset = 16;
191 var->green.offset = 8;
192 var->blue.offset = 0;
193 var->red.length = 8;
194 var->green.length = 8;
195 var->blue.length = 8;
196 var->transp.length = 8;
197 var->transp.offset = 24;
198 break;
Michel Dänzer61b576d2009-06-24 00:12:55 +1000199#else
200 case 24:
201 var->red.offset = 8;
202 var->green.offset = 16;
203 var->blue.offset = 24;
204 var->red.length = 8;
205 var->green.length = 8;
206 var->blue.length = 8;
207 var->transp.length = 0;
208 var->transp.offset = 0;
209 break;
210 case 32:
211 var->red.offset = 8;
212 var->green.offset = 16;
213 var->blue.offset = 24;
214 var->red.length = 8;
215 var->green.length = 8;
216 var->blue.length = 8;
217 var->transp.length = 8;
218 var->transp.offset = 0;
219 break;
220#endif
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200221 default:
222 return -EINVAL;
223 }
224 return 0;
225}
226
227/* this will let fbcon do the mode init */
228static int radeonfb_set_par(struct fb_info *info)
229{
230 struct radeon_fb_device *rfbdev = info->par;
231 struct drm_device *dev = rfbdev->rdev->ddev;
232 struct fb_var_screeninfo *var = &info->var;
233 struct drm_crtc *crtc;
234 int ret;
235 int i;
236
237 if (var->pixclock != -1) {
238 DRM_ERROR("PIXEL CLCOK SET\n");
239 return -EINVAL;
240 }
241
242 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
243 struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
244
245 for (i = 0; i < rfbdev->crtc_count; i++) {
246 if (crtc->base.id == rfbdev->crtc_ids[i]) {
247 break;
248 }
249 }
250 if (i == rfbdev->crtc_count) {
251 continue;
252 }
253 if (crtc->fb == radeon_crtc->mode_set.fb) {
254 mutex_lock(&dev->mode_config.mutex);
255 ret = crtc->funcs->set_config(&radeon_crtc->mode_set);
256 mutex_unlock(&dev->mode_config.mutex);
257 if (ret) {
258 return ret;
259 }
260 }
261 }
262 return 0;
263}
264
265static int radeonfb_pan_display(struct fb_var_screeninfo *var,
266 struct fb_info *info)
267{
268 struct radeon_fb_device *rfbdev = info->par;
269 struct drm_device *dev = rfbdev->rdev->ddev;
270 struct drm_mode_set *modeset;
271 struct drm_crtc *crtc;
272 struct radeon_crtc *radeon_crtc;
273 int ret = 0;
274 int i;
275
276 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
277 for (i = 0; i < rfbdev->crtc_count; i++) {
278 if (crtc->base.id == rfbdev->crtc_ids[i]) {
279 break;
280 }
281 }
282
283 if (i == rfbdev->crtc_count) {
284 continue;
285 }
286
287 radeon_crtc = to_radeon_crtc(crtc);
288 modeset = &radeon_crtc->mode_set;
289
290 modeset->x = var->xoffset;
291 modeset->y = var->yoffset;
292
293 if (modeset->num_connectors) {
294 mutex_lock(&dev->mode_config.mutex);
295 ret = crtc->funcs->set_config(modeset);
296 mutex_unlock(&dev->mode_config.mutex);
297 if (!ret) {
298 info->var.xoffset = var->xoffset;
299 info->var.yoffset = var->yoffset;
300 }
301 }
302 }
303 return ret;
304}
305
306static void radeonfb_on(struct fb_info *info)
307{
308 struct radeon_fb_device *rfbdev = info->par;
309 struct drm_device *dev = rfbdev->rdev->ddev;
310 struct drm_crtc *crtc;
311 struct drm_encoder *encoder;
312 int i;
313
314 /*
315 * For each CRTC in this fb, find all associated encoders
316 * and turn them off, then turn off the CRTC.
317 */
318 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
319 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
320
321 for (i = 0; i < rfbdev->crtc_count; i++) {
322 if (crtc->base.id == rfbdev->crtc_ids[i]) {
323 break;
324 }
325 }
326
327 mutex_lock(&dev->mode_config.mutex);
328 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
329 mutex_unlock(&dev->mode_config.mutex);
330
331 /* Found a CRTC on this fb, now find encoders */
332 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
333 if (encoder->crtc == crtc) {
334 struct drm_encoder_helper_funcs *encoder_funcs;
335
336 encoder_funcs = encoder->helper_private;
337 mutex_lock(&dev->mode_config.mutex);
338 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
339 mutex_unlock(&dev->mode_config.mutex);
340 }
341 }
342 }
343}
344
345static void radeonfb_off(struct fb_info *info, int dpms_mode)
346{
347 struct radeon_fb_device *rfbdev = info->par;
348 struct drm_device *dev = rfbdev->rdev->ddev;
349 struct drm_crtc *crtc;
350 struct drm_encoder *encoder;
351 int i;
352
353 /*
354 * For each CRTC in this fb, find all associated encoders
355 * and turn them off, then turn off the CRTC.
356 */
357 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
358 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
359
360 for (i = 0; i < rfbdev->crtc_count; i++) {
361 if (crtc->base.id == rfbdev->crtc_ids[i]) {
362 break;
363 }
364 }
365
366 /* Found a CRTC on this fb, now find encoders */
367 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
368 if (encoder->crtc == crtc) {
369 struct drm_encoder_helper_funcs *encoder_funcs;
370
371 encoder_funcs = encoder->helper_private;
372 mutex_lock(&dev->mode_config.mutex);
373 encoder_funcs->dpms(encoder, dpms_mode);
374 mutex_unlock(&dev->mode_config.mutex);
375 }
376 }
377 if (dpms_mode == DRM_MODE_DPMS_OFF) {
378 mutex_lock(&dev->mode_config.mutex);
379 crtc_funcs->dpms(crtc, dpms_mode);
380 mutex_unlock(&dev->mode_config.mutex);
381 }
382 }
383}
384
385int radeonfb_blank(int blank, struct fb_info *info)
386{
387 switch (blank) {
388 case FB_BLANK_UNBLANK:
389 radeonfb_on(info);
390 break;
391 case FB_BLANK_NORMAL:
392 radeonfb_off(info, DRM_MODE_DPMS_STANDBY);
393 break;
394 case FB_BLANK_HSYNC_SUSPEND:
395 radeonfb_off(info, DRM_MODE_DPMS_STANDBY);
396 break;
397 case FB_BLANK_VSYNC_SUSPEND:
398 radeonfb_off(info, DRM_MODE_DPMS_SUSPEND);
399 break;
400 case FB_BLANK_POWERDOWN:
401 radeonfb_off(info, DRM_MODE_DPMS_OFF);
402 break;
403 }
404 return 0;
405}
406
407static struct fb_ops radeonfb_ops = {
408 .owner = THIS_MODULE,
409 .fb_check_var = radeonfb_check_var,
410 .fb_set_par = radeonfb_set_par,
411 .fb_setcolreg = radeonfb_setcolreg,
412 .fb_fillrect = cfb_fillrect,
413 .fb_copyarea = cfb_copyarea,
414 .fb_imageblit = cfb_imageblit,
415 .fb_pan_display = radeonfb_pan_display,
416 .fb_blank = radeonfb_blank,
417};
418
419/**
420 * Curretly it is assumed that the old framebuffer is reused.
421 *
422 * LOCKING
423 * caller should hold the mode config lock.
424 *
425 */
426int radeonfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
427{
428 struct fb_info *info;
429 struct drm_framebuffer *fb;
430 struct drm_display_mode *mode = crtc->desired_mode;
431
432 fb = crtc->fb;
433 if (fb == NULL) {
434 return 1;
435 }
436 info = fb->fbdev;
437 if (info == NULL) {
438 return 1;
439 }
440 if (mode == NULL) {
441 return 1;
442 }
443 info->var.xres = mode->hdisplay;
444 info->var.right_margin = mode->hsync_start - mode->hdisplay;
445 info->var.hsync_len = mode->hsync_end - mode->hsync_start;
446 info->var.left_margin = mode->htotal - mode->hsync_end;
447 info->var.yres = mode->vdisplay;
448 info->var.lower_margin = mode->vsync_start - mode->vdisplay;
449 info->var.vsync_len = mode->vsync_end - mode->vsync_start;
450 info->var.upper_margin = mode->vtotal - mode->vsync_end;
451 info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
452 /* avoid overflow */
453 info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
454
455 return 0;
456}
457EXPORT_SYMBOL(radeonfb_resize);
458
459static struct drm_mode_set panic_mode;
460
461int radeonfb_panic(struct notifier_block *n, unsigned long ununsed,
462 void *panic_str)
463{
464 DRM_ERROR("panic occurred, switching back to text console\n");
465 drm_crtc_helper_set_config(&panic_mode);
466 return 0;
467}
468EXPORT_SYMBOL(radeonfb_panic);
469
470static struct notifier_block paniced = {
471 .notifier_call = radeonfb_panic,
472};
473
Dave Airliee024e112009-06-24 09:48:08 +1000474static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200475{
476 int aligned = width;
Dave Airliee024e112009-06-24 09:48:08 +1000477 int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200478 int pitch_mask = 0;
479
480 switch (bpp / 8) {
481 case 1:
482 pitch_mask = align_large ? 255 : 127;
483 break;
484 case 2:
485 pitch_mask = align_large ? 127 : 31;
486 break;
487 case 3:
488 case 4:
489 pitch_mask = align_large ? 63 : 15;
490 break;
491 }
492
493 aligned += pitch_mask;
494 aligned &= ~pitch_mask;
495 return aligned;
496}
497
498int radeonfb_create(struct radeon_device *rdev,
499 uint32_t fb_width, uint32_t fb_height,
500 uint32_t surface_width, uint32_t surface_height,
501 struct radeon_framebuffer **rfb_p)
502{
503 struct fb_info *info;
504 struct radeon_fb_device *rfbdev;
Jerome Glissef92e93e2009-06-22 18:15:58 +0200505 struct drm_framebuffer *fb = NULL;
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200506 struct radeon_framebuffer *rfb;
507 struct drm_mode_fb_cmd mode_cmd;
508 struct drm_gem_object *gobj = NULL;
509 struct radeon_object *robj = NULL;
510 struct device *device = &rdev->pdev->dev;
511 int size, aligned_size, ret;
Jerome Glissef92e93e2009-06-22 18:15:58 +0200512 u64 fb_gpuaddr;
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200513 void *fbptr = NULL;
Jerome Glissef92e93e2009-06-22 18:15:58 +0200514 unsigned long tmp;
Dave Airliee024e112009-06-24 09:48:08 +1000515 bool fb_tiled = false; /* useful for testing */
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200516
517 mode_cmd.width = surface_width;
518 mode_cmd.height = surface_height;
519 mode_cmd.bpp = 32;
520 /* need to align pitch with crtc limits */
Dave Airliee024e112009-06-24 09:48:08 +1000521 mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8);
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200522 mode_cmd.depth = 24;
523
524 size = mode_cmd.pitch * mode_cmd.height;
525 aligned_size = ALIGN(size, PAGE_SIZE);
526
527 ret = radeon_gem_object_create(rdev, aligned_size, 0,
Jerome Glissef92e93e2009-06-22 18:15:58 +0200528 RADEON_GEM_DOMAIN_VRAM,
529 false, ttm_bo_type_kernel,
530 false, &gobj);
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200531 if (ret) {
Jerome Glissef92e93e2009-06-22 18:15:58 +0200532 printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n",
533 surface_width, surface_height);
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200534 ret = -ENOMEM;
535 goto out;
536 }
537 robj = gobj->driver_private;
538
Dave Airliee024e112009-06-24 09:48:08 +1000539 if (fb_tiled)
540 radeon_object_set_tiling_flags(robj, RADEON_TILING_MACRO|RADEON_TILING_SURFACE, mode_cmd.pitch);
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200541 mutex_lock(&rdev->ddev->struct_mutex);
542 fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj);
543 if (fb == NULL) {
544 DRM_ERROR("failed to allocate fb.\n");
545 ret = -ENOMEM;
546 goto out_unref;
547 }
Jerome Glissef92e93e2009-06-22 18:15:58 +0200548 ret = radeon_object_pin(robj, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
549 if (ret) {
550 printk(KERN_ERR "failed to pin framebuffer\n");
551 ret = -ENOMEM;
552 goto out_unref;
553 }
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200554
555 list_add(&fb->filp_head, &rdev->ddev->mode_config.fb_kernel_list);
556
557 rfb = to_radeon_framebuffer(fb);
558 *rfb_p = rfb;
559 rdev->fbdev_rfb = rfb;
Jerome Glissef92e93e2009-06-22 18:15:58 +0200560 rdev->fbdev_robj = robj;
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200561
562 info = framebuffer_alloc(sizeof(struct radeon_fb_device), device);
563 if (info == NULL) {
564 ret = -ENOMEM;
565 goto out_unref;
566 }
567 rfbdev = info->par;
568
Dave Airliee024e112009-06-24 09:48:08 +1000569 if (fb_tiled)
570 radeon_object_check_tiling(robj, 0, 0);
571
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200572 ret = radeon_object_kmap(robj, &fbptr);
573 if (ret) {
574 goto out_unref;
575 }
576
577 strcpy(info->fix.id, "radeondrmfb");
578 info->fix.type = FB_TYPE_PACKED_PIXELS;
579 info->fix.visual = FB_VISUAL_TRUECOLOR;
580 info->fix.type_aux = 0;
581 info->fix.xpanstep = 1; /* doing it in hw */
582 info->fix.ypanstep = 1; /* doing it in hw */
583 info->fix.ywrapstep = 0;
Michel Dänzer696d4df2009-06-23 16:12:53 +0200584 info->fix.accel = FB_ACCEL_NONE;
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200585 info->fix.type_aux = 0;
586 info->flags = FBINFO_DEFAULT;
587 info->fbops = &radeonfb_ops;
588 info->fix.line_length = fb->pitch;
Jerome Glissef92e93e2009-06-22 18:15:58 +0200589 tmp = fb_gpuaddr - rdev->mc.vram_location;
590 info->fix.smem_start = rdev->mc.aper_base + tmp;
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200591 info->fix.smem_len = size;
592 info->screen_base = fbptr;
593 info->screen_size = size;
594 info->pseudo_palette = fb->pseudo_palette;
595 info->var.xres_virtual = fb->width;
596 info->var.yres_virtual = fb->height;
597 info->var.bits_per_pixel = fb->bits_per_pixel;
598 info->var.xoffset = 0;
599 info->var.yoffset = 0;
600 info->var.activate = FB_ACTIVATE_NOW;
601 info->var.height = -1;
602 info->var.width = -1;
603 info->var.xres = fb_width;
604 info->var.yres = fb_height;
Dave Airlieed8f0d92009-07-29 17:07:38 +1000605
606 /* setup aperture base/size for vesafb takeover */
607 info->aperture_base = rdev->ddev->mode_config.fb_base;
608 info->aperture_size = rdev->mc.real_vram_size;
609
Michel Dänzer696d4df2009-06-23 16:12:53 +0200610 info->fix.mmio_start = 0;
611 info->fix.mmio_len = 0;
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200612 info->pixmap.size = 64*1024;
613 info->pixmap.buf_align = 8;
614 info->pixmap.access_align = 32;
615 info->pixmap.flags = FB_PIXMAP_SYSTEM;
616 info->pixmap.scan_align = 1;
617 if (info->screen_base == NULL) {
618 ret = -ENOSPC;
619 goto out_unref;
620 }
621 DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start);
622 DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base);
623 DRM_INFO("size %lu\n", (unsigned long)size);
624 DRM_INFO("fb depth is %d\n", fb->depth);
625 DRM_INFO(" pitch is %d\n", fb->pitch);
626
627 switch (fb->depth) {
628 case 8:
629 info->var.red.offset = 0;
630 info->var.green.offset = 0;
631 info->var.blue.offset = 0;
632 info->var.red.length = 8; /* 8bit DAC */
633 info->var.green.length = 8;
634 info->var.blue.length = 8;
635 info->var.transp.offset = 0;
636 info->var.transp.length = 0;
637 break;
Michel Dänzer61b576d2009-06-24 00:12:55 +1000638#ifdef __LITTLE_ENDIAN
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200639 case 15:
640 info->var.red.offset = 10;
641 info->var.green.offset = 5;
642 info->var.blue.offset = 0;
643 info->var.red.length = 5;
644 info->var.green.length = 5;
645 info->var.blue.length = 5;
646 info->var.transp.offset = 15;
647 info->var.transp.length = 1;
648 break;
649 case 16:
650 info->var.red.offset = 11;
651 info->var.green.offset = 5;
652 info->var.blue.offset = 0;
653 info->var.red.length = 5;
654 info->var.green.length = 6;
655 info->var.blue.length = 5;
656 info->var.transp.offset = 0;
657 break;
658 case 24:
659 info->var.red.offset = 16;
660 info->var.green.offset = 8;
661 info->var.blue.offset = 0;
662 info->var.red.length = 8;
663 info->var.green.length = 8;
664 info->var.blue.length = 8;
665 info->var.transp.offset = 0;
666 info->var.transp.length = 0;
667 break;
668 case 32:
669 info->var.red.offset = 16;
670 info->var.green.offset = 8;
671 info->var.blue.offset = 0;
672 info->var.red.length = 8;
673 info->var.green.length = 8;
674 info->var.blue.length = 8;
675 info->var.transp.offset = 24;
676 info->var.transp.length = 8;
677 break;
Michel Dänzer61b576d2009-06-24 00:12:55 +1000678#else
679 case 24:
680 info->var.red.offset = 8;
681 info->var.green.offset = 16;
682 info->var.blue.offset = 24;
683 info->var.red.length = 8;
684 info->var.green.length = 8;
685 info->var.blue.length = 8;
686 info->var.transp.offset = 0;
687 info->var.transp.length = 0;
688 break;
689 case 32:
690 info->var.red.offset = 8;
691 info->var.green.offset = 16;
692 info->var.blue.offset = 24;
693 info->var.red.length = 8;
694 info->var.green.length = 8;
695 info->var.blue.length = 8;
696 info->var.transp.offset = 0;
697 info->var.transp.length = 8;
698 break;
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200699 default:
Michel Dänzer61b576d2009-06-24 00:12:55 +1000700#endif
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200701 break;
702 }
703
704 fb->fbdev = info;
705 rfbdev->rfb = rfb;
706 rfbdev->rdev = rdev;
707
708 mutex_unlock(&rdev->ddev->struct_mutex);
709 return 0;
710
711out_unref:
712 if (robj) {
713 radeon_object_kunmap(robj);
714 }
Jerome Glissef92e93e2009-06-22 18:15:58 +0200715 if (fb && ret) {
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200716 list_del(&fb->filp_head);
717 drm_gem_object_unreference(gobj);
718 drm_framebuffer_cleanup(fb);
719 kfree(fb);
720 }
721 drm_gem_object_unreference(gobj);
722 mutex_unlock(&rdev->ddev->struct_mutex);
723out:
724 return ret;
725}
726
727static int radeonfb_single_fb_probe(struct radeon_device *rdev)
728{
729 struct drm_crtc *crtc;
730 struct drm_connector *connector;
731 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
732 unsigned int surface_width = 0, surface_height = 0;
733 int new_fb = 0;
734 int crtc_count = 0;
735 int ret, i, conn_count = 0;
736 struct radeon_framebuffer *rfb;
737 struct fb_info *info;
738 struct radeon_fb_device *rfbdev;
739 struct drm_mode_set *modeset = NULL;
740
741 /* first up get a count of crtcs now in use and new min/maxes width/heights */
742 list_for_each_entry(crtc, &rdev->ddev->mode_config.crtc_list, head) {
743 if (drm_helper_crtc_in_use(crtc)) {
744 if (crtc->desired_mode) {
745 if (crtc->desired_mode->hdisplay < fb_width)
746 fb_width = crtc->desired_mode->hdisplay;
747
748 if (crtc->desired_mode->vdisplay < fb_height)
749 fb_height = crtc->desired_mode->vdisplay;
750
751 if (crtc->desired_mode->hdisplay > surface_width)
752 surface_width = crtc->desired_mode->hdisplay;
753
754 if (crtc->desired_mode->vdisplay > surface_height)
755 surface_height = crtc->desired_mode->vdisplay;
756 }
757 crtc_count++;
758 }
759 }
760
761 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
762 /* hmm everyone went away - assume VGA cable just fell out
763 and will come back later. */
764 return 0;
765 }
766
767 /* do we have an fb already? */
768 if (list_empty(&rdev->ddev->mode_config.fb_kernel_list)) {
769 /* create an fb if we don't have one */
770 ret = radeonfb_create(rdev, fb_width, fb_height, surface_width, surface_height, &rfb);
771 if (ret) {
772 return -EINVAL;
773 }
774 new_fb = 1;
775 } else {
776 struct drm_framebuffer *fb;
777 fb = list_first_entry(&rdev->ddev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head);
778 rfb = to_radeon_framebuffer(fb);
779
780 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
781 As really we can't resize an fbdev that is in the wild currently due to fbdev
782 not really being designed for the lower layers moving stuff around under it.
783 - so in the grand style of things - punt. */
784 if ((fb->width < surface_width) || (fb->height < surface_height)) {
785 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
786 return -EINVAL;
787 }
788 }
789
790 info = rfb->base.fbdev;
791 rdev->fbdev_info = info;
792 rfbdev = info->par;
793
794 crtc_count = 0;
795 /* okay we need to setup new connector sets in the crtcs */
796 list_for_each_entry(crtc, &rdev->ddev->mode_config.crtc_list, head) {
797 struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
798 modeset = &radeon_crtc->mode_set;
799 modeset->fb = &rfb->base;
800 conn_count = 0;
801 list_for_each_entry(connector, &rdev->ddev->mode_config.connector_list, head) {
802 if (connector->encoder)
803 if (connector->encoder->crtc == modeset->crtc) {
804 modeset->connectors[conn_count] = connector;
805 conn_count++;
806 if (conn_count > RADEONFB_CONN_LIMIT)
807 BUG();
808 }
809 }
810
811 for (i = conn_count; i < RADEONFB_CONN_LIMIT; i++)
812 modeset->connectors[i] = NULL;
813
814
815 rfbdev->crtc_ids[crtc_count++] = crtc->base.id;
816
817 modeset->num_connectors = conn_count;
818 if (modeset->crtc->desired_mode) {
819 if (modeset->mode) {
820 drm_mode_destroy(rdev->ddev, modeset->mode);
821 }
822 modeset->mode = drm_mode_duplicate(rdev->ddev,
823 modeset->crtc->desired_mode);
824 }
825 }
826 rfbdev->crtc_count = crtc_count;
827
828 if (new_fb) {
829 info->var.pixclock = -1;
830 if (register_framebuffer(info) < 0)
831 return -EINVAL;
832 } else {
833 radeonfb_set_par(info);
834 }
835 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
836 info->fix.id);
837
838 /* Switch back to kernel console on panic */
839 panic_mode = *modeset;
840 atomic_notifier_chain_register(&panic_notifier_list, &paniced);
841 printk(KERN_INFO "registered panic notifier\n");
842
843 return 0;
844}
845
846int radeonfb_probe(struct drm_device *dev)
847{
848 int ret;
849
850 /* something has changed in the lower levels of hell - deal with it
851 here */
852
853 /* two modes : a) 1 fb to rule all crtcs.
854 b) one fb per crtc.
855 two actions 1) new connected device
856 2) device removed.
857 case a/1 : if the fb surface isn't big enough - resize the surface fb.
858 if the fb size isn't big enough - resize fb into surface.
859 if everything big enough configure the new crtc/etc.
860 case a/2 : undo the configuration
861 possibly resize down the fb to fit the new configuration.
862 case b/1 : see if it is on a new crtc - setup a new fb and add it.
863 case b/2 : teardown the new fb.
864 */
865 ret = radeonfb_single_fb_probe(dev->dev_private);
866 return ret;
867}
868EXPORT_SYMBOL(radeonfb_probe);
869
870int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
871{
872 struct fb_info *info;
873 struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb);
874 struct radeon_object *robj;
875
876 if (!fb) {
877 return -EINVAL;
878 }
879 info = fb->fbdev;
880 if (info) {
881 robj = rfb->obj->driver_private;
882 unregister_framebuffer(info);
883 radeon_object_kunmap(robj);
Jerome Glissef92e93e2009-06-22 18:15:58 +0200884 radeon_object_unpin(robj);
Jerome Glisse771fe6b2009-06-05 14:42:42 +0200885 framebuffer_release(info);
886 }
887
888 printk(KERN_INFO "unregistered panic notifier\n");
889 atomic_notifier_chain_unregister(&panic_notifier_list, &paniced);
890 memset(&panic_mode, 0, sizeof(struct drm_mode_set));
891 return 0;
892}
893EXPORT_SYMBOL(radeonfb_remove);
894MODULE_LICENSE("GPL");