ps3fb.c

来自「Linux环境下视频显示卡设备的驱动程序源代码」· C语言 代码 · 共 1,352 行 · 第 1/3 页

C
1,352
字号
/* *  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/abs_addr.h>#include <asm/lv1call.h>#include <asm/ps3av.h>#include <asm/ps3fb.h>#include <asm/ps3.h>#define DEVICE_NAME		"ps3fb"#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#define GPU_CMD_BUF_SIZE			(2 * 1024 * 1024)#define GPU_FB_START				(64 * 1024)#define GPU_IOIF				(0x0d000000UL)#define GPU_ALIGN_UP(x)				_ALIGN_UP((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	10static 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 __devinitdata;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,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?