aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>2007-02-12 03:55:23 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-12 12:48:44 -0500
commit310d8c11126d21e417206c874c6382c44ece1baa (patch)
treef09e4f821a34346f412689d4da365688fad7ba89 /drivers
parentfbdb3e5be36619c4acf415d870eceab4cbce2850 (diff)
[PATCH] ps3: Virtual Frame Buffer Driver
Add the PS3 Virtual Frame Buffer Driver. As the actual graphics hardware cannot be accessed directly by Linux, ps3fb uses a virtual frame buffer in main memory. The actual screen image is copied to graphics memory by the GPU on every vertical blank, by making a hypervisor call. Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> Cc: James Simmons <jsimmons@infradead.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/Kconfig20
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/ps3fb.c1229
3 files changed, 1250 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 95cbc836f57a..8874cf2fd279 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1598,6 +1598,26 @@ config FB_IBM_GXT4500
1598 Say Y here to enable support for the IBM GXT4500P display 1598 Say Y here to enable support for the IBM GXT4500P display
1599 adaptor, found on some IBM System P (pSeries) machines. 1599 adaptor, found on some IBM System P (pSeries) machines.
1600 1600
1601config FB_PS3
1602 bool "PS3 GPU framebuffer driver"
1603 depends on FB && PPC_PS3
1604 select PS3_PS3AV
1605 select FB_CFB_FILLRECT
1606 select FB_CFB_COPYAREA
1607 select FB_CFB_IMAGEBLIT
1608 ---help---
1609 Include support for the virtual frame buffer in the PS3 platform.
1610
1611config FB_PS3_DEFAULT_SIZE_M
1612 int "PS3 default frame buffer size (in MiB)"
1613 depends on FB_PS3
1614 default 18
1615 ---help---
1616 This is the default size (in MiB) of the virtual frame buffer in
1617 the PS3.
1618 The default value can be overridden on the kernel command line
1619 using the "ps3fb" option (e.g. "ps3fb=9M");
1620
1601config FB_VIRTUAL 1621config FB_VIRTUAL
1602 tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" 1622 tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
1603 depends on FB 1623 depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 8b6fc0a6041b..6801edff36d9 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
97obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/ 97obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/
98obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ 98obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/
99obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o 99obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
100obj-$(CONFIG_FB_PS3) += ps3fb.o
100 101
101# Platform or fallback drivers go here 102# Platform or fallback drivers go here
102obj-$(CONFIG_FB_VESA) += vesafb.o 103obj-$(CONFIG_FB_VESA) += vesafb.o
diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c
new file mode 100644
index 000000000000..81e43cda7d8b
--- /dev/null
+++ b/drivers/video/ps3fb.c
@@ -0,0 +1,1229 @@
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/tty.h>
26#include <linux/slab.h>
27#include <linux/vmalloc.h>
28#include <linux/delay.h>
29#include <linux/interrupt.h>
30#include <linux/platform_device.h>
31#include <linux/console.h>
32#include <linux/ioctl.h>
33#include <linux/notifier.h>
34#include <linux/reboot.h>
35
36#include <asm/uaccess.h>
37#include <linux/fb.h>
38#include <linux/init.h>
39#include <asm/time.h>
40
41#include <asm/abs_addr.h>
42#include <asm/lv1call.h>
43#include <asm/ps3av.h>
44#include <asm/ps3fb.h>
45#include <asm/ps3.h>
46
47#ifdef PS3FB_DEBUG
48#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ##args)
49#else
50#define DPRINTK(fmt, args...)
51#endif
52
53#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x101
54#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x102
55#define L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP 0x600
56#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
57#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT_SYNC 0x602
58
59#define L1GPU_FB_BLIT_WAIT_FOR_COMPLETION (1ULL << 32)
60
61#define L1GPU_DISPLAY_SYNC_HSYNC 1
62#define L1GPU_DISPLAY_SYNC_VSYNC 2
63
64#define DDR_SIZE (0) /* used no ddr */
65#define GPU_OFFSET (64 * 1024)
66#define GPU_IOIF (0x0d000000UL)
67
68#define PS3FB_FULL_MODE_BIT 0x80
69
70#define GPU_INTR_STATUS_VSYNC_0 0 /* vsync on head A */
71#define GPU_INTR_STATUS_VSYNC_1 1 /* vsync on head B */
72#define GPU_INTR_STATUS_FLIP_0 3 /* flip head A */
73#define GPU_INTR_STATUS_FLIP_1 4 /* flip head B */
74#define GPU_INTR_STATUS_QUEUE_0 5 /* queue head A */
75#define GPU_INTR_STATUS_QUEUE_1 6 /* queue head B */
76
77#define GPU_DRIVER_INFO_VERSION 0x211
78
79/* gpu internals */
80struct display_head {
81 u64 be_time_stamp;
82 u32 status;
83 u32 offset;
84 u32 res1;
85 u32 res2;
86 u32 field;
87 u32 reserved1;
88
89 u64 res3;
90 u32 raster;
91
92 u64 vblank_count;
93 u32 field_vsync;
94 u32 reserved2;
95};
96
97struct gpu_irq {
98 u32 irq_outlet;
99 u32 status;
100 u32 mask;
101 u32 video_cause;
102 u32 graph_cause;
103 u32 user_cause;
104
105 u32 res1;
106 u64 res2;
107
108 u32 reserved[4];
109};
110
111struct gpu_driver_info {
112 u32 version_driver;
113 u32 version_gpu;
114 u32 memory_size;
115 u32 hardware_channel;
116
117 u32 nvcore_frequency;
118 u32 memory_frequency;
119
120 u32 reserved[1063];
121 struct display_head display_head[8];
122 struct gpu_irq irq;
123};
124
125struct ps3fb_priv {
126 unsigned int irq_no;
127 void *dev;
128
129 u64 context_handle, memory_handle;
130 void *xdr_ea;
131 struct gpu_driver_info *dinfo;
132 struct semaphore sem;
133 u32 res_index;
134
135 u64 vblank_count; /* frame count */
136 wait_queue_head_t wait_vsync;
137
138 u32 num_frames; /* num of frame buffers */
139 atomic_t ext_flip; /* on/off flip with vsync */
140 atomic_t f_count; /* fb_open count */
141 int is_blanked;
142};
143static struct ps3fb_priv ps3fb;
144
145struct ps3fb_res_table {
146 u32 xres;
147 u32 yres;
148 u32 xoff;
149 u32 yoff;
150 u32 type;
151};
152#define PS3FB_RES_FULL 1
153static const struct ps3fb_res_table ps3fb_res[] = {
154 /* res_x,y margin_x,y full */
155 { 720, 480, 72, 48 , 0},
156 { 720, 576, 72, 58 , 0},
157 { 1280, 720, 78, 38 , 0},
158 { 1920, 1080, 116, 58 , 0},
159 /* full mode */
160 { 720, 480, 0, 0 , PS3FB_RES_FULL},
161 { 720, 576, 0, 0 , PS3FB_RES_FULL},
162 { 1280, 720, 0, 0 , PS3FB_RES_FULL},
163 { 1920, 1080, 0, 0 , PS3FB_RES_FULL},
164 /* vesa: normally full mode */
165 { 1280, 768, 0, 0 , 0},
166 { 1280, 1024, 0, 0 , 0},
167 { 1920, 1200, 0, 0 , 0},
168 { 0, 0, 0, 0 , 0} };
169
170/* default resolution */
171#define GPU_RES_INDEX 0 /* 720 x 480 */
172
173static const struct fb_videomode ps3fb_modedb[] = {
174 /* 60 Hz broadcast modes (modes "1" to "5") */
175 {
176 /* 480i */
177 "480i", 60, 576, 384, 74074, 130, 89, 78, 57, 63, 6,
178 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
179 }, {
180 /* 480p */
181 "480p", 60, 576, 384, 37037, 130, 89, 78, 57, 63, 6,
182 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
183 }, {
184 /* 720p */
185 "720p", 60, 1124, 644, 13481, 298, 148, 57, 44, 80, 5,
186 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
187 }, {
188 /* 1080i */
189 "1080i", 60, 1688, 964, 13481, 264, 160, 94, 62, 88, 5,
190 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
191 }, {
192 /* 1080p */
193 "1080p", 60, 1688, 964, 6741, 264, 160, 94, 62, 88, 5,
194 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
195 },
196
197 /* 50 Hz broadcast modes (modes "6" to "10") */
198 {
199 /* 576i */
200 "576i", 50, 576, 460, 74074, 142, 83, 97, 63, 63, 5,
201 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
202 }, {
203 /* 576p */
204 "576p", 50, 576, 460, 37037, 142, 83, 97, 63, 63, 5,
205 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
206 }, {
207 /* 720p */
208 "720p", 50, 1124, 644, 13468, 298, 478, 57, 44, 80, 5,
209 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
210 }, {
211 /* 1080 */
212 "1080i", 50, 1688, 964, 13468, 264, 600, 94, 62, 88, 5,
213 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
214 }, {
215 /* 1080p */
216 "1080p", 50, 1688, 964, 6734, 264, 600, 94, 62, 88, 5,
217 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
218 },
219
220 /* VESA modes (modes "11" to "13") */
221 {
222 /* WXGA */
223 "wxga", 60, 1280, 768, 12924, 160, 24, 29, 3, 136, 6,
224 0, FB_VMODE_NONINTERLACED,
225 FB_MODE_IS_VESA
226 }, {
227 /* SXGA */
228 "sxga", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
229 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED,
230 FB_MODE_IS_VESA
231 }, {
232 /* WUXGA */
233 "wuxga", 60, 1920, 1200, 6494, 80, 48, 26, 3, 32, 6,
234 FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED,
235 FB_MODE_IS_VESA
236 },
237
238 /* 60 Hz broadcast modes (full resolution versions of modes "1" to "5") */
239 {
240 /* 480if */
241 "480if", 60, 720, 480, 74074, 58, 17, 30, 9, 63, 6,
242 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
243 }, {
244 /* 480pf */
245 "480pf", 60, 720, 480, 37037, 58, 17, 30, 9, 63, 6,
246 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
247 }, {
248 /* 720pf */
249 "720pf", 60, 1280, 720, 13481, 220, 70, 19, 6, 80, 5,
250 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
251 }, {
252 /* 1080if */
253 "1080if", 60, 1920, 1080, 13481, 148, 44, 36, 4, 88, 5,
254 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
255 }, {
256 /* 1080pf */
257 "1080pf", 60, 1920, 1080, 6741, 148, 44, 36, 4, 88, 5,
258 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
259 },
260
261 /* 50 Hz broadcast modes (full resolution versions of modes "6" to "10") */
262 {
263 /* 576if */
264 "576if", 50, 720, 576, 74074, 70, 11, 39, 5, 63, 5,
265 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
266 }, {
267 /* 576pf */
268 "576pf", 50, 720, 576, 37037, 70, 11, 39, 5, 63, 5,
269 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
270 }, {
271 /* 720pf */
272 "720pf", 50, 1280, 720, 13468, 220, 400, 19, 6, 80, 5,
273 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
274 }, {
275 /* 1080if */
276 "1080f", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5,
277 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
278 }, {
279 /* 1080pf */
280 "1080pf", 50, 1920, 1080, 6734, 148, 484, 36, 4, 88, 5,
281 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
282 }
283};
284
285
286#define HEAD_A
287#define HEAD_B
288
289#define X_OFF(i) (ps3fb_res[i].xoff) /* left/right margin (pixel) */
290#define Y_OFF(i) (ps3fb_res[i].yoff) /* top/bottom margin (pixel) */
291#define WIDTH(i) (ps3fb_res[i].xres) /* width of FB */
292#define HEIGHT(i) (ps3fb_res[i].yres) /* height of FB */
293#define BPP 4 /* number of bytes per pixel */
294#define VP_OFF(i) (WIDTH(i) * Y_OFF(i) * BPP + X_OFF(i) * BPP)
295#define FB_OFF(i) (GPU_OFFSET - VP_OFF(i) % GPU_OFFSET)
296
297static int ps3fb_mode = 0;
298module_param(ps3fb_mode, bool, 0);
299
300static char *mode_option __initdata = NULL;
301
302
303static int ps3fb_get_res_table(u32 xres, u32 yres)
304{
305 int full_mode;
306 unsigned int i;
307 u32 x, y, f;
308
309 full_mode = (ps3fb_mode & PS3FB_FULL_MODE_BIT) ? PS3FB_RES_FULL : 0;
310 for (i = 0;; i++) {
311 x = ps3fb_res[i].xres;
312 y = ps3fb_res[i].yres;
313 f = ps3fb_res[i].type;
314
315 if (!x) {
316 DPRINTK("ERROR: ps3fb_get_res_table()\n");
317 return -1;
318 }
319
320 if (full_mode == PS3FB_RES_FULL && f != PS3FB_RES_FULL)
321 continue;
322
323 if (x == xres && (yres == 0 || y == yres))
324 break;
325
326 x = x - 2 * ps3fb_res[i].xoff;
327 y = y - 2 * ps3fb_res[i].yoff;
328 if (x == xres && (yres == 0 || y == yres))
329 break;
330 }
331 return i;
332}
333
334static unsigned int ps3fb_find_mode(const struct fb_var_screeninfo *var,
335 u32 *line_length)
336{
337 unsigned int i, mode;
338
339 for (i = 0; i < ARRAY_SIZE(ps3fb_modedb); i++)
340 if (var->xres == ps3fb_modedb[i].xres &&
341 var->yres == ps3fb_modedb[i].yres &&
342 var->pixclock == ps3fb_modedb[i].pixclock &&
343 var->hsync_len == ps3fb_modedb[i].hsync_len &&
344 var->vsync_len == ps3fb_modedb[i].vsync_len &&
345 var->left_margin == ps3fb_modedb[i].left_margin &&
346 var->right_margin == ps3fb_modedb[i].right_margin &&
347 var->upper_margin == ps3fb_modedb[i].upper_margin &&
348 var->lower_margin == ps3fb_modedb[i].lower_margin &&
349 var->sync == ps3fb_modedb[i].sync &&
350 (var->vmode & FB_VMODE_MASK) == ps3fb_modedb[i].vmode) {
351 /* Cropped broadcast modes use the full line_length */
352 *line_length =
353 ps3fb_modedb[i < 10 ? i + 13 : i].xres * 4;
354 /* Full broadcast modes have the full mode bit set */
355 mode = i > 12 ? (i - 12) | PS3FB_FULL_MODE_BIT : i + 1;
356
357 DPRINTK("ps3fb_find_mode: mode %u\n", mode);
358 return mode;
359 }
360
361 DPRINTK("ps3fb_find_mode: mode not found\n");
362 return 0;
363
364}
365
366static const struct fb_videomode *ps3fb_default_mode(void)
367{
368 u32 mode = ps3fb_mode & PS3AV_MODE_MASK;
369 u32 flags;
370
371 if (mode < 1 || mode > 13)
372 return NULL;
373
374 flags = ps3fb_mode & ~PS3AV_MODE_MASK;
375
376 if (mode <= 10 && flags & PS3FB_FULL_MODE_BIT) {
377 /* Full broadcast mode */
378 return &ps3fb_modedb[mode + 12];
379 }
380
381 return &ps3fb_modedb[mode - 1];
382}
383
384static int ps3fb_sync(u32 frame)
385{
386 int i, status;
387 u32 xres, yres;
388 u64 fb_ioif, offset;
389
390 i = ps3fb.res_index;
391 xres = ps3fb_res[i].xres;
392 yres = ps3fb_res[i].yres;
393
394 if (frame > ps3fb.num_frames - 1) {
395 printk(KERN_WARNING "%s: invalid frame number (%u)\n",
396 __FUNCTION__, frame);
397 return -EINVAL;
398 }
399 offset = xres * yres * BPP * frame;
400
401 fb_ioif = GPU_IOIF + FB_OFF(i) + offset;
402 status = lv1_gpu_context_attribute(ps3fb.context_handle,
403 L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
404 offset, fb_ioif,
405 L1GPU_FB_BLIT_WAIT_FOR_COMPLETION |
406 (xres << 16) | yres,
407 xres * BPP); /* line_length */
408 if (status)
409 printk(KERN_ERR "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n",
410 __FUNCTION__, status);
411#ifdef HEAD_A
412 status = lv1_gpu_context_attribute(ps3fb.context_handle,
413 L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
414 0, offset, 0, 0);
415 if (status)
416 printk(KERN_ERR "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
417 __FUNCTION__, status);
418#endif
419#ifdef HEAD_B
420 status = lv1_gpu_context_attribute(ps3fb.context_handle,
421 L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
422 1, offset, 0, 0);
423 if (status)
424 printk(KERN_ERR "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
425 __FUNCTION__, status);
426#endif
427 return 0;
428}
429
430
431static int ps3fb_open(struct fb_info *info, int user)
432{
433 atomic_inc(&ps3fb.f_count);
434 return 0;
435}
436
437static int ps3fb_release(struct fb_info *info, int user)
438{
439 if (atomic_dec_and_test(&ps3fb.f_count)) {
440 if (atomic_read(&ps3fb.ext_flip)) {
441 atomic_set(&ps3fb.ext_flip, 0);
442 ps3fb_sync(0); /* single buffer */
443 }
444 }
445 return 0;
446}
447
448 /*
449 * Setting the video mode has been split into two parts.
450 * First part, xxxfb_check_var, must not write anything
451 * to hardware, it should only verify and adjust var.
452 * This means it doesn't alter par but it does use hardware
453 * data from it to check this var.
454 */
455
456static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
457{
458 u32 line_length;
459 int mode;
460 int i;
461
462 DPRINTK("var->xres:%u info->var.xres:%u\n", var->xres, info->var.xres);
463 DPRINTK("var->yres:%u info->var.yres:%u\n", var->yres, info->var.yres);
464
465 /* FIXME For now we do exact matches only */
466 mode = ps3fb_find_mode(var, &line_length);
467 if (!mode)
468 return -EINVAL;
469
470 /*
471 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
472 * as FB_VMODE_SMOOTH_XPAN is only used internally
473 */
474
475 if (var->vmode & FB_VMODE_CONUPDATE) {
476 var->vmode |= FB_VMODE_YWRAP;
477 var->xoffset = info->var.xoffset;
478 var->yoffset = info->var.yoffset;
479 }
480
481 /* Virtual screen and panning are not supported */
482 if (var->xres_virtual > var->xres || var->yres_virtual > var->yres ||
483 var->xoffset || var->yoffset) {
484 DPRINTK("Virtual screen and panning are not supported\n");
485 return -EINVAL;
486 }
487
488 var->xres_virtual = var->xres;
489 var->yres_virtual = var->yres;
490
491 /* We support ARGB8888 only */
492 if (var->bits_per_pixel > 32 || var->grayscale ||
493 var->red.offset > 16 || var->green.offset > 8 ||
494 var->blue.offset > 0 || var->transp.offset > 24 ||
495 var->red.length > 8 || var->green.length > 8 ||
496 var->blue.length > 8 || var->transp.length > 8 ||
497 var->red.msb_right || var->green.msb_right ||
498 var->blue.msb_right || var->transp.msb_right || var->nonstd) {
499 DPRINTK("We support ARGB8888 only\n");
500 return -EINVAL;
501 }
502
503 var->bits_per_pixel = 32;
504 var->red.offset = 16;
505 var->green.offset = 8;
506 var->blue.offset = 0;
507 var->transp.offset = 24;
508 var->red.length = 8;
509 var->green.length = 8;
510 var->blue.length = 8;
511 var->transp.length = 8;
512 var->red.msb_right = 0;
513 var->green.msb_right = 0;
514 var->blue.msb_right = 0;
515 var->transp.msb_right = 0;
516
517 /* Rotation is not supported */
518 if (var->rotate) {
519 DPRINTK("Rotation is not supported\n");
520 return -EINVAL;
521 }
522
523 /* Memory limit */
524 i = ps3fb_get_res_table(var->xres, var->yres);
525 if (ps3fb_res[i].xres*ps3fb_res[i].yres*BPP > ps3fb_videomemory.size) {
526 DPRINTK("Not enough memory\n");
527 return -ENOMEM;
528 }
529
530 var->height = -1;
531 var->width = -1;
532
533 return 0;
534}
535
536 /*
537 * This routine actually sets the video mode.
538 */
539
540static int ps3fb_set_par(struct fb_info *info)
541{
542 unsigned int mode;
543 int i;
544 unsigned long offset;
545 static int first = 1;
546
547 DPRINTK("xres:%d xv:%d yres:%d yv:%d clock:%d\n",
548 info->var.xres, info->var.xres_virtual,
549 info->var.yres, info->var.yres_virtual, info->var.pixclock);
550 i = ps3fb_get_res_table(info->var.xres, info->var.yres);
551 ps3fb.res_index = i;
552
553 mode = ps3fb_find_mode(&info->var, &info->fix.line_length);
554 if (!mode)
555 return -EINVAL;
556
557 offset = FB_OFF(i) + VP_OFF(i);
558 info->fix.smem_len = ps3fb_videomemory.size - offset;
559 info->screen_base = (char __iomem *)ps3fb.xdr_ea + offset;
560 memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size);
561
562 ps3fb.num_frames = ps3fb_videomemory.size/
563 (ps3fb_res[i].xres*ps3fb_res[i].yres*BPP);
564
565 /* Keep the special bits we cannot set using fb_var_screeninfo */
566 ps3fb_mode = (ps3fb_mode & ~PS3AV_MODE_MASK) | mode;
567
568 if (ps3av_set_video_mode(ps3fb_mode, first))
569 return -EINVAL;
570
571 first = 0;
572 return 0;
573}
574
575 /*
576 * Set a single color register. The values supplied are already
577 * rounded down to the hardware's capabilities (according to the
578 * entries in the var structure). Return != 0 for invalid regno.
579 */
580
581static int ps3fb_setcolreg(unsigned int regno, unsigned int red,
582 unsigned int green, unsigned int blue,
583 unsigned int transp, struct fb_info *info)
584{
585 if (regno >= 16)
586 return 1;
587
588 red >>= 8;
589 green >>= 8;
590 blue >>= 8;
591 transp >>= 8;
592
593 ((u32 *)info->pseudo_palette)[regno] = transp << 24 | red << 16 |
594 green << 8 | blue;
595 return 0;
596}
597
598 /*
599 * As we have a virtual frame buffer, we need our own mmap function
600 */
601
602static int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
603{
604 unsigned long size, offset;
605 int i;
606
607 i = ps3fb_get_res_table(info->var.xres, info->var.yres);
608 if (i == -1)
609 return -EINVAL;
610
611 size = vma->vm_end - vma->vm_start;
612 offset = vma->vm_pgoff << PAGE_SHIFT;
613 if (offset + size > info->fix.smem_len)
614 return -EINVAL;
615
616 offset += info->fix.smem_start + FB_OFF(i) + VP_OFF(i);
617 if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT,
618 size, vma->vm_page_prot))
619 return -EAGAIN;
620
621 printk(KERN_DEBUG "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n", offset,
622 vma->vm_start);
623 return 0;
624}
625
626 /*
627 * Blank the display
628 */
629
630static int ps3fb_blank(int blank, struct fb_info *info)
631{
632 int retval;
633
634 DPRINTK("%s: blank:%d\n", __FUNCTION__, blank);
635 switch (blank) {
636 case FB_BLANK_POWERDOWN:
637 case FB_BLANK_HSYNC_SUSPEND:
638 case FB_BLANK_VSYNC_SUSPEND:
639 case FB_BLANK_NORMAL:
640 retval = ps3av_video_mute(1); /* mute on */
641 if (!retval)
642 ps3fb.is_blanked = 1;
643 break;
644
645 default: /* unblank */
646 retval = ps3av_video_mute(0); /* mute off */
647 if (!retval)
648 ps3fb.is_blanked = 0;
649 break;
650 }
651 return retval;
652}
653
654static int ps3fb_get_vblank(struct fb_vblank *vblank)
655{
656 memset(vblank, 0, sizeof(&vblank));
657 vblank->flags = FB_VBLANK_HAVE_VSYNC;
658 return 0;
659}
660
661int ps3fb_wait_for_vsync(u32 crtc)
662{
663 int ret;
664 u64 count;
665
666 count = ps3fb.vblank_count;
667 ret = wait_event_interruptible_timeout(ps3fb.wait_vsync,
668 count != ps3fb.vblank_count,
669 HZ / 10);
670 if (!ret)
671 return -ETIMEDOUT;
672
673 return 0;
674}
675
676EXPORT_SYMBOL_GPL(ps3fb_wait_for_vsync);
677
678void ps3fb_flip_ctl(int on)
679{
680 if (on) {
681 if (atomic_read(&ps3fb.ext_flip) > 0) {
682 atomic_dec(&ps3fb.ext_flip);
683 }
684 } else {
685 atomic_inc(&ps3fb.ext_flip);
686 }
687}
688
689EXPORT_SYMBOL_GPL(ps3fb_flip_ctl);
690
691 /*
692 * ioctl
693 */
694
695static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd,
696 unsigned long arg)
697{
698 void __user *argp = (void __user *)arg;
699 u32 val, old_mode;
700 int retval = -EFAULT;
701
702 switch (cmd) {
703 case FBIOGET_VBLANK:
704 {
705 struct fb_vblank vblank;
706 DPRINTK("FBIOGET_VBLANK:\n");
707 retval = ps3fb_get_vblank(&vblank);
708 if (retval)
709 break;
710
711 if (copy_to_user(argp, &vblank, sizeof(vblank)))
712 retval = -EFAULT;
713 break;
714 }
715
716 case FBIO_WAITFORVSYNC:
717 {
718 u32 crt;
719 DPRINTK("FBIO_WAITFORVSYNC:\n");
720 if (get_user(crt, (u32 __user *) arg))
721 break;
722
723 retval = ps3fb_wait_for_vsync(crt);
724 break;
725 }
726
727 case PS3FB_IOCTL_SETMODE:
728 {
729 const struct fb_videomode *mode;
730 struct fb_var_screeninfo var;
731
732 if (copy_from_user(&val, argp, sizeof(val)))
733 break;
734
735 DPRINTK("PS3FB_IOCTL_SETMODE:%x\n", val);
736 retval = -EINVAL;
737 old_mode = ps3fb_mode;
738 ps3fb_mode = val;
739 mode = ps3fb_default_mode();
740 if (mode) {
741 var = info->var;
742 fb_videomode_to_var(&var, mode);
743 acquire_console_sem();
744 info->flags |= FBINFO_MISC_USEREVENT;
745 /* Force, in case only special bits changed */
746 var.activate |= FB_ACTIVATE_FORCE;
747 retval = fb_set_var(info, &var);
748 info->flags &= ~FBINFO_MISC_USEREVENT;
749 release_console_sem();
750 }
751 if (retval)
752 ps3fb_mode = old_mode;
753 break;
754 }
755
756 case PS3FB_IOCTL_GETMODE:
757 val = ps3av_get_mode();
758 DPRINTK("PS3FB_IOCTL_GETMODE:%x\n", val);
759 if (!copy_to_user(argp, &val, sizeof(val)))
760 retval = 0;
761 break;
762
763 case PS3FB_IOCTL_SCREENINFO:
764 {
765 struct ps3fb_ioctl_res res;
766 int i = ps3fb.res_index;
767 DPRINTK("PS3FB_IOCTL_SCREENINFO:\n");
768 res.xres = ps3fb_res[i].xres;
769 res.yres = ps3fb_res[i].yres;
770 res.xoff = ps3fb_res[i].xoff;
771 res.yoff = ps3fb_res[i].yoff;
772 res.num_frames = ps3fb.num_frames;
773 if (!copy_to_user(argp, &res, sizeof(res)))
774 retval = 0;
775 break;
776 }
777
778 case PS3FB_IOCTL_ON:
779 DPRINTK("PS3FB_IOCTL_ON:\n");
780 atomic_inc(&ps3fb.ext_flip);
781 retval = 0;
782 break;
783
784 case PS3FB_IOCTL_OFF:
785 DPRINTK("PS3FB_IOCTL_OFF:\n");
786 if (atomic_read(&ps3fb.ext_flip) > 0)
787 atomic_dec(&ps3fb.ext_flip);
788 retval = 0;
789 break;
790
791 case PS3FB_IOCTL_FSEL:
792 if (copy_from_user(&val, argp, sizeof(val)))
793 break;
794
795 DPRINTK("PS3FB_IOCTL_FSEL:%d\n", val);
796 retval = ps3fb_sync(val);
797 break;
798
799 default:
800 retval = -ENOIOCTLCMD;
801 break;
802 }
803 return retval;
804}
805
806static int ps3fbd(void *arg)
807{
808 daemonize("ps3fbd");
809 for (;;) {
810 down(&ps3fb.sem);
811 if (atomic_read(&ps3fb.ext_flip) == 0)
812 ps3fb_sync(0); /* single buffer */
813 }
814 return 0;
815}
816
817static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr)
818{
819 u64 v1;
820 int status;
821 struct display_head *head = &ps3fb.dinfo->display_head[1];
822
823 status = lv1_gpu_context_intr(ps3fb.context_handle, &v1);
824 if (status) {
825 printk(KERN_ERR "%s: lv1_gpu_context_intr failed: %d\n",
826 __FUNCTION__, status);
827 return IRQ_NONE;
828 }
829
830 if (v1 & (1 << GPU_INTR_STATUS_VSYNC_1)) {
831 /* VSYNC */
832 ps3fb.vblank_count = head->vblank_count;
833 if (!ps3fb.is_blanked)
834 up(&ps3fb.sem);
835 wake_up_interruptible(&ps3fb.wait_vsync);
836 }
837
838 return IRQ_HANDLED;
839}
840
841#ifndef MODULE
842static int __init ps3fb_setup(char *options)
843{
844 char *this_opt;
845 int mode = 0;
846
847 if (!options || !*options)
848 return 0; /* no options */
849
850 while ((this_opt = strsep(&options, ",")) != NULL) {
851 if (!*this_opt)
852 continue;
853 if (!strncmp(this_opt, "mode:", 5))
854 mode = simple_strtoul(this_opt + 5, NULL, 0);
855 else
856 mode_option = this_opt;
857 }
858 return mode;
859}
860#endif /* MODULE */
861
862 /*
863 * Initialisation
864 */
865
866static void ps3fb_platform_release(struct device *device)
867{
868 /* This is called when the reference count goes to zero. */
869}
870
871static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev)
872{
873 int error;
874
875 DPRINTK("version_driver:%x\n", dinfo->version_driver);
876 DPRINTK("irq outlet:%x\n", dinfo->irq.irq_outlet);
877 DPRINTK("version_gpu:%x memory_size:%x ch:%x core_freq:%d mem_freq:%d\n",
878 dinfo->version_gpu, dinfo->memory_size, dinfo->hardware_channel,
879 dinfo->nvcore_frequency/1000000, dinfo->memory_frequency/1000000);
880
881 if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) {
882 printk(KERN_ERR "%s: version_driver err:%x\n", __FUNCTION__,
883 dinfo->version_driver);
884 return -EINVAL;
885 }
886
887 ps3fb.dev = dev;
888 error = ps3_alloc_irq(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet,
889 &ps3fb.irq_no);
890 if (error) {
891 printk(KERN_ERR "%s: ps3_alloc_irq failed %d\n", __FUNCTION__,
892 error);
893 return error;
894 }
895
896 error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED,
897 "ps3fb vsync", ps3fb.dev);
898 if (error) {
899 printk(KERN_ERR "%s: request_irq failed %d\n", __FUNCTION__,
900 error);
901 ps3_free_irq(ps3fb.irq_no);
902 return error;
903 }
904
905 dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) |
906 (1 << GPU_INTR_STATUS_FLIP_1);
907 return 0;
908}
909
910static int ps3fb_xdr_settings(u64 xdr_lpar)
911{
912 int status;
913
914 status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF,
915 xdr_lpar, ps3fb_videomemory.size, 0);
916 if (status) {
917 printk(KERN_ERR "%s: lv1_gpu_context_iomap failed: %d\n",
918 __FUNCTION__, status);
919 return -ENXIO;
920 }
921 DPRINTK("video:%p xdr_ea:%p ioif:%lx lpar:%lx phys:%lx size:%lx\n",
922 ps3fb_videomemory.address, ps3fb.xdr_ea, GPU_IOIF, xdr_lpar,
923 virt_to_abs(ps3fb.xdr_ea), ps3fb_videomemory.size);
924
925 status = lv1_gpu_context_attribute(ps3fb.context_handle,
926 L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP,
927 xdr_lpar, ps3fb_videomemory.size,
928 GPU_IOIF, 0);
929 if (status) {
930 printk(KERN_ERR "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n",
931 __FUNCTION__, status);
932 return -ENXIO;
933 }
934 return 0;
935}
936
937static struct fb_ops ps3fb_ops = {
938 .fb_open = ps3fb_open,
939 .fb_release = ps3fb_release,
940 .fb_check_var = ps3fb_check_var,
941 .fb_set_par = ps3fb_set_par,
942 .fb_setcolreg = ps3fb_setcolreg,
943 .fb_fillrect = cfb_fillrect,
944 .fb_copyarea = cfb_copyarea,
945 .fb_imageblit = cfb_imageblit,
946 .fb_mmap = ps3fb_mmap,
947 .fb_blank = ps3fb_blank,
948 .fb_ioctl = ps3fb_ioctl,
949 .fb_compat_ioctl = ps3fb_ioctl
950};
951
952static struct fb_fix_screeninfo ps3fb_fix __initdata = {
953 .id = "PS3 FB",
954 .type = FB_TYPE_PACKED_PIXELS,
955 .visual = FB_VISUAL_TRUECOLOR,
956 .accel = FB_ACCEL_NONE,
957};
958
959static int __init ps3fb_probe(struct platform_device *dev)
960{
961 struct fb_info *info;
962 int retval = -ENOMEM;
963 u64 ddr_lpar = 0;
964 u64 lpar_dma_control = 0;
965 u64 lpar_driver_info = 0;
966 u64 lpar_reports = 0;
967 u64 lpar_reports_size = 0;
968 u64 xdr_lpar;
969 int status;
970 unsigned long offset;
971
972 /* get gpu context handle */
973 status = lv1_gpu_memory_allocate(DDR_SIZE, 0, 0, 0, 0,
974 &ps3fb.memory_handle, &ddr_lpar);
975 if (status) {
976 printk(KERN_ERR "%s: lv1_gpu_memory_allocate failed: %d\n",
977 __FUNCTION__, status);
978 goto err;
979 }
980 DPRINTK("ddr:lpar:0x%lx\n", ddr_lpar);
981
982 status = lv1_gpu_context_allocate(ps3fb.memory_handle, 0,
983 &ps3fb.context_handle,
984 &lpar_dma_control, &lpar_driver_info,
985 &lpar_reports, &lpar_reports_size);
986 if (status) {
987 printk(KERN_ERR "%s: lv1_gpu_context_attribute failed: %d\n",
988 __FUNCTION__, status);
989 goto err_gpu_memory_free;
990 }
991
992 /* vsync interrupt */
993 ps3fb.dinfo = ioremap(lpar_driver_info, 128 * 1024);
994 if (!ps3fb.dinfo) {
995 printk(KERN_ERR "%s: ioremap failed\n", __FUNCTION__);
996 goto err_gpu_context_free;
997 }
998
999 retval = ps3fb_vsync_settings(ps3fb.dinfo, dev);
1000 if (retval)
1001 goto err_iounmap_dinfo;
1002
1003 /* xdr frame buffer */
1004 ps3fb.xdr_ea = ps3fb_videomemory.address;
1005 xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb.xdr_ea));
1006 retval = ps3fb_xdr_settings(xdr_lpar);
1007 if (retval)
1008 goto err_free_irq;
1009
1010 /*
1011 * ps3fb must clear memory to prevent kernel info
1012 * leakage into userspace
1013 */
1014 memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size);
1015 info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
1016 if (!info)
1017 goto err_free_irq;
1018
1019 offset = FB_OFF(ps3fb.res_index) + VP_OFF(ps3fb.res_index);
1020 info->screen_base = (char __iomem *)ps3fb.xdr_ea + offset;
1021 info->fbops = &ps3fb_ops;
1022
1023 info->fix = ps3fb_fix;
1024 info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea);
1025 info->fix.smem_len = ps3fb_videomemory.size - offset;
1026 info->pseudo_palette = info->par;
1027 info->par = NULL;
1028 info->flags = FBINFO_FLAG_DEFAULT;
1029
1030 retval = fb_alloc_cmap(&info->cmap, 256, 0);
1031 if (retval < 0)
1032 goto err_framebuffer_release;
1033
1034 if (!fb_find_mode(&info->var, info, mode_option, ps3fb_modedb,
1035 ARRAY_SIZE(ps3fb_modedb), ps3fb_default_mode(), 32)) {
1036 retval = -EINVAL;
1037 goto err_fb_dealloc;
1038 }
1039
1040 fb_videomode_to_modelist(ps3fb_modedb, ARRAY_SIZE(ps3fb_modedb),
1041 &info->modelist);
1042
1043 retval = register_framebuffer(info);
1044 if (retval < 0)
1045 goto err_fb_dealloc;
1046
1047 platform_set_drvdata(dev, info);
1048
1049 printk(KERN_INFO
1050 "fb%d: PS3 frame buffer device, using %ld KiB of video memory\n",
1051 info->node, ps3fb_videomemory.size >> 10);
1052
1053 kernel_thread(ps3fbd, info, CLONE_KERNEL);
1054 return 0;
1055
1056err_fb_dealloc:
1057 fb_dealloc_cmap(&info->cmap);
1058err_framebuffer_release:
1059 framebuffer_release(info);
1060err_free_irq:
1061 free_irq(ps3fb.irq_no, ps3fb.dev);
1062 ps3_free_irq(ps3fb.irq_no);
1063err_iounmap_dinfo:
1064 iounmap((u8 __iomem *)ps3fb.dinfo);
1065err_gpu_context_free:
1066 lv1_gpu_context_free(ps3fb.context_handle);
1067err_gpu_memory_free:
1068 lv1_gpu_memory_free(ps3fb.memory_handle);
1069err:
1070 return retval;
1071}
1072
1073static void ps3fb_shutdown(struct platform_device *dev)
1074{
1075 ps3fb_flip_ctl(0); /* flip off */
1076 ps3fb.dinfo->irq.mask = 0;
1077 free_irq(ps3fb.irq_no, ps3fb.dev);
1078 ps3_free_irq(ps3fb.irq_no);
1079 iounmap((u8 __iomem *)ps3fb.dinfo);
1080}
1081
1082void ps3fb_cleanup(void)
1083{
1084 int status;
1085
1086 if (ps3fb.irq_no) {
1087 free_irq(ps3fb.irq_no, ps3fb.dev);
1088 ps3_free_irq(ps3fb.irq_no);
1089 }
1090 iounmap((u8 __iomem *)ps3fb.dinfo);
1091
1092 status = lv1_gpu_context_free(ps3fb.context_handle);
1093 if (status)
1094 DPRINTK("lv1_gpu_context_free failed: %d\n", status);
1095
1096 status = lv1_gpu_memory_free(ps3fb.memory_handle);
1097 if (status)
1098 DPRINTK("lv1_gpu_memory_free failed: %d\n", status);
1099
1100 ps3av_dev_close();
1101}
1102
1103EXPORT_SYMBOL_GPL(ps3fb_cleanup);
1104
1105static int ps3fb_remove(struct platform_device *dev)
1106{
1107 struct fb_info *info = platform_get_drvdata(dev);
1108
1109 if (info) {
1110 unregister_framebuffer(info);
1111 fb_dealloc_cmap(&info->cmap);
1112 framebuffer_release(info);
1113 }
1114 ps3fb_cleanup();
1115 return 0;
1116}
1117
1118static struct platform_driver ps3fb_driver = {
1119 .probe = ps3fb_probe,
1120 .remove = ps3fb_remove,
1121 .shutdown = ps3fb_shutdown,
1122 .driver = { .name = "ps3fb" }
1123};
1124
1125static struct platform_device ps3fb_device = {
1126 .name = "ps3fb",
1127 .id = 0,
1128 .dev = { .release = ps3fb_platform_release }
1129};
1130
1131int ps3fb_set_sync(void)
1132{
1133 int status;
1134
1135#ifdef HEAD_A
1136 status = lv1_gpu_context_attribute(0x0,
1137 L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
1138 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
1139 if (status) {
1140 printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: %d\n",
1141 __FUNCTION__, status);
1142 return -1;
1143 }
1144#endif
1145#ifdef HEAD_B
1146 status = lv1_gpu_context_attribute(0x0,
1147 L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
1148 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
1149
1150 if (status) {
1151 printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_MODE failed: %d\n",
1152 __FUNCTION__, status);
1153 return -1;
1154 }
1155#endif
1156 return 0;
1157}
1158
1159EXPORT_SYMBOL_GPL(ps3fb_set_sync);
1160
1161static int __init ps3fb_init(void)
1162{
1163 int error;
1164#ifndef MODULE
1165 int mode;
1166 char *option = NULL;
1167
1168 if (fb_get_options("ps3fb", &option))
1169 goto err;
1170#endif
1171
1172 if (!ps3fb_videomemory.address)
1173 goto err;
1174
1175 error = ps3av_dev_open();
1176 if (error) {
1177 printk(KERN_ERR "%s: ps3av_dev_open failed\n", __FUNCTION__);
1178 goto err;
1179 }
1180
1181 ps3fb_mode = ps3av_get_mode();
1182 DPRINTK("ps3av_mode:%d\n", ps3fb_mode);
1183#ifndef MODULE
1184 mode = ps3fb_setup(option); /* check boot option */
1185 if (mode)
1186 ps3fb_mode = mode;
1187#endif
1188 if (ps3fb_mode > 0) {
1189 u32 xres, yres;
1190 ps3av_video_mode2res(ps3fb_mode, &xres, &yres);
1191 ps3fb.res_index = ps3fb_get_res_table(xres, yres);
1192 DPRINTK("res_index:%d\n", ps3fb.res_index);
1193 } else
1194 ps3fb.res_index = GPU_RES_INDEX;
1195
1196 atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */
1197 atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */
1198 init_MUTEX(&ps3fb.sem);
1199 init_waitqueue_head(&ps3fb.wait_vsync);
1200 ps3fb.num_frames = 1;
1201
1202 error = platform_driver_register(&ps3fb_driver);
1203 if (!error) {
1204 error = platform_device_register(&ps3fb_device);
1205 if (error)
1206 platform_driver_unregister(&ps3fb_driver);
1207 }
1208
1209 ps3fb_set_sync();
1210
1211 return error;
1212
1213err:
1214 return -ENXIO;
1215}
1216
1217module_init(ps3fb_init);
1218
1219#ifdef MODULE
1220static void __exit ps3fb_exit(void)
1221{
1222 platform_device_unregister(&ps3fb_device);
1223 platform_driver_unregister(&ps3fb_driver);
1224}
1225
1226module_exit(ps3fb_exit);
1227
1228MODULE_LICENSE("GPL");
1229#endif /* MODULE */