162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/video/pvr2fb.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Frame buffer and fbcon support for the NEC PowerVR2 found within the Sega 662306a36Sopenharmony_ci * Dreamcast. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (c) 2001 M. R. Brown <mrbrown@0xd6.org> 962306a36Sopenharmony_ci * Copyright (c) 2001 - 2008 Paul Mundt <lethal@linux-sh.org> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This driver is mostly based on the excellent amifb and vfb sources. It uses 1262306a36Sopenharmony_ci * an odd scheme for converting hardware values to/from framebuffer values, 1362306a36Sopenharmony_ci * here are some hacked-up formulas: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * The Dreamcast has screen offsets from each side of its four borders and 1662306a36Sopenharmony_ci * the start offsets of the display window. I used these values to calculate 1762306a36Sopenharmony_ci * 'pseudo' values (think of them as placeholders) for the fb video mode, so 1862306a36Sopenharmony_ci * that when it came time to convert these values back into their hardware 1962306a36Sopenharmony_ci * values, I could just add mode- specific offsets to get the correct mode 2062306a36Sopenharmony_ci * settings: 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * left_margin = diwstart_h - borderstart_h; 2362306a36Sopenharmony_ci * right_margin = borderstop_h - (diwstart_h + xres); 2462306a36Sopenharmony_ci * upper_margin = diwstart_v - borderstart_v; 2562306a36Sopenharmony_ci * lower_margin = borderstop_v - (diwstart_h + yres); 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * hsync_len = borderstart_h + (hsync_total - borderstop_h); 2862306a36Sopenharmony_ci * vsync_len = borderstart_v + (vsync_total - borderstop_v); 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Then, when it's time to convert back to hardware settings, the only 3162306a36Sopenharmony_ci * constants are the borderstart_* offsets, all other values are derived from 3262306a36Sopenharmony_ci * the fb video mode: 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * // PAL 3562306a36Sopenharmony_ci * borderstart_h = 116; 3662306a36Sopenharmony_ci * borderstart_v = 44; 3762306a36Sopenharmony_ci * ... 3862306a36Sopenharmony_ci * borderstop_h = borderstart_h + hsync_total - hsync_len; 3962306a36Sopenharmony_ci * ... 4062306a36Sopenharmony_ci * diwstart_v = borderstart_v - upper_margin; 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * However, in the current implementation, the borderstart values haven't had 4362306a36Sopenharmony_ci * the benefit of being fully researched, so some modes may be broken. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#undef DEBUG 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include <linux/aperture.h> 4962306a36Sopenharmony_ci#include <linux/module.h> 5062306a36Sopenharmony_ci#include <linux/kernel.h> 5162306a36Sopenharmony_ci#include <linux/errno.h> 5262306a36Sopenharmony_ci#include <linux/string.h> 5362306a36Sopenharmony_ci#include <linux/mm.h> 5462306a36Sopenharmony_ci#include <linux/slab.h> 5562306a36Sopenharmony_ci#include <linux/delay.h> 5662306a36Sopenharmony_ci#include <linux/interrupt.h> 5762306a36Sopenharmony_ci#include <linux/fb.h> 5862306a36Sopenharmony_ci#include <linux/init.h> 5962306a36Sopenharmony_ci#include <linux/pci.h> 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#ifdef CONFIG_SH_DREAMCAST 6262306a36Sopenharmony_ci#include <asm/machvec.h> 6362306a36Sopenharmony_ci#include <mach-dreamcast/mach/sysasic.h> 6462306a36Sopenharmony_ci#endif 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 6762306a36Sopenharmony_ci#include <linux/pagemap.h> 6862306a36Sopenharmony_ci#include <mach/dma.h> 6962306a36Sopenharmony_ci#include <asm/dma.h> 7062306a36Sopenharmony_ci#endif 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#ifdef CONFIG_SH_STORE_QUEUES 7362306a36Sopenharmony_ci#include <linux/uaccess.h> 7462306a36Sopenharmony_ci#include <cpu/sq.h> 7562306a36Sopenharmony_ci#endif 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#ifndef PCI_DEVICE_ID_NEC_NEON250 7862306a36Sopenharmony_ci# define PCI_DEVICE_ID_NEC_NEON250 0x0067 7962306a36Sopenharmony_ci#endif 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 2D video registers */ 8262306a36Sopenharmony_ci#define DISP_BASE par->mmio_base 8362306a36Sopenharmony_ci#define DISP_BRDRCOLR (DISP_BASE + 0x40) 8462306a36Sopenharmony_ci#define DISP_DIWMODE (DISP_BASE + 0x44) 8562306a36Sopenharmony_ci#define DISP_DIWADDRL (DISP_BASE + 0x50) 8662306a36Sopenharmony_ci#define DISP_DIWADDRS (DISP_BASE + 0x54) 8762306a36Sopenharmony_ci#define DISP_DIWSIZE (DISP_BASE + 0x5c) 8862306a36Sopenharmony_ci#define DISP_SYNCCONF (DISP_BASE + 0xd0) 8962306a36Sopenharmony_ci#define DISP_BRDRHORZ (DISP_BASE + 0xd4) 9062306a36Sopenharmony_ci#define DISP_SYNCSIZE (DISP_BASE + 0xd8) 9162306a36Sopenharmony_ci#define DISP_BRDRVERT (DISP_BASE + 0xdc) 9262306a36Sopenharmony_ci#define DISP_DIWCONF (DISP_BASE + 0xe8) 9362306a36Sopenharmony_ci#define DISP_DIWHSTRT (DISP_BASE + 0xec) 9462306a36Sopenharmony_ci#define DISP_DIWVSTRT (DISP_BASE + 0xf0) 9562306a36Sopenharmony_ci#define DISP_PIXDEPTH (DISP_BASE + 0x108) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Pixel clocks, one for TV output, doubled for VGA output */ 9862306a36Sopenharmony_ci#define TV_CLK 74239 9962306a36Sopenharmony_ci#define VGA_CLK 37119 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* This is for 60Hz - the VTOTAL is doubled for interlaced modes */ 10262306a36Sopenharmony_ci#define PAL_HTOTAL 863 10362306a36Sopenharmony_ci#define PAL_VTOTAL 312 10462306a36Sopenharmony_ci#define NTSC_HTOTAL 857 10562306a36Sopenharmony_ci#define NTSC_VTOTAL 262 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* Supported cable types */ 10862306a36Sopenharmony_cienum { CT_VGA, CT_NONE, CT_RGB, CT_COMPOSITE }; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* Supported video output types */ 11162306a36Sopenharmony_cienum { VO_PAL, VO_NTSC, VO_VGA }; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Supported palette types */ 11462306a36Sopenharmony_cienum { PAL_ARGB1555, PAL_RGB565, PAL_ARGB4444, PAL_ARGB8888 }; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct pvr2_params { unsigned int val; char *name; }; 11762306a36Sopenharmony_cistatic struct pvr2_params cables[] = { 11862306a36Sopenharmony_ci { CT_VGA, "VGA" }, { CT_RGB, "RGB" }, { CT_COMPOSITE, "COMPOSITE" }, 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic struct pvr2_params outputs[] = { 12262306a36Sopenharmony_ci { VO_PAL, "PAL" }, { VO_NTSC, "NTSC" }, { VO_VGA, "VGA" }, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * This describes the current video mode 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct pvr2fb_par { 13062306a36Sopenharmony_ci unsigned int hsync_total; /* Clocks/line */ 13162306a36Sopenharmony_ci unsigned int vsync_total; /* Lines/field */ 13262306a36Sopenharmony_ci unsigned int borderstart_h; 13362306a36Sopenharmony_ci unsigned int borderstop_h; 13462306a36Sopenharmony_ci unsigned int borderstart_v; 13562306a36Sopenharmony_ci unsigned int borderstop_v; 13662306a36Sopenharmony_ci unsigned int diwstart_h; /* Horizontal offset of the display field */ 13762306a36Sopenharmony_ci unsigned int diwstart_v; /* Vertical offset of the display field, for 13862306a36Sopenharmony_ci interlaced modes, this is the long field */ 13962306a36Sopenharmony_ci unsigned long disp_start; /* Address of image within VRAM */ 14062306a36Sopenharmony_ci unsigned char is_interlaced; /* Is the display interlaced? */ 14162306a36Sopenharmony_ci unsigned char is_doublescan; /* Are scanlines output twice? (doublescan) */ 14262306a36Sopenharmony_ci unsigned char is_lowres; /* Is horizontal pixel-doubling enabled? */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci void __iomem *mmio_base; /* MMIO base */ 14562306a36Sopenharmony_ci u32 palette[16]; 14662306a36Sopenharmony_ci} *currentpar; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct fb_info *fb_info; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct fb_fix_screeninfo pvr2_fix = { 15162306a36Sopenharmony_ci .id = "NEC PowerVR2", 15262306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 15362306a36Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 15462306a36Sopenharmony_ci .ypanstep = 1, 15562306a36Sopenharmony_ci .ywrapstep = 1, 15662306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic const struct fb_var_screeninfo pvr2_var = { 16062306a36Sopenharmony_ci .xres = 640, 16162306a36Sopenharmony_ci .yres = 480, 16262306a36Sopenharmony_ci .xres_virtual = 640, 16362306a36Sopenharmony_ci .yres_virtual = 480, 16462306a36Sopenharmony_ci .bits_per_pixel =16, 16562306a36Sopenharmony_ci .red = { 11, 5, 0 }, 16662306a36Sopenharmony_ci .green = { 5, 6, 0 }, 16762306a36Sopenharmony_ci .blue = { 0, 5, 0 }, 16862306a36Sopenharmony_ci .activate = FB_ACTIVATE_NOW, 16962306a36Sopenharmony_ci .height = -1, 17062306a36Sopenharmony_ci .width = -1, 17162306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int cable_type = CT_VGA; 17562306a36Sopenharmony_cistatic int video_output = VO_VGA; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int nopan = 0; 17862306a36Sopenharmony_cistatic int nowrap = 1; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* 18162306a36Sopenharmony_ci * We do all updating, blanking, etc. during the vertical retrace period 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic unsigned int do_vmode_full = 0; /* Change the video mode */ 18462306a36Sopenharmony_cistatic unsigned int do_vmode_pan = 0; /* Update the video mode */ 18562306a36Sopenharmony_cistatic short do_blank = 0; /* (Un)Blank the screen */ 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic unsigned int is_blanked = 0; /* Is the screen blanked? */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci#ifdef CONFIG_SH_STORE_QUEUES 19062306a36Sopenharmony_cistatic unsigned long pvr2fb_map; 19162306a36Sopenharmony_ci#endif 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 19462306a36Sopenharmony_cistatic unsigned int shdma = PVR2_CASCADE_CHAN; 19562306a36Sopenharmony_cistatic unsigned int pvr2dma = ONCHIP_NR_DMA_CHANNELS; 19662306a36Sopenharmony_ci#endif 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic struct fb_videomode pvr2_modedb[] = { 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * Broadcast video modes (PAL and NTSC). I'm unfamiliar with 20162306a36Sopenharmony_ci * PAL-M and PAL-N, but from what I've read both modes parallel PAL and 20262306a36Sopenharmony_ci * NTSC, so it shouldn't be a problem (I hope). 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci { 20662306a36Sopenharmony_ci /* 640x480 @ 60Hz interlaced (NTSC) */ 20762306a36Sopenharmony_ci "ntsc_640x480i", 60, 640, 480, TV_CLK, 38, 33, 0, 18, 146, 26, 20862306a36Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP 20962306a36Sopenharmony_ci }, { 21062306a36Sopenharmony_ci /* 640x240 @ 60Hz (NTSC) */ 21162306a36Sopenharmony_ci /* XXX: Broken! Don't use... */ 21262306a36Sopenharmony_ci "ntsc_640x240", 60, 640, 240, TV_CLK, 38, 33, 0, 0, 146, 22, 21362306a36Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_YWRAP 21462306a36Sopenharmony_ci }, { 21562306a36Sopenharmony_ci /* 640x480 @ 60hz (VGA) */ 21662306a36Sopenharmony_ci "vga_640x480", 60, 640, 480, VGA_CLK, 38, 33, 0, 18, 146, 26, 21762306a36Sopenharmony_ci 0, FB_VMODE_YWRAP 21862306a36Sopenharmony_ci }, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci#define NUM_TOTAL_MODES ARRAY_SIZE(pvr2_modedb) 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci#define DEFMODE_NTSC 0 22462306a36Sopenharmony_ci#define DEFMODE_PAL 0 22562306a36Sopenharmony_ci#define DEFMODE_VGA 2 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int defmode = DEFMODE_NTSC; 22862306a36Sopenharmony_cistatic char *mode_option = NULL; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic inline void pvr2fb_set_pal_type(unsigned int type) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *)fb_info->par; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci fb_writel(type, par->mmio_base + 0x108); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic inline void pvr2fb_set_pal_entry(struct pvr2fb_par *par, 23862306a36Sopenharmony_ci unsigned int regno, 23962306a36Sopenharmony_ci unsigned int val) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci fb_writel(val, par->mmio_base + 0x1000 + (4 * regno)); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int pvr2fb_blank(int blank, struct fb_info *info) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci do_blank = blank ? blank : -1; 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic inline unsigned long get_line_length(int xres_virtual, int bpp) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci return (unsigned long)((((xres_virtual*bpp)+31)&~31) >> 3); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic void set_color_bitfields(struct fb_var_screeninfo *var) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci switch (var->bits_per_pixel) { 25862306a36Sopenharmony_ci case 16: /* RGB 565 */ 25962306a36Sopenharmony_ci pvr2fb_set_pal_type(PAL_RGB565); 26062306a36Sopenharmony_ci var->red.offset = 11; var->red.length = 5; 26162306a36Sopenharmony_ci var->green.offset = 5; var->green.length = 6; 26262306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 5; 26362306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci case 24: /* RGB 888 */ 26662306a36Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 26762306a36Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 26862306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 26962306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case 32: /* ARGB 8888 */ 27262306a36Sopenharmony_ci pvr2fb_set_pal_type(PAL_ARGB8888); 27362306a36Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 27462306a36Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 27562306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 27662306a36Sopenharmony_ci var->transp.offset = 24; var->transp.length = 8; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int pvr2fb_setcolreg(unsigned int regno, unsigned int red, 28262306a36Sopenharmony_ci unsigned int green, unsigned int blue, 28362306a36Sopenharmony_ci unsigned int transp, struct fb_info *info) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; 28662306a36Sopenharmony_ci unsigned int tmp; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (regno > info->cmap.len) 28962306a36Sopenharmony_ci return 1; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * We only support the hardware palette for 16 and 32bpp. It's also 29362306a36Sopenharmony_ci * expected that the palette format has been set by the time we get 29462306a36Sopenharmony_ci * here, so we don't waste time setting it again. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 29762306a36Sopenharmony_ci case 16: /* RGB 565 */ 29862306a36Sopenharmony_ci tmp = (red & 0xf800) | 29962306a36Sopenharmony_ci ((green & 0xfc00) >> 5) | 30062306a36Sopenharmony_ci ((blue & 0xf800) >> 11); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci pvr2fb_set_pal_entry(par, regno, tmp); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case 24: /* RGB 888 */ 30562306a36Sopenharmony_ci red >>= 8; green >>= 8; blue >>= 8; 30662306a36Sopenharmony_ci tmp = (red << 16) | (green << 8) | blue; 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci case 32: /* ARGB 8888 */ 30962306a36Sopenharmony_ci red >>= 8; green >>= 8; blue >>= 8; 31062306a36Sopenharmony_ci tmp = (transp << 24) | (red << 16) | (green << 8) | blue; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci pvr2fb_set_pal_entry(par, regno, tmp); 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci default: 31562306a36Sopenharmony_ci pr_debug("Invalid bit depth %d?!?\n", info->var.bits_per_pixel); 31662306a36Sopenharmony_ci return 1; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (regno < 16) 32062306a36Sopenharmony_ci ((u32*)(info->pseudo_palette))[regno] = tmp; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * Determine the cable type and initialize the cable output format. Don't do 32762306a36Sopenharmony_ci * anything if the cable type has been overidden (via "cable:XX"). 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci#define PCTRA ((void __iomem *)0xff80002c) 33162306a36Sopenharmony_ci#define PDTRA ((void __iomem *)0xff800030) 33262306a36Sopenharmony_ci#define VOUTC ((void __iomem *)0xa0702c00) 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int pvr2_init_cable(void) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci if (cable_type < 0) { 33762306a36Sopenharmony_ci fb_writel((fb_readl(PCTRA) & 0xfff0ffff) | 0x000a0000, 33862306a36Sopenharmony_ci PCTRA); 33962306a36Sopenharmony_ci cable_type = (fb_readw(PDTRA) >> 8) & 3; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Now select the output format (either composite or other) */ 34362306a36Sopenharmony_ci /* XXX: Save the previous val first, as this reg is also AICA 34462306a36Sopenharmony_ci related */ 34562306a36Sopenharmony_ci if (cable_type == CT_COMPOSITE) 34662306a36Sopenharmony_ci fb_writel(3 << 8, VOUTC); 34762306a36Sopenharmony_ci else if (cable_type == CT_RGB) 34862306a36Sopenharmony_ci fb_writel(1 << 9, VOUTC); 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci fb_writel(0, VOUTC); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return cable_type; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int pvr2fb_set_par(struct fb_info *info) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; 35862306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 35962306a36Sopenharmony_ci unsigned long line_length; 36062306a36Sopenharmony_ci unsigned int vtotal; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* 36362306a36Sopenharmony_ci * XXX: It's possible that a user could use a VGA box, change the cable 36462306a36Sopenharmony_ci * type in hardware (i.e. switch from VGA<->composite), then change 36562306a36Sopenharmony_ci * modes (i.e. switching to another VT). If that happens we should 36662306a36Sopenharmony_ci * automagically change the output format to cope, but currently I 36762306a36Sopenharmony_ci * don't have a VGA box to make sure this works properly. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci cable_type = pvr2_init_cable(); 37062306a36Sopenharmony_ci if (cable_type == CT_VGA && video_output != VO_VGA) 37162306a36Sopenharmony_ci video_output = VO_VGA; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci var->vmode &= FB_VMODE_MASK; 37462306a36Sopenharmony_ci if (var->vmode & FB_VMODE_INTERLACED && video_output != VO_VGA) 37562306a36Sopenharmony_ci par->is_interlaced = 1; 37662306a36Sopenharmony_ci /* 37762306a36Sopenharmony_ci * XXX: Need to be more creative with this (i.e. allow doublecan for 37862306a36Sopenharmony_ci * PAL/NTSC output). 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci if (var->vmode & FB_VMODE_DOUBLE && video_output == VO_VGA) 38162306a36Sopenharmony_ci par->is_doublescan = 1; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci par->hsync_total = var->left_margin + var->xres + var->right_margin + 38462306a36Sopenharmony_ci var->hsync_len; 38562306a36Sopenharmony_ci par->vsync_total = var->upper_margin + var->yres + var->lower_margin + 38662306a36Sopenharmony_ci var->vsync_len; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (var->sync & FB_SYNC_BROADCAST) { 38962306a36Sopenharmony_ci vtotal = par->vsync_total; 39062306a36Sopenharmony_ci if (par->is_interlaced) 39162306a36Sopenharmony_ci vtotal /= 2; 39262306a36Sopenharmony_ci if (vtotal > (PAL_VTOTAL + NTSC_VTOTAL)/2) { 39362306a36Sopenharmony_ci /* XXX: Check for start values here... */ 39462306a36Sopenharmony_ci /* XXX: Check hardware for PAL-compatibility */ 39562306a36Sopenharmony_ci par->borderstart_h = 116; 39662306a36Sopenharmony_ci par->borderstart_v = 44; 39762306a36Sopenharmony_ci } else { 39862306a36Sopenharmony_ci /* NTSC video output */ 39962306a36Sopenharmony_ci par->borderstart_h = 126; 40062306a36Sopenharmony_ci par->borderstart_v = 18; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci } else { 40362306a36Sopenharmony_ci /* VGA mode */ 40462306a36Sopenharmony_ci /* XXX: What else needs to be checked? */ 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * XXX: We have a little freedom in VGA modes, what ranges 40762306a36Sopenharmony_ci * should be here (i.e. hsync/vsync totals, etc.)? 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci par->borderstart_h = 126; 41062306a36Sopenharmony_ci par->borderstart_v = 40; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Calculate the remainding offsets */ 41462306a36Sopenharmony_ci par->diwstart_h = par->borderstart_h + var->left_margin; 41562306a36Sopenharmony_ci par->diwstart_v = par->borderstart_v + var->upper_margin; 41662306a36Sopenharmony_ci par->borderstop_h = par->diwstart_h + var->xres + 41762306a36Sopenharmony_ci var->right_margin; 41862306a36Sopenharmony_ci par->borderstop_v = par->diwstart_v + var->yres + 41962306a36Sopenharmony_ci var->lower_margin; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (!par->is_interlaced) 42262306a36Sopenharmony_ci par->borderstop_v /= 2; 42362306a36Sopenharmony_ci if (info->var.xres < 640) 42462306a36Sopenharmony_ci par->is_lowres = 1; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); 42762306a36Sopenharmony_ci par->disp_start = info->fix.smem_start + (line_length * var->yoffset) * line_length; 42862306a36Sopenharmony_ci info->fix.line_length = line_length; 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; 43562306a36Sopenharmony_ci unsigned int vtotal, hsync_total; 43662306a36Sopenharmony_ci unsigned long line_length; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (var->pixclock != TV_CLK && var->pixclock != VGA_CLK) { 43962306a36Sopenharmony_ci pr_debug("Invalid pixclock value %d\n", var->pixclock); 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (var->xres < 320) 44462306a36Sopenharmony_ci var->xres = 320; 44562306a36Sopenharmony_ci if (var->yres < 240) 44662306a36Sopenharmony_ci var->yres = 240; 44762306a36Sopenharmony_ci if (var->xres_virtual < var->xres) 44862306a36Sopenharmony_ci var->xres_virtual = var->xres; 44962306a36Sopenharmony_ci if (var->yres_virtual < var->yres) 45062306a36Sopenharmony_ci var->yres_virtual = var->yres; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (var->bits_per_pixel <= 16) 45362306a36Sopenharmony_ci var->bits_per_pixel = 16; 45462306a36Sopenharmony_ci else if (var->bits_per_pixel <= 24) 45562306a36Sopenharmony_ci var->bits_per_pixel = 24; 45662306a36Sopenharmony_ci else if (var->bits_per_pixel <= 32) 45762306a36Sopenharmony_ci var->bits_per_pixel = 32; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci set_color_bitfields(var); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) { 46262306a36Sopenharmony_ci if (var->xoffset || var->yoffset >= var->yres_virtual) { 46362306a36Sopenharmony_ci var->xoffset = var->yoffset = 0; 46462306a36Sopenharmony_ci } else { 46562306a36Sopenharmony_ci if (var->xoffset > var->xres_virtual - var->xres || 46662306a36Sopenharmony_ci var->yoffset > var->yres_virtual - var->yres) 46762306a36Sopenharmony_ci var->xoffset = var->yoffset = 0; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci } else { 47062306a36Sopenharmony_ci var->xoffset = var->yoffset = 0; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * XXX: Need to be more creative with this (i.e. allow doublecan for 47562306a36Sopenharmony_ci * PAL/NTSC output). 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci if (var->yres < 480 && video_output == VO_VGA) 47862306a36Sopenharmony_ci var->vmode |= FB_VMODE_DOUBLE; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (video_output != VO_VGA) { 48162306a36Sopenharmony_ci var->sync |= FB_SYNC_BROADCAST; 48262306a36Sopenharmony_ci var->vmode |= FB_VMODE_INTERLACED; 48362306a36Sopenharmony_ci } else { 48462306a36Sopenharmony_ci var->sync &= ~FB_SYNC_BROADCAST; 48562306a36Sopenharmony_ci var->vmode &= ~FB_VMODE_INTERLACED; 48662306a36Sopenharmony_ci var->vmode |= FB_VMODE_NONINTERLACED; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_TEST) { 49062306a36Sopenharmony_ci var->right_margin = par->borderstop_h - 49162306a36Sopenharmony_ci (par->diwstart_h + var->xres); 49262306a36Sopenharmony_ci var->left_margin = par->diwstart_h - par->borderstart_h; 49362306a36Sopenharmony_ci var->hsync_len = par->borderstart_h + 49462306a36Sopenharmony_ci (par->hsync_total - par->borderstop_h); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci var->upper_margin = par->diwstart_v - par->borderstart_v; 49762306a36Sopenharmony_ci var->lower_margin = par->borderstop_v - 49862306a36Sopenharmony_ci (par->diwstart_v + var->yres); 49962306a36Sopenharmony_ci var->vsync_len = par->borderstop_v + 50062306a36Sopenharmony_ci (par->vsync_total - par->borderstop_v); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci hsync_total = var->left_margin + var->xres + var->right_margin + 50462306a36Sopenharmony_ci var->hsync_len; 50562306a36Sopenharmony_ci vtotal = var->upper_margin + var->yres + var->lower_margin + 50662306a36Sopenharmony_ci var->vsync_len; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (var->sync & FB_SYNC_BROADCAST) { 50962306a36Sopenharmony_ci if (var->vmode & FB_VMODE_INTERLACED) 51062306a36Sopenharmony_ci vtotal /= 2; 51162306a36Sopenharmony_ci if (vtotal > (PAL_VTOTAL + NTSC_VTOTAL)/2) { 51262306a36Sopenharmony_ci /* PAL video output */ 51362306a36Sopenharmony_ci /* XXX: Should be using a range here ... ? */ 51462306a36Sopenharmony_ci if (hsync_total != PAL_HTOTAL) { 51562306a36Sopenharmony_ci pr_debug("invalid hsync total for PAL\n"); 51662306a36Sopenharmony_ci return -EINVAL; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci } else { 51962306a36Sopenharmony_ci /* NTSC video output */ 52062306a36Sopenharmony_ci if (hsync_total != NTSC_HTOTAL) { 52162306a36Sopenharmony_ci pr_debug("invalid hsync total for NTSC\n"); 52262306a36Sopenharmony_ci return -EINVAL; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Check memory sizes */ 52862306a36Sopenharmony_ci line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); 52962306a36Sopenharmony_ci if (line_length * var->yres_virtual > info->fix.smem_len) 53062306a36Sopenharmony_ci return -ENOMEM; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void pvr2_update_display(struct fb_info *info) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *) info->par; 53862306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Update the start address of the display image */ 54162306a36Sopenharmony_ci fb_writel(par->disp_start, DISP_DIWADDRL); 54262306a36Sopenharmony_ci fb_writel(par->disp_start + 54362306a36Sopenharmony_ci get_line_length(var->xoffset+var->xres, var->bits_per_pixel), 54462306a36Sopenharmony_ci DISP_DIWADDRS); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/* 54862306a36Sopenharmony_ci * Initialize the video mode. Currently, the 16bpp and 24bpp modes aren't 54962306a36Sopenharmony_ci * very stable. It's probably due to the fact that a lot of the 2D video 55062306a36Sopenharmony_ci * registers are still undocumented. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic void pvr2_init_display(struct fb_info *info) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *) info->par; 55662306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 55762306a36Sopenharmony_ci unsigned int diw_height, diw_width, diw_modulo = 1; 55862306a36Sopenharmony_ci unsigned int bytesperpixel = var->bits_per_pixel >> 3; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* hsync and vsync totals */ 56162306a36Sopenharmony_ci fb_writel((par->vsync_total << 16) | par->hsync_total, DISP_SYNCSIZE); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* column height, modulo, row width */ 56462306a36Sopenharmony_ci /* since we're "panning" within vram, we need to offset things based 56562306a36Sopenharmony_ci * on the offset from the virtual x start to our real gfx. */ 56662306a36Sopenharmony_ci if (video_output != VO_VGA && par->is_interlaced) 56762306a36Sopenharmony_ci diw_modulo += info->fix.line_length / 4; 56862306a36Sopenharmony_ci diw_height = (par->is_interlaced ? var->yres / 2 : var->yres); 56962306a36Sopenharmony_ci diw_width = get_line_length(var->xres, var->bits_per_pixel) / 4; 57062306a36Sopenharmony_ci fb_writel((diw_modulo << 20) | (--diw_height << 10) | --diw_width, 57162306a36Sopenharmony_ci DISP_DIWSIZE); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* display address, long and short fields */ 57462306a36Sopenharmony_ci fb_writel(par->disp_start, DISP_DIWADDRL); 57562306a36Sopenharmony_ci fb_writel(par->disp_start + 57662306a36Sopenharmony_ci get_line_length(var->xoffset+var->xres, var->bits_per_pixel), 57762306a36Sopenharmony_ci DISP_DIWADDRS); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* border horizontal, border vertical, border color */ 58062306a36Sopenharmony_ci fb_writel((par->borderstart_h << 16) | par->borderstop_h, DISP_BRDRHORZ); 58162306a36Sopenharmony_ci fb_writel((par->borderstart_v << 16) | par->borderstop_v, DISP_BRDRVERT); 58262306a36Sopenharmony_ci fb_writel(0, DISP_BRDRCOLR); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* display window start position */ 58562306a36Sopenharmony_ci fb_writel(par->diwstart_h, DISP_DIWHSTRT); 58662306a36Sopenharmony_ci fb_writel((par->diwstart_v << 16) | par->diwstart_v, DISP_DIWVSTRT); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* misc. settings */ 58962306a36Sopenharmony_ci fb_writel((0x16 << 16) | par->is_lowres, DISP_DIWCONF); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* clock doubler (for VGA), scan doubler, display enable */ 59262306a36Sopenharmony_ci fb_writel(((video_output == VO_VGA) << 23) | 59362306a36Sopenharmony_ci (par->is_doublescan << 1) | 1, DISP_DIWMODE); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* bits per pixel */ 59662306a36Sopenharmony_ci fb_writel(fb_readl(DISP_DIWMODE) | (--bytesperpixel << 2), DISP_DIWMODE); 59762306a36Sopenharmony_ci fb_writel(bytesperpixel << 2, DISP_PIXDEPTH); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* video enable, color sync, interlace, 60062306a36Sopenharmony_ci * hsync and vsync polarity (currently unused) */ 60162306a36Sopenharmony_ci fb_writel(0x100 | ((par->is_interlaced /*|4*/) << 4), DISP_SYNCCONF); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/* Simulate blanking by making the border cover the entire screen */ 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci#define BLANK_BIT (1<<3) 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic void pvr2_do_blank(void) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct pvr2fb_par *par = currentpar; 61162306a36Sopenharmony_ci unsigned long diwconf; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci diwconf = fb_readl(DISP_DIWCONF); 61462306a36Sopenharmony_ci if (do_blank > 0) 61562306a36Sopenharmony_ci fb_writel(diwconf | BLANK_BIT, DISP_DIWCONF); 61662306a36Sopenharmony_ci else 61762306a36Sopenharmony_ci fb_writel(diwconf & ~BLANK_BIT, DISP_DIWCONF); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci is_blanked = do_blank > 0 ? do_blank : 0; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic irqreturn_t __maybe_unused pvr2fb_interrupt(int irq, void *dev_id) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct fb_info *info = dev_id; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (do_vmode_pan || do_vmode_full) 62762306a36Sopenharmony_ci pvr2_update_display(info); 62862306a36Sopenharmony_ci if (do_vmode_full) 62962306a36Sopenharmony_ci pvr2_init_display(info); 63062306a36Sopenharmony_ci if (do_vmode_pan) 63162306a36Sopenharmony_ci do_vmode_pan = 0; 63262306a36Sopenharmony_ci if (do_vmode_full) 63362306a36Sopenharmony_ci do_vmode_full = 0; 63462306a36Sopenharmony_ci if (do_blank) { 63562306a36Sopenharmony_ci pvr2_do_blank(); 63662306a36Sopenharmony_ci do_blank = 0; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci return IRQ_HANDLED; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 64262306a36Sopenharmony_cistatic ssize_t pvr2fb_write(struct fb_info *info, const char *buf, 64362306a36Sopenharmony_ci size_t count, loff_t *ppos) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci unsigned long dst, start, end, len; 64662306a36Sopenharmony_ci unsigned int nr_pages; 64762306a36Sopenharmony_ci struct page **pages; 64862306a36Sopenharmony_ci int ret, i; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (!info->screen_base) 65162306a36Sopenharmony_ci return -ENODEV; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci nr_pages = (count + PAGE_SIZE - 1) >> PAGE_SHIFT; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_KERNEL); 65662306a36Sopenharmony_ci if (!pages) 65762306a36Sopenharmony_ci return -ENOMEM; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci ret = pin_user_pages_fast((unsigned long)buf, nr_pages, FOLL_WRITE, pages); 66062306a36Sopenharmony_ci if (ret < nr_pages) { 66162306a36Sopenharmony_ci if (ret < 0) { 66262306a36Sopenharmony_ci /* 66362306a36Sopenharmony_ci * Clamp the unsigned nr_pages to zero so that the 66462306a36Sopenharmony_ci * error handling works. And leave ret at whatever 66562306a36Sopenharmony_ci * -errno value was returned from GUP. 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ci nr_pages = 0; 66862306a36Sopenharmony_ci } else { 66962306a36Sopenharmony_ci nr_pages = ret; 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * Use -EINVAL to represent a mildly desperate guess at 67262306a36Sopenharmony_ci * why we got fewer pages (maybe even zero pages) than 67362306a36Sopenharmony_ci * requested. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci ret = -EINVAL; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci goto out_unmap; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci dma_configure_channel(shdma, 0x12c1); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci dst = (unsigned long)fb_info->screen_base + *ppos; 68362306a36Sopenharmony_ci start = (unsigned long)page_address(pages[0]); 68462306a36Sopenharmony_ci end = (unsigned long)page_address(pages[nr_pages]); 68562306a36Sopenharmony_ci len = nr_pages << PAGE_SHIFT; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Half-assed contig check */ 68862306a36Sopenharmony_ci if (start + len == end) { 68962306a36Sopenharmony_ci /* As we do this in one shot, it's either all or nothing.. */ 69062306a36Sopenharmony_ci if ((*ppos + len) > fb_info->fix.smem_len) { 69162306a36Sopenharmony_ci ret = -ENOSPC; 69262306a36Sopenharmony_ci goto out_unmap; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci dma_write(shdma, start, 0, len); 69662306a36Sopenharmony_ci dma_write(pvr2dma, 0, dst, len); 69762306a36Sopenharmony_ci dma_wait_for_completion(pvr2dma); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci goto out; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Not contiguous, writeout per-page instead.. */ 70362306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++, dst += PAGE_SIZE) { 70462306a36Sopenharmony_ci if ((*ppos + (i << PAGE_SHIFT)) > fb_info->fix.smem_len) { 70562306a36Sopenharmony_ci ret = -ENOSPC; 70662306a36Sopenharmony_ci goto out_unmap; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci dma_write_page(shdma, (unsigned long)page_address(pages[i]), 0); 71062306a36Sopenharmony_ci dma_write_page(pvr2dma, 0, dst); 71162306a36Sopenharmony_ci dma_wait_for_completion(pvr2dma); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ciout: 71562306a36Sopenharmony_ci *ppos += count; 71662306a36Sopenharmony_ci ret = count; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ciout_unmap: 71962306a36Sopenharmony_ci unpin_user_pages(pages, nr_pages); 72062306a36Sopenharmony_ci kfree(pages); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return ret; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci#endif /* CONFIG_PVR2_DMA */ 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic const struct fb_ops pvr2fb_ops = { 72762306a36Sopenharmony_ci .owner = THIS_MODULE, 72862306a36Sopenharmony_ci .fb_setcolreg = pvr2fb_setcolreg, 72962306a36Sopenharmony_ci .fb_blank = pvr2fb_blank, 73062306a36Sopenharmony_ci .fb_check_var = pvr2fb_check_var, 73162306a36Sopenharmony_ci .fb_set_par = pvr2fb_set_par, 73262306a36Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 73362306a36Sopenharmony_ci .fb_write = pvr2fb_write, 73462306a36Sopenharmony_ci#endif 73562306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 73662306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 73762306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 73862306a36Sopenharmony_ci}; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci#ifndef MODULE 74162306a36Sopenharmony_cistatic int pvr2_get_param_val(const struct pvr2_params *p, const char *s, 74262306a36Sopenharmony_ci int size) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci int i; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci for (i = 0; i < size; i++) { 74762306a36Sopenharmony_ci if (!strncasecmp(p[i].name, s, strlen(s))) 74862306a36Sopenharmony_ci return p[i].val; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci return -1; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci#endif 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic char *pvr2_get_param_name(const struct pvr2_params *p, int val, 75562306a36Sopenharmony_ci int size) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int i; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci for (i = 0; i < size; i++) { 76062306a36Sopenharmony_ci if (p[i].val == val) 76162306a36Sopenharmony_ci return p[i].name; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci return NULL; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci/** 76762306a36Sopenharmony_ci * pvr2fb_common_init 76862306a36Sopenharmony_ci * 76962306a36Sopenharmony_ci * Common init code for the PVR2 chips. 77062306a36Sopenharmony_ci * 77162306a36Sopenharmony_ci * This mostly takes care of the common aspects of the fb setup and 77262306a36Sopenharmony_ci * registration. It's expected that the board-specific init code has 77362306a36Sopenharmony_ci * already setup pvr2_fix with something meaningful at this point. 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * Device info reporting is also done here, as well as picking a sane 77662306a36Sopenharmony_ci * default from the modedb. For board-specific modelines, simply define 77762306a36Sopenharmony_ci * a per-board modedb. 77862306a36Sopenharmony_ci * 77962306a36Sopenharmony_ci * Also worth noting is that the cable and video output types are likely 78062306a36Sopenharmony_ci * always going to be VGA for the PCI-based PVR2 boards, but we leave this 78162306a36Sopenharmony_ci * in for flexibility anyways. Who knows, maybe someone has tv-out on a 78262306a36Sopenharmony_ci * PCI-based version of these things ;-) 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_cistatic int __maybe_unused pvr2fb_common_init(void) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct pvr2fb_par *par = currentpar; 78762306a36Sopenharmony_ci unsigned long modememused, rev; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci fb_info->screen_base = ioremap(pvr2_fix.smem_start, 79062306a36Sopenharmony_ci pvr2_fix.smem_len); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (!fb_info->screen_base) { 79362306a36Sopenharmony_ci printk(KERN_ERR "pvr2fb: Failed to remap smem space\n"); 79462306a36Sopenharmony_ci goto out_err; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci par->mmio_base = ioremap(pvr2_fix.mmio_start, 79862306a36Sopenharmony_ci pvr2_fix.mmio_len); 79962306a36Sopenharmony_ci if (!par->mmio_base) { 80062306a36Sopenharmony_ci printk(KERN_ERR "pvr2fb: Failed to remap mmio space\n"); 80162306a36Sopenharmony_ci goto out_err; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci fb_memset_io(fb_info->screen_base, 0, pvr2_fix.smem_len); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci pvr2_fix.ypanstep = nopan ? 0 : 1; 80762306a36Sopenharmony_ci pvr2_fix.ywrapstep = nowrap ? 0 : 1; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci fb_info->fbops = &pvr2fb_ops; 81062306a36Sopenharmony_ci fb_info->fix = pvr2_fix; 81162306a36Sopenharmony_ci fb_info->par = currentpar; 81262306a36Sopenharmony_ci fb_info->pseudo_palette = currentpar->palette; 81362306a36Sopenharmony_ci fb_info->flags = FBINFO_HWACCEL_YPAN; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (video_output == VO_VGA) 81662306a36Sopenharmony_ci defmode = DEFMODE_VGA; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (!mode_option) 81962306a36Sopenharmony_ci mode_option = "640x480@60"; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (!fb_find_mode(&fb_info->var, fb_info, mode_option, pvr2_modedb, 82262306a36Sopenharmony_ci NUM_TOTAL_MODES, &pvr2_modedb[defmode], 16)) 82362306a36Sopenharmony_ci fb_info->var = pvr2_var; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci fb_alloc_cmap(&fb_info->cmap, 256, 0); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (register_framebuffer(fb_info) < 0) 82862306a36Sopenharmony_ci goto out_err; 82962306a36Sopenharmony_ci /*Must write PIXDEPTH to register before anything is displayed - so force init */ 83062306a36Sopenharmony_ci pvr2_init_display(fb_info); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci modememused = get_line_length(fb_info->var.xres_virtual, 83362306a36Sopenharmony_ci fb_info->var.bits_per_pixel); 83462306a36Sopenharmony_ci modememused *= fb_info->var.yres_virtual; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci rev = fb_readl(par->mmio_base + 0x04); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci fb_info(fb_info, "%s (rev %ld.%ld) frame buffer device, using %ldk/%ldk of video memory\n", 83962306a36Sopenharmony_ci fb_info->fix.id, (rev >> 4) & 0x0f, rev & 0x0f, 84062306a36Sopenharmony_ci modememused >> 10, 84162306a36Sopenharmony_ci (unsigned long)(fb_info->fix.smem_len >> 10)); 84262306a36Sopenharmony_ci fb_info(fb_info, "Mode %dx%d-%d pitch = %ld cable: %s video output: %s\n", 84362306a36Sopenharmony_ci fb_info->var.xres, fb_info->var.yres, 84462306a36Sopenharmony_ci fb_info->var.bits_per_pixel, 84562306a36Sopenharmony_ci get_line_length(fb_info->var.xres, fb_info->var.bits_per_pixel), 84662306a36Sopenharmony_ci pvr2_get_param_name(cables, cable_type, 3), 84762306a36Sopenharmony_ci pvr2_get_param_name(outputs, video_output, 3)); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci#ifdef CONFIG_SH_STORE_QUEUES 85062306a36Sopenharmony_ci fb_notice(fb_info, "registering with SQ API\n"); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci pvr2fb_map = sq_remap(fb_info->fix.smem_start, fb_info->fix.smem_len, 85362306a36Sopenharmony_ci fb_info->fix.id, PAGE_SHARED); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci fb_notice(fb_info, "Mapped video memory to SQ addr 0x%lx\n", 85662306a36Sopenharmony_ci pvr2fb_map); 85762306a36Sopenharmony_ci#endif 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ciout_err: 86262306a36Sopenharmony_ci if (fb_info->screen_base) 86362306a36Sopenharmony_ci iounmap(fb_info->screen_base); 86462306a36Sopenharmony_ci if (par->mmio_base) 86562306a36Sopenharmony_ci iounmap(par->mmio_base); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return -ENXIO; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci#ifdef CONFIG_SH_DREAMCAST 87162306a36Sopenharmony_cistatic int __init pvr2fb_dc_init(void) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci if (!mach_is_dreamcast()) 87462306a36Sopenharmony_ci return -ENXIO; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* Make a guess at the monitor based on the attached cable */ 87762306a36Sopenharmony_ci if (pvr2_init_cable() == CT_VGA) { 87862306a36Sopenharmony_ci fb_info->monspecs.hfmin = 30000; 87962306a36Sopenharmony_ci fb_info->monspecs.hfmax = 70000; 88062306a36Sopenharmony_ci fb_info->monspecs.vfmin = 60; 88162306a36Sopenharmony_ci fb_info->monspecs.vfmax = 60; 88262306a36Sopenharmony_ci } else { 88362306a36Sopenharmony_ci /* Not VGA, using a TV (taken from acornfb) */ 88462306a36Sopenharmony_ci fb_info->monspecs.hfmin = 15469; 88562306a36Sopenharmony_ci fb_info->monspecs.hfmax = 15781; 88662306a36Sopenharmony_ci fb_info->monspecs.vfmin = 49; 88762306a36Sopenharmony_ci fb_info->monspecs.vfmax = 51; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* 89162306a36Sopenharmony_ci * XXX: This needs to pull default video output via BIOS or other means 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_ci if (video_output < 0) { 89462306a36Sopenharmony_ci if (cable_type == CT_VGA) { 89562306a36Sopenharmony_ci video_output = VO_VGA; 89662306a36Sopenharmony_ci } else { 89762306a36Sopenharmony_ci video_output = VO_NTSC; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* 90262306a36Sopenharmony_ci * Nothing exciting about the DC PVR2 .. only a measly 8MiB. 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_ci pvr2_fix.smem_start = 0xa5000000; /* RAM starts here */ 90562306a36Sopenharmony_ci pvr2_fix.smem_len = 8 << 20; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci pvr2_fix.mmio_start = 0xa05f8000; /* registers start here */ 90862306a36Sopenharmony_ci pvr2_fix.mmio_len = 0x2000; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (request_irq(HW_EVENT_VSYNC, pvr2fb_interrupt, IRQF_SHARED, 91162306a36Sopenharmony_ci "pvr2 VBL handler", fb_info)) { 91262306a36Sopenharmony_ci return -EBUSY; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 91662306a36Sopenharmony_ci if (request_dma(pvr2dma, "pvr2") != 0) { 91762306a36Sopenharmony_ci free_irq(HW_EVENT_VSYNC, fb_info); 91862306a36Sopenharmony_ci return -EBUSY; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci#endif 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return pvr2fb_common_init(); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic void pvr2fb_dc_exit(void) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci if (fb_info->screen_base) { 92862306a36Sopenharmony_ci iounmap(fb_info->screen_base); 92962306a36Sopenharmony_ci fb_info->screen_base = NULL; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci if (currentpar->mmio_base) { 93262306a36Sopenharmony_ci iounmap(currentpar->mmio_base); 93362306a36Sopenharmony_ci currentpar->mmio_base = NULL; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci free_irq(HW_EVENT_VSYNC, fb_info); 93762306a36Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 93862306a36Sopenharmony_ci free_dma(pvr2dma); 93962306a36Sopenharmony_ci#endif 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci#endif /* CONFIG_SH_DREAMCAST */ 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci#ifdef CONFIG_PCI 94462306a36Sopenharmony_cistatic int pvr2fb_pci_probe(struct pci_dev *pdev, 94562306a36Sopenharmony_ci const struct pci_device_id *ent) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci int ret; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci ret = aperture_remove_conflicting_pci_devices(pdev, "pvrfb"); 95062306a36Sopenharmony_ci if (ret) 95162306a36Sopenharmony_ci return ret; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ret = pci_enable_device(pdev); 95462306a36Sopenharmony_ci if (ret) { 95562306a36Sopenharmony_ci printk(KERN_ERR "pvr2fb: PCI enable failed\n"); 95662306a36Sopenharmony_ci return ret; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci ret = pci_request_regions(pdev, "pvr2fb"); 96062306a36Sopenharmony_ci if (ret) { 96162306a36Sopenharmony_ci printk(KERN_ERR "pvr2fb: PCI request regions failed\n"); 96262306a36Sopenharmony_ci return ret; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* 96662306a36Sopenharmony_ci * Slightly more exciting than the DC PVR2 .. 16MiB! 96762306a36Sopenharmony_ci */ 96862306a36Sopenharmony_ci pvr2_fix.smem_start = pci_resource_start(pdev, 0); 96962306a36Sopenharmony_ci pvr2_fix.smem_len = pci_resource_len(pdev, 0); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci pvr2_fix.mmio_start = pci_resource_start(pdev, 1); 97262306a36Sopenharmony_ci pvr2_fix.mmio_len = pci_resource_len(pdev, 1); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci fb_info->device = &pdev->dev; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci return pvr2fb_common_init(); 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistatic void pvr2fb_pci_remove(struct pci_dev *pdev) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci if (fb_info->screen_base) { 98262306a36Sopenharmony_ci iounmap(fb_info->screen_base); 98362306a36Sopenharmony_ci fb_info->screen_base = NULL; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci if (currentpar->mmio_base) { 98662306a36Sopenharmony_ci iounmap(currentpar->mmio_base); 98762306a36Sopenharmony_ci currentpar->mmio_base = NULL; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci pci_release_regions(pdev); 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic const struct pci_device_id pvr2fb_pci_tbl[] = { 99462306a36Sopenharmony_ci { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NEON250, 99562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 99662306a36Sopenharmony_ci { 0, }, 99762306a36Sopenharmony_ci}; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pvr2fb_pci_tbl); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic struct pci_driver pvr2fb_pci_driver = { 100262306a36Sopenharmony_ci .name = "pvr2fb", 100362306a36Sopenharmony_ci .id_table = pvr2fb_pci_tbl, 100462306a36Sopenharmony_ci .probe = pvr2fb_pci_probe, 100562306a36Sopenharmony_ci .remove = pvr2fb_pci_remove, 100662306a36Sopenharmony_ci}; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic int __init pvr2fb_pci_init(void) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci return pci_register_driver(&pvr2fb_pci_driver); 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic void pvr2fb_pci_exit(void) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci pci_unregister_driver(&pvr2fb_pci_driver); 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci#endif /* CONFIG_PCI */ 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/* 102062306a36Sopenharmony_ci * Parse command arguments. Supported arguments are: 102162306a36Sopenharmony_ci * inverse Use inverse color maps 102262306a36Sopenharmony_ci * cable:composite|rgb|vga Override the video cable type 102362306a36Sopenharmony_ci * output:NTSC|PAL|VGA Override the video output format 102462306a36Sopenharmony_ci * 102562306a36Sopenharmony_ci * <xres>x<yres>[-<bpp>][@<refresh>] or, 102662306a36Sopenharmony_ci * <name>[-<bpp>][@<refresh>] Startup using this video mode 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci#ifndef MODULE 103062306a36Sopenharmony_cistatic int __init pvr2fb_setup(char *options) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci char *this_opt; 103362306a36Sopenharmony_ci char cable_arg[80]; 103462306a36Sopenharmony_ci char output_arg[80]; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (!options || !*options) 103762306a36Sopenharmony_ci return 0; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci cable_arg[0] = output_arg[0] = 0; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci while ((this_opt = strsep(&options, ","))) { 104262306a36Sopenharmony_ci if (!*this_opt) 104362306a36Sopenharmony_ci continue; 104462306a36Sopenharmony_ci if (!strcmp(this_opt, "inverse")) { 104562306a36Sopenharmony_ci fb_invert_cmaps(); 104662306a36Sopenharmony_ci } else if (!strncmp(this_opt, "cable:", 6)) { 104762306a36Sopenharmony_ci strcpy(cable_arg, this_opt + 6); 104862306a36Sopenharmony_ci } else if (!strncmp(this_opt, "output:", 7)) { 104962306a36Sopenharmony_ci strcpy(output_arg, this_opt + 7); 105062306a36Sopenharmony_ci } else if (!strncmp(this_opt, "nopan", 5)) { 105162306a36Sopenharmony_ci nopan = 1; 105262306a36Sopenharmony_ci } else if (!strncmp(this_opt, "nowrap", 6)) { 105362306a36Sopenharmony_ci nowrap = 1; 105462306a36Sopenharmony_ci } else { 105562306a36Sopenharmony_ci mode_option = this_opt; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (*cable_arg) 106062306a36Sopenharmony_ci cable_type = pvr2_get_param_val(cables, cable_arg, 3); 106162306a36Sopenharmony_ci if (*output_arg) 106262306a36Sopenharmony_ci video_output = pvr2_get_param_val(outputs, output_arg, 3); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci return 0; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci#endif 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic struct pvr2_board { 106962306a36Sopenharmony_ci int (*init)(void); 107062306a36Sopenharmony_ci void (*exit)(void); 107162306a36Sopenharmony_ci char name[16]; 107262306a36Sopenharmony_ci} board_driver[] __refdata = { 107362306a36Sopenharmony_ci#ifdef CONFIG_SH_DREAMCAST 107462306a36Sopenharmony_ci { pvr2fb_dc_init, pvr2fb_dc_exit, "Sega DC PVR2" }, 107562306a36Sopenharmony_ci#endif 107662306a36Sopenharmony_ci#ifdef CONFIG_PCI 107762306a36Sopenharmony_ci { pvr2fb_pci_init, pvr2fb_pci_exit, "PCI PVR2" }, 107862306a36Sopenharmony_ci#endif 107962306a36Sopenharmony_ci { 0, }, 108062306a36Sopenharmony_ci}; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cistatic int __init pvr2fb_init(void) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci int i, ret = -ENODEV; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci#ifndef MODULE 108762306a36Sopenharmony_ci char *option = NULL; 108862306a36Sopenharmony_ci#endif 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (fb_modesetting_disabled("pvr2fb")) 109162306a36Sopenharmony_ci return -ENODEV; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci#ifndef MODULE 109462306a36Sopenharmony_ci if (fb_get_options("pvr2fb", &option)) 109562306a36Sopenharmony_ci return -ENODEV; 109662306a36Sopenharmony_ci pvr2fb_setup(option); 109762306a36Sopenharmony_ci#endif 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci fb_info = framebuffer_alloc(sizeof(struct pvr2fb_par), NULL); 110062306a36Sopenharmony_ci if (!fb_info) 110162306a36Sopenharmony_ci return -ENOMEM; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci currentpar = fb_info->par; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(board_driver); i++) { 110662306a36Sopenharmony_ci struct pvr2_board *pvr_board = board_driver + i; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (!pvr_board->init) 110962306a36Sopenharmony_ci continue; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci ret = pvr_board->init(); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (ret != 0) { 111462306a36Sopenharmony_ci printk(KERN_ERR "pvr2fb: Failed init of %s device\n", 111562306a36Sopenharmony_ci pvr_board->name); 111662306a36Sopenharmony_ci framebuffer_release(fb_info); 111762306a36Sopenharmony_ci break; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci return ret; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic void __exit pvr2fb_exit(void) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci int i; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(board_driver); i++) { 112962306a36Sopenharmony_ci struct pvr2_board *pvr_board = board_driver + i; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (pvr_board->exit) 113262306a36Sopenharmony_ci pvr_board->exit(); 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci#ifdef CONFIG_SH_STORE_QUEUES 113662306a36Sopenharmony_ci sq_unmap(pvr2fb_map); 113762306a36Sopenharmony_ci#endif 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci unregister_framebuffer(fb_info); 114062306a36Sopenharmony_ci framebuffer_release(fb_info); 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cimodule_init(pvr2fb_init); 114462306a36Sopenharmony_cimodule_exit(pvr2fb_exit); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ciMODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>"); 114762306a36Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for NEC PowerVR 2 based graphics boards"); 114862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1149