brintos

brintos / linux-shallow public Read only

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