Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.30-rc2 1351 lines 35 kB view raw
1/* 2 * linux/drivers/video/ps3fb.c -- PS3 GPU frame buffer device 3 * 4 * Copyright (C) 2006 Sony Computer Entertainment Inc. 5 * Copyright 2006, 2007 Sony Corporation 6 * 7 * This file is based on : 8 * 9 * linux/drivers/video/vfb.c -- Virtual frame buffer device 10 * 11 * Copyright (C) 2002 James Simmons 12 * 13 * Copyright (C) 1997 Geert Uytterhoeven 14 * 15 * This file is subject to the terms and conditions of the GNU General Public 16 * License. See the file COPYING in the main directory of this archive for 17 * more details. 18 */ 19 20#include <linux/module.h> 21#include <linux/kernel.h> 22#include <linux/errno.h> 23#include <linux/string.h> 24#include <linux/mm.h> 25#include <linux/interrupt.h> 26#include <linux/console.h> 27#include <linux/ioctl.h> 28#include <linux/kthread.h> 29#include <linux/freezer.h> 30#include <linux/uaccess.h> 31#include <linux/fb.h> 32#include <linux/init.h> 33 34#include <asm/abs_addr.h> 35#include <asm/lv1call.h> 36#include <asm/ps3av.h> 37#include <asm/ps3fb.h> 38#include <asm/ps3.h> 39 40 41#define DEVICE_NAME "ps3fb" 42 43#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x101 44#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x102 45#define L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP 0x600 46#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601 47#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT_SYNC 0x602 48 49#define L1GPU_FB_BLIT_WAIT_FOR_COMPLETION (1ULL << 32) 50 51#define L1GPU_DISPLAY_SYNC_HSYNC 1 52#define L1GPU_DISPLAY_SYNC_VSYNC 2 53 54#define GPU_CMD_BUF_SIZE (2 * 1024 * 1024) 55#define GPU_FB_START (64 * 1024) 56#define GPU_IOIF (0x0d000000UL) 57#define GPU_ALIGN_UP(x) _ALIGN_UP((x), 64) 58#define GPU_MAX_LINE_LENGTH (65536 - 64) 59 60#define GPU_INTR_STATUS_VSYNC_0 0 /* vsync on head A */ 61#define GPU_INTR_STATUS_VSYNC_1 1 /* vsync on head B */ 62#define GPU_INTR_STATUS_FLIP_0 3 /* flip head A */ 63#define GPU_INTR_STATUS_FLIP_1 4 /* flip head B */ 64#define GPU_INTR_STATUS_QUEUE_0 5 /* queue head A */ 65#define GPU_INTR_STATUS_QUEUE_1 6 /* queue head B */ 66 67#define GPU_DRIVER_INFO_VERSION 0x211 68 69/* gpu internals */ 70struct display_head { 71 u64 be_time_stamp; 72 u32 status; 73 u32 offset; 74 u32 res1; 75 u32 res2; 76 u32 field; 77 u32 reserved1; 78 79 u64 res3; 80 u32 raster; 81 82 u64 vblank_count; 83 u32 field_vsync; 84 u32 reserved2; 85}; 86 87struct gpu_irq { 88 u32 irq_outlet; 89 u32 status; 90 u32 mask; 91 u32 video_cause; 92 u32 graph_cause; 93 u32 user_cause; 94 95 u32 res1; 96 u64 res2; 97 98 u32 reserved[4]; 99}; 100 101struct gpu_driver_info { 102 u32 version_driver; 103 u32 version_gpu; 104 u32 memory_size; 105 u32 hardware_channel; 106 107 u32 nvcore_frequency; 108 u32 memory_frequency; 109 110 u32 reserved[1063]; 111 struct display_head display_head[8]; 112 struct gpu_irq irq; 113}; 114 115struct ps3fb_priv { 116 unsigned int irq_no; 117 118 u64 context_handle, memory_handle; 119 struct gpu_driver_info *dinfo; 120 121 u64 vblank_count; /* frame count */ 122 wait_queue_head_t wait_vsync; 123 124 atomic_t ext_flip; /* on/off flip with vsync */ 125 atomic_t f_count; /* fb_open count */ 126 int is_blanked; 127 int is_kicked; 128 struct task_struct *task; 129}; 130static struct ps3fb_priv ps3fb; 131 132struct ps3fb_par { 133 u32 pseudo_palette[16]; 134 int mode_id, new_mode_id; 135 unsigned int num_frames; /* num of frame buffers */ 136 unsigned int width; 137 unsigned int height; 138 unsigned int ddr_line_length; 139 unsigned int ddr_frame_size; 140 unsigned int xdr_frame_size; 141 unsigned int full_offset; /* start of fullscreen DDR fb */ 142 unsigned int fb_offset; /* start of actual DDR fb */ 143 unsigned int pan_offset; 144}; 145 146 147#define FIRST_NATIVE_MODE_INDEX 10 148 149static const struct fb_videomode ps3fb_modedb[] = { 150 /* 60 Hz broadcast modes (modes "1" to "5") */ 151 { 152 /* 480i */ 153 "480i", 60, 576, 384, 74074, 130, 89, 78, 57, 63, 6, 154 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 155 }, { 156 /* 480p */ 157 "480p", 60, 576, 384, 37037, 130, 89, 78, 57, 63, 6, 158 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 159 }, { 160 /* 720p */ 161 "720p", 60, 1124, 644, 13481, 298, 148, 57, 44, 80, 5, 162 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 163 }, { 164 /* 1080i */ 165 "1080i", 60, 1688, 964, 13481, 264, 160, 94, 62, 88, 5, 166 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 167 }, { 168 /* 1080p */ 169 "1080p", 60, 1688, 964, 6741, 264, 160, 94, 62, 88, 5, 170 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 171 }, 172 173 /* 50 Hz broadcast modes (modes "6" to "10") */ 174 { 175 /* 576i */ 176 "576i", 50, 576, 460, 74074, 142, 83, 97, 63, 63, 5, 177 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 178 }, { 179 /* 576p */ 180 "576p", 50, 576, 460, 37037, 142, 83, 97, 63, 63, 5, 181 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 182 }, { 183 /* 720p */ 184 "720p", 50, 1124, 644, 13468, 298, 478, 57, 44, 80, 5, 185 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 186 }, { 187 /* 1080i */ 188 "1080i", 50, 1688, 964, 13468, 264, 600, 94, 62, 88, 5, 189 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 190 }, { 191 /* 1080p */ 192 "1080p", 50, 1688, 964, 6734, 264, 600, 94, 62, 88, 5, 193 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 194 }, 195 196 [FIRST_NATIVE_MODE_INDEX] = 197 /* 60 Hz broadcast modes (full resolution versions of modes "1" to "5") */ 198 { 199 /* 480if */ 200 "480if", 60, 720, 480, 74074, 58, 17, 30, 9, 63, 6, 201 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 202 }, { 203 /* 480pf */ 204 "480pf", 60, 720, 480, 37037, 58, 17, 30, 9, 63, 6, 205 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 206 }, { 207 /* 720pf */ 208 "720pf", 60, 1280, 720, 13481, 220, 70, 19, 6, 80, 5, 209 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 210 }, { 211 /* 1080if */ 212 "1080if", 60, 1920, 1080, 13481, 148, 44, 36, 4, 88, 5, 213 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 214 }, { 215 /* 1080pf */ 216 "1080pf", 60, 1920, 1080, 6741, 148, 44, 36, 4, 88, 5, 217 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 218 }, 219 220 /* 50 Hz broadcast modes (full resolution versions of modes "6" to "10") */ 221 { 222 /* 576if */ 223 "576if", 50, 720, 576, 74074, 70, 11, 39, 5, 63, 5, 224 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 225 }, { 226 /* 576pf */ 227 "576pf", 50, 720, 576, 37037, 70, 11, 39, 5, 63, 5, 228 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 229 }, { 230 /* 720pf */ 231 "720pf", 50, 1280, 720, 13468, 220, 400, 19, 6, 80, 5, 232 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 233 }, { 234 /* 1080if */ 235 "1080if", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5, 236 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 237 }, { 238 /* 1080pf */ 239 "1080pf", 50, 1920, 1080, 6734, 148, 484, 36, 4, 88, 5, 240 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 241 }, 242 243 /* VESA modes (modes "11" to "13") */ 244 { 245 /* WXGA */ 246 "wxga", 60, 1280, 768, 12924, 160, 24, 29, 3, 136, 6, 247 0, FB_VMODE_NONINTERLACED, 248 FB_MODE_IS_VESA 249 }, { 250 /* SXGA */ 251 "sxga", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, 252 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 253 FB_MODE_IS_VESA 254 }, { 255 /* WUXGA */ 256 "wuxga", 60, 1920, 1200, 6494, 80, 48, 26, 3, 32, 6, 257 FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, 258 FB_MODE_IS_VESA 259 } 260}; 261 262 263#define HEAD_A 264#define HEAD_B 265 266#define BPP 4 /* number of bytes per pixel */ 267 268 269static int ps3fb_mode; 270module_param(ps3fb_mode, int, 0); 271 272static char *mode_option __devinitdata; 273 274static int ps3fb_cmp_mode(const struct fb_videomode *vmode, 275 const struct fb_var_screeninfo *var) 276{ 277 long xres, yres, left_margin, right_margin, upper_margin, lower_margin; 278 long dx, dy; 279 280 /* maximum values */ 281 if (var->xres > vmode->xres || var->yres > vmode->yres || 282 var->pixclock > vmode->pixclock || 283 var->hsync_len > vmode->hsync_len || 284 var->vsync_len > vmode->vsync_len) 285 return -1; 286 287 /* progressive/interlaced must match */ 288 if ((var->vmode & FB_VMODE_MASK) != vmode->vmode) 289 return -1; 290 291 /* minimum resolution */ 292 xres = max(var->xres, 1U); 293 yres = max(var->yres, 1U); 294 295 /* minimum margins */ 296 left_margin = max(var->left_margin, vmode->left_margin); 297 right_margin = max(var->right_margin, vmode->right_margin); 298 upper_margin = max(var->upper_margin, vmode->upper_margin); 299 lower_margin = max(var->lower_margin, vmode->lower_margin); 300 301 /* resolution + margins may not exceed native parameters */ 302 dx = ((long)vmode->left_margin + (long)vmode->xres + 303 (long)vmode->right_margin) - 304 (left_margin + xres + right_margin); 305 if (dx < 0) 306 return -1; 307 308 dy = ((long)vmode->upper_margin + (long)vmode->yres + 309 (long)vmode->lower_margin) - 310 (upper_margin + yres + lower_margin); 311 if (dy < 0) 312 return -1; 313 314 /* exact match */ 315 if (!dx && !dy) 316 return 0; 317 318 /* resolution difference */ 319 return (vmode->xres - xres) * (vmode->yres - yres); 320} 321 322static const struct fb_videomode *ps3fb_native_vmode(enum ps3av_mode_num id) 323{ 324 return &ps3fb_modedb[FIRST_NATIVE_MODE_INDEX + id - 1]; 325} 326 327static const struct fb_videomode *ps3fb_vmode(int id) 328{ 329 u32 mode = id & PS3AV_MODE_MASK; 330 331 if (mode < PS3AV_MODE_480I || mode > PS3AV_MODE_WUXGA) 332 return NULL; 333 334 if (mode <= PS3AV_MODE_1080P50 && !(id & PS3AV_MODE_FULL)) { 335 /* Non-fullscreen broadcast mode */ 336 return &ps3fb_modedb[mode - 1]; 337 } 338 339 return ps3fb_native_vmode(mode); 340} 341 342static unsigned int ps3fb_find_mode(struct fb_var_screeninfo *var, 343 u32 *ddr_line_length, u32 *xdr_line_length) 344{ 345 unsigned int id, best_id; 346 int diff, best_diff; 347 const struct fb_videomode *vmode; 348 long gap; 349 350 best_id = 0; 351 best_diff = INT_MAX; 352 pr_debug("%s: wanted %u [%u] %u x %u [%u] %u\n", __func__, 353 var->left_margin, var->xres, var->right_margin, 354 var->upper_margin, var->yres, var->lower_margin); 355 for (id = PS3AV_MODE_480I; id <= PS3AV_MODE_WUXGA; id++) { 356 vmode = ps3fb_native_vmode(id); 357 diff = ps3fb_cmp_mode(vmode, var); 358 pr_debug("%s: mode %u: %u [%u] %u x %u [%u] %u: diff = %d\n", 359 __func__, id, vmode->left_margin, vmode->xres, 360 vmode->right_margin, vmode->upper_margin, 361 vmode->yres, vmode->lower_margin, diff); 362 if (diff < 0) 363 continue; 364 if (diff < best_diff) { 365 best_id = id; 366 if (!diff) 367 break; 368 best_diff = diff; 369 } 370 } 371 372 if (!best_id) { 373 pr_debug("%s: no suitable mode found\n", __func__); 374 return 0; 375 } 376 377 id = best_id; 378 vmode = ps3fb_native_vmode(id); 379 380 *ddr_line_length = vmode->xres * BPP; 381 382 /* minimum resolution */ 383 if (!var->xres) 384 var->xres = 1; 385 if (!var->yres) 386 var->yres = 1; 387 388 /* minimum virtual resolution */ 389 if (var->xres_virtual < var->xres) 390 var->xres_virtual = var->xres; 391 if (var->yres_virtual < var->yres) 392 var->yres_virtual = var->yres; 393 394 /* minimum margins */ 395 if (var->left_margin < vmode->left_margin) 396 var->left_margin = vmode->left_margin; 397 if (var->right_margin < vmode->right_margin) 398 var->right_margin = vmode->right_margin; 399 if (var->upper_margin < vmode->upper_margin) 400 var->upper_margin = vmode->upper_margin; 401 if (var->lower_margin < vmode->lower_margin) 402 var->lower_margin = vmode->lower_margin; 403 404 /* extra margins */ 405 gap = ((long)vmode->left_margin + (long)vmode->xres + 406 (long)vmode->right_margin) - 407 ((long)var->left_margin + (long)var->xres + 408 (long)var->right_margin); 409 if (gap > 0) { 410 var->left_margin += gap/2; 411 var->right_margin += (gap+1)/2; 412 pr_debug("%s: rounded up H to %u [%u] %u\n", __func__, 413 var->left_margin, var->xres, var->right_margin); 414 } 415 416 gap = ((long)vmode->upper_margin + (long)vmode->yres + 417 (long)vmode->lower_margin) - 418 ((long)var->upper_margin + (long)var->yres + 419 (long)var->lower_margin); 420 if (gap > 0) { 421 var->upper_margin += gap/2; 422 var->lower_margin += (gap+1)/2; 423 pr_debug("%s: rounded up V to %u [%u] %u\n", __func__, 424 var->upper_margin, var->yres, var->lower_margin); 425 } 426 427 /* fixed fields */ 428 var->pixclock = vmode->pixclock; 429 var->hsync_len = vmode->hsync_len; 430 var->vsync_len = vmode->vsync_len; 431 var->sync = vmode->sync; 432 433 if (ps3_compare_firmware_version(1, 9, 0) >= 0) { 434 *xdr_line_length = GPU_ALIGN_UP(var->xres_virtual * BPP); 435 if (*xdr_line_length > GPU_MAX_LINE_LENGTH) 436 *xdr_line_length = GPU_MAX_LINE_LENGTH; 437 } else 438 *xdr_line_length = *ddr_line_length; 439 440 if (vmode->sync & FB_SYNC_BROADCAST) { 441 /* Full broadcast modes have the full mode bit set */ 442 if (vmode->xres == var->xres && vmode->yres == var->yres) 443 id |= PS3AV_MODE_FULL; 444 } 445 446 pr_debug("%s: mode %u\n", __func__, id); 447 return id; 448} 449 450static void ps3fb_sync_image(struct device *dev, u64 frame_offset, 451 u64 dst_offset, u64 src_offset, u32 width, 452 u32 height, u32 dst_line_length, 453 u32 src_line_length) 454{ 455 int status; 456 u64 line_length; 457 458 line_length = dst_line_length; 459 if (src_line_length != dst_line_length) 460 line_length |= (u64)src_line_length << 32; 461 462 src_offset += GPU_FB_START; 463 464 mutex_lock(&ps3_gpu_mutex); 465 status = lv1_gpu_context_attribute(ps3fb.context_handle, 466 L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 467 dst_offset, GPU_IOIF + src_offset, 468 L1GPU_FB_BLIT_WAIT_FOR_COMPLETION | 469 (width << 16) | height, 470 line_length); 471 mutex_unlock(&ps3_gpu_mutex); 472 473 if (status) 474 dev_err(dev, 475 "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n", 476 __func__, status); 477#ifdef HEAD_A 478 status = lv1_gpu_context_attribute(ps3fb.context_handle, 479 L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 480 0, frame_offset, 0, 0); 481 if (status) 482 dev_err(dev, "%s: lv1_gpu_context_attribute FLIP failed: %d\n", 483 __func__, status); 484#endif 485#ifdef HEAD_B 486 status = lv1_gpu_context_attribute(ps3fb.context_handle, 487 L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 488 1, frame_offset, 0, 0); 489 if (status) 490 dev_err(dev, "%s: lv1_gpu_context_attribute FLIP failed: %d\n", 491 __func__, status); 492#endif 493} 494 495static int ps3fb_sync(struct fb_info *info, u32 frame) 496{ 497 struct ps3fb_par *par = info->par; 498 int error = 0; 499 u64 ddr_base, xdr_base; 500 501 if (frame > par->num_frames - 1) { 502 dev_dbg(info->device, "%s: invalid frame number (%u)\n", 503 __func__, frame); 504 error = -EINVAL; 505 goto out; 506 } 507 508 xdr_base = frame * par->xdr_frame_size; 509 ddr_base = frame * par->ddr_frame_size; 510 511 ps3fb_sync_image(info->device, ddr_base + par->full_offset, 512 ddr_base + par->fb_offset, xdr_base + par->pan_offset, 513 par->width, par->height, par->ddr_line_length, 514 info->fix.line_length); 515 516out: 517 return error; 518} 519 520static int ps3fb_open(struct fb_info *info, int user) 521{ 522 atomic_inc(&ps3fb.f_count); 523 return 0; 524} 525 526static int ps3fb_release(struct fb_info *info, int user) 527{ 528 if (atomic_dec_and_test(&ps3fb.f_count)) { 529 if (atomic_read(&ps3fb.ext_flip)) { 530 atomic_set(&ps3fb.ext_flip, 0); 531 if (!try_acquire_console_sem()) { 532 ps3fb_sync(info, 0); /* single buffer */ 533 release_console_sem(); 534 } 535 } 536 } 537 return 0; 538} 539 540 /* 541 * Setting the video mode has been split into two parts. 542 * First part, xxxfb_check_var, must not write anything 543 * to hardware, it should only verify and adjust var. 544 * This means it doesn't alter par but it does use hardware 545 * data from it to check this var. 546 */ 547 548static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 549{ 550 u32 xdr_line_length, ddr_line_length; 551 int mode; 552 553 mode = ps3fb_find_mode(var, &ddr_line_length, &xdr_line_length); 554 if (!mode) 555 return -EINVAL; 556 557 /* Virtual screen */ 558 if (var->xres_virtual > xdr_line_length / BPP) { 559 dev_dbg(info->device, 560 "Horizontal virtual screen size too large\n"); 561 return -EINVAL; 562 } 563 564 if (var->xoffset + var->xres > var->xres_virtual || 565 var->yoffset + var->yres > var->yres_virtual) { 566 dev_dbg(info->device, "panning out-of-range\n"); 567 return -EINVAL; 568 } 569 570 /* We support ARGB8888 only */ 571 if (var->bits_per_pixel > 32 || var->grayscale || 572 var->red.offset > 16 || var->green.offset > 8 || 573 var->blue.offset > 0 || var->transp.offset > 24 || 574 var->red.length > 8 || var->green.length > 8 || 575 var->blue.length > 8 || var->transp.length > 8 || 576 var->red.msb_right || var->green.msb_right || 577 var->blue.msb_right || var->transp.msb_right || var->nonstd) { 578 dev_dbg(info->device, "We support ARGB8888 only\n"); 579 return -EINVAL; 580 } 581 582 var->bits_per_pixel = 32; 583 var->red.offset = 16; 584 var->green.offset = 8; 585 var->blue.offset = 0; 586 var->transp.offset = 24; 587 var->red.length = 8; 588 var->green.length = 8; 589 var->blue.length = 8; 590 var->transp.length = 8; 591 var->red.msb_right = 0; 592 var->green.msb_right = 0; 593 var->blue.msb_right = 0; 594 var->transp.msb_right = 0; 595 596 /* Rotation is not supported */ 597 if (var->rotate) { 598 dev_dbg(info->device, "Rotation is not supported\n"); 599 return -EINVAL; 600 } 601 602 /* Memory limit */ 603 if (var->yres_virtual * xdr_line_length > info->fix.smem_len) { 604 dev_dbg(info->device, "Not enough memory\n"); 605 return -ENOMEM; 606 } 607 608 var->height = -1; 609 var->width = -1; 610 611 return 0; 612} 613 614 /* 615 * This routine actually sets the video mode. 616 */ 617 618static int ps3fb_set_par(struct fb_info *info) 619{ 620 struct ps3fb_par *par = info->par; 621 unsigned int mode, ddr_line_length, xdr_line_length, lines, maxlines; 622 unsigned int ddr_xoff, ddr_yoff, offset; 623 const struct fb_videomode *vmode; 624 u64 dst; 625 626 mode = ps3fb_find_mode(&info->var, &ddr_line_length, &xdr_line_length); 627 if (!mode) 628 return -EINVAL; 629 630 vmode = ps3fb_native_vmode(mode & PS3AV_MODE_MASK); 631 632 info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0; 633 info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0; 634 info->fix.line_length = xdr_line_length; 635 636 par->ddr_line_length = ddr_line_length; 637 par->ddr_frame_size = vmode->yres * ddr_line_length; 638 par->xdr_frame_size = info->var.yres_virtual * xdr_line_length; 639 640 par->num_frames = info->fix.smem_len / 641 max(par->ddr_frame_size, par->xdr_frame_size); 642 643 /* Keep the special bits we cannot set using fb_var_screeninfo */ 644 par->new_mode_id = (par->new_mode_id & ~PS3AV_MODE_MASK) | mode; 645 646 par->width = info->var.xres; 647 par->height = info->var.yres; 648 649 /* Start of the virtual frame buffer (relative to fullscreen) */ 650 ddr_xoff = info->var.left_margin - vmode->left_margin; 651 ddr_yoff = info->var.upper_margin - vmode->upper_margin; 652 offset = ddr_yoff * ddr_line_length + ddr_xoff * BPP; 653 654 par->fb_offset = GPU_ALIGN_UP(offset); 655 par->full_offset = par->fb_offset - offset; 656 par->pan_offset = info->var.yoffset * xdr_line_length + 657 info->var.xoffset * BPP; 658 659 if (par->new_mode_id != par->mode_id) { 660 if (ps3av_set_video_mode(par->new_mode_id)) { 661 par->new_mode_id = par->mode_id; 662 return -EINVAL; 663 } 664 par->mode_id = par->new_mode_id; 665 } 666 667 /* Clear XDR frame buffer memory */ 668 memset((void __force *)info->screen_base, 0, info->fix.smem_len); 669 670 /* Clear DDR frame buffer memory */ 671 lines = vmode->yres * par->num_frames; 672 if (par->full_offset) 673 lines++; 674 maxlines = info->fix.smem_len / ddr_line_length; 675 for (dst = 0; lines; dst += maxlines * ddr_line_length) { 676 unsigned int l = min(lines, maxlines); 677 ps3fb_sync_image(info->device, 0, dst, 0, vmode->xres, l, 678 ddr_line_length, ddr_line_length); 679 lines -= l; 680 } 681 682 return 0; 683} 684 685 /* 686 * Set a single color register. The values supplied are already 687 * rounded down to the hardware's capabilities (according to the 688 * entries in the var structure). Return != 0 for invalid regno. 689 */ 690 691static int ps3fb_setcolreg(unsigned int regno, unsigned int red, 692 unsigned int green, unsigned int blue, 693 unsigned int transp, struct fb_info *info) 694{ 695 if (regno >= 16) 696 return 1; 697 698 red >>= 8; 699 green >>= 8; 700 blue >>= 8; 701 transp >>= 8; 702 703 ((u32 *)info->pseudo_palette)[regno] = transp << 24 | red << 16 | 704 green << 8 | blue; 705 return 0; 706} 707 708static int ps3fb_pan_display(struct fb_var_screeninfo *var, 709 struct fb_info *info) 710{ 711 struct ps3fb_par *par = info->par; 712 713 par->pan_offset = var->yoffset * info->fix.line_length + 714 var->xoffset * BPP; 715 return 0; 716} 717 718 /* 719 * As we have a virtual frame buffer, we need our own mmap function 720 */ 721 722static int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 723{ 724 unsigned long size, offset; 725 726 size = vma->vm_end - vma->vm_start; 727 offset = vma->vm_pgoff << PAGE_SHIFT; 728 if (offset + size > info->fix.smem_len) 729 return -EINVAL; 730 731 offset += info->fix.smem_start; 732 if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, 733 size, vma->vm_page_prot)) 734 return -EAGAIN; 735 736 dev_dbg(info->device, "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n", 737 offset, vma->vm_start); 738 return 0; 739} 740 741 /* 742 * Blank the display 743 */ 744 745static int ps3fb_blank(int blank, struct fb_info *info) 746{ 747 int retval; 748 749 dev_dbg(info->device, "%s: blank:%d\n", __func__, blank); 750 switch (blank) { 751 case FB_BLANK_POWERDOWN: 752 case FB_BLANK_HSYNC_SUSPEND: 753 case FB_BLANK_VSYNC_SUSPEND: 754 case FB_BLANK_NORMAL: 755 retval = ps3av_video_mute(1); /* mute on */ 756 if (!retval) 757 ps3fb.is_blanked = 1; 758 break; 759 760 default: /* unblank */ 761 retval = ps3av_video_mute(0); /* mute off */ 762 if (!retval) 763 ps3fb.is_blanked = 0; 764 break; 765 } 766 return retval; 767} 768 769static int ps3fb_get_vblank(struct fb_vblank *vblank) 770{ 771 memset(vblank, 0, sizeof(*vblank)); 772 vblank->flags = FB_VBLANK_HAVE_VSYNC; 773 return 0; 774} 775 776static int ps3fb_wait_for_vsync(u32 crtc) 777{ 778 int ret; 779 u64 count; 780 781 count = ps3fb.vblank_count; 782 ret = wait_event_interruptible_timeout(ps3fb.wait_vsync, 783 count != ps3fb.vblank_count, 784 HZ / 10); 785 if (!ret) 786 return -ETIMEDOUT; 787 788 return 0; 789} 790 791 792 /* 793 * ioctl 794 */ 795 796static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd, 797 unsigned long arg) 798{ 799 void __user *argp = (void __user *)arg; 800 u32 val; 801 int retval = -EFAULT; 802 803 switch (cmd) { 804 case FBIOGET_VBLANK: 805 { 806 struct fb_vblank vblank; 807 dev_dbg(info->device, "FBIOGET_VBLANK:\n"); 808 retval = ps3fb_get_vblank(&vblank); 809 if (retval) 810 break; 811 812 if (copy_to_user(argp, &vblank, sizeof(vblank))) 813 retval = -EFAULT; 814 break; 815 } 816 817 case FBIO_WAITFORVSYNC: 818 { 819 u32 crt; 820 dev_dbg(info->device, "FBIO_WAITFORVSYNC:\n"); 821 if (get_user(crt, (u32 __user *) arg)) 822 break; 823 824 retval = ps3fb_wait_for_vsync(crt); 825 break; 826 } 827 828 case PS3FB_IOCTL_SETMODE: 829 { 830 struct ps3fb_par *par = info->par; 831 const struct fb_videomode *vmode; 832 struct fb_var_screeninfo var; 833 834 if (copy_from_user(&val, argp, sizeof(val))) 835 break; 836 837 if (!(val & PS3AV_MODE_MASK)) { 838 u32 id = ps3av_get_auto_mode(); 839 if (id > 0) 840 val = (val & ~PS3AV_MODE_MASK) | id; 841 } 842 dev_dbg(info->device, "PS3FB_IOCTL_SETMODE:%x\n", val); 843 retval = -EINVAL; 844 vmode = ps3fb_vmode(val); 845 if (vmode) { 846 var = info->var; 847 fb_videomode_to_var(&var, vmode); 848 acquire_console_sem(); 849 info->flags |= FBINFO_MISC_USEREVENT; 850 /* Force, in case only special bits changed */ 851 var.activate |= FB_ACTIVATE_FORCE; 852 par->new_mode_id = val; 853 retval = fb_set_var(info, &var); 854 info->flags &= ~FBINFO_MISC_USEREVENT; 855 release_console_sem(); 856 } 857 break; 858 } 859 860 case PS3FB_IOCTL_GETMODE: 861 val = ps3av_get_mode(); 862 dev_dbg(info->device, "PS3FB_IOCTL_GETMODE:%x\n", val); 863 if (!copy_to_user(argp, &val, sizeof(val))) 864 retval = 0; 865 break; 866 867 case PS3FB_IOCTL_SCREENINFO: 868 { 869 struct ps3fb_par *par = info->par; 870 struct ps3fb_ioctl_res res; 871 dev_dbg(info->device, "PS3FB_IOCTL_SCREENINFO:\n"); 872 res.xres = info->fix.line_length / BPP; 873 res.yres = info->var.yres_virtual; 874 res.xoff = (res.xres - info->var.xres) / 2; 875 res.yoff = (res.yres - info->var.yres) / 2; 876 res.num_frames = par->num_frames; 877 if (!copy_to_user(argp, &res, sizeof(res))) 878 retval = 0; 879 break; 880 } 881 882 case PS3FB_IOCTL_ON: 883 dev_dbg(info->device, "PS3FB_IOCTL_ON:\n"); 884 atomic_inc(&ps3fb.ext_flip); 885 retval = 0; 886 break; 887 888 case PS3FB_IOCTL_OFF: 889 dev_dbg(info->device, "PS3FB_IOCTL_OFF:\n"); 890 atomic_dec_if_positive(&ps3fb.ext_flip); 891 retval = 0; 892 break; 893 894 case PS3FB_IOCTL_FSEL: 895 if (copy_from_user(&val, argp, sizeof(val))) 896 break; 897 898 dev_dbg(info->device, "PS3FB_IOCTL_FSEL:%d\n", val); 899 acquire_console_sem(); 900 retval = ps3fb_sync(info, val); 901 release_console_sem(); 902 break; 903 904 default: 905 retval = -ENOIOCTLCMD; 906 break; 907 } 908 return retval; 909} 910 911static int ps3fbd(void *arg) 912{ 913 struct fb_info *info = arg; 914 915 set_freezable(); 916 while (!kthread_should_stop()) { 917 try_to_freeze(); 918 set_current_state(TASK_INTERRUPTIBLE); 919 if (ps3fb.is_kicked) { 920 ps3fb.is_kicked = 0; 921 acquire_console_sem(); 922 ps3fb_sync(info, 0); /* single buffer */ 923 release_console_sem(); 924 } 925 schedule(); 926 } 927 return 0; 928} 929 930static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr) 931{ 932 struct device *dev = ptr; 933 u64 v1; 934 int status; 935 struct display_head *head = &ps3fb.dinfo->display_head[1]; 936 937 status = lv1_gpu_context_intr(ps3fb.context_handle, &v1); 938 if (status) { 939 dev_err(dev, "%s: lv1_gpu_context_intr failed: %d\n", __func__, 940 status); 941 return IRQ_NONE; 942 } 943 944 if (v1 & (1 << GPU_INTR_STATUS_VSYNC_1)) { 945 /* VSYNC */ 946 ps3fb.vblank_count = head->vblank_count; 947 if (ps3fb.task && !ps3fb.is_blanked && 948 !atomic_read(&ps3fb.ext_flip)) { 949 ps3fb.is_kicked = 1; 950 wake_up_process(ps3fb.task); 951 } 952 wake_up_interruptible(&ps3fb.wait_vsync); 953 } 954 955 return IRQ_HANDLED; 956} 957 958 959static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, 960 struct device *dev) 961{ 962 int error; 963 964 dev_dbg(dev, "version_driver:%x\n", dinfo->version_driver); 965 dev_dbg(dev, "irq outlet:%x\n", dinfo->irq.irq_outlet); 966 dev_dbg(dev, 967 "version_gpu: %x memory_size: %x ch: %x core_freq: %d " 968 "mem_freq:%d\n", 969 dinfo->version_gpu, dinfo->memory_size, dinfo->hardware_channel, 970 dinfo->nvcore_frequency/1000000, dinfo->memory_frequency/1000000); 971 972 if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) { 973 dev_err(dev, "%s: version_driver err:%x\n", __func__, 974 dinfo->version_driver); 975 return -EINVAL; 976 } 977 978 error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, 979 &ps3fb.irq_no); 980 if (error) { 981 dev_err(dev, "%s: ps3_alloc_irq failed %d\n", __func__, error); 982 return error; 983 } 984 985 error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED, 986 DEVICE_NAME, dev); 987 if (error) { 988 dev_err(dev, "%s: request_irq failed %d\n", __func__, error); 989 ps3_irq_plug_destroy(ps3fb.irq_no); 990 return error; 991 } 992 993 dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) | 994 (1 << GPU_INTR_STATUS_FLIP_1); 995 return 0; 996} 997 998static int ps3fb_xdr_settings(u64 xdr_lpar, struct device *dev) 999{ 1000 int status; 1001 1002 status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, 1003 xdr_lpar, ps3fb_videomemory.size, 0); 1004 if (status) { 1005 dev_err(dev, "%s: lv1_gpu_context_iomap failed: %d\n", 1006 __func__, status); 1007 return -ENXIO; 1008 } 1009 dev_dbg(dev, "video:%p ioif:%lx lpar:%llx size:%lx\n", 1010 ps3fb_videomemory.address, GPU_IOIF, xdr_lpar, 1011 ps3fb_videomemory.size); 1012 1013 status = lv1_gpu_context_attribute(ps3fb.context_handle, 1014 L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP, 1015 xdr_lpar, GPU_CMD_BUF_SIZE, 1016 GPU_IOIF, 0); 1017 if (status) { 1018 dev_err(dev, 1019 "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n", 1020 __func__, status); 1021 return -ENXIO; 1022 } 1023 return 0; 1024} 1025 1026static struct fb_ops ps3fb_ops = { 1027 .fb_open = ps3fb_open, 1028 .fb_release = ps3fb_release, 1029 .fb_read = fb_sys_read, 1030 .fb_write = fb_sys_write, 1031 .fb_check_var = ps3fb_check_var, 1032 .fb_set_par = ps3fb_set_par, 1033 .fb_setcolreg = ps3fb_setcolreg, 1034 .fb_pan_display = ps3fb_pan_display, 1035 .fb_fillrect = sys_fillrect, 1036 .fb_copyarea = sys_copyarea, 1037 .fb_imageblit = sys_imageblit, 1038 .fb_mmap = ps3fb_mmap, 1039 .fb_blank = ps3fb_blank, 1040 .fb_ioctl = ps3fb_ioctl, 1041 .fb_compat_ioctl = ps3fb_ioctl 1042}; 1043 1044static struct fb_fix_screeninfo ps3fb_fix __initdata = { 1045 .id = DEVICE_NAME, 1046 .type = FB_TYPE_PACKED_PIXELS, 1047 .visual = FB_VISUAL_TRUECOLOR, 1048 .accel = FB_ACCEL_NONE, 1049}; 1050 1051static int ps3fb_set_sync(struct device *dev) 1052{ 1053 int status; 1054 1055#ifdef HEAD_A 1056 status = lv1_gpu_context_attribute(0x0, 1057 L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, 1058 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); 1059 if (status) { 1060 dev_err(dev, 1061 "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: " 1062 "%d\n", 1063 __func__, status); 1064 return -1; 1065 } 1066#endif 1067#ifdef HEAD_B 1068 status = lv1_gpu_context_attribute(0x0, 1069 L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, 1070 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); 1071 1072 if (status) { 1073 dev_err(dev, 1074 "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: " 1075 "%d\n", 1076 __func__, status); 1077 return -1; 1078 } 1079#endif 1080 return 0; 1081} 1082 1083static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) 1084{ 1085 struct fb_info *info; 1086 struct ps3fb_par *par; 1087 int retval = -ENOMEM; 1088 u64 ddr_lpar = 0; 1089 u64 lpar_dma_control = 0; 1090 u64 lpar_driver_info = 0; 1091 u64 lpar_reports = 0; 1092 u64 lpar_reports_size = 0; 1093 u64 xdr_lpar; 1094 void *fb_start; 1095 int status; 1096 struct task_struct *task; 1097 unsigned long max_ps3fb_size; 1098 1099 if (ps3fb_videomemory.size < GPU_CMD_BUF_SIZE) { 1100 dev_err(&dev->core, "%s: Not enough video memory\n", __func__); 1101 return -ENOMEM; 1102 } 1103 1104 status = ps3_open_hv_device(dev); 1105 if (status) { 1106 dev_err(&dev->core, "%s: ps3_open_hv_device failed\n", 1107 __func__); 1108 goto err; 1109 } 1110 1111 if (!ps3fb_mode) 1112 ps3fb_mode = ps3av_get_mode(); 1113 dev_dbg(&dev->core, "ps3fb_mode: %d\n", ps3fb_mode); 1114 1115 atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ 1116 atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ 1117 init_waitqueue_head(&ps3fb.wait_vsync); 1118 1119 ps3fb_set_sync(&dev->core); 1120 1121 max_ps3fb_size = _ALIGN_UP(GPU_IOIF, 256*1024*1024) - GPU_IOIF; 1122 if (ps3fb_videomemory.size > max_ps3fb_size) { 1123 dev_info(&dev->core, "Limiting ps3fb mem size to %lu bytes\n", 1124 max_ps3fb_size); 1125 ps3fb_videomemory.size = max_ps3fb_size; 1126 } 1127 1128 /* get gpu context handle */ 1129 status = lv1_gpu_memory_allocate(ps3fb_videomemory.size, 0, 0, 0, 0, 1130 &ps3fb.memory_handle, &ddr_lpar); 1131 if (status) { 1132 dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n", 1133 __func__, status); 1134 goto err; 1135 } 1136 dev_dbg(&dev->core, "ddr:lpar:0x%llx\n", ddr_lpar); 1137 1138 status = lv1_gpu_context_allocate(ps3fb.memory_handle, 0, 1139 &ps3fb.context_handle, 1140 &lpar_dma_control, &lpar_driver_info, 1141 &lpar_reports, &lpar_reports_size); 1142 if (status) { 1143 dev_err(&dev->core, 1144 "%s: lv1_gpu_context_attribute failed: %d\n", __func__, 1145 status); 1146 goto err_gpu_memory_free; 1147 } 1148 1149 /* vsync interrupt */ 1150 ps3fb.dinfo = (void __force *)ioremap(lpar_driver_info, 128 * 1024); 1151 if (!ps3fb.dinfo) { 1152 dev_err(&dev->core, "%s: ioremap failed\n", __func__); 1153 goto err_gpu_context_free; 1154 } 1155 1156 retval = ps3fb_vsync_settings(ps3fb.dinfo, &dev->core); 1157 if (retval) 1158 goto err_iounmap_dinfo; 1159 1160 /* Clear memory to prevent kernel info leakage into userspace */ 1161 memset(ps3fb_videomemory.address, 0, ps3fb_videomemory.size); 1162 1163 xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address)); 1164 retval = ps3fb_xdr_settings(xdr_lpar, &dev->core); 1165 if (retval) 1166 goto err_free_irq; 1167 1168 info = framebuffer_alloc(sizeof(struct ps3fb_par), &dev->core); 1169 if (!info) 1170 goto err_free_irq; 1171 1172 par = info->par; 1173 par->mode_id = ~ps3fb_mode; /* != ps3fb_mode, to trigger change */ 1174 par->new_mode_id = ps3fb_mode; 1175 par->num_frames = 1; 1176 1177 info->fbops = &ps3fb_ops; 1178 info->fix = ps3fb_fix; 1179 1180 /* 1181 * The GPU command buffer is at the start of video memory 1182 * As we don't use the full command buffer, we can put the actual 1183 * frame buffer at offset GPU_FB_START and save some precious XDR 1184 * memory 1185 */ 1186 fb_start = ps3fb_videomemory.address + GPU_FB_START; 1187 info->screen_base = (char __force __iomem *)fb_start; 1188 info->fix.smem_start = virt_to_abs(fb_start); 1189 info->fix.smem_len = ps3fb_videomemory.size - GPU_FB_START; 1190 1191 info->pseudo_palette = par->pseudo_palette; 1192 info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST | 1193 FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; 1194 1195 retval = fb_alloc_cmap(&info->cmap, 256, 0); 1196 if (retval < 0) 1197 goto err_framebuffer_release; 1198 1199 if (!fb_find_mode(&info->var, info, mode_option, ps3fb_modedb, 1200 ARRAY_SIZE(ps3fb_modedb), 1201 ps3fb_vmode(par->new_mode_id), 32)) { 1202 retval = -EINVAL; 1203 goto err_fb_dealloc; 1204 } 1205 1206 fb_videomode_to_modelist(ps3fb_modedb, ARRAY_SIZE(ps3fb_modedb), 1207 &info->modelist); 1208 1209 retval = register_framebuffer(info); 1210 if (retval < 0) 1211 goto err_fb_dealloc; 1212 1213 dev->core.driver_data = info; 1214 1215 dev_info(info->device, "%s %s, using %u KiB of video memory\n", 1216 dev_driver_string(info->dev), dev_name(info->dev), 1217 info->fix.smem_len >> 10); 1218 1219 task = kthread_run(ps3fbd, info, DEVICE_NAME); 1220 if (IS_ERR(task)) { 1221 retval = PTR_ERR(task); 1222 goto err_unregister_framebuffer; 1223 } 1224 1225 ps3fb.task = task; 1226 1227 return 0; 1228 1229err_unregister_framebuffer: 1230 unregister_framebuffer(info); 1231err_fb_dealloc: 1232 fb_dealloc_cmap(&info->cmap); 1233err_framebuffer_release: 1234 framebuffer_release(info); 1235err_free_irq: 1236 free_irq(ps3fb.irq_no, &dev->core); 1237 ps3_irq_plug_destroy(ps3fb.irq_no); 1238err_iounmap_dinfo: 1239 iounmap((u8 __force __iomem *)ps3fb.dinfo); 1240err_gpu_context_free: 1241 lv1_gpu_context_free(ps3fb.context_handle); 1242err_gpu_memory_free: 1243 lv1_gpu_memory_free(ps3fb.memory_handle); 1244err: 1245 return retval; 1246} 1247 1248static int ps3fb_shutdown(struct ps3_system_bus_device *dev) 1249{ 1250 int status; 1251 struct fb_info *info = dev->core.driver_data; 1252 1253 dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); 1254 1255 atomic_inc(&ps3fb.ext_flip); /* flip off */ 1256 ps3fb.dinfo->irq.mask = 0; 1257 1258 if (ps3fb.task) { 1259 struct task_struct *task = ps3fb.task; 1260 ps3fb.task = NULL; 1261 kthread_stop(task); 1262 } 1263 if (ps3fb.irq_no) { 1264 free_irq(ps3fb.irq_no, &dev->core); 1265 ps3_irq_plug_destroy(ps3fb.irq_no); 1266 } 1267 if (info) { 1268 unregister_framebuffer(info); 1269 fb_dealloc_cmap(&info->cmap); 1270 framebuffer_release(info); 1271 info = dev->core.driver_data = NULL; 1272 } 1273 iounmap((u8 __force __iomem *)ps3fb.dinfo); 1274 1275 status = lv1_gpu_context_free(ps3fb.context_handle); 1276 if (status) 1277 dev_dbg(&dev->core, "lv1_gpu_context_free failed: %d\n", 1278 status); 1279 1280 status = lv1_gpu_memory_free(ps3fb.memory_handle); 1281 if (status) 1282 dev_dbg(&dev->core, "lv1_gpu_memory_free failed: %d\n", 1283 status); 1284 1285 ps3_close_hv_device(dev); 1286 dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 1287 1288 return 0; 1289} 1290 1291static struct ps3_system_bus_driver ps3fb_driver = { 1292 .match_id = PS3_MATCH_ID_GPU, 1293 .match_sub_id = PS3_MATCH_SUB_ID_GPU_FB, 1294 .core.name = DEVICE_NAME, 1295 .core.owner = THIS_MODULE, 1296 .probe = ps3fb_probe, 1297 .remove = ps3fb_shutdown, 1298 .shutdown = ps3fb_shutdown, 1299}; 1300 1301static int __init ps3fb_setup(void) 1302{ 1303 char *options; 1304 1305#ifdef MODULE 1306 return 0; 1307#endif 1308 1309 if (fb_get_options(DEVICE_NAME, &options)) 1310 return -ENXIO; 1311 1312 if (!options || !*options) 1313 return 0; 1314 1315 while (1) { 1316 char *this_opt = strsep(&options, ","); 1317 1318 if (!this_opt) 1319 break; 1320 if (!*this_opt) 1321 continue; 1322 if (!strncmp(this_opt, "mode:", 5)) 1323 ps3fb_mode = simple_strtoul(this_opt + 5, NULL, 0); 1324 else 1325 mode_option = this_opt; 1326 } 1327 return 0; 1328} 1329 1330static int __init ps3fb_init(void) 1331{ 1332 if (!ps3fb_videomemory.address || ps3fb_setup()) 1333 return -ENXIO; 1334 1335 return ps3_system_bus_driver_register(&ps3fb_driver); 1336} 1337 1338static void __exit ps3fb_exit(void) 1339{ 1340 pr_debug(" -> %s:%d\n", __func__, __LINE__); 1341 ps3_system_bus_driver_unregister(&ps3fb_driver); 1342 pr_debug(" <- %s:%d\n", __func__, __LINE__); 1343} 1344 1345module_init(ps3fb_init); 1346module_exit(ps3fb_exit); 1347 1348MODULE_LICENSE("GPL"); 1349MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver"); 1350MODULE_AUTHOR("Sony Computer Entertainment Inc."); 1351MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_FB);