1301 lines · 32.7 KB · C
RawDownload
/* *  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>#include <linux/kthread.h>#include <linux/freezer.h>#include <linux/uaccess.h>#include <linux/fb.h>#include <linux/init.h> #include <asm/cell-regs.h>#include <asm/lv1call.h>#include <asm/ps3av.h>#include <asm/ps3fb.h>#include <asm/ps3.h>#include <asm/ps3gpu.h>  #define DEVICE_NAME		"ps3fb" #define GPU_CMD_BUF_SIZE			(2 * 1024 * 1024)#define GPU_FB_START				(64 * 1024)#define GPU_IOIF				(0x0d000000UL)#define GPU_ALIGN_UP(x)				ALIGN((x), 64)#define GPU_MAX_LINE_LENGTH			(65536 - 64) #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;	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;	int is_kicked;	struct task_struct *task;};static struct ps3fb_priv ps3fb; struct ps3fb_par {	u32 pseudo_palette[16];	int mode_id, new_mode_id;	unsigned int num_frames;	/* num of frame buffers */	unsigned int width;	unsigned int height;	unsigned int ddr_line_length;	unsigned int ddr_frame_size;	unsigned int xdr_frame_size;	unsigned int full_offset;	/* start of fullscreen DDR fb */	unsigned int fb_offset;		/* start of actual DDR fb */	unsigned int pan_offset;};  #define FIRST_NATIVE_MODE_INDEX	10 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    },    {        /* 1080i */        "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    },     [FIRST_NATIVE_MODE_INDEX] =    /* 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 */	"1080if", 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    },     /* 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    }};  #define HEAD_A#define HEAD_B #define BPP		4			/* number of bytes per pixel */  static int ps3fb_mode;module_param(ps3fb_mode, int, 0); static char *mode_option; static int ps3fb_cmp_mode(const struct fb_videomode *vmode,			  const struct fb_var_screeninfo *var){	long xres, yres, left_margin, right_margin, upper_margin, lower_margin;	long dx, dy; 	/* maximum values */	if (var->xres > vmode->xres || var->yres > vmode->yres ||	    var->pixclock > vmode->pixclock ||	    var->hsync_len > vmode->hsync_len ||	    var->vsync_len > vmode->vsync_len)		return -1; 	/* progressive/interlaced must match */	if ((var->vmode & FB_VMODE_MASK) != vmode->vmode)		return -1; 	/* minimum resolution */	xres = max(var->xres, 1U);	yres = max(var->yres, 1U); 	/* minimum margins */	left_margin = max(var->left_margin, vmode->left_margin);	right_margin = max(var->right_margin, vmode->right_margin);	upper_margin = max(var->upper_margin, vmode->upper_margin);	lower_margin = max(var->lower_margin, vmode->lower_margin); 	/* resolution + margins may not exceed native parameters */	dx = ((long)vmode->left_margin + (long)vmode->xres +	      (long)vmode->right_margin) -	     (left_margin + xres + right_margin);	if (dx < 0)		return -1; 	dy = ((long)vmode->upper_margin + (long)vmode->yres +	      (long)vmode->lower_margin) -	     (upper_margin + yres + lower_margin);	if (dy < 0)		return -1; 	/* exact match */	if (!dx && !dy)		return 0; 	/* resolution difference */	return (vmode->xres - xres) * (vmode->yres - yres);} 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);} static unsigned int ps3fb_find_mode(struct fb_var_screeninfo *var,				    u32 *ddr_line_length, u32 *xdr_line_length){	unsigned int id, best_id;	int diff, best_diff;	const struct fb_videomode *vmode;	long gap; 	best_id = 0;	best_diff = INT_MAX;	pr_debug("%s: wanted %u [%u] %u x %u [%u] %u\n", __func__,		 var->left_margin, var->xres, var->right_margin,		 var->upper_margin, var->yres, var->lower_margin);	for (id = PS3AV_MODE_480I; id <= PS3AV_MODE_WUXGA; id++) {		vmode = ps3fb_native_vmode(id);		diff = ps3fb_cmp_mode(vmode, var);		pr_debug("%s: mode %u: %u [%u] %u x %u [%u] %u: diff = %d\n",			 __func__, id, vmode->left_margin, vmode->xres,			 vmode->right_margin, vmode->upper_margin,			 vmode->yres, vmode->lower_margin, diff);		if (diff < 0)			continue;		if (diff < best_diff) {			best_id = id;			if (!diff)				break;			best_diff = diff;		}	} 	if (!best_id) {		pr_debug("%s: no suitable mode found\n", __func__);		return 0;	} 	id = best_id;	vmode = ps3fb_native_vmode(id); 	*ddr_line_length = vmode->xres * BPP; 	/* minimum resolution */	if (!var->xres)		var->xres = 1;	if (!var->yres)		var->yres = 1; 	/* minimum virtual resolution */	if (var->xres_virtual < var->xres)		var->xres_virtual = var->xres;	if (var->yres_virtual < var->yres)		var->yres_virtual = var->yres; 	/* minimum margins */	if (var->left_margin < vmode->left_margin)		var->left_margin = vmode->left_margin;	if (var->right_margin < vmode->right_margin)		var->right_margin = vmode->right_margin;	if (var->upper_margin < vmode->upper_margin)		var->upper_margin = vmode->upper_margin;	if (var->lower_margin < vmode->lower_margin)		var->lower_margin = vmode->lower_margin; 	/* extra margins */	gap = ((long)vmode->left_margin + (long)vmode->xres +	       (long)vmode->right_margin) -	      ((long)var->left_margin + (long)var->xres +	       (long)var->right_margin);	if (gap > 0) {		var->left_margin += gap/2;		var->right_margin += (gap+1)/2;		pr_debug("%s: rounded up H to %u [%u] %u\n", __func__,			 var->left_margin, var->xres, var->right_margin);	} 	gap = ((long)vmode->upper_margin + (long)vmode->yres +	       (long)vmode->lower_margin) -	      ((long)var->upper_margin + (long)var->yres +	       (long)var->lower_margin);	if (gap > 0) {		var->upper_margin += gap/2;		var->lower_margin += (gap+1)/2;		pr_debug("%s: rounded up V to %u [%u] %u\n", __func__,			 var->upper_margin, var->yres, var->lower_margin);	} 	/* fixed fields */	var->pixclock = vmode->pixclock;	var->hsync_len = vmode->hsync_len;	var->vsync_len = vmode->vsync_len;	var->sync = vmode->sync; 	if (ps3_compare_firmware_version(1, 9, 0) >= 0) {		*xdr_line_length = GPU_ALIGN_UP(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; 	if (vmode->sync & FB_SYNC_BROADCAST) {		/* Full broadcast modes have the full mode bit set */		if (vmode->xres == var->xres && vmode->yres == var->yres)			id |= PS3AV_MODE_FULL;	} 	pr_debug("%s: mode %u\n", __func__, id);	return id;} static void ps3fb_sync_image(struct device *dev, u64 frame_offset,			     u64 dst_offset, u64 src_offset, u32 width,			     u32 height, u32 dst_line_length,			     u32 src_line_length){	int status;	u64 line_length; 	line_length = dst_line_length;	if (src_line_length != dst_line_length)		line_length |= (u64)src_line_length << 32; 	src_offset += GPU_FB_START; 	mutex_lock(&ps3_gpu_mutex);	status = lv1_gpu_fb_blit(ps3fb.context_handle, dst_offset,				 GPU_IOIF + src_offset,				 L1GPU_FB_BLIT_WAIT_FOR_COMPLETION |				 (width << 16) | height,				 line_length);	mutex_unlock(&ps3_gpu_mutex); 	if (status)		dev_err(dev, "%s: lv1_gpu_fb_blit failed: %d\n", __func__,			status);#ifdef HEAD_A	status = lv1_gpu_display_flip(ps3fb.context_handle, 0, frame_offset);	if (status)		dev_err(dev, "%s: lv1_gpu_display_flip failed: %d\n", __func__,			status);#endif#ifdef HEAD_B	status = lv1_gpu_display_flip(ps3fb.context_handle, 1, frame_offset);	if (status)		dev_err(dev, "%s: lv1_gpu_display_flip failed: %d\n", __func__,			status);#endif} static int ps3fb_sync(struct fb_info *info, u32 frame){	struct ps3fb_par *par = info->par;	int error = 0;	u64 ddr_base, xdr_base; 	if (frame > par->num_frames - 1) {		dev_dbg(info->device, "%s: invalid frame number (%u)\n",			__func__, frame);		error = -EINVAL;		goto out;	} 	xdr_base = frame * par->xdr_frame_size;	ddr_base = frame * par->ddr_frame_size; 	ps3fb_sync_image(info->device, ddr_base + par->full_offset,			 ddr_base + par->fb_offset, xdr_base + par->pan_offset,			 par->width, par->height, par->ddr_line_length,			 info->fix.line_length); out:

Lines 1–500 of 1301.