📄 pvr2fb.c
字号:
/* * drivers/video/pvr2fb.c * * Frame buffer and fbcon support for the NEC PowerVR2 found within the Sega * Dreamcast. * * Copyright (c) 2001 M. R. Brown <mrbrown@0xd6.org> * Copyright (c) 2001 - 2008 Paul Mundt <lethal@linux-sh.org> * * This driver is mostly based on the excellent amifb and vfb sources. It uses * an odd scheme for converting hardware values to/from framebuffer values, * here are some hacked-up formulas: * * The Dreamcast has screen offsets from each side of its four borders and * the start offsets of the display window. I used these values to calculate * 'pseudo' values (think of them as placeholders) for the fb video mode, so * that when it came time to convert these values back into their hardware * values, I could just add mode- specific offsets to get the correct mode * settings: * * left_margin = diwstart_h - borderstart_h; * right_margin = borderstop_h - (diwstart_h + xres); * upper_margin = diwstart_v - borderstart_v; * lower_margin = borderstop_v - (diwstart_h + yres); * * hsync_len = borderstart_h + (hsync_total - borderstop_h); * vsync_len = borderstart_v + (vsync_total - borderstop_v); * * Then, when it's time to convert back to hardware settings, the only * constants are the borderstart_* offsets, all other values are derived from * the fb video mode: * * // PAL * borderstart_h = 116; * borderstart_v = 44; * ... * borderstop_h = borderstart_h + hsync_total - hsync_len; * ... * diwstart_v = borderstart_v - upper_margin; * * However, in the current implementation, the borderstart values haven't had * the benefit of being fully researched, so some modes may be broken. */#undef DEBUG#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/pci.h>#ifdef CONFIG_SH_DREAMCAST#include <asm/machvec.h>#include <mach-dreamcast/mach/sysasic.h>#endif#ifdef CONFIG_SH_DMA#include <linux/pagemap.h>#include <mach/dma.h>#include <asm/dma.h>#endif#ifdef CONFIG_SH_STORE_QUEUES#include <linux/uaccess.h>#include <cpu/sq.h>#endif#ifndef PCI_DEVICE_ID_NEC_NEON250# define PCI_DEVICE_ID_NEC_NEON250 0x0067#endif/* 2D video registers */#define DISP_BASE par->mmio_base#define DISP_BRDRCOLR (DISP_BASE + 0x40)#define DISP_DIWMODE (DISP_BASE + 0x44)#define DISP_DIWADDRL (DISP_BASE + 0x50)#define DISP_DIWADDRS (DISP_BASE + 0x54)#define DISP_DIWSIZE (DISP_BASE + 0x5c)#define DISP_SYNCCONF (DISP_BASE + 0xd0)#define DISP_BRDRHORZ (DISP_BASE + 0xd4)#define DISP_SYNCSIZE (DISP_BASE + 0xd8)#define DISP_BRDRVERT (DISP_BASE + 0xdc)#define DISP_DIWCONF (DISP_BASE + 0xe8)#define DISP_DIWHSTRT (DISP_BASE + 0xec)#define DISP_DIWVSTRT (DISP_BASE + 0xf0)#define DISP_PIXDEPTH (DISP_BASE + 0x108)/* Pixel clocks, one for TV output, doubled for VGA output */#define TV_CLK 74239#define VGA_CLK 37119/* This is for 60Hz - the VTOTAL is doubled for interlaced modes */#define PAL_HTOTAL 863#define PAL_VTOTAL 312#define NTSC_HTOTAL 857#define NTSC_VTOTAL 262/* Supported cable types */enum { CT_VGA, CT_NONE, CT_RGB, CT_COMPOSITE };/* Supported video output types */enum { VO_PAL, VO_NTSC, VO_VGA };/* Supported palette types */enum { PAL_ARGB1555, PAL_RGB565, PAL_ARGB4444, PAL_ARGB8888 };struct pvr2_params { unsigned int val; char *name; };static struct pvr2_params cables[] __devinitdata = { { CT_VGA, "VGA" }, { CT_RGB, "RGB" }, { CT_COMPOSITE, "COMPOSITE" },};static struct pvr2_params outputs[] __devinitdata = { { VO_PAL, "PAL" }, { VO_NTSC, "NTSC" }, { VO_VGA, "VGA" },};/* * This describes the current video mode */static struct pvr2fb_par { unsigned int hsync_total; /* Clocks/line */ unsigned int vsync_total; /* Lines/field */ unsigned int borderstart_h; unsigned int borderstop_h; unsigned int borderstart_v; unsigned int borderstop_v; unsigned int diwstart_h; /* Horizontal offset of the display field */ unsigned int diwstart_v; /* Vertical offset of the display field, for interlaced modes, this is the long field */ unsigned long disp_start; /* Address of image within VRAM */ unsigned char is_interlaced; /* Is the display interlaced? */ unsigned char is_doublescan; /* Are scanlines output twice? (doublescan) */ unsigned char is_lowres; /* Is horizontal pixel-doubling enabled? */ unsigned long mmio_base; /* MMIO base */ u32 palette[16];} *currentpar;static struct fb_info *fb_info;static struct fb_fix_screeninfo pvr2_fix __devinitdata = { .id = "NEC PowerVR2", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .ypanstep = 1, .ywrapstep = 1, .accel = FB_ACCEL_NONE,};static struct fb_var_screeninfo pvr2_var __devinitdata = { .xres = 640, .yres = 480, .xres_virtual = 640, .yres_virtual = 480, .bits_per_pixel =16, .red = { 11, 5, 0 }, .green = { 5, 6, 0 }, .blue = { 0, 5, 0 }, .activate = FB_ACTIVATE_NOW, .height = -1, .width = -1, .vmode = FB_VMODE_NONINTERLACED,};static int cable_type = CT_VGA;static int video_output = VO_VGA;static int nopan = 0;static int nowrap = 1;/* * We do all updating, blanking, etc. during the vertical retrace period */static unsigned int do_vmode_full = 0; /* Change the video mode */static unsigned int do_vmode_pan = 0; /* Update the video mode */static short do_blank = 0; /* (Un)Blank the screen */static unsigned int is_blanked = 0; /* Is the screen blanked? */#ifdef CONFIG_SH_STORE_QUEUESstatic unsigned long pvr2fb_map;#endif#ifdef CONFIG_SH_DMAstatic unsigned int shdma = PVR2_CASCADE_CHAN;static unsigned int pvr2dma = ONCHIP_NR_DMA_CHANNELS;#endifstatic int pvr2fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info);static int pvr2fb_blank(int blank, struct fb_info *info);static unsigned long get_line_length(int xres_virtual, int bpp);static void set_color_bitfields(struct fb_var_screeninfo *var);static int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info);static int pvr2fb_set_par(struct fb_info *info);static void pvr2_update_display(struct fb_info *info);static void pvr2_init_display(struct fb_info *info);static void pvr2_do_blank(void);static irqreturn_t pvr2fb_interrupt(int irq, void *dev_id);static int pvr2_init_cable(void);static int pvr2_get_param(const struct pvr2_params *p, const char *s, int val, int size);#ifdef CONFIG_SH_DMAstatic ssize_t pvr2fb_write(struct fb_info *info, const char *buf, size_t count, loff_t *ppos);#endifstatic struct fb_ops pvr2fb_ops = { .owner = THIS_MODULE, .fb_setcolreg = pvr2fb_setcolreg, .fb_blank = pvr2fb_blank, .fb_check_var = pvr2fb_check_var, .fb_set_par = pvr2fb_set_par,#ifdef CONFIG_SH_DMA .fb_write = pvr2fb_write,#endif .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit,};static struct fb_videomode pvr2_modedb[] __devinitdata = { /* * Broadcast video modes (PAL and NTSC). I'm unfamiliar with * PAL-M and PAL-N, but from what I've read both modes parallel PAL and * NTSC, so it shouldn't be a problem (I hope). */ { /* 640x480 @ 60Hz interlaced (NTSC) */ "ntsc_640x480i", 60, 640, 480, TV_CLK, 38, 33, 0, 18, 146, 26, FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP }, { /* 640x240 @ 60Hz (NTSC) */ /* XXX: Broken! Don't use... */ "ntsc_640x240", 60, 640, 240, TV_CLK, 38, 33, 0, 0, 146, 22, FB_SYNC_BROADCAST, FB_VMODE_YWRAP }, { /* 640x480 @ 60hz (VGA) */ "vga_640x480", 60, 640, 480, VGA_CLK, 38, 33, 0, 18, 146, 26, 0, FB_VMODE_YWRAP },};#define NUM_TOTAL_MODES ARRAY_SIZE(pvr2_modedb)#define DEFMODE_NTSC 0#define DEFMODE_PAL 0#define DEFMODE_VGA 2static int defmode = DEFMODE_NTSC;static char *mode_option __devinitdata = NULL;static inline void pvr2fb_set_pal_type(unsigned int type){ struct pvr2fb_par *par = (struct pvr2fb_par *)fb_info->par; fb_writel(type, par->mmio_base + 0x108);}static inline void pvr2fb_set_pal_entry(struct pvr2fb_par *par, unsigned int regno, unsigned int val){ fb_writel(val, par->mmio_base + 0x1000 + (4 * regno));}static int pvr2fb_blank(int blank, struct fb_info *info){ do_blank = blank ? blank : -1; return 0;}static inline unsigned long get_line_length(int xres_virtual, int bpp){ return (unsigned long)((((xres_virtual*bpp)+31)&~31) >> 3);}static void set_color_bitfields(struct fb_var_screeninfo *var){ switch (var->bits_per_pixel) { case 16: /* RGB 565 */ pvr2fb_set_pal_type(PAL_RGB565); var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = 0; var->transp.length = 0; break; case 24: /* RGB 888 */ var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; break; case 32: /* ARGB 8888 */ pvr2fb_set_pal_type(PAL_ARGB8888); var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 24; var->transp.length = 8; break; }}static int pvr2fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info){ struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; unsigned int tmp; if (regno > info->cmap.len) return 1; /* * We only support the hardware palette for 16 and 32bpp. It's also * expected that the palette format has been set by the time we get * here, so we don't waste time setting it again. */ switch (info->var.bits_per_pixel) { case 16: /* RGB 565 */ tmp = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); pvr2fb_set_pal_entry(par, regno, tmp); break; case 24: /* RGB 888 */ red >>= 8; green >>= 8; blue >>= 8; tmp = (red << 16) | (green << 8) | blue; break; case 32: /* ARGB 8888 */ red >>= 8; green >>= 8; blue >>= 8; tmp = (transp << 24) | (red << 16) | (green << 8) | blue; pvr2fb_set_pal_entry(par, regno, tmp); break; default: pr_debug("Invalid bit depth %d?!?\n", info->var.bits_per_pixel); return 1; } if (regno < 16) ((u32*)(info->pseudo_palette))[regno] = tmp; return 0;}static int pvr2fb_set_par(struct fb_info *info){ struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; struct fb_var_screeninfo *var = &info->var; unsigned long line_length; unsigned int vtotal; /* * XXX: It's possible that a user could use a VGA box, change the cable * type in hardware (i.e. switch from VGA<->composite), then change * modes (i.e. switching to another VT). If that happens we should * automagically change the output format to cope, but currently I * don't have a VGA box to make sure this works properly. */ cable_type = pvr2_init_cable(); if (cable_type == CT_VGA && video_output != VO_VGA) video_output = VO_VGA; var->vmode &= FB_VMODE_MASK; if (var->vmode & FB_VMODE_INTERLACED && video_output != VO_VGA) par->is_interlaced = 1; /* * XXX: Need to be more creative with this (i.e. allow doublecan for * PAL/NTSC output). */ if (var->vmode & FB_VMODE_DOUBLE && video_output == VO_VGA)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -