ps3fb.c 32.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 *  linux/drivers/video/ps3fb.c -- PS3 GPU frame buffer device
 *
 *	Copyright (C) 2006 Sony Computer Entertainment Inc.
 *	Copyright 2006, 2007 Sony Corporation
 *
 *  This file is based on :
 *
 *  linux/drivers/video/vfb.c -- Virtual frame buffer device
 *
 *	Copyright (C) 2002 James Simmons
 *
 *	Copyright (C) 1997 Geert Uytterhoeven
 *
 *  This file is subject to the terms and conditions of the GNU General Public
 *  License. See the file COPYING in the main directory of this archive for
 *  more details.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/console.h>
#include <linux/ioctl.h>
G
Geert Uytterhoeven 已提交
28 29
#include <linux/kthread.h>
#include <linux/freezer.h>
30
#include <linux/uaccess.h>
31 32 33 34 35 36 37 38 39
#include <linux/fb.h>
#include <linux/init.h>

#include <asm/abs_addr.h>
#include <asm/lv1call.h>
#include <asm/ps3av.h>
#include <asm/ps3fb.h>
#include <asm/ps3.h>

40 41 42

#define DEVICE_NAME		"ps3fb"

43 44 45 46 47 48 49 50 51 52 53
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC	0x101
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP	0x102
#define L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP	0x600
#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT		0x601
#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT_SYNC	0x602

#define L1GPU_FB_BLIT_WAIT_FOR_COMPLETION	(1ULL << 32)

#define L1GPU_DISPLAY_SYNC_HSYNC		1
#define L1GPU_DISPLAY_SYNC_VSYNC		2

54 55
#define GPU_CMD_BUF_SIZE			(2 * 1024 * 1024)
#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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

#define GPU_INTR_STATUS_VSYNC_0			0	/* vsync on head A */
#define GPU_INTR_STATUS_VSYNC_1			1	/* vsync on head B */
#define GPU_INTR_STATUS_FLIP_0			3	/* flip head A */
#define GPU_INTR_STATUS_FLIP_1			4	/* flip head B */
#define GPU_INTR_STATUS_QUEUE_0			5	/* queue head A */
#define GPU_INTR_STATUS_QUEUE_1			6	/* queue head B */

#define GPU_DRIVER_INFO_VERSION			0x211

/* gpu internals */
struct display_head {
	u64 be_time_stamp;
	u32 status;
	u32 offset;
	u32 res1;
	u32 res2;
	u32 field;
	u32 reserved1;

	u64 res3;
	u32 raster;

	u64 vblank_count;
	u32 field_vsync;
	u32 reserved2;
};

struct gpu_irq {
	u32 irq_outlet;
	u32 status;
	u32 mask;
	u32 video_cause;
	u32 graph_cause;
	u32 user_cause;

	u32 res1;
	u64 res2;

	u32 reserved[4];
};

struct gpu_driver_info {
	u32 version_driver;
	u32 version_gpu;
	u32 memory_size;
	u32 hardware_channel;

	u32 nvcore_frequency;
	u32 memory_frequency;

	u32 reserved[1063];
	struct display_head display_head[8];
	struct gpu_irq irq;
};

struct ps3fb_priv {
	unsigned int irq_no;

	u64 context_handle, memory_handle;
	void *xdr_ea;
120
	size_t xdr_size;
121 122 123 124 125 126 127 128
	struct gpu_driver_info *dinfo;

	u64 vblank_count;	/* frame count */
	wait_queue_head_t wait_vsync;

	atomic_t ext_flip;	/* on/off flip with vsync */
	atomic_t f_count;	/* fb_open count */
	int is_blanked;
G
Geert Uytterhoeven 已提交
129 130
	int is_kicked;
	struct task_struct *task;
131 132 133
};
static struct ps3fb_priv ps3fb;

134 135 136 137
struct ps3fb_par {
	u32 pseudo_palette[16];
	int mode_id, new_mode_id;
	unsigned int num_frames;	/* num of frame buffers */
138 139
	unsigned int width;
	unsigned int height;
G
Geert Uytterhoeven 已提交
140 141 142
	unsigned int ddr_line_length;
	unsigned int ddr_frame_size;
	unsigned int xdr_frame_size;
143 144 145
	unsigned int full_offset;	/* start of fullscreen DDR fb */
	unsigned int fb_offset;		/* start of actual DDR fb */
	unsigned int pan_offset;
146 147
};

148

149 150
#define FIRST_NATIVE_MODE_INDEX	10

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
static const struct fb_videomode ps3fb_modedb[] = {
    /* 60 Hz broadcast modes (modes "1" to "5") */
    {
        /* 480i */
        "480i", 60, 576, 384, 74074, 130, 89, 78, 57, 63, 6,
        FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
    },    {
        /* 480p */
        "480p", 60, 576, 384, 37037, 130, 89, 78, 57, 63, 6,
        FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    },    {
        /* 720p */
        "720p", 60, 1124, 644, 13481, 298, 148, 57, 44, 80, 5,
        FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    },    {
        /* 1080i */
        "1080i", 60, 1688, 964, 13481, 264, 160, 94, 62, 88, 5,
        FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
    },    {
        /* 1080p */
        "1080p", 60, 1688, 964, 6741, 264, 160, 94, 62, 88, 5,
        FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    },

    /* 50 Hz broadcast modes (modes "6" to "10") */
    {
        /* 576i */
        "576i", 50, 576, 460, 74074, 142, 83, 97, 63, 63, 5,
        FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
    },    {
        /* 576p */
        "576p", 50, 576, 460, 37037, 142, 83, 97, 63, 63, 5,
        FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    },    {
        /* 720p */
        "720p", 50, 1124, 644, 13468, 298, 478, 57, 44, 80, 5,
        FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    },    {
        /* 1080 */
        "1080i", 50, 1688, 964, 13468, 264, 600, 94, 62, 88, 5,
        FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
    },    {
        /* 1080p */
        "1080p", 50, 1688, 964, 6734, 264, 600, 94, 62, 88, 5,
        FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    },

198
    [FIRST_NATIVE_MODE_INDEX] =
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
    /* 60 Hz broadcast modes (full resolution versions of modes "1" to "5") */
    {
	/* 480if */
	"480if", 60, 720, 480, 74074, 58, 17, 30, 9, 63, 6,
	FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
    }, {
	/* 480pf */
	"480pf", 60, 720, 480, 37037, 58, 17, 30, 9, 63, 6,
	FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    }, {
	/* 720pf */
	"720pf", 60, 1280, 720, 13481, 220, 70, 19, 6, 80, 5,
	FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    }, {
	/* 1080if */
	"1080if", 60, 1920, 1080, 13481, 148, 44, 36, 4, 88, 5,
	FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
    }, {
	/* 1080pf */
	"1080pf", 60, 1920, 1080, 6741, 148, 44, 36, 4, 88, 5,
	FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    },

    /* 50 Hz broadcast modes (full resolution versions of modes "6" to "10") */
    {
	/* 576if */
	"576if", 50, 720, 576, 74074, 70, 11, 39, 5, 63, 5,
	FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
    }, {
	/* 576pf */
	"576pf", 50, 720, 576, 37037, 70, 11, 39, 5, 63, 5,
	FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    }, {
	/* 720pf */
	"720pf", 50, 1280, 720, 13468, 220, 400, 19, 6, 80, 5,
	FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
    }, {
	/* 1080if */
	"1080f", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5,
	FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
    }, {
	/* 1080pf */
	"1080pf", 50, 1920, 1080, 6734, 148, 484, 36, 4, 88, 5,
	FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    },

    /* VESA modes (modes "11" to "13") */
    {
	/* WXGA */
	"wxga", 60, 1280, 768, 12924, 160, 24, 29, 3, 136, 6,
	0, FB_VMODE_NONINTERLACED,
	FB_MODE_IS_VESA
    }, {
	/* SXGA */
	"sxga", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
	FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED,
	FB_MODE_IS_VESA
    }, {
	/* WUXGA */
	"wuxga", 60, 1920, 1200, 6494, 80, 48, 26, 3, 32, 6,
	FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED,
	FB_MODE_IS_VESA
261 262 263 264 265 266 267
    }
};


#define HEAD_A
#define HEAD_B

268 269
#define BPP		4			/* number of bytes per pixel */

270

271
static int ps3fb_mode;
272
module_param(ps3fb_mode, int, 0);
273

274
static char *mode_option __devinitdata;
275

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
static int ps3fb_cmp_mode(const struct fb_videomode *vmode,
			  const struct fb_var_screeninfo *var)
{
	/* resolution + black border must match a native resolution */
	if (vmode->left_margin + vmode->xres + vmode->right_margin !=
	    var->left_margin + var->xres + var->right_margin ||
	    vmode->upper_margin + vmode->yres + vmode->lower_margin !=
	    var->upper_margin + var->yres + var->lower_margin)
		return -1;

	/* minimum limits for margins */
	if (vmode->left_margin > var->left_margin ||
	    vmode->right_margin > var->right_margin ||
	    vmode->upper_margin > var->upper_margin ||
	    vmode->lower_margin > var->lower_margin)
		return -1;

	/* these fields must match exactly */
	if (vmode->pixclock != var->pixclock ||
	    vmode->hsync_len != var->hsync_len ||
	    vmode->vsync_len != var->vsync_len ||
	    vmode->sync != var->sync ||
	    vmode->vmode != (var->vmode & FB_VMODE_MASK))
		return -1;

	return 0;
}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
static const struct fb_videomode *ps3fb_native_vmode(enum ps3av_mode_num id)
{
	return &ps3fb_modedb[FIRST_NATIVE_MODE_INDEX + id - 1];
}

static const struct fb_videomode *ps3fb_vmode(int id)
{
	u32 mode = id & PS3AV_MODE_MASK;

	if (mode < PS3AV_MODE_480I || mode > PS3AV_MODE_WUXGA)
		return NULL;

	if (mode <= PS3AV_MODE_1080P50 && !(id & PS3AV_MODE_FULL)) {
		/* Non-fullscreen broadcast mode */
		return &ps3fb_modedb[mode - 1];
	}

	return ps3fb_native_vmode(mode);
}

324
static unsigned int ps3fb_find_mode(struct fb_var_screeninfo *var,
325
				    u32 *ddr_line_length, u32 *xdr_line_length)
326
{
327 328
	unsigned int id;
	const struct fb_videomode *vmode;
329

330 331 332
	for (id = PS3AV_MODE_480I; id <= PS3AV_MODE_WUXGA; id++) {
		vmode = ps3fb_native_vmode(id);
		if (!ps3fb_cmp_mode(vmode, var))
333
			goto found;
334
	}
335

336
	pr_debug("%s: mode not found\n", __func__);
337
	return 0;
338 339

found:
340
	*ddr_line_length = vmode->xres * BPP;
341 342 343 344 345 346 347 348 349

	if (!var->xres) {
		var->xres = 1;
		var->right_margin--;
	}
	if (!var->yres) {
		var->yres = 1;
		var->lower_margin--;
	}
350 351 352 353 354 355 356 357 358

	if (ps3_compare_firmware_version(1, 9, 0) >= 0) {
		*xdr_line_length = GPU_ALIGN_UP(max(var->xres,
						    var->xres_virtual) * BPP);
		if (*xdr_line_length > GPU_MAX_LINE_LENGTH)
			*xdr_line_length = GPU_MAX_LINE_LENGTH;
	} else
		*xdr_line_length = *ddr_line_length;

359
	if (vmode->sync & FB_SYNC_BROADCAST) {
360
		/* Full broadcast modes have the full mode bit set */
361 362
		if (vmode->xres == var->xres && vmode->yres == var->yres)
			id |= PS3AV_MODE_FULL;
363 364
	}

365 366
	pr_debug("%s: mode %u\n", __func__, id);
	return id;
367 368
}

369 370
static void ps3fb_sync_image(struct device *dev, u64 frame_offset,
			     u64 dst_offset, u64 src_offset, u32 width,
371 372
			     u32 height, u32 dst_line_length,
			     u32 src_line_length)
373
{
374
	int status;
375 376 377 378 379
	u64 line_length;

	line_length = dst_line_length;
	if (src_line_length != dst_line_length)
		line_length |= (u64)src_line_length << 32;
380

381
	src_offset += GPU_FB_START;
382 383
	status = lv1_gpu_context_attribute(ps3fb.context_handle,
					   L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
384
					   dst_offset, GPU_IOIF + src_offset,
385
					   L1GPU_FB_BLIT_WAIT_FOR_COMPLETION |
386 387
					   (width << 16) | height,
					   line_length);
388
	if (status)
389
		dev_err(dev,
390 391
			"%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n",
			__func__, status);
392 393 394
#ifdef HEAD_A
	status = lv1_gpu_context_attribute(ps3fb.context_handle,
					   L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
395
					   0, frame_offset, 0, 0);
396
	if (status)
397
		dev_err(dev, "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
398
			__func__, status);
399 400 401 402
#endif
#ifdef HEAD_B
	status = lv1_gpu_context_attribute(ps3fb.context_handle,
					   L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
403
					   1, frame_offset, 0, 0);
404
	if (status)
405
		dev_err(dev, "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
406
			__func__, status);
407
#endif
408 409 410 411 412
}

static int ps3fb_sync(struct fb_info *info, u32 frame)
{
	struct ps3fb_par *par = info->par;
G
Geert Uytterhoeven 已提交
413
	int error = 0;
414
	u64 ddr_base, xdr_base;
415 416 417 418 419 420 421 422

	if (frame > par->num_frames - 1) {
		dev_dbg(info->device, "%s: invalid frame number (%u)\n",
			__func__, frame);
		error = -EINVAL;
		goto out;
	}

G
Geert Uytterhoeven 已提交
423 424
	xdr_base = frame * par->xdr_frame_size;
	ddr_base = frame * par->ddr_frame_size;
425

426 427
	ps3fb_sync_image(info->device, ddr_base + par->full_offset,
			 ddr_base + par->fb_offset, xdr_base + par->pan_offset,
G
Geert Uytterhoeven 已提交
428 429
			 par->width, par->height, par->ddr_line_length,
			 info->fix.line_length);
430 431 432

out:
	return error;
433 434 435 436 437 438 439 440 441 442 443 444 445
}

static int ps3fb_open(struct fb_info *info, int user)
{
	atomic_inc(&ps3fb.f_count);
	return 0;
}

static int ps3fb_release(struct fb_info *info, int user)
{
	if (atomic_dec_and_test(&ps3fb.f_count)) {
		if (atomic_read(&ps3fb.ext_flip)) {
			atomic_set(&ps3fb.ext_flip, 0);
J
Jeremy Kerr 已提交
446 447 448 449
			if (!try_acquire_console_sem()) {
				ps3fb_sync(info, 0);	/* single buffer */
				release_console_sem();
			}
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
		}
	}
	return 0;
}

    /*
     *  Setting the video mode has been split into two parts.
     *  First part, xxxfb_check_var, must not write anything
     *  to hardware, it should only verify and adjust var.
     *  This means it doesn't alter par but it does use hardware
     *  data from it to check this var.
     */

static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
465
	u32 xdr_line_length, ddr_line_length;
466 467
	int mode;

468 469 470 471
	dev_dbg(info->device, "var->xres:%u info->var.xres:%u\n", var->xres,
		info->var.xres);
	dev_dbg(info->device, "var->yres:%u info->var.yres:%u\n", var->yres,
		info->var.yres);
472 473

	/* FIXME For now we do exact matches only */
474
	mode = ps3fb_find_mode(var, &ddr_line_length, &xdr_line_length);
475 476 477
	if (!mode)
		return -EINVAL;

478 479 480 481 482
	/* Virtual screen */
	if (var->xres_virtual < var->xres)
		var->xres_virtual = var->xres;
	if (var->yres_virtual < var->yres)
		var->yres_virtual = var->yres;
483

484
	if (var->xres_virtual > xdr_line_length / BPP) {
485
		dev_dbg(info->device,
486
			"Horizontal virtual screen size too large\n");
487 488 489
		return -EINVAL;
	}

490 491 492 493 494
	if (var->xoffset + var->xres > var->xres_virtual ||
	    var->yoffset + var->yres > var->yres_virtual) {
		dev_dbg(info->device, "panning out-of-range\n");
		return -EINVAL;
	}
495 496 497 498 499 500 501 502 503

	/* We support ARGB8888 only */
	if (var->bits_per_pixel > 32 || var->grayscale ||
	    var->red.offset > 16 || var->green.offset > 8 ||
	    var->blue.offset > 0 || var->transp.offset > 24 ||
	    var->red.length > 8 || var->green.length > 8 ||
	    var->blue.length > 8 || var->transp.length > 8 ||
	    var->red.msb_right || var->green.msb_right ||
	    var->blue.msb_right || var->transp.msb_right || var->nonstd) {
504
		dev_dbg(info->device, "We support ARGB8888 only\n");
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
		return -EINVAL;
	}

	var->bits_per_pixel = 32;
	var->red.offset = 16;
	var->green.offset = 8;
	var->blue.offset = 0;
	var->transp.offset = 24;
	var->red.length = 8;
	var->green.length = 8;
	var->blue.length = 8;
	var->transp.length = 8;
	var->red.msb_right = 0;
	var->green.msb_right = 0;
	var->blue.msb_right = 0;
	var->transp.msb_right = 0;

	/* Rotation is not supported */
	if (var->rotate) {
524
		dev_dbg(info->device, "Rotation is not supported\n");
525 526 527 528
		return -EINVAL;
	}

	/* Memory limit */
529
	if (var->yres_virtual * xdr_line_length > ps3fb.xdr_size) {
530
		dev_dbg(info->device, "Not enough memory\n");
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
		return -ENOMEM;
	}

	var->height = -1;
	var->width = -1;

	return 0;
}

    /*
     * This routine actually sets the video mode.
     */

static int ps3fb_set_par(struct fb_info *info)
{
546
	struct ps3fb_par *par = info->par;
547
	unsigned int mode, ddr_line_length, xdr_line_length, lines, maxlines;
548
	unsigned int ddr_xoff, ddr_yoff, offset;
G
Geert Uytterhoeven 已提交
549
	const struct fb_videomode *vmode;
550
	u64 dst;
551

552
	dev_dbg(info->device, "xres:%d xv:%d yres:%d yv:%d clock:%d\n",
553 554 555
		info->var.xres, info->var.xres_virtual,
		info->var.yres, info->var.yres_virtual, info->var.pixclock);

556
	mode = ps3fb_find_mode(&info->var, &ddr_line_length, &xdr_line_length);
557 558 559
	if (!mode)
		return -EINVAL;

560
	vmode = ps3fb_native_vmode(mode & PS3AV_MODE_MASK);
561

562 563
	info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea);
	info->fix.smem_len = ps3fb.xdr_size;
564 565
	info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
	info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
566
	info->fix.line_length = xdr_line_length;
567

568
	info->screen_base = (char __iomem *)ps3fb.xdr_ea;
569

G
Geert Uytterhoeven 已提交
570 571 572 573
	par->ddr_line_length = ddr_line_length;
	par->ddr_frame_size = vmode->yres * ddr_line_length;
	par->xdr_frame_size = info->var.yres_virtual * xdr_line_length;

574
	par->num_frames = ps3fb.xdr_size /
G
Geert Uytterhoeven 已提交
575
			  max(par->ddr_frame_size, par->xdr_frame_size);
576 577

	/* Keep the special bits we cannot set using fb_var_screeninfo */
578
	par->new_mode_id = (par->new_mode_id & ~PS3AV_MODE_MASK) | mode;
579

580 581
	par->width = info->var.xres;
	par->height = info->var.yres;
582 583

	/* Start of the virtual frame buffer (relative to fullscreen) */
G
Geert Uytterhoeven 已提交
584 585 586
	ddr_xoff = info->var.left_margin - vmode->left_margin;
	ddr_yoff = info->var.upper_margin - vmode->upper_margin;
	offset = ddr_yoff * ddr_line_length + ddr_xoff * BPP;
587

588 589
	par->fb_offset = GPU_ALIGN_UP(offset);
	par->full_offset = par->fb_offset - offset;
590
	par->pan_offset = info->var.yoffset * xdr_line_length +
591
			  info->var.xoffset * BPP;
592

593 594 595 596 597 598 599
	if (par->new_mode_id != par->mode_id) {
		if (ps3av_set_video_mode(par->new_mode_id)) {
			par->new_mode_id = par->mode_id;
			return -EINVAL;
		}
		par->mode_id = par->new_mode_id;
	}
600

601 602 603 604
	/* Clear XDR frame buffer memory */
	memset(ps3fb.xdr_ea, 0, ps3fb.xdr_size);

	/* Clear DDR frame buffer memory */
G
Geert Uytterhoeven 已提交
605
	lines = vmode->yres * par->num_frames;
606 607
	if (par->full_offset)
		lines++;
608 609
	maxlines = ps3fb.xdr_size / ddr_line_length;
	for (dst = 0; lines; dst += maxlines * ddr_line_length) {
610
		unsigned int l = min(lines, maxlines);
G
Geert Uytterhoeven 已提交
611
		ps3fb_sync_image(info->device, 0, dst, 0, vmode->xres, l,
612
				 ddr_line_length, ddr_line_length);
613 614 615
		lines -= l;
	}

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
	return 0;
}

    /*
     *  Set a single color register. The values supplied are already
     *  rounded down to the hardware's capabilities (according to the
     *  entries in the var structure). Return != 0 for invalid regno.
     */

static int ps3fb_setcolreg(unsigned int regno, unsigned int red,
			   unsigned int green, unsigned int blue,
			   unsigned int transp, struct fb_info *info)
{
	if (regno >= 16)
		return 1;

	red >>= 8;
	green >>= 8;
	blue >>= 8;
	transp >>= 8;

	((u32 *)info->pseudo_palette)[regno] = transp << 24 | red << 16 |
					       green << 8 | blue;
	return 0;
}

642 643 644 645 646 647 648 649 650 651
static int ps3fb_pan_display(struct fb_var_screeninfo *var,
			     struct fb_info *info)
{
	struct ps3fb_par *par = info->par;

	par->pan_offset = var->yoffset * info->fix.line_length +
			  var->xoffset * BPP;
	return 0;
}

652 653 654 655 656 657 658 659 660 661 662 663 664
    /*
     *  As we have a virtual frame buffer, we need our own mmap function
     */

static int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
	unsigned long size, offset;

	size = vma->vm_end - vma->vm_start;
	offset = vma->vm_pgoff << PAGE_SHIFT;
	if (offset + size > info->fix.smem_len)
		return -EINVAL;

665
	offset += info->fix.smem_start;
666 667 668 669
	if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT,
			    size, vma->vm_page_prot))
		return -EAGAIN;

670 671
	dev_dbg(info->device, "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n",
		offset, vma->vm_start);
672 673 674 675 676 677 678 679 680 681 682
	return 0;
}

    /*
     * Blank the display
     */

static int ps3fb_blank(int blank, struct fb_info *info)
{
	int retval;

683
	dev_dbg(info->device, "%s: blank:%d\n", __func__, blank);
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
	switch (blank) {
	case FB_BLANK_POWERDOWN:
	case FB_BLANK_HSYNC_SUSPEND:
	case FB_BLANK_VSYNC_SUSPEND:
	case FB_BLANK_NORMAL:
		retval = ps3av_video_mute(1);	/* mute on */
		if (!retval)
			ps3fb.is_blanked = 1;
		break;

	default:		/* unblank */
		retval = ps3av_video_mute(0);	/* mute off */
		if (!retval)
			ps3fb.is_blanked = 0;
		break;
	}
	return retval;
}

static int ps3fb_get_vblank(struct fb_vblank *vblank)
{
705
	memset(vblank, 0, sizeof(*vblank));
706 707 708 709
	vblank->flags = FB_VBLANK_HAVE_VSYNC;
	return 0;
}

710
static int ps3fb_wait_for_vsync(u32 crtc)
711 712 713 714 715 716 717 718 719 720 721 722 723 724
{
	int ret;
	u64 count;

	count = ps3fb.vblank_count;
	ret = wait_event_interruptible_timeout(ps3fb.wait_vsync,
					       count != ps3fb.vblank_count,
					       HZ / 10);
	if (!ret)
		return -ETIMEDOUT;

	return 0;
}

725
static void ps3fb_flip_ctl(int on, void *data)
726
{
727
	struct ps3fb_priv *priv = data;
G
Geert Uytterhoeven 已提交
728
	if (on)
729
		atomic_dec_if_positive(&priv->ext_flip);
G
Geert Uytterhoeven 已提交
730
	else
731
		atomic_inc(&priv->ext_flip);
732 733 734 735 736 737 738 739 740 741 742
}


    /*
     * ioctl
     */

static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd,
		       unsigned long arg)
{
	void __user *argp = (void __user *)arg;
743
	u32 val;
744 745 746 747 748 749
	int retval = -EFAULT;

	switch (cmd) {
	case FBIOGET_VBLANK:
		{
			struct fb_vblank vblank;
750
			dev_dbg(info->device, "FBIOGET_VBLANK:\n");
751 752 753 754 755 756 757 758 759 760 761 762
			retval = ps3fb_get_vblank(&vblank);
			if (retval)
				break;

			if (copy_to_user(argp, &vblank, sizeof(vblank)))
				retval = -EFAULT;
			break;
		}

	case FBIO_WAITFORVSYNC:
		{
			u32 crt;
763
			dev_dbg(info->device, "FBIO_WAITFORVSYNC:\n");
764 765 766 767 768 769 770 771 772
			if (get_user(crt, (u32 __user *) arg))
				break;

			retval = ps3fb_wait_for_vsync(crt);
			break;
		}

	case PS3FB_IOCTL_SETMODE:
		{
773
			struct ps3fb_par *par = info->par;
774
			const struct fb_videomode *vmode;
775 776 777 778 779
			struct fb_var_screeninfo var;

			if (copy_from_user(&val, argp, sizeof(val)))
				break;

780
			if (!(val & PS3AV_MODE_MASK)) {
781
				u32 id = ps3av_get_auto_mode();
782 783 784
				if (id > 0)
					val = (val & ~PS3AV_MODE_MASK) | id;
			}
785
			dev_dbg(info->device, "PS3FB_IOCTL_SETMODE:%x\n", val);
786
			retval = -EINVAL;
787 788
			vmode = ps3fb_vmode(val);
			if (vmode) {
789
				var = info->var;
790
				fb_videomode_to_var(&var, vmode);
791 792 793 794
				acquire_console_sem();
				info->flags |= FBINFO_MISC_USEREVENT;
				/* Force, in case only special bits changed */
				var.activate |= FB_ACTIVATE_FORCE;
795
				par->new_mode_id = val;
796 797 798 799 800 801 802 803 804
				retval = fb_set_var(info, &var);
				info->flags &= ~FBINFO_MISC_USEREVENT;
				release_console_sem();
			}
			break;
		}

	case PS3FB_IOCTL_GETMODE:
		val = ps3av_get_mode();
805
		dev_dbg(info->device, "PS3FB_IOCTL_GETMODE:%x\n", val);
806 807 808 809 810 811
		if (!copy_to_user(argp, &val, sizeof(val)))
			retval = 0;
		break;

	case PS3FB_IOCTL_SCREENINFO:
		{
812
			struct ps3fb_par *par = info->par;
813
			struct ps3fb_ioctl_res res;
814
			dev_dbg(info->device, "PS3FB_IOCTL_SCREENINFO:\n");
815 816 817 818
			res.xres = info->fix.line_length / BPP;
			res.yres = info->var.yres_virtual;
			res.xoff = (res.xres - info->var.xres) / 2;
			res.yoff = (res.yres - info->var.yres) / 2;
819
			res.num_frames = par->num_frames;
820 821 822 823 824 825
			if (!copy_to_user(argp, &res, sizeof(res)))
				retval = 0;
			break;
		}

	case PS3FB_IOCTL_ON:
826
		dev_dbg(info->device, "PS3FB_IOCTL_ON:\n");
827 828 829 830 831
		atomic_inc(&ps3fb.ext_flip);
		retval = 0;
		break;

	case PS3FB_IOCTL_OFF:
832
		dev_dbg(info->device, "PS3FB_IOCTL_OFF:\n");
G
Geert Uytterhoeven 已提交
833
		atomic_dec_if_positive(&ps3fb.ext_flip);
834 835 836 837 838 839 840
		retval = 0;
		break;

	case PS3FB_IOCTL_FSEL:
		if (copy_from_user(&val, argp, sizeof(val)))
			break;

841
		dev_dbg(info->device, "PS3FB_IOCTL_FSEL:%d\n", val);
J
Jeremy Kerr 已提交
842
		acquire_console_sem();
843
		retval = ps3fb_sync(info, val);
J
Jeremy Kerr 已提交
844
		release_console_sem();
845 846 847 848 849 850 851 852 853 854 855
		break;

	default:
		retval = -ENOIOCTLCMD;
		break;
	}
	return retval;
}

static int ps3fbd(void *arg)
{
856 857
	struct fb_info *info = arg;

858
	set_freezable();
G
Geert Uytterhoeven 已提交
859 860 861 862 863
	while (!kthread_should_stop()) {
		try_to_freeze();
		set_current_state(TASK_INTERRUPTIBLE);
		if (ps3fb.is_kicked) {
			ps3fb.is_kicked = 0;
J
Jeremy Kerr 已提交
864
			acquire_console_sem();
865
			ps3fb_sync(info, 0);	/* single buffer */
J
Jeremy Kerr 已提交
866
			release_console_sem();
G
Geert Uytterhoeven 已提交
867 868
		}
		schedule();
869 870 871 872 873 874
	}
	return 0;
}

static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr)
{
875
	struct device *dev = ptr;
876 877 878 879 880 881
	u64 v1;
	int status;
	struct display_head *head = &ps3fb.dinfo->display_head[1];

	status = lv1_gpu_context_intr(ps3fb.context_handle, &v1);
	if (status) {
882 883
		dev_err(dev, "%s: lv1_gpu_context_intr failed: %d\n", __func__,
			status);
884 885 886 887 888 889
		return IRQ_NONE;
	}

	if (v1 & (1 << GPU_INTR_STATUS_VSYNC_1)) {
		/* VSYNC */
		ps3fb.vblank_count = head->vblank_count;
G
Geert Uytterhoeven 已提交
890 891 892 893 894
		if (ps3fb.task && !ps3fb.is_blanked &&
		    !atomic_read(&ps3fb.ext_flip)) {
			ps3fb.is_kicked = 1;
			wake_up_process(ps3fb.task);
		}
895 896 897 898 899 900 901
		wake_up_interruptible(&ps3fb.wait_vsync);
	}

	return IRQ_HANDLED;
}


902
static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo,
903
				struct device *dev)
904 905 906
{
	int error;

907 908 909 910 911
	dev_dbg(dev, "version_driver:%x\n", dinfo->version_driver);
	dev_dbg(dev, "irq outlet:%x\n", dinfo->irq.irq_outlet);
	dev_dbg(dev,
		"version_gpu: %x memory_size: %x ch: %x core_freq: %d "
		"mem_freq:%d\n",
912 913 914 915
		dinfo->version_gpu, dinfo->memory_size, dinfo->hardware_channel,
		dinfo->nvcore_frequency/1000000, dinfo->memory_frequency/1000000);

	if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) {
916 917
		dev_err(dev, "%s: version_driver err:%x\n", __func__,
			dinfo->version_driver);
918 919 920
		return -EINVAL;
	}

921 922
	error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet,
				   &ps3fb.irq_no);
923
	if (error) {
924
		dev_err(dev, "%s: ps3_alloc_irq failed %d\n", __func__, error);
925 926 927 928
		return error;
	}

	error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED,
929
			    DEVICE_NAME, dev);
930
	if (error) {
931
		dev_err(dev, "%s: request_irq failed %d\n", __func__, error);
932
		ps3_irq_plug_destroy(ps3fb.irq_no);
933 934 935 936 937 938 939 940
		return error;
	}

	dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) |
			  (1 << GPU_INTR_STATUS_FLIP_1);
	return 0;
}

941
static int ps3fb_xdr_settings(u64 xdr_lpar, struct device *dev)
942 943 944 945 946 947
{
	int status;

	status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF,
				       xdr_lpar, ps3fb_videomemory.size, 0);
	if (status) {
948 949
		dev_err(dev, "%s: lv1_gpu_context_iomap failed: %d\n",
			__func__, status);
950 951
		return -ENXIO;
	}
952 953
	dev_dbg(dev,
		"video:%p xdr_ea:%p ioif:%lx lpar:%lx phys:%lx size:%lx\n",
954 955 956 957 958
		ps3fb_videomemory.address, ps3fb.xdr_ea, GPU_IOIF, xdr_lpar,
		virt_to_abs(ps3fb.xdr_ea), ps3fb_videomemory.size);

	status = lv1_gpu_context_attribute(ps3fb.context_handle,
					   L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP,
959 960
					   xdr_lpar, GPU_CMD_BUF_SIZE,
					   GPU_IOIF, 0);
961
	if (status) {
962 963 964
		dev_err(dev,
			"%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n",
			__func__, status);
965 966 967 968 969 970 971 972
		return -ENXIO;
	}
	return 0;
}

static struct fb_ops ps3fb_ops = {
	.fb_open	= ps3fb_open,
	.fb_release	= ps3fb_release,
973 974
	.fb_read        = fb_sys_read,
	.fb_write       = fb_sys_write,
975 976 977
	.fb_check_var	= ps3fb_check_var,
	.fb_set_par	= ps3fb_set_par,
	.fb_setcolreg	= ps3fb_setcolreg,
978
	.fb_pan_display	= ps3fb_pan_display,
979 980 981
	.fb_fillrect	= sys_fillrect,
	.fb_copyarea	= sys_copyarea,
	.fb_imageblit	= sys_imageblit,
982 983 984 985 986 987 988
	.fb_mmap	= ps3fb_mmap,
	.fb_blank	= ps3fb_blank,
	.fb_ioctl	= ps3fb_ioctl,
	.fb_compat_ioctl = ps3fb_ioctl
};

static struct fb_fix_screeninfo ps3fb_fix __initdata = {
989
	.id =		DEVICE_NAME,
990 991 992 993 994
	.type =		FB_TYPE_PACKED_PIXELS,
	.visual =	FB_VISUAL_TRUECOLOR,
	.accel =	FB_ACCEL_NONE,
};

995
static int ps3fb_set_sync(struct device *dev)
996 997 998 999 1000 1001 1002 1003
{
	int status;

#ifdef HEAD_A
	status = lv1_gpu_context_attribute(0x0,
					   L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
					   0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
	if (status) {
1004 1005 1006 1007
		dev_err(dev,
			"%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: "
			"%d\n",
			__func__, status);
1008 1009 1010 1011 1012 1013 1014 1015 1016
		return -1;
	}
#endif
#ifdef HEAD_B
	status = lv1_gpu_context_attribute(0x0,
					   L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
					   1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);

	if (status) {
1017 1018 1019 1020
		dev_err(dev,
			"%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: "
			"%d\n",
			__func__, status);
1021 1022 1023 1024 1025 1026 1027
		return -1;
	}
#endif
	return 0;
}

static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
1028 1029
{
	struct fb_info *info;
1030
	struct ps3fb_par *par;
1031 1032 1033 1034 1035 1036 1037
	int retval = -ENOMEM;
	u64 ddr_lpar = 0;
	u64 lpar_dma_control = 0;
	u64 lpar_driver_info = 0;
	u64 lpar_reports = 0;
	u64 lpar_reports_size = 0;
	u64 xdr_lpar;
G
Geert Uytterhoeven 已提交
1038
	int status;
G
Geert Uytterhoeven 已提交
1039
	struct task_struct *task;
1040
	unsigned long max_ps3fb_size;
1041

1042 1043 1044 1045 1046
	if (ps3fb_videomemory.size < GPU_CMD_BUF_SIZE) {
		dev_err(&dev->core, "%s: Not enough video memory\n", __func__);
		return -ENOMEM;
	}

1047 1048
	status = ps3_open_hv_device(dev);
	if (status) {
1049 1050
		dev_err(&dev->core, "%s: ps3_open_hv_device failed\n",
			__func__);
1051 1052 1053 1054 1055
		goto err;
	}

	if (!ps3fb_mode)
		ps3fb_mode = ps3av_get_mode();
1056
	dev_dbg(&dev->core, "ps3fb_mode: %d\n", ps3fb_mode);
1057 1058 1059 1060 1061

	atomic_set(&ps3fb.f_count, -1);	/* fbcon opens ps3fb */
	atomic_set(&ps3fb.ext_flip, 0);	/* for flip with vsync */
	init_waitqueue_head(&ps3fb.wait_vsync);

1062
	ps3fb_set_sync(&dev->core);
1063

1064 1065 1066 1067 1068 1069 1070
	max_ps3fb_size = _ALIGN_UP(GPU_IOIF, 256*1024*1024) - GPU_IOIF;
	if (ps3fb_videomemory.size > max_ps3fb_size) {
		dev_info(&dev->core, "Limiting ps3fb mem size to %lu bytes\n",
			 max_ps3fb_size);
		ps3fb_videomemory.size = max_ps3fb_size;
	}

1071
	/* get gpu context handle */
1072
	status = lv1_gpu_memory_allocate(ps3fb_videomemory.size, 0, 0, 0, 0,
1073 1074
					 &ps3fb.memory_handle, &ddr_lpar);
	if (status) {
1075 1076
		dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n",
			__func__, status);
1077 1078
		goto err;
	}
1079
	dev_dbg(&dev->core, "ddr:lpar:0x%lx\n", ddr_lpar);
1080 1081 1082 1083 1084 1085

	status = lv1_gpu_context_allocate(ps3fb.memory_handle, 0,
					  &ps3fb.context_handle,
					  &lpar_dma_control, &lpar_driver_info,
					  &lpar_reports, &lpar_reports_size);
	if (status) {
1086 1087 1088
		dev_err(&dev->core,
			"%s: lv1_gpu_context_attribute failed: %d\n", __func__,
			status);
1089 1090 1091 1092 1093 1094
		goto err_gpu_memory_free;
	}

	/* vsync interrupt */
	ps3fb.dinfo = ioremap(lpar_driver_info, 128 * 1024);
	if (!ps3fb.dinfo) {
1095
		dev_err(&dev->core, "%s: ioremap failed\n", __func__);
1096 1097 1098
		goto err_gpu_context_free;
	}

1099
	retval = ps3fb_vsync_settings(ps3fb.dinfo, &dev->core);
1100 1101 1102
	if (retval)
		goto err_iounmap_dinfo;

1103
	/* XDR frame buffer */
1104 1105
	ps3fb.xdr_ea = ps3fb_videomemory.address;
	xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb.xdr_ea));
1106 1107 1108 1109

	/* Clear memory to prevent kernel info leakage into userspace */
	memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size);

1110 1111 1112 1113 1114 1115 1116 1117
	/*
	 * The GPU command buffer is at the start of video memory
	 * As we don't use the full command buffer, we can put the actual
	 * frame buffer at offset GPU_FB_START and save some precious XDR
	 * memory
	 */
	ps3fb.xdr_ea += GPU_FB_START;
	ps3fb.xdr_size = ps3fb_videomemory.size - GPU_FB_START;
1118

1119
	retval = ps3fb_xdr_settings(xdr_lpar, &dev->core);
1120 1121 1122
	if (retval)
		goto err_free_irq;

1123
	info = framebuffer_alloc(sizeof(struct ps3fb_par), &dev->core);
1124 1125 1126
	if (!info)
		goto err_free_irq;

1127 1128 1129 1130 1131
	par = info->par;
	par->mode_id = ~ps3fb_mode;	/* != ps3fb_mode, to trigger change */
	par->new_mode_id = ps3fb_mode;
	par->num_frames = 1;

1132
	info->screen_base = (char __iomem *)ps3fb.xdr_ea;
1133 1134 1135
	info->fbops = &ps3fb_ops;

	info->fix = ps3fb_fix;
1136 1137
	info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea);
	info->fix.smem_len = ps3fb.xdr_size;
1138
	info->pseudo_palette = par->pseudo_palette;
1139 1140
	info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST |
		      FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
1141 1142 1143 1144 1145 1146

	retval = fb_alloc_cmap(&info->cmap, 256, 0);
	if (retval < 0)
		goto err_framebuffer_release;

	if (!fb_find_mode(&info->var, info, mode_option, ps3fb_modedb,
1147
			  ARRAY_SIZE(ps3fb_modedb),
1148
			  ps3fb_vmode(par->new_mode_id), 32)) {
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
		retval = -EINVAL;
		goto err_fb_dealloc;
	}

	fb_videomode_to_modelist(ps3fb_modedb, ARRAY_SIZE(ps3fb_modedb),
				 &info->modelist);

	retval = register_framebuffer(info);
	if (retval < 0)
		goto err_fb_dealloc;

1160
	dev->core.driver_data = info;
1161

1162 1163
	dev_info(info->device, "%s %s, using %lu KiB of video memory\n",
		 dev_driver_string(info->dev), info->dev->bus_id,
1164
		 ps3fb.xdr_size >> 10);
1165

1166
	task = kthread_run(ps3fbd, info, DEVICE_NAME);
G
Geert Uytterhoeven 已提交
1167 1168 1169 1170 1171 1172
	if (IS_ERR(task)) {
		retval = PTR_ERR(task);
		goto err_unregister_framebuffer;
	}

	ps3fb.task = task;
1173
	ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb);
G
Geert Uytterhoeven 已提交
1174

1175 1176
	return 0;

G
Geert Uytterhoeven 已提交
1177 1178
err_unregister_framebuffer:
	unregister_framebuffer(info);
1179 1180 1181 1182 1183
err_fb_dealloc:
	fb_dealloc_cmap(&info->cmap);
err_framebuffer_release:
	framebuffer_release(info);
err_free_irq:
G
Geoff Levand 已提交
1184
	free_irq(ps3fb.irq_no, &dev->core);
1185
	ps3_irq_plug_destroy(ps3fb.irq_no);
1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
err_iounmap_dinfo:
	iounmap((u8 __iomem *)ps3fb.dinfo);
err_gpu_context_free:
	lv1_gpu_context_free(ps3fb.context_handle);
err_gpu_memory_free:
	lv1_gpu_memory_free(ps3fb.memory_handle);
err:
	return retval;
}

1196
static int ps3fb_shutdown(struct ps3_system_bus_device *dev)
1197
{
1198 1199 1200
	int status;
	struct fb_info *info = dev->core.driver_data;

1201
	dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
1202 1203

	ps3fb_flip_ctl(0, &ps3fb);	/* flip off */
1204 1205
	ps3fb.dinfo->irq.mask = 0;

1206
	ps3av_register_flip_ctl(NULL, NULL);
G
Geert Uytterhoeven 已提交
1207 1208 1209 1210 1211
	if (ps3fb.task) {
		struct task_struct *task = ps3fb.task;
		ps3fb.task = NULL;
		kthread_stop(task);
	}
1212
	if (ps3fb.irq_no) {
G
Geoff Levand 已提交
1213
		free_irq(ps3fb.irq_no, &dev->core);
1214
		ps3_irq_plug_destroy(ps3fb.irq_no);
1215
	}
1216 1217 1218 1219 1220 1221
	if (info) {
		unregister_framebuffer(info);
		fb_dealloc_cmap(&info->cmap);
		framebuffer_release(info);
		info = dev->core.driver_data = NULL;
	}
1222 1223 1224 1225
	iounmap((u8 __iomem *)ps3fb.dinfo);

	status = lv1_gpu_context_free(ps3fb.context_handle);
	if (status)
1226 1227
		dev_dbg(&dev->core, "lv1_gpu_context_free failed: %d\n",
			status);
1228 1229 1230

	status = lv1_gpu_memory_free(ps3fb.memory_handle);
	if (status)
1231 1232
		dev_dbg(&dev->core, "lv1_gpu_memory_free failed: %d\n",
			status);
1233

1234
	ps3_close_hv_device(dev);
1235
	dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
1236 1237 1238 1239

	return 0;
}

1240 1241 1242 1243 1244 1245 1246
static struct ps3_system_bus_driver ps3fb_driver = {
	.match_id	= PS3_MATCH_ID_GRAPHICS,
	.core.name	= DEVICE_NAME,
	.core.owner	= THIS_MODULE,
	.probe		= ps3fb_probe,
	.remove		= ps3fb_shutdown,
	.shutdown	= ps3fb_shutdown,
1247 1248
};

1249
static int __init ps3fb_setup(void)
1250
{
1251
	char *options;
1252

1253
#ifdef MODULE
1254 1255 1256
	return 0;
#endif

1257 1258
	if (fb_get_options(DEVICE_NAME, &options))
		return -ENXIO;
1259

1260 1261
	if (!options || !*options)
		return 0;
1262

1263 1264
	while (1) {
		char *this_opt = strsep(&options, ",");
1265

1266 1267 1268 1269 1270 1271 1272 1273
		if (!this_opt)
			break;
		if (!*this_opt)
			continue;
		if (!strncmp(this_opt, "mode:", 5))
			ps3fb_mode = simple_strtoul(this_opt + 5, NULL, 0);
		else
			mode_option = this_opt;
1274
	}
1275 1276
	return 0;
}
1277

1278 1279 1280 1281
static int __init ps3fb_init(void)
{
	if (!ps3fb_videomemory.address ||  ps3fb_setup())
		return -ENXIO;
1282

1283
	return ps3_system_bus_driver_register(&ps3fb_driver);
1284 1285 1286 1287
}

static void __exit ps3fb_exit(void)
{
1288
	pr_debug(" -> %s:%d\n", __func__, __LINE__);
1289
	ps3_system_bus_driver_unregister(&ps3fb_driver);
1290
	pr_debug(" <- %s:%d\n", __func__, __LINE__);
1291 1292
}

1293
module_init(ps3fb_init);
1294 1295 1296
module_exit(ps3fb_exit);

MODULE_LICENSE("GPL");
1297 1298 1299
MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver");
MODULE_AUTHOR("Sony Computer Entertainment Inc.");
MODULE_ALIAS(PS3_MODULE_ALIAS_GRAPHICS);