/*
* 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_PVR2_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[] = {
{ CT_VGA, "VGA" }, { CT_RGB, "RGB" }, { CT_COMPOSITE, "COMPOSITE" },
};
static struct pvr2_params outputs[] = {
{ 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 = {
.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 = {
.xres = 640,
.yres = 480,
.xres_virtual = 640,
.yres_virtual = 480,
.bits_per_pixel =16,
.red =